Get it on Google Play


Wm뮤 :: 'Unity/C#' 카테고리의 글 목록 (7 Page)

블로그 이미지
가끔 그림그리거나 3D모델링하거나
취미로 로봇만드는
퇴직한 전자과 게임프로그래머
2020.3.48f1 , 2022.3.6f1 주로 사용
모카쨩
@Ahzkwid

Recent Comment

Archive


2020. 7. 20. 11:34 Unity/C#

응가 싸는 패턴이다

싱글톤은 유용하지만 유니티에서 지원이 빈약하다

써야한다면 static final로 한번만 생성되게 막자

'Unity > C#' 카테고리의 다른 글

OnGUI  (0) 2020.07.27
유니티 버그, 에러, 오류모음  (0) 2020.07.20
자주 쓰는 유니티 코드 모음  (0) 2020.07.09
posted by 모카쨩
2020. 7. 20. 11:03 Unity/C#

 

 

2019.4.0f1버전의 유니티의

안드로이드 update에서 Time.deltaTime이 제대로 안 먹힐때가 있다. 

    float Timetime_previous = 0; 
    void Update()
    {
        Debug.Log(Time.time - Timetime_previous);
        Timetime_previous = Time.time;
    }

이걸로 해결 가능하다.

 

후일담)

2019.4.31f1에서도 동일한 증상이 있고

첫프레임에서 발생하는거 보니 델타타임으로 중요루틴 굴리면 안 될듯

------------------------------------------------------------------------------

 

 

Image 컴포넌트를 인풋으로 받을수 없을때

public Image img_comp;

이렇게 쳐도 안되면 

using UnityEngine.UIElements로 되어있기 때문

using UnityEngine.UI가 맞음

 

------------------------------------------------------------------------------

 

 

GetCharacterInfo의 characterInfo.advance를 못 받아오는 문제

콜백을 걸어서 사용해야 한다

    void Start()
    {
        Font.textureRebuilt += GetTextWidth;
    }
    void GetTextWidth(Font font)
    {
        CharacterInfo characterInfo;
        char c = 'a';
        if (font.GetCharacterInfo(c, out characterInfo, font.fontSize, FontStyle.Normal))
        {
            Debug.Log($"c: {c}, characterInfo.advance: {characterInfo.advance}");
            Debug.Log($"c: {c}, characterInfo.bearing: {characterInfo.bearing}");

            Debug.Log($"GetCharacterInfo Success, textUI.fontSize:{font.fontSize}");
        }
        else
        {
            Debug.Log($"GetCharacterInfo Fail, textUI.fontSize:{font.fontSize}");
        }
    }

 

이렇게 붙이면 된다. 결국 그래서 별로 쓸모는 없음

 

 

 

 

 

 

------------------------------------------------------------------------------

UGUI 월드스페이스에서 동작 안할경우

 

 

------------------------------------------------------------------------------

unrecognized identifier 'UNITY_FOG_COORDS'

 

 

상단에

#include "Lighting.cginc"

를 넣어주면 된다

 

 

 

------------------------------------------------------------------------------

CommandInvokationFailure: Unable to install APK to device. Please make sure the Android SDK is installed and is properly configured in the Editor. See the Console for more details.

 

키스토어가 바뀌어버린것이거나 Bundle Version Code가 더 낮은걸 올려버림

 

------------------------------------------------------------------------------

 

 

안드로이드에서만 30프레임으로 고정될때



매낮으로 설정됨

둘중하나를 선택

 

 

------------------------------------------------------------------------------

에셋번들로 빌드시 3D모델일부(스킨드 메쉬 렌더러)가 보이지 않는 현상

 

3D모델들을 Resources폴더 하위에 넣으면 된다

 

------------------------------------------------------------------------------

에셋번들로 빌드시 믹서가 크래시 나는 현상

 

믹서들을 Resources폴더 하위에 넣으면 된다

 

------------------------------------------------------------------------------

빌드시 동영상이 보이지 않을경우

 

동영상을 Resources폴더 하위에 넣으면 된다

 

 

 

------------------------------------------------------------------------------

안드로이드 이미지 로드시 튕길경우

 

jpg로 넣어라


png는 TextureFormat.ETC2_RGBA8Crunched 포맷만 되었던가 이것만 안되었던가 둘중하나인데
이걸 구분하기 힘드니까 jpg써라

 

 

------------------------------------------------------------------------------

 

애니메이션이 초기화가 안 되고 자꾸 덧씌우기로 동작할경우

 

 

체크해라

 

 

------------------------------------------------------------------------------

압축풀때 java.lang.IllegalArgumentExcetion 뜨면서 안될때

파일명들에 한글이랑 - , ` 제거하고 영어랑 띄어쓰기만 써라

 

 

------------------------------------------------------------------------------

애니메이션에 마테리얼 프로퍼티값을 등록할수 없을때

 

Image같은 UI컴포넌트는 원래 안 되고, 메쉬렌더러같이 렌더러계열만 가능

 

------------------------------------------------------------------------------

2019.4.1f1에서 에셋번들로 실행시 AudioSouce의 PlayDelayed (float)함수가 버튼으로 실행을 넘겨받을경우 작동이 안됨

 

 

 

 

------------------------------------------------------------------------------

ArgumentException: Mesh can not have more than 65000 vertices
UnityEngine.UI.VertexHelper.FillMesh (UnityEngine.Mesh mesh) (at 

 

아웃라인 컴포넌트 오류이다

텍스트 컴포넌트에 달았는데 텍스트가 너무 길면 발생한다

 

 

------------------------------------------------------------------------------

iOS에서 플러그인 익스포트가 안 될때

DirectoryNotFoundException: Could not find a part of the path 
"C:/unity/Test/Assets/Plugins/iOS/Helloworld.swift" 
or "//DESKTOP-0000/Xcode/Test\Libraries\Plugins\iOS\Helloworld.swift"

 

폴더경로가 이상해서 그렇다.

네트워크 드라이브나 한글경로 쓰지말고 자신의 드라이브의 영어경로를 쓰자

 

------------------------------------------------------------------------------

<unknown>:0: error: no such file or directory:

'/Volumes/Xcode/Test/Libraries/Plugins/iOS/Helloworld.swift'

Command

/Users/Test/Desktop/Xcode.app/Contents/Developer/Toolchains

/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1

 

 

위와 같은 문제다

내부경로 영어경로를 쓰자

 

------------------------------------------------------------------------------

 

유니티 zip가 IOS에서 빌드 안될때

 

 


넣으삼

 

------------------------------------------------------------------------------

app transport security has blocked a cleartext http (http //) resource load since it is insecure

 

 

 

 

 

  <key>NSAppTransportSecurity</key>
  <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
  </dict>

추가한다.

추가해도 안된다면 Security에 잡다한게 들어가서 그럼

 

 

 

------------------------------------------------------------------------------

렌더카메라가 메인카메라로 잡힐때

태그달아라

 

------------------------------------------------------------------------------

 

 

 

 

2019.4.1f1버전의 유니티에서 iOS빌드시 리플렉션 해상도가 다르게 바뀜

 

 

 

------------------------------------------------------------------------------

Found plugins with same names,  and . Delete the one of the duplicate plugins.UnityEditor.AndroidPluginImporterExtension:CheckFileCollisions(String)UnityEditorInternal.PluginsHelper:CheckFileCollisions(BuildTarget)UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

 

 

2018에서 2019로 올릴때 발생한다

라이브러리 폴더 지우면 폴더가 재구축 되면서 해결된다

 

 

 

 

 

------------------------------------------------------------------------------

SerializedObject target has been destroyed.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

 

특정 인스펙터를 열어둔상태에서 재생하면 발생한다. 다른곳으로 인스펙터를 열면 된다

 

 

 

------------------------------------------------------------------------------

2019.4.1.f1의 particle system force field가 android, ios에서 실행 안됨

 

에셋번들로 로드할때 실행이 안 되는 현상이 발생하고

레이어를 동일하게 맞춰주고 풀빌드 하면 잘됨

 

 

------------------------------------------------------------------------------

unrecognized identifier 'SHADOW_COORDS'

 

아래 코드를 넣는다

#pragma multi_compile_fwdbase
#include "AutoLight.cginc"

 

 

------------------------------------------------------------------------------

씬 이동후 캔버스 먹통되는 현상

 

이동할때 같이 EventSystem을 이동시켜주거나

이동한 씬에도 EventSystem을 달아준다

평소에는 자동할당 되어서 눈치채기 힘든부분이다

 

 

 

------------------------------------------------------------------------------

AssetBundle(에셋번들)에서 Polygon Collider 2D 미동작

AssetBundle(에셋번들)에서 Particle System Force Field 미동작

체크 해제한다.

동적 메쉬 데이터는 에셋번들에서 제외되기 때문IL2CPP에서만 그런듯?

단점은 불필요한 파일들의 필터링이 안 되기 때문에 빌드시간과 용량이 늘어난다

다행히 스크립트만 포함시키는듯하다

차라리 번들로더랑 같이 해당씬을 포함시키던지 해당기능을 쓰지 않거나 link.xml을 작성하여 Asset폴더에 넣자

 

 

참조 : https://docs.unity3d.com/kr/2018.4/Manual/ManagedCodeStripping.html

 

 

 

------------------------------------------------------------------------------

RenderTexture.Create: Depth|ShadowMap RenderTexture 

requested without a depth buffer. Changing to a 16 bit depth buffer.

 

2019.4.1f1에서 렌더텍스처의 DepthTexture를 쓰려고 하면 발생한다

정확히는 Camera의 RenderTexture의 Color Format의 DEPTH_AUTO를 줄때

Depth Buffer가 16bit가 아닐때 발생한다고 하는데 걍 계속 생김

 

 

해결방법은 해당 카메라를 쓸때만 키자

 

 

 

------------------------------------------------------------------------------

Attempting to bind Texture ID 125 as UAV, the texture wasn't created with the UAV usage flag set!

enableRandomWrite가 설정된 렌더텍스처만 가능하다

enableRandomWrite는 동적으로 생성시에만 설정 가능하며

만일 기존 렌더텍스처에 할당하려고 하면 RenderTexture.Create failed: format unsupported for random writes - RGBA4 (142).

라는 새로운 오류를 발생시킨다

 

 

 

 

------------------------------------------------------------------------------

Not allowed to access vertices on mesh '메쉬이름'

Read/Write 체크

 

 

 

------------------------------------------------------------------------------

java.lang.securityexception permission denied (missing internet permission )

 

 

------------------------------------------------------------------------------

 

java.io.IOException: Cleartext HTTP traffic to 링크 not permitted

 

해당 링크가 https가 아니라서 발생하는 문제이다

예외속성에 해당링크를 추가하는 방법도 있긴한데 ios 지원할거라면 걍 https쓰자

 

 

 

------------------------------------------------------------------------------

Failed running C:\Program Files\Unity\Hub\Editor\2019.4.1f1\Editor\Data\il2cpp/build/deploy/net471/il2cpp.exe

stdout:
Building libil2cpp.so with AndroidToolChain
Output directory: C:\unity\GameName\Temp\StagingArea\assets\bin\Data\Native\arm64-v8a
Cache directory: C:\unity\GameName\Library\il2cpp_android_arm64-v8a\il2cpp_cache

 

 

빌드중에 크래시 나면 빌드불가에 빠지는 버그이다

프로젝트 폴더의 Library 폴더아래의

il2cpp_android_arm64-v8a폴더와

il2cpp_android_armeabi-v7a폴더를 지워주면 된다

 

 

 

------------------------------------------------------------------------------

iOS framework addition failed due to a CocoaPods installation failure. This will will likely result in an non-functional Xcode project.

 

 

 

 

 

------------------------------------------------------------------------------

1. undefined symbol _objc_class_$_

2. _objc_class_$_firtransactionresult

3. unity _OBJC_CLASS_$_ GKLocalPlayer

4. 'FBSDKShareKit/FBSDKShareKit.h' file not found

5. ios framework addition failed due to a cocoapods installation failure

 

위 5개 전부 동일한 원인의 버그이다

얼핏보면 코코아포드 손상이나 GameKit.framework 미설치로 보이겠지만 전혀 아니다 

유니티 서비스 인증 만료로 생긴 버그이다. 해결방법은 하단 이미지 참조

 

 

알고보니 하단설정이다

 

 

 

------------------------------------------------------------------------------

cocoapods installation detected /usr/local/bin/pod

이미 설치되어 있다는 소리이다

지우고 싶다면 터미널에서 sudo gem uninstall cocoapods를 친다

 

 

 

 

 

 

 

 

 

 

 

------------------------------------------------------------------------------

error CS0234: The type or namespace name 'Graphics' does not exist in the namespace 'System.Drawing' (are you missing an assembly reference?)

 

dll을 넣어도 문제가 생긴다면 System.Drawing 중복으로 인한 참조 오류이다

 

4.0으로 올리고 dll을 넣으면 된다

 

 

------------------------------------------------------------------------------

Signing for "Unity-iPhone" requires a development team.
Select a development team in the Signing & Capabilities editor that matches the selected profile "키".

 

인증서 기간 조금 남았어도 뜨는듯

------------------------------------------------------------------------------

 

 

UnityPlayerActivity.java uses or overrides a deprecated API.
UnityEditor.BuildPlayerWindow:BuildPlayerAndRun()

 

안드로이드 Minimum API레벨이 너무 낮아서 그렇다

30으로 올려주자

 

 

------------------------------------------------------------------------------

Cycle in dependencies between targets 'Unity-iPhone' and 'UnityFramework'; building could produce unreliable results.

 

빌드할때 뭔가 빨간줄 뜨면서 헤더가 빠진채 빌드가 되었을것이다

빨간줄 안뜨게 수정하면 된다

 

 

 

 

------------------------------------------------------------------------------

Image UGUI에서 마테리얼값 바꿀때

 

https://answers.unity.com/questions/920091/how-can-i-change-the-shader-parameters-for-an-ui-i.html

Material mat = Instantiate(image.material);
mat.SetFloat("_SomeProperty", 1f);
image.material = mat;

 

이거쓰면 전역 건드리는 문제 해결된다

 

 

 

------------------------------------------------------------------------------

블루투스 권한 못 받아올때

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>

이걸 Plugins/Android/AndroidManifest.xml에 넣어준다

위치는 이런식으로</application>와 </manifest>사이에 넣어야 한다

 

 

---------------------------------------------------------------------------------

Note: \Temp\gradleOut\unityLibrary\src\main\java\com\unity3d\player\UnityPlayerActivity.java uses or overrides a deprecated API.

UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

 

api 올려주면 된다 24쯤?

 

올려도 발생할때 있는데 타겟api가 낮은 경우로 블루투스의 경우 31을 깔아주면 된다

 

 

 

 

---------------------------------------------------------------------------------

Duplicated input semantics can't change type, size, or layout ('TEXCOORD1'). at line 82 (on d3d11)

 

이 문제인데 보다시피 설명과 달리 TEXCOORD1는 한번밖에 쓰여있지 않다

이유는 UNITY_FOG_COORDS에서 TEXCOORD1을 쓰기 때문

TEXCOORD2로 수정해주면 해결된다 

 

 

 

 

 

 

 

---------------------------------------------------------------------------------

ArgumentException: The Assembly Mono.WebBrowser is referenced by System.Windows.Forms

 

 

 

 

---------------------------------------------------------------------------------

Not a WAVE file - no RIFF header

 

NAudio의 오류

파일명의 확장자에 .wav가 안 붙어있어서 발생함

 

 

 

 

 

 

---------------------------------------------------------------------------------

CommandInvokationFailure: Unable to install APK to device. Please make sure the Android SDK is installed and is properly configured in the Editor. See the Console for more details.
C:/Program Files/Unity/Hub/Editor/2019.4.1f1/Editor/Data/PlaybackEngines/AndroidPlayer\SDK\platform-tools\adb.exe -s "RF9NC0529JN" install -r -d "D:\~~~.apk"
stderr[
adb: failed to install D:\~~.apk: Failure [INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: /data/app/vmdl1319470982.tmp/base.apk (at Binary XML file line #40): com.unity3d.player.UnityPlayerActivity: Targeting S+ (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present]
]
stdout[
]
exit code: 1

 

Target API Level이 Automatic이면 가끔씩 잘 못 잡아서 그럼

수동으로 할당

 

 

 

---------------------------------------------------------------------------------

Microsoft (R) Visual C# Compiler version 2.9.1.65535 (9d34608e) Copyright (C) Microsoft Corporation. All rights reserved

 

 

특정 컴퓨터에서 포톤을 열려고 하면 이렇게 된다

2019.4.1f1에서 발생했고 2019.4.31f1으로 올리면 해결된다

 

---------------------------------------------------------------------------------

File couldn't be read! UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

 

위 버그랑 동일한 문제다

해결법도 같다

 

 

 

---------------------------------------------------------------------------------

The referenced script on this Behaviour

미싱컴포넌트 있어서 그렇다

지워

 

 

 

 

---------------------------------------------------------------------------------

Unsupported type BitField
UnityEditor.EditorApplication:Internal_CallUpdateFunctions ()

 

32비트까지만 지원해서 안된다느니 어쩌구~

결론은

보통 레이어마스크를 인자로 받다가 프리팹에서 꼬여서 발생한다

public LayerMask layerMask = -1;

이 부분이 문제인데 그냥은 안 생긴다

원래 int형으로 만들고 저장한 후에 나중에 LayerMask로 바꾼 괴랄한 경우에 생긴다

해당 컴포넌트를 Revert해서 기록된 쓰레기 데이터를 날려주면 된다

 

 

 

 

---------------------------------------------------------------------------------

 

래그돌(Ragdoll)에 AddExplosionForce가 안먹히는 현상

GetComponent<Rigidbody>() 써서 그렇다.

콜라이더는 회전문제때문에 보통 오브젝트가 분리되어 있으니까..
attachedRigidbody로 수정해주자

//var rigidbody = collider.GetComponent<Rigidbody>();
var rigidbody = collider.attachedRigidbody;
if (rigidbody)
{
    rigidbody.AddExplosionForce(explosionForce, explosionCenter, radius);
}

 

---------------------------------------------------------------------------------

2019.4.31f1에서

hit.transform.name에 이상한게 뜨는 현상

내부적으로 hit.rigidbody.transform.name을 쓰기 때문이다 (이뭐병...)

반드시 hit.collider.transform.name을 쓰자

 

 

---------------------------------------------------------------------------------

2019.4.1f1에서 발생

 

Missing Project ID

 

걍 계정 두개 쓰다가 다른 계정으로 열어서 걸림

즉 권한부족

일단 아래사진대로 눌러보면

이렇게 뜰것이다

https://dashboard.unity3d.com/

먼저 유니티 대시보드 들어가서

 

 

계정을 초대하고 권한을 설정해준다

나는 부계정이라서 매니저 권한 줬는데 보통은 게스트나 유저를 주면 될것이다

그리고 유니티에서 리프레시를 누르면

짜잔 액세스 성공

내가 특이케이스라 그렇지 일반적인 경우라면 다른 직원이 초대를 못받은 경우겠다

 

 

 

---------------------------------------------------------------------------------

 

 

Target Android SDK not installed

혹은

UnityException: Target Android SDK not installed
Android SDK does not include your Target SDK of 30.

 

 

SDK를 순차적으로 설치하지 않은 상태에서 없는 SDK를 요청하면 뜬다

이게 무슨 소리나면 내가 원래 34버전을 요청해서 업데이트 받았는데

도중에 30이 필요해서 맥시멈을 30으로 내렸더니 업데이트창 없이 바로 이 창이 뜸

해결방법은

https://wmmu.tistory.com/entry/%EA%B5%AC%EA%B8%80%ED%94%8C%EB%A0%88%EC%9D%B4-%EB%B9%8C%EB%93%9C%EC%97%90%EB%9F%AC

 

안드로이드 빌드관련

Minimum API Level api 29 설치방법 프로젝트 세팅에서 타겟api를 29로 놓고 빌드하면 이런창이 뜬다 눌러야 하는데 관리자 모드로 안하면 아래같은 창이 뜬다 Required API level 29 Unable to install additional SDK P

wmmu.tistory.com

이 글의 '중간버전 설치방법' 탭을 확인하자

 

 

 

 

 

---------------------------------------------------------------------------------

패키지를 파싱하는 중 문제가 발생했습니다

영어로는 There was a problem while parsing the package라고 한다

 

https://discussions.unity.com/t/problem-parsing-package-with-api-level-31/249651

 

 

여기서 보면 android:exported="true"인지 머시긴지를 넣으라고만 한다

이렇게 적으면 으뜩케 알아먹으란 건데?

 

먼저 Plugins/Android/AndroidManifest.xml를 열면 이렇게 적혀있다

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.unity.ads" android:versionName="1.0" android:versionCode="1">
  <application>
    <uses-library android:required="false" android:name="org.apache.http.legacy" />
    <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713" />
    <meta-data android:name="com.google.unity.ads.UNITY_VERSION" android:value="2021.1.10f1" />
  </application>
</manifest>

여기에

<activity android:name="com.unity3d.player.UnityPlayerActivity" android:theme="@style/UnityThemeSelector" android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
  <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>

이걸 넣어서 아래처럼 수정했다

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.unity.ads" android:versionName="1.0" android:versionCode="1">
  <application>
    <uses-library android:required="false" android:name="org.apache.http.legacy" />
    <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713" />
    <meta-data android:name="com.google.unity.ads.UNITY_VERSION" android:value="2021.1.10f1" />
    <activity android:name="com.unity3d.player.UnityPlayerActivity" android:theme="@style/UnityThemeSelector" android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>
  </application>
</manifest>

틀린그림 찾기 알아서 잘 하시길

잘보면 알겠지만 activity에 android:exported="true"를 넣어야 하는데

activity가 없어서 activity를 추가하고 거기에 android:exported="true"를 넣었다

activity는 application안에 들어가야 해서 </application> 바로 위에 써넣었다

 

그리고 ca-app-pub-3940256099942544~3347511713는 데모키니까 알아서 각자의 키로 수정해야한다

 

 

당장 급하다면 API를 낮춰서 빌드하거나 컴퓨터랑 폰 연결후 유니티에서 Build And Run하면 될듯

 

테스트 하다 말긴 했는데 위에 풀코드 말고

<activity android:name="com.unity3d.player.UnityPlayerActivity" android:theme="@style/UnityThemeSelector" android:exported="true">
</activity>

이렇게 넣어도 부팅은 되긴 했다.

하지만 유니티 버전을 낮게해서 도중에 팅겼었음

 

 

 

 

 

---------------------------------------------------------------------------------

Android resource linking failed See the Console for details.

말그대로 오류는 콘솔창을 보고 해결하란 뜻

 

 

 

---------------------------------------------------------------------------------

2019.4.1.f1과 2019.4.31f1에서 발생

> Task :unityLibrary:preBuild UP-TO-DATE
> Task :launcher:preBuild UP-TO-DATE
> Task :unityLibrary:preReleaseBuild UP-TO-DATE
> Task :unityLibrary:GoogleMobileAdsPlugin.androidlib:preBuild UP-TO-DATE
> Task :unityLibrary:GoogleMobileAdsPlugin.androidlib:preReleaseBuild UP-TO-DATE
> Task :unityLibrary:checkReleaseManifest UP-TO-DATE
> Task :launcher:prepareLintJar UP-TO-DATE

 

 

애드몹 업뎃하고나니 발생함

저거랑 별개로

Note: \Temp\gradleOut\unityLibrary\src\main\java\com\unity3d\player\UnityPlayerActivity.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

CommandInvokationFailure: Gradle build failed. 

도 같이 뜸

찾아보니 커스텀 그래들을 쓰라고 한다

근데 난 이런 해결방식 존나 싫어함

왜?

나중에 또 고쳐야 하니까

 

생각해보면 걍 유니티가 옛날버전이라 발생하는 문제니까

유니티를 2019.4.1.f1에서 2020.3.48f1으로 올려서 고침

 

오류코드wmup20230925ASM7과도 관련이 있을수 있으니

또 발생하면 참고바람

 

 

 

 

---------------------------------------------------------------------------------

A failure occurred while executing

 

GoogleMobileAds-v8.5.2가 설치된 상태에서 유니티 버전이 2020.3.48f1이 아니라서 발생함

 

 

 

---------------------------------------------------------------------------------

Assembly 'Assets/ExternalDependencyManager/Editor/1.2.177/Google.IOSResolver.dll' will not be loaded due to errors:
Unable to resolve reference 'UnityEditor.iOS.Extensions.Xcode'. Is the assembly missing or incompatible with the current platform?
Reference validation can be disabled in the Plugin Inspector.

 

GoogleMobileAds-v8.5.2가 설치된 상태에서

유니티를 2019.4.1.f1에서 2022.3.5f1으로 올려서 발생함

 

IOSResolver 두개를 지워서 고치긴 했는데 원론적인 방법은 아닌듯

그래서 더 찾아봄

 

https://github.com/googlesamples/unity-jar-resolver/issues/441

 

[Known Issue] iOSResolver.dll failed to load with Unity 2021.1.11+ on Mac · Issue #441 · googlesamples/unity-jar-resolver

[REQUIRED] Please fill in the following fields: Unity editor version: Issue occurred with 2021.1.11, 2021.1.12 (Works with 2021.1.10 and 2021.2.0) External Dependency Manager version: Tried 1.2.160...

github.com

여기 보면 2021.1.10에서는 정상 동작한단다.

 

그래서 찾아보니 이런거도 있었음

보니까 걍 얘내들이 2021.1.10 기준으로 만들어서 생긴 문제다

그래서 2022.3.5f1에서 2021.1.10으로 다운그레이드해서 해결

근데 이거 비안정버전이라 2020.3.48f1으로 더 낮췄다

 

그리고 해결했단거도 정확히 따지면 재생할때 오류창이 뜨긴 하는데 동작은 한다.(...)

 

이거 체크하면 재생해도 오류 안 뜬다

 

 

 

---------------------------------------------------------------------------------

 

오류코드wmup20230925ASM7

java.lang.UnsupportedOperationException: This feature requires ASM7 See the Console for details.

 

빌드버전코드 올리고 빌드하니 발생함

응?

오류창을 더 보니 이런게 쓰여있다


> Configure project :launcher
WARNING: The option setting 'android.enableR8=false' is deprecated.
It will be removed in version 5.0 of the Android Gradle plugin.
You will no longer be able to disable R8

 

또 그래들 오류냐?

근데 이게 유니티랑 애드몹 패키지 버전업하면서 사라졌는데

버전수정하면서 그래들이 재갱신 되면서 문제가 발생한듯 하다

 

https://forum.unity.com/threads/java-lang-unsupportedoperationexception-this-feature-requires-asm.1377564/

여기서 보니까 안드로이드 7.0으로 올리니 문제없단다

그러고보니 원래 미니멈 올리고 테스트 했었는데 그게 잔류했었나보다

API 24로 올리고 해결

 

 

 

 

 

 

---------------------------------------------------------------------------------

유니티 드래그 안 될때

 

컴퓨터 재부팅 하면 됨

 

 

 

 

 

'Unity > C#' 카테고리의 다른 글

싱글톤 패턴  (0) 2020.07.20
자주 쓰는 유니티 코드 모음  (0) 2020.07.09
사소한 c# 테크닉  (0) 2020.07.08
posted by 모카쨩
2020. 7. 9. 15:27 Unity/C#

 

 

뭔가 유용한데 정리하기 애매한것들

나중에 정리하자

 

 

 

 

 

 

씬 어딘가에 있는 컴포넌트를 불러옴. 매니저컴포넌트같은걸 다룰때 좋음. 할당 안해도 되니까

부하가 크니 사용시 주의

자주 호출할땐 아래의 '싱글톤에서 최상위 부모를 불러올때' 항목 참고

FindObjectOfType<클래스명>();

 

 

 

FindObjectsOfType을 대체하는 함수

2021.3.18에 추가됨

차이점은 FindObjectsByType은 정렬을 할지 안할지 정할수 있어서 정렬을 안 하면 속도가 더 빠르다는정도

var targets = FindObjectsByType<Renderer>(FindObjectsSortMode.None);

 

 

 

 

 

모든 자식들중에 해당 게임오브젝트가 존재하는지 검색함

단 Transform이 달려있어야함

System.Array.Find(transform.GetComponentsInChildren<Transform>(), x => x.name == "게임오브젝트명")

 

태그검색

GameObject.FindGameObjectsWithTag("태그명");

 

문자열로 레이어 체크

gameObject.layer == LayerMask.NameToLayer("레이어명")

 

카메라 레이어 토글

var layerIndex=LayerMask.NameToLayer("레이어명");
camera.cullingMask ^= 1 << layerIndex;

 

레이어마스크 추가 삭제

//추가
layerMask |= 1 << layer;

//삭제
layerMask &= ~(1 << layer);

 

 

 

컴포넌트 달린 오브젝트 생성

var gameObject = new GameObject(nameof(SampleClass), typeof(SampleClass));

 

 

 

 

컴포넌트를 활성화 하거나 비활성화할때

GetComponent<Image>().enabled = false;

 

 

 

유니티 에셋 파일 변경시 호출

#if UNITY_EDITOR
    클래스명()
    {
        EditorApplication.projectChanged += GetFileData;
    }
    void GetFileData()
    {
    	//실행문
    }
#endif

 

 

 

 

 

프리팹 저장

var target_ClassName = target as ClassName;
if (PrefabUtility.GetPrefabAssetType(target_ClassName.gameObject) != PrefabAssetType.NotAPrefab)
{
    PrefabUtility.SavePrefabAsset(target_ClassName.gameObject);
}

 

 

 

텍스트 일부분만 컬러주기, 리치텍스트

Rich Text가 적용되어 있어야 하는데 일반적으론 적용되어있으니 상관없음

텍스트컴포넌트.richText = true;
텍스트컴포넌트.text=
"<color=#ff0000>빨간색</color>
<color=green>초록색</color> 
<color=#0000FFc0>반투명파란색</color>
<b>굵게</b>
<i>기울임</i>
<size=50>사이즈50</size>
<material=2>매터리얼2번</material>
"
;

 

버튼을 누르면 URL실행

            if (GUILayout.Button("URL실행"))
            {
                Application.OpenURL("https://wmmu.tistory.com/");
            }

 

 

인스펙터의 값을 글로벌로 저장할때 ScriptableObjects를 사용

스크립트테이블오브젝트

using UnityEngine;

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject
{
    public string prefabName;

    public int numberOfPrefabsToCreate;
    public Vector3[] spawnPoints;
}

 

 

 

 

string.IsNullOrEmpty("문자열")

 

 

폴더 이름 반환

var folderPath=AssetDatabase.GetAssetPath(object형폴더);
string[] fileNames = AssetDatabase.GetSubFolders(folderPath);

 

폴더 아래의 Sprite이름들 반환

{
  var folderPath=AssetDatabase.GetAssetPath(object형폴더);
  string[] guidFilePathSprites = AssetDatabase.FindAssets($"t:{typeof(Sprite).Name}", new string[] { folderPath });
  Sprite[] sprites = new Sprite[guidFilePathSprites.Length];
  for (int i = 0; i < sprites.Length; i++)
  {
    var path=AssetDatabase.GUIDToAssetPath(sprites[i]));//Assets/FileName.Sprite 같은식으로 반환
    sprites[i]=AssetDatabase.LoadAssetAtPath<Sprite>(path);
  }
}

 

 

정적 업데이트

[InitializeOnLoad]
public class MyWindow : EditorWindow
{
    static MyWindow()
    {
        EditorApplication.update -= StaticUpdate;
        EditorApplication.update += StaticUpdate;
    }
    static long lastTime = 0;
    public static void StaticUpdate()
    {
        if ((System.DateTime.Now.Ticks-lastTime)/10000000 >= 3)//3초마다 동작
        { 
            //실행문
            lastTime = System.DateTime.Now.Ticks;
        }
    }
}

 

 

 

 

Time.time 타이머 예제

가끔 지속체크 해야해서 코루틴 쓰기 곤란한거 쓸때 쓰면 좋음

public Text respawnTimeText;
public Image respawnProgress;
 float lastDeathTime = float.MinValue;

/// <summary>
/// update에서 지속 호출
/// </summary>
public void RespawnCheck()
{
    var nowTime = Time.time;
    var delay = 10;

    if (nowTime < lastDeathTime + delay)
    {
        //부활대기중
        var respawnTime = (lastDeathTime + delay) - nowTime;
        respawnTimeText.text = respawnTime.ToString("N0"); //10부터 시작
        respawnProgress.fillAmount = 1f - respawnTime / delay; //0부터 시작

        respawnTimeText.gameObject.SetActive(true);
        respawnProgress.gameObject.SetActive(true);
    }
    else
    {
        if (lastDeathTime > 0)
        {
            //리스폰
            Respawn();
            lastDeathTime = float.MinValue;
        }
        //생존중
        respawnTimeText.gameObject.SetActive(false);
        respawnProgress.gameObject.SetActive(false);
    }

}

public void Respawn()
{
    (var poses, var rot) = GameSystem.GetInstance().GetSpawnPoints(GetComponent<PhotonCharacter>().team);
    transform.position = poses[0];
    transform.rotation = rot;
    GetComponent<PhotonCharacter>().hp = 100;
    GetAnimator().GetComponent<DynamicRagdoll>().Deactive();
}

public void Death()
{
    lastDeathTime = Time.time;
}

 

 

 

 

 

 

 

 

모든 씬에서 단 한번만 자동생성되는 오브젝트

public class GameManager : MonoBehaviour
{
	static GameObject gameManager;
    static void CreateInstance()
    {
        if (gameManager == null)
        {
        	DontDestroyOnLoad(new GameObject(nameof(GameManager), typeof(GameManager)));
            //nameof가 오브젝트명이고 typeof가 addcomponant
        }
    }
    static GameManager()
    {
        if(Application.isEditor==false)
        {
            CreateInstance();
        }
        #if UNITY_EDITOR
        if (UnityEditor.EditorApplication.isPlaying)
        {
            CreateInstance();
        }
        #endif
    }

 

 

 

열닫 가능한 코드

#region 주석


#endregion

 

 

 

 

웹컬러 변환

출력은 #이 빠져나오고 입력은 #이 붙어야 하는 미쿠친구 함수니까 조심하자

string webColor = $"#{ColorUtility.ToHtmlStringRGB(Color.red)}";
//webColor = #FF0000 

string webColorRGBA = $"#{ColorUtility.ToHtmlStringRGBA(Color.red)}";
//webColor = #FF0000FF 

Color color;
ColorUtility.TryParseHtmlString(webColor, out color);
//color = RGBA(1.000, 0.000, 0.000, 1.000)

 

에디터 모드에서도 MonoBehaviour를 동작하게 함

update나 start이런 함수도 작동

[ExecuteInEditMode]

 

 

데이터 타입 이름 구한거

Debug.Log($"bool: {typeof(bool).Name}"); //Boolean
Debug.Log($"char: {typeof(char).Name}"); //Char
Debug.Log($"string: {typeof(string).Name}");  //String
Debug.Log($"short: {typeof(short).Name}"); //Int16
Debug.Log($"int: {typeof(int).Name}");  //Int32
Debug.Log($"long: {typeof(long).Name}");  //Int64
Debug.Log($"float: {typeof(float).Name}");  //Single
Debug.Log($"double: {typeof(double).Name}");  //Double

 

 

 

배열을 콤마가 붙은 문자열로 반환

string strData = $"({string.Join(",", 배열데이터)})";

string형이 아닌 배열일때

string strData = $"({string.Join(",", Array.ConvertAll(배열데이터, x => x.ToString()))})";

 

 

 

 

 

 

 

 

하이어라키의 해당 오브젝트의 자식계층에서 최하위로 설정함, 부모는 변하지 않음

transform.SetAsLastSibling();

 

 

 

리플렉션으로 애니메이션 윈도우 접근

var animationWindowType = System.Type.GetType("UnityEditor.AnimationWindow,UnityEditor");
var animationWindow = Resources.FindObjectsOfTypeAll(animationWindowType)[0];

 

마우스에 대고있는 윈도우 이름

var mouseOverWindow = EditorWindow.mouseOverWindow;
if (mouseOverWindow != null)
{
    Debug.Log("mouseOverWindow: " + mouseOverWindow.GetType().ToString());
}

 

폰이 스스로 종료되지 않도록

Screen.sleepTimeout = SleepTimeout.NeverSleep; //네버슬립모드

백그라운드에서도 동작함

Application.runInBackground=true;

 

 

 

 

TextUI의 적절한 가로 사이즈, 캐릭터(문자)의 위치를 구할때 가아아아끔씩 쓴다

GetComponent<Text>().preferredWidth

 

씬에디터상에 박스를 표시

//에디터상에서 박스위치 표시할때 사용
private void OnDrawGizmos()
{
    Gizmos.DrawWireCube(borderCenter, borderSize);
}
public Vector3 borderCenter = Vector3.zero;
public Vector3 borderSize = new Vector3(200,100,200);

 

 

 

 

배속설정, 디버깅할때 유용하더라

Time.timeScale=2;

 

 

싱글톤에서 최상위 부모를 불러올때

v1

더보기
static MainSystem mainSystemPrivate;
public static MainSystem mainSystem
{
    get
    {
        if (mainSystemPrivate == null)
        {
            mainSystemPrivate = FindObjectOfType<MainSystem>();
        }
        return mainSystemPrivate;
    }
}

v2

네이밍만 바뀜

static GameSystem gameSystem = null;
public static GameSystem GetInstance()
{
    if (gameSystem == null)
    {
        gameSystem = FindObjectOfType<GameSystem>();
    }
    return gameSystem;
}

 

 

 

 

 

 

 

팝업창을 띄움, 에디터 전용

#if UNITY_EDITOR
[UnityEditor.InitializeOnLoad]
public class Class : MonoBehaviour {

	static Class()
	{
		UnityEditor.EditorUtility.DisplayDialog("제목", "내용", "네", "아니요");
	}
	// Use this for initialization
	void Start () {

	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

#endif

 

 

 

인스턴트 SE

    public void InstantiateSE(AudioClip clip)
    {
        if (clip==null)
        {
            Debug.Log("clip==null");
            return;
        }
        var gameObject = new GameObject($"SoundEffect({clip.name})");
        var audioSource = gameObject.AddComponent<AudioSource>();
        audioSource.clip = clip;
        audioSource.Play();
        Destroy(gameObject, audioSource.clip.length + 3f);
        DontDestroyOnLoad (gameObject);
    }

 

 

유니티 버전

Application.version

 

 

 

 

Predicate 샘플

    public static class Array
    {
        public static int[] FindIndexAll<T>(T[] array, System.Predicate<T> match)
        {
            var indexList = new List<int>();
            for (int i = 0; i < array.Length; i++)
            {
                if (match.Invoke(array[i]))
                {
                    indexList.Add(i);
                }
            }
            return indexList.ToArray();
        }
    }
    // Start is called before the first frame update
    void Start()
    {
        var strTest = new string[] { "A", "B", "C", "A", "B" };
        var indexes = Array.FindIndexAll(strTest, x => x == "A");
        Debug.Log(string.Join(",",indexes)); //0,3을 반환
    }

 

 

 

 

현재 언어가 영어인지

if(Application.systemLanguage == SystemLanguage.English)

 

 

 

인풋필드가 활성화 되어있는지

if((InputField!=null)&& (InputField.isFocused))
{

}

 

 

스크린사이즈 변경

    private void Awake()
    {

        if (Debug.isDebugBuild)
        {
            var hei = 480;
            Screen.SetResolution(Screen.width * hei / Screen.height, hei, true);
        }
    }

 

 

fps측정

    float fps = 60;
    private void OnGUI()
    {
        fps = Mathf.Lerp(fps, (1 / Time.deltaTime), 0.05f);
        GUI.Box(new Rect(100, 0, 200, 24), "FPS: " + (int)fps);
    }

 

 

 

 

스크롤뷰의 콘텐츠 위치를 항상 아래쪽으로 잡을때

scrollRect.verticalScrollbar.value = 0;

 

 

 

 

이름으로 블렌드 쉐이프 설정

var skinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
var blendShapeIndex=skinnedMeshRenderer.sharedMesh.GetBlendShapeIndex("ShapeKeyName");
skinnedMeshRenderer.SetBlendShapeWeight(blendShapeIndex, weight);

 

 

 

 

마우스 위치 표시

Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
    Debug.DrawLine(ray.origin, hit.point,Color.red);
}
else
{
    Debug.DrawLine(ray.origin, ray.origin + ray.direction * 5);
}

 

 

자식리스트를 불러옴

//켜진것만
var childList = new List<GameObject>();
for (int i = 0; i < transform.childCount; i++)
{
    var child = transform.GetChild(i).gameObject;
    if (child.activeInHierarchy)
    {
        childList.Add(child);
    }
}

//전부
var childList = new List<GameObject>();
for (int i = 0; i < transform.childCount; i++)
{
    childList.Add(transform.GetChild(i).gameObject);
}

 

 

 

 

 

uuid

빌드조건에 따라 쉽게 바뀌니 주의

안드로이드면 키스토어만 바뀌어도 기준값이 바뀌고

애플이면 xcode에서 직접 빌드했는지 아닌지에 따라서도 바뀐다

SystemInfo.deviceUniqueIdentifier

 

 

 

 

자주쓰는 애니메이션 이벤트

    public void InstantiateSE(AudioClip clip)
    {
        if (clip==null)
        {
            Debug.Log("clip==null");
            return;
        }
        var gameObject = new GameObject($"SoundEffect({clip.name})");
        var audioSource = gameObject.AddComponent<AudioSource>();
        audioSource.clip = clip;
        audioSource.Play();
        Destroy(gameObject, audioSource.clip.length + 3f);
    }

 

 

scrollRect(Scroll Rect) 스크롤좌표 초기화

scrollRect.horizontalNormalizedPosition = 0f;
scrollRect.verticalNormalizedPosition = 0f;

 

 

 

 

 

 

 

GPGS토큰

string googleIdToken = null;
string googleAccessToken = null;

if (PlayGamesPlatform.Instance.IsAuthenticated())
{
	googleIdToken = PlayGamesPlatform.Instance.GetIdToken();
    //googleIdToken = ((PlayGamesLocalUser)Social.localUser).GetIdToken(); 이거도 된다고는 함
	googleAccessToken = PlayGamesPlatform.Instance.GetServerAuthCode();
}
else
{
	Debug.LogError("GPGS에 연결되어있지않아 토큰을 받아올수 없음");
}
//ID토큰은 공개적인거고 Access토큰은 비공개적인것인듯하다(불확실)

 

 

 

 

 

 

최대값을 지정 해 줌으로서 min값을 구할때 쓸 수 있다

long minScore = long.MaxValue;

 

 

게임종료

일반적인 상황에서는 쓸일이 없지만

번들로더같은곳에서 다운로드 취소버튼을 누른다던지 PC용게임을 만들때 사용함

    public void Exit()
    {
#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
#else
        Application.Quit();
#endif
    }

 

 

 

 

Enumerable To List

IEnumerable<DataSnapshot> childrens;
List<DataSnapshot> snapshots= Enumerable.ToList(childrens);

 

Enumerable To Array

IEnumerable<DataSnapshot> childrens;
DataSnapshot[] snapshots= Enumerable.ToArray(childrens);

 

 

레이캐스트2D

유니티4버전이라서 추가 업뎃 해야할듯

더보기
RaycastHit2D ray;
Vector3 pos = Camera.main.ScreenToWorldPoint (Input.mousePosition);
ray = Physics2D.CircleCast ((Vector2)pos,0.1f,Vector2.zero);


Debug.DrawRay (mpos,ray.direction,Color.red);
if(ray.transform != null) //빔을 쏴서 존재하는지
{
    Debug.Log (ray.point);
    Debug.DrawLine(mpos, ray.point, Color.blue);
    Debug.Log (ray.transform.GetInstanceID ().ToString());
}

 

레이캐스트 everything

var everything = ~0;

 

 

레이캐스트 샘플

유니티 공식문서 코드에서 조금만 수정했다

주의 : hit.transform은 절대 절대 절대 절대 절대 쓰지말자

float maxDistance = 1000;
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out RaycastHit hit, maxDistance >= 0 ? maxDistance : Mathf.Infinity))
{
    Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * hit.distance, Color.yellow);
}
else
{
    Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * maxDistance, Color.white);
}

 

레이캐스트 2점쇼바

public class Piston : MonoBehaviour
{
    public Transform pistonStart;
    public Transform pistonEnd;

    void Update()
    {
        var forward = (pistonEnd.position - pistonStart.position).normalized;
        var distance = Vector3.Distance(pistonStart.position, pistonEnd.position);
        if (Physics.Raycast(pistonStart.position, forward, out RaycastHit hit, distance,1))
        {
            transform.position = pistonEnd.position - forward*(distance - hit.distance);
        }
        else
        {
            transform.position= pistonEnd.position;
        }
    }
}

 

 

레이캐스트 화면터치

Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray))
{
    //클릭시 실행
}

 

레이캐스트 화면기준 발사 3D

//var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
var screenCenter = new Vector3(Screen.width / 2, Screen.height / 2, 0);
var ray = Camera.main.ScreenPointToRay(screenCenter);
float maxDistance = 1000;
if (Physics.Raycast(ray, out RaycastHit hit, maxDistance >= 0 ? maxDistance : Mathf.Infinity))
{
    Debug.DrawLine(ray.origin, ray.GetPoint(hit.distance), Color.green);
}
else
{
    Debug.DrawLine(ray.origin, ray.GetPoint(1000), Color.yellow);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

디버그 로그 비활성

v1

더보기

디버그모드일때만 작동

public class SampleClass : MonoBehaviour
{
    static class Debug
    {
        public static void Log(string log)
        {
#if UNITY_EDITOR
            if (UnityEditor.EditorUserBuildSettings.development)
#else
        if (Debug.isDebugBuild)
#endif
            {
                UnityEngine.Debug.Log(log);
            }
        }
        public static void LogError(string log)
        {
#if UNITY_EDITOR
            if (UnityEditor.EditorUserBuildSettings.development)
#else
        if (Debug.isDebugBuild)
#endif
            {
                UnityEngine.Debug.LogError(log);
            }
        }

    }

 

 

v2

맨 처음에 한번만 적어주면 됨

#if UNITY_EDITOR
            if (UnityEditor.EditorUserBuildSettings.development)
#else
        if (Debug.isDebugBuild)
#endif
            {
Debug.logger.logEnabled=false
            }

 

 

 

 

 

Define 전역설정

특정 패키지가 임포트 되었을때만 활성화 된다던지가 가능

 

 

 

충돌반사

public float damageSpeed = 3;
public void OnCollisionEnter(Collision collision)
{
    if (collision.relativeVelocity.magnitude > damageSpeed)
    {
        explosionRot = Vector3.Reflect(-collision.relativeVelocity, collision.contacts[0].normal);
        explosionPower = collision.relativeVelocity.magnitude * 0.5f;
    }
}

 

 

충돌반사 네트워킹

https://ahzkwid.booth.pm/items/4471500 에서 사용중이다

public void OnCollisionEnter(Collision collision)
{
    //https://ahzkwid.booth.pm/items/4471500
    if (GetComponent<VRC_Pickup>() == null)
    {
        return;
    }
    if (Networking.IsOwner(gameObject))
    {
        if (collision.relativeVelocity.magnitude > damageSpeed* DamageSpeedMultiple)
        {
            explosionRot = Vector3.Reflect(-collision.relativeVelocity, collision.contacts[0].normal);
            explosionPower = collision.relativeVelocity.magnitude * 0.5f;
            SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(ExplosionEffect));
            Delete();
        }
    }
}

 

 

충돌파워에 따른 사운드

void OnCollisionEnter(Collision collision)
{
    if (impactSound.isPlaying)
    {
        return;
    }
    var volume = collision.relativeVelocity.magnitude;
    volume = Mathf.Clamp(volume, 0.1f, 1f);
    impactSound.volume = volume;
    impactSound.Play();
}

 

 

 

 

일정시간뒤 자동 삭제

using UnityEngine;
public class DelayDestroy : MonoBehaviour
{
    public float delay = 3;
    void Start()
    {
        Destroy(gameObject, delay);
    }
}

 

 

부위데미지

var partDamage = 1f;
switch (humanBodyBones)
{
    case HumanBodyBones.LeftUpperLeg:
    case HumanBodyBones.RightUpperLeg:
        partDamage = 0.9f;
        break;
    case HumanBodyBones.LeftLowerLeg:
    case HumanBodyBones.RightLowerLeg:
        partDamage = 0.7f;
        break;
    case HumanBodyBones.Spine:
    case HumanBodyBones.Chest:
    case HumanBodyBones.UpperChest:
    case HumanBodyBones.Hips:
        partDamage = 1f;
        break;
    case HumanBodyBones.Neck:
    case HumanBodyBones.Head:
        partDamage = 2f;
        break;
    case HumanBodyBones.LeftShoulder:
    case HumanBodyBones.RightShoulder:
    case HumanBodyBones.LeftUpperArm:
    case HumanBodyBones.RightUpperArm:
        partDamage = 0.8f;
        break;
    case HumanBodyBones.LeftLowerArm:
    case HumanBodyBones.RightLowerArm:
        partDamage = 0.7f;
        break;
    case HumanBodyBones.LeftHand:
    case HumanBodyBones.RightHand:
    case HumanBodyBones.LeftFoot:
    case HumanBodyBones.RightFoot:
    case HumanBodyBones.LeftToes:
    case HumanBodyBones.RightToes:
        partDamage = 0.5f;
        break;
}

 

 

Enum1에서 Enum2로 변환

var enum2 = (Enum2)System.Enum.Parse(typeof(Enum2), enum1.ToString());

 

 

 

 

해당 게임오브젝트의 최상위루트 반환

v1

더보기

적용예제 : https://github.com/ahzkwid/AhzkwidAvatarTools

Transform GetRoot(Transform transform)
{

    var parents = transform.GetComponentsInParent<Transform>(true);
    Transform root = null;
    if (parents.Length == 1)
    {
        root = transform;
    }
    else
    {
        root = System.Array.Find(parents, parent => parent.GetComponentsInParent<Transform>(true).Length == 1);
    }
    return root;
}

 

 

근데 내장함수 있었음;;

v2

transform.root

'Unity > C#' 카테고리의 다른 글

유니티 버그, 에러, 오류모음  (0) 2020.07.20
사소한 c# 테크닉  (0) 2020.07.08
스파인 관련함수  (0) 2020.07.02
posted by 모카쨩
2020. 7. 8. 15:11 Unity/C#

 

 

//인스펙터에 script표시
GUI.enabled = false;
{
    var script = MonoScript.FromMonoBehaviour((MonoBehaviour)target);
    EditorGUILayout.ObjectField("Script", script, typeof(MonoScript), false);
}
GUI.enabled = true;

중괄호를 if문이나 for문같은게 없더라도 구획을 나누는데 쓰면 좋다

이렇게 만들어두면 필요할때 함수로 묶기도 편하다

region따위보다 가독성이 훨씬 좋다

 

그리고 아래는 아웃풋이 있을경우 예시


//worldPosition 2 screenPosition
Vector2[] screenPositions;
Vector2 screenHandlePosition;
{
    screenPositions = System.Array.ConvertAll(worldPositions, wp => RectTransformUtility.WorldToScreenPoint(cam, wp));
    screenHandlePosition = RectTransformUtility.WorldToScreenPoint(cam, worldHandlePoint);
}

 

 

 

 

 

foreach (var reloadEndSound in reloadEndSounds)
{
    reloadEndSound.Play();
}

foreach는 무조건 var를 쓰자. 어차피 용도는 한정되어 있으니까

참조변수는 s만 빼서 쓰면 식별도 편하고 좋다

 

 

 

 

switch (humanBodyBones)
{
    case HumanBodyBones.LeftUpperLeg:
    case HumanBodyBones.RightUpperLeg:
    case HumanBodyBones.LeftLowerLeg:
    case HumanBodyBones.RightLowerLeg:
        partDamage = 0.7f;
        break;
    case HumanBodyBones.Spine:
    case HumanBodyBones.Chest:
    case HumanBodyBones.UpperChest:
    case HumanBodyBones.Hips:
        partDamage = 1f;
        break;
    case HumanBodyBones.Neck:
    case HumanBodyBones.Head:
        partDamage = 2f;
        break;
    case HumanBodyBones.LeftShoulder:
    case HumanBodyBones.RightShoulder:
    case HumanBodyBones.LeftUpperArm:
    case HumanBodyBones.RightUpperArm:
    case HumanBodyBones.LeftLowerArm:
    case HumanBodyBones.RightLowerArm:
        partDamage = 0.7f;
        break;
}

 

책이나 교수들이 스위치문 이상하게 알려주는데

조건마다 break 달 필요없다.

애초에 그럴거면 다중if 쓰지 뭐라러 스위치문을 쓰겠는가

 

 

 

public void PressButton(string enumName, int index)
{
    Debug.Log($"PressButton({enumName}, {index})");
    if (enumName == nameof(MainCategory))
    {
        mainCategory = (MainCategory)index;
    }
    if (enumName == nameof(CostumeCategory))
    {
        costumeCategory = (CostumeCategory)index;
    }
    if (enumName == nameof(AppearanceCategory))
    {
        appearanceCategory = (AppearanceCategory)index;
    }
}

else if 대신 다중if를 쓰자.

가끔 switch문이 제약조건으로 안 먹는 경우 다중if를 많이 쓴다

오잉? '처리량이 늘어나는데 왜 else if를 안 쓰지?' 하고 의아해 할것이다.

보통 이렇게 수동으로 분기처리하는 데이터는 양이 적기 때문에

처리량이 늘어나도 거의 영향을 안 주는데

else if는 맨 윗열이 삭제되거나 하면 일일히 else를 지워가면서 해야한다

몰론 엄청나게 무거운 로직을 처리할땐 else if를 써야겠지만 그럴일은 별로 없다

 

아래는 또다른 예시

public void Skill(int index)
{
    if (hp<=0)
    {
        return;
    }
    if (IsMine)
    {
        if (CurrentSkillDelay(index) > 0)
        {
            return;
        }
        for (int i = 0; i < 2; i++)
        {
            if (CurrentSkillTime(i) > 0)
            {
                return;
            }
        }
    }
    //스킬실행
}

만약 위 코드에 if else를 쓰고 작동부를 if블록 안쪽에 두었다면 엄청나게 복잡해졌을것이다

 

 

 

 

다중루프 탈출

변수둬서 if체크하며 탈출하지 말고

goto로 탈출하자

for (int i = 0; i < players.Length; i++)
{
    for (int j = 0; j < bullets.Length; j++)
    {
        if (CollisionCheck(players[i], bullets[j]))
        {
            goto LOOP_OUT;
        }
    }
}
LOOP_OUT:;

'Unity > C#' 카테고리의 다른 글

자주 쓰는 유니티 코드 모음  (0) 2020.07.09
스파인 관련함수  (0) 2020.07.02
Parent of RectTransform is being set with parent property  (0) 2020.06.29
posted by 모카쨩
2020. 7. 2. 17:50 Unity/C#

스파인 컴포넌트 써오면서 깨달은점은 스파인 만든놈들은 유니티를 존나 못한단것이다

 

 

 

스파인 애니메이션 변경관련

 

 

using계열

using Spine.Unity

 

-스파인 애니메이션 변경시 해당 오브젝트 하위에 SkeletonAnimation이 있을경우

public SkeletonAnimation skeletonAnimation;

[SpineAnimation(dataField: "skeletonAnimation")]
public String spineAnim;
    
    
 SkelAnim.AnimationName = spineAnim;

-스파인 애니메이션 변경시 다른 오브젝트에서 SkeletonAnimation를 호출할경우

(SkeletonAnimation타입)

public SkeletonAnimation skeletonAnimation;
public AnimationReferenceAsset spineAnim;

SkelAnim.loop = false; //루프는 반드시 애니메이션 설정전에 실행되어야함
SkelAnim.AnimationName = spineAnim.name;

(SkeletonGraphic타입)

public SkeletonGraphic skeletonGraphic;
public AnimationReferenceAsset spineAnim;

skeletonGraphic.AnimationState.SetAnimation(trackIndex:0, spineAnim.name, loop:false); //false는 루프, 0은 인덱스 번호

 

 

-현재 SkeletonAnimation의 애니메이션의 지속시간

(SkeletonAnimation타입)

SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
float Duration=skeletonAnimation.skeleton.Data.FindAnimation(skeletonAnimation.AnimationName).Duration;

(SkeletonGraphic타입)

SkeletonGraphic skelGrap = GetComponent<SkeletonGraphic>();
string animName = skelGrap.AnimationState.Tracks.Items[0].ToString();
float duration=skelGrap.SkeletonData.FindAnimation(animName).Duration;

 

스파인 특정 본의 y축 좌표 추적

skeletonGraphic.Skeleton.FindBone("본이름").Y * 100f;

스파인 스킨 변경

GetComponent<Spine.Unity.SkeletonGraphic>().Skeleton.SetSkin("스킨 이름");

스파인 스킨을 번호로 가져올때

var skeletonGraphic = GetComponent<Spine.Unity.SkeletonGraphic>();
var skins = skeletonGraphic.AnimationState.Data.SkeletonData.Skins.ToArray();
skeletonGraphic.Skeleton.SetSkin(skins[번호]);

 

 

 

해당 skeletonGraphic의 애니메이션 리스트 가져오기

skeletonGraphic.AnimationState.Data.SkeletonData.Animations.ToArray()

해당 skeletonGraphic의 스킨 리스트 가져오기

skeletonGraphic.AnimationState.Data.SkeletonData.Skins.ToArray()

 

SkeletonDataAsset을 가져올때

public Spine.Unity.SkeletonDataAsset skeletonDataAsset;

 

SkeletonDataAsset 변경

public Spine.Unity.SkeletonDataAsset skeletonDataAsset;

var skeletonGraphic = GetComponent<Spine.Unity.SkeletonGraphic>();
if(skeletonGraphic.skeletonDataAsset!= SkeletonDataAsset)
{
    skeletonGraphic.skeletonDataAsset = SkeletonDataAsset;
    skeletonGraphic.Initialize(true);
}

 

현재 애니메이션의 최대시간

SkeletonGraphic skeletonGraphic = GetComponent<SkeletonGraphic>();
if (skeletonGraphic.gameObject.activeInHierarchy && skeletonGraphic.IsActive())
{
    float duration = skeletonGraphic.AnimationState.Tracks.Items[0].AnimationTime;
}

 

 

정지

skeletonGraphic.timeScale = 0;
if (skeletonGraphic.gameObject.activeInHierarchy && skeletonGraphic.IsActive())
{
    skeletonGraphic.AnimationState.Tracks.Items[0].TrackTime = 0;
}

 

일시정지

skeletonGraphic.timeScale = 0;

 

 

재생


var entry = skeletonGraphic.AnimationState.SetAnimation(0, animation.Name, loop);
entry.MixDuration = 0f;

 

되감기

SkeletonGraphic skeletonGraphic = curtainNormal.GetComponent<SkeletonGraphic>();
if (skeletonGraphic.gameObject.activeInHierarchy && skeletonGraphic.IsActive())
{
    float duration = skeletonGraphic.AnimationState.Tracks.Items[0].AnimationTime;
    skeletonGraphic.unscaledTime = true;
    skeletonGraphic.AnimationState.Tracks.Items[0].TrackTime = duration;
    skeletonGraphic.timeScale = -1;
}

 

 

 

애니메이션이 끝났을때에만 재생(Update에 넣어야함)

if (skeletonGraphic.AnimationState.Tracks.Items.Length == 0)
{
    var entry = skeletonGraphic.AnimationState.SetAnimation(0, animation.Name, loop);
    entry.MixDuration = 0f;
    skeletonGraphic.Initialize(true);
    skeletonGraphic.Update(0f);
}
else
{
    Spine.TrackEntry trackItemNow = skeletonGraphic.AnimationState.Tracks.Items[0];
    trackItemNow.Loop = loop;
    float duration = skeletonGraphic.SkeletonData.FindAnimation(trackItemNow.ToString()).Duration;
    float trackTime = trackItemNow.TrackTime;
    if (trackTime > duration-0.1f)
    {
        var entry = skeletonGraphic.AnimationState.SetAnimation(0, animation.Name, loop);
        entry.MixDuration = 0f;
        skeletonGraphic.Initialize(true);
    }
}

 

 

애니메이션 종료 콜

단점은 루프가 꺼져있어도 애니메이션이 타임만 증가하며 영원히 돌아가는거로 나오기 때문에 작동을 제대로 안함

void Awake()
{
	//애니메이션 루틴이 끝났을경우 호출(루프중이면 주기마다 호출됨)
    GetComponent<SkeletonGraphic>().AnimationState.Complete -= SpineEndEvent;
    GetComponent<SkeletonGraphic>().AnimationState.Complete += SpineEndEvent;
	//애니메이션이 종료되었을경우 호출(제대로 작동 안 함)
    GetComponent<SkeletonGraphic>().AnimationState.End -= SpineEndEvent;
    GetComponent<SkeletonGraphic>().AnimationState.End += SpineEndEvent;
}
void SpineEndEvent(Spine.TrackEntry te)
{
//동작할 코드
}

 

 

스켈레톤 그래픽 사이즈

skeletonGraphic.SkeletonData.Width //가로
skeletonGraphic.SkeletonData.Height //세로

 

 

 

특정폴더의 스켈레톤 데이터를 전부 불러옴


#if UNITY_EDITOR
    public static Spine.Unity.SkeletonDataAsset[] GetFolderToSkeletonDatas(UnityEngine.Object folder)
    {
        var folderPath = UnityEditor.AssetDatabase.GetAssetPath(folder);
        var filePathSkeletonDatas = UnityEditor.AssetDatabase.FindAssets($"t:{typeof(Spine.Unity.SkeletonDataAsset).Name}", new string[] { folderPath });
        var skeletonDatas = new Spine.Unity.SkeletonDataAsset[filePathSkeletonDatas.Length];
        for (int i = 0; i < skeletonDatas.Length; i++)
        {
            skeletonDatas[i] = UnityEditor.AssetDatabase.LoadAssetAtPath<Spine.Unity.SkeletonDataAsset>(UnityEditor.AssetDatabase.GUIDToAssetPath(filePathSkeletonDatas[i]));
        }
        return skeletonDatas;
    }
#endif

 

 

'Unity > C#' 카테고리의 다른 글

사소한 c# 테크닉  (0) 2020.07.08
Parent of RectTransform is being set with parent property  (0) 2020.06.29
SQLite 유니티 사용방법  (0) 2020.05.24
posted by 모카쨩
2020. 6. 29. 18:47 Unity/C#

new_go.transform.parent=target.transform.parent;

를 아래처럼 고침

new_go.transform.SetParent(target.transform.parent);

'Unity > C#' 카테고리의 다른 글

스파인 관련함수  (0) 2020.07.02
SQLite 유니티 사용방법  (0) 2020.05.24
유니티 커스텀 인스펙터  (0) 2020.05.19
posted by 모카쨩
2020. 5. 24. 23:59 Unity/C#

 

https://www.sqlite.org/download.html

여기서 sqlite를 다운받는다

아래거 다운받으면 된다 32비트는 쓰이지 않으니까

 

C:\Program Files\Unity\Editor\Data\Mono\lib\mono\2.0

혹은

C:\Program Files\Unity\Hub\Editor\2019.4.1f1\Editor\Data\MonoBleedingEdge\lib\mono\unityjit

여기에 있는 Mono.Data.Sqlite.dll을 Plugins 폴더에 집어넣고 (굵은 글씨는 현재 유니티 버전)

 

위에서 다운받은 sqlite3.def와 sqlite3.dll도 풀어서 넣는다

그리고 sqllite브라우저로 만든 DB파일도 넣어주면 최종적으로 아래처럼 된다

 

 

V2

 

 

v3

 

 

 

안드로이드도 사용할경우 아까 링크에서 하단의 것도 다운받는다

 

 

 

7-Zip으로 푼다

 

 

 

Android하위에 넣고 이름을 

상기와 같이 수정한다

 

SampleDB.db
0.01MB

그리고

 

 

 

기동시 메모리를 1.0~1.5GB정도 사용한다

 

그냥 이거 써라

SQLite3.unitypackage
1.85MB

 

 

 

 

그리고 옛날기술이라 이제는 더이상 쓰이지 않기는 한다

'Unity > C#' 카테고리의 다른 글

Parent of RectTransform is being set with parent property  (0) 2020.06.29
유니티 커스텀 인스펙터  (0) 2020.05.19
유니티 어트리뷰트  (0) 2020.05.19
posted by 모카쨩
2020. 5. 19. 21:48 Unity/C#

//손이 많이가지만 많은 기능을 쓸수 있습니다.

 

 

 

 

기본형

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;


#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(RoomGoto))]
public class RoomGoto_Inspector : Editor
{
    public override void OnInspectorGUI()
    {
        EditorGUILayout.LabelField("버튼조작을 위한 컴포넌트");
    }
}
#endif

public class RoomGoto : MonoBehaviour
{
    public void room_goto(string room_name)
    {
        SceneManager.LoadScene(room_name);
    }
    public void game_quit()
    {
        Application.Quit();
    }
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

 

기존 인스펙터 호출

public override void OnInspectorGUI()
{
    DrawDefaultInspector();
}

혹은

public override void OnInspectorGUI()
{
    base.OnInspectorGUI();
}

 

 

 

 

버튼 조작시

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();//기본 인스펙터를 받아올때
        serializedObject.Update();
        {
            if (GUILayout.Button("실행"))
            {
                ((SampleClass)target).PressInspecterButton();
            }
            EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(SampleClass.notice)), GUIContent.none);

        }
        serializedObject.ApplyModifiedProperties();


    }
}

#endif
public class SampleClass : MonoBehaviour
{
    [HideInInspector]
    [Header("주석")]
    public string notice;
    
    
    public void PressInspecterButton()
    {
        //버튼을 누를시 활성화될 코드
        if (Application.isPlaying)
        {
            //게임중일때 실행될 코드
        }
        else
        {
            //인스펙터에서 수정중일때 실행될 코드
        }
    }
}

 

 

슬라이더 조작시

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();//기본 인스펙터를 받아올때
        serializedObject.Update();
        {
        
            EditorGUI.BeginChangeCheck();
            {
            	EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(SampleClass.border)));
            }
            if (EditorGUI.EndChangeCheck())
            {
                ((AccountSelect)target).SelectInspecterSlider();
            }
            EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(SampleClass.border)), GUIContent.none);
        }
        serializedObject.ApplyModifiedProperties();


    }
}

#endif
public class SampleClass : MonoBehaviour
{
    [HideInInspector]
    [Header("주석")]
    public string notice;
    
    
    [HideInInspector]
    [Range(0,6)]
    public float border;
    
    public void SelectInspecterSlider()
    {
    	//값변경시 활성화될 코드
    	if (Application.isPlaying)
        {
        	게임중일때 실행될 코드
        }
        else
        {
        	인스펙터에서 수정중일때 실행될 코드
        }
    }
}

 

 

라벨필드

public override void OnInspectorGUI()
{
    EditorGUILayout.LabelField("일반라벨");
    EditorGUILayout.LabelField("굵은글씨", EditorStyles.boldLabel);
}

 

 

 

 

 

메뉴를 만들어야 할때

#if UNITY_EDITOR
using UnityEditor;

public class CustomMenuEditor : Editor
{
    [MenuItem("MyMenu/Run")]
    static void Run()
    {
        //실행되는 코드
    }
}

#endif

 

 

 

 

 

커스텀 윈도우 프로퍼티 필드(수동 serializedObject)

public Object folder;
SerializedObject serializedObject;
void OnGUI()
{
    if (serializedObject == null)
    {
        serializedObject = new SerializedObject(this);
    }
    serializedObject.Update();
    {
        EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(folder)));
    }
    serializedObject.ApplyModifiedProperties();
}

 

 

 

 

비활성필드

GUI.enabled = false;
{
    EditorGUILayout.TextField("Name", "value");
}
GUI.enabled = true;

 

NonSerialized 및 정적필드뷰어

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public void DrawHiddenInspectors()
    {
        serializedObject.Update();
        {
            var fields = target.GetType().GetFields();
            GUI.enabled = false;
            {
                foreach (var field in fields)
                {
                    if ((field.IsStatic) || (field.IsNotSerialized))
                    {

                        var fieldName = field.Name;
                        if (field.IsStatic)
                        {
                            fieldName += " (static)";
                        }
                        var fieldType = field.FieldType;
                        if ((fieldType.IsArray) || ((fieldType.IsGenericType) && fieldType.GetGenericTypeDefinition() == typeof(List<>)))
                        {

                            EditorGUI.indentLevel--;
                            {
                                EditorGUILayout.LabelField($"▼{field.Name}");
                            }
                            EditorGUI.indentLevel++;
                            EditorGUI.indentLevel++;
                            {
                                var arrDatas = (System.Collections.IList)field.GetValue(target);
                                if (arrDatas == null)
                                {
                                    EditorGUILayout.LabelField("null");
                                }
                                else
                                {
                                    EditorGUILayout.TextField($"Size", arrDatas.Count.ToString());
                                    for (int i = 0; i < arrDatas.Count; i++)
                                    {
                                        EditorGUILayout.TextField($"Element {i}", arrDatas[i].ToString());
                                    }
                                }
                            }
                            EditorGUI.indentLevel--;

                        }
                        else if (fieldType.IsClass && (fieldType.IsArray == false) && (fieldType.Equals(typeof(string)) == false))
                        {
                            EditorGUI.indentLevel--;
                            {
                                EditorGUILayout.LabelField($"▼{field.Name}");
                            }
                            EditorGUI.indentLevel++;
                            EditorGUI.indentLevel++;
                            {
                                var fieldValue = field.GetValue(target);
                                var subFields = fieldType.GetFields();
                                foreach (var subField in subFields)
                                {
                                    //subField.GetValue(fieldValue).ToString()
                                    EditorGUILayout.LabelField(subField.Name);
                                }
                            }
                            EditorGUI.indentLevel--;
                        }
                        else
                        {
                            EditorGUILayout.TextField($"{field.Name}", field.GetValue(target).ToString());
                        }
                    }
                }
            }
            GUI.enabled = true;
        }
        serializedObject.ApplyModifiedProperties();
    }
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        DrawHiddenInspectors();

    }
}

#endif
public class SampleClass : MonoBehaviour
{
    public int number = 1;
    public int[] array = new int[] { 1, 2, 3 };
    public static float realStatic = 1.5f;
    public static int numberStatic = 2;
    public static int[] arrayStatic = new int[] { 1,2,3 };



    public static QuestionSelect questionSelect;
    [System.Serializable]
    public class QuestionSelect
    {
        public int number = 1;
        public float real = 2f;
    }
}

 

 

커스텀 readonly

더보기
#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public void BaseInspectorGUI(params string[] readOnlyPropertyNames)
    {
        serializedObject.Update();
        {
            var fields = target.GetType().GetFields();
            foreach (var field in fields)
            {
                if ((field.IsStatic) || (field.IsNotSerialized))
                {
                }
                else
                {
                    if (System.Array.FindIndex(readOnlyPropertyNames, x => x == field.Name) >= 0)
                    {
                        GUI.enabled = false;
                    }
                    EditorGUILayout.PropertyField(serializedObject.FindProperty(field.Name));
                    GUI.enabled = true;
                }
            }
        }
        serializedObject.ApplyModifiedProperties();
    }
    public override void OnInspectorGUI()
    {
        BaseInspectorGUI(nameof(SampleClass.SampleVariable));
    }
}
#endif

 

커스텀 readonly V2

더보기
#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public void BaseOnInspectorGUI(params string[] readOnlyPropertyNames)
    {
        DrawPropertiesExcluding(serializedObject, readOnlyPropertyNames);
        serializedObject.Update();
        {
            GUI.enabled = false;
            foreach (var readOnlyPropertyName in readOnlyPropertyNames)
            {
                EditorGUILayout.PropertyField(serializedObject.FindProperty(readOnlyPropertyName));
            }
            GUI.enabled = true;
        }
    }
    public override void OnInspectorGUI()
    {
        BaseOnInspectorGUI(nameof(SampleClass.SampleVariable));
    }
}
#endif

 

 

 

 

 

 

 

커스텀 히든(v3)

이걸써야 디버그모드에서도 보인다

v2에 비해서 코드는 길어졌지만 script를 숨겨놓게 했다 (m_Script가 표시숨김)

#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public override void OnInspectorGUI()
    {
        GUI.enabled = false;
        {
            var script = MonoScript.FromMonoBehaviour((MonoBehaviour)target);
            EditorGUILayout.ObjectField("Script", script, typeof(MonoScript), false);
        }
        GUI.enabled = true;
        
        serializedObject.Update();
        {
        	DrawPropertiesExcluding(serializedObject, "m_Script",nameof(SampleClass.SampleVariable));
        }
        serializedObject.ApplyModifiedProperties();
    }
}
#endif
더보기

커스텀 히든(v1)

#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public void BaseInspectorGUI(params string[] hiddenPropertyNames)
    {
        serializedObject.Update();
        {
            var fields = target.GetType().GetFields();
            foreach (var field in fields)
            {
                if ((field.IsStatic) || (field.IsNotSerialized))
                {
                }
                else
                {
                    if ((System.Array.FindIndex(hiddenPropertyNames, x => x == field.Name) >= 0)==false)
                    {
                        EditorGUILayout.PropertyField(serializedObject.FindProperty(field.Name));
                    }
                }
            }
        }
        serializedObject.ApplyModifiedProperties();
    }
    public override void OnInspectorGUI()
    {
        BaseInspectorGUI(nameof(SampleClass.SampleVariable));
    }
}
#endif

 

 

커스텀 히든(v2)

이걸써야 디버그모드에서도 보인다

[HideInInspector]는 여전히 안보임

#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        {
        	DrawPropertiesExcluding(serializedObject,nameof(SampleClass.SampleVariable));
        }
        serializedObject.ApplyModifiedProperties();
    }
}
#endif

 

 

 

값변경 미리보기

미리보기수준에서 끝나는게 아니라 실제로 적용하려면 더티플래그를 박아라

#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        serializedObject.Update();
        {
            EditorGUI.BeginChangeCheck();
            {
                EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(SampleClass.SampleValue)));
            }
            if (EditorGUI.EndChangeCheck())
            {
                //변경되었을때 실행
            }
            else
            {
                //인스펙터를 벗어났을때 실행
            }

        }
        serializedObject.ApplyModifiedProperties();
    }
}
#endif

 

 

더티플래그를 이용한 프리팹상 저장



if (GUILayout.Button("Auto Setting"))
{
    AutoFind();
    EditorUtility.SetDirty(target); //이부분
}

 

 

 

 

 

 

ReorderableList 예제

튜토리얼 모드에서는 라이프 미표시하는 ReorderableList 예제

더보기

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR

using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{

    Hashtable reorderableListTable = new Hashtable();
    public void DrawStageDatas(string propertyPath)
    {

        var reorderableListProperty = serializedObject.FindProperty(propertyPath);

        if (reorderableListTable[propertyPath] == null)
        {
            reorderableListTable[propertyPath] = new ReorderableList(serializedObject, reorderableListProperty);
        }
        var reorderableList = (ReorderableList)reorderableListTable[propertyPath];

        serializedObject.Update();
        {

            //헤더명
            reorderableList.drawHeaderCallback = (rect) => EditorGUI.LabelField(rect, $"{propertyPath} ({reorderableListProperty.arraySize})");


            //요소

            var elementName = nameof(SampleClass.StageData.stageName);
            reorderableList.drawElementCallback =
            (Rect rect, int index, bool isActive, bool isFocused) =>
            {
                EditorGUI.PropertyField(rect, reorderableListProperty.GetArrayElementAtIndex(index).FindPropertyRelative(elementName));
            };
            reorderableList.DoLayoutList();


            //선택된 필드
            if (reorderableList.index >= 0)
            {
                var index = reorderableList.index;
                if ((reorderableListProperty != null) && (reorderableListProperty.arraySize > 0) && (index < reorderableListProperty.arraySize))
                {
                    var elementAtIndex = reorderableListProperty.GetArrayElementAtIndex(index);

                    if (elementAtIndex.FindPropertyRelative(nameof(SampleClass.StageData.isTutorial)).boolValue)
                    {
                        //튜토리얼 모드라면 이름과 제한시간만 표시
                        EditorGUILayout.PropertyField(elementAtIndex.FindPropertyRelative(nameof(SampleClass.StageData.isTutorial)));
                        EditorGUILayout.PropertyField(elementAtIndex.FindPropertyRelative(nameof(SampleClass.StageData.timeLimit)));
                    }
                    else
                    {
                        //기본표시
                        foreach (var field in typeof(SampleClass.StageData).GetFields())
                        {
                            if ((field.IsStatic) || (field.IsNotSerialized))
                            {
                            }
                            else
                            {
                                if (field.Name != elementName)
                                {
                                    EditorGUILayout.PropertyField(elementAtIndex.FindPropertyRelative(field.Name));
                                }
                            }
                        }
                        
                        
                        
                        //life값은 0으로 초기화
                        {
                            var sampleClass = (SampleClass)target;
                            var field = sampleClass.GetType().GetField(propertyPath);
                            var datas = (SampleClass.StageData)field.GetValue(sampleClass);
                            datas[index].life = 0;
                            //구조체라면 field.SetValue(sampleClass, datas)를 사용한다
                        }
                    }
                }
            }

        }
        serializedObject.ApplyModifiedProperties();
    }





    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        DrawStageDatas(nameof(SampleClass.stageDatas));
        DrawStageDatas(nameof(SampleClass.stageDatas2));

    }
}
#endif
public class SampleClass : MonoBehaviour
{

    [HideInInspector]
    public StageData[] stageDatas;
    
    [HideInInspector]
    public StageData[] stageDatas2;

    [System.Serializable]
    public class StageData
    {
        public string stageName;
        public bool isTutorial;
        public int timeLimit;
        public int life;
    }


    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }
}

 

 

 

한칸에 여러줄표시하는 ReorderableList 예제

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR

using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(SampleClass))]
public class SampleClassEditor : Editor
{

    Hashtable reorderableListTable = new Hashtable();
    public void DrawStageDatas(string propertyPath)
    {

        var reorderableListProperty = serializedObject.FindProperty(propertyPath);

        if (reorderableListTable[propertyPath] == null)
        {
            reorderableListTable[propertyPath] = new ReorderableList(serializedObject, reorderableListProperty);
        }
        var reorderableList = (ReorderableList)reorderableListTable[propertyPath];

        serializedObject.Update();
        {

            //헤더명
            reorderableList.drawHeaderCallback = (rect) => EditorGUI.LabelField(rect, $"{propertyPath} ({reorderableListProperty.arraySize})");

            //요소크기
            reorderableList.elementHeight = EditorGUIUtility.singleLineHeight * 4;
            
            reorderableList.drawElementCallback =
            (Rect rect, int index, bool isActive, bool isFocused) =>
            {
                var elementProperty = reorderableListProperty.GetArrayElementAtIndex(index);

                var fieldRect = rect;
                fieldRect.height = EditorGUIUtility.singleLineHeight;
                EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(nameof(SampleClass.StageData.stageName)));
                fieldRect.y += EditorGUIUtility.singleLineHeight;
                EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(nameof(SampleClass.StageData.isTutorial)));
                fieldRect.y += EditorGUIUtility.singleLineHeight;
                EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(nameof(SampleClass.StageData.timeLimit)));
                fieldRect.y += EditorGUIUtility.singleLineHeight;
                EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(nameof(SampleClass.StageData.life)));
            };
            reorderableList.DoLayoutList();



        }
        serializedObject.ApplyModifiedProperties();
    }





    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        DrawStageDatas(nameof(SampleClass.stageDatas));
        DrawStageDatas(nameof(SampleClass.stageDatas2));

    }
}
#endif
public class SampleClass : MonoBehaviour
{

    [HideInInspector]
    public StageData[] stageDatas;

    [HideInInspector]
    public StageData[] stageDatas2;

    [System.Serializable]
    public class StageData
    {
        public string stageName;
        public bool isTutorial;
        public int timeLimit;
        public int life;
    }


    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }
}

참고용 단순 예제

 

 

 

 

#if UNITY_EDITOR

using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(SampleClass))]
public class SampleClassEditor : Editor
{

    Hashtable reorderableListTable = new Hashtable();
    public static System.Type GetType(SerializedProperty property)
    {
        var parentType = property.serializedObject.targetObject.GetType();
        var fieldInfo = parentType.GetField(property.propertyPath);
        return fieldInfo.FieldType;
    }
    public void DrawReorderableList(string propertyPath)
    {

        var reorderableListProperty = serializedObject.FindProperty(propertyPath);

        if (reorderableListTable[propertyPath] == null)
        {
            reorderableListTable[propertyPath] = new ReorderableList(serializedObject, reorderableListProperty);
        }
        var reorderableList = (ReorderableList)reorderableListTable[propertyPath];

        serializedObject.Update();
        {

            //헤더명
            reorderableList.drawHeaderCallback = (rect) => EditorGUI.LabelField(rect, $"{propertyPath} ({reorderableListProperty.arraySize})");


            //요소
            var targetType = GetType(reorderableListProperty).GetElementType();

            reorderableList.elementHeight = EditorGUIUtility.singleLineHeight * Mathf.Max(1, targetType.GetFields().Length);

            reorderableList.drawElementCallback =
            (Rect rect, int index, bool isActive, bool isFocused) =>
            {
                var elementProperty = reorderableListProperty.GetArrayElementAtIndex(index);

                var fieldRect = rect;
                fieldRect.height = EditorGUIUtility.singleLineHeight;


                fieldRect.y -= EditorGUIUtility.singleLineHeight;
                foreach (var fields in targetType.GetFields())
                {
                    fieldRect.y += EditorGUIUtility.singleLineHeight;
                    EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(fields.Name));
                }

            };
            reorderableList.DoLayoutList();



        }
        serializedObject.ApplyModifiedProperties();
    }





    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        DrawReorderableList(nameof(SampleClass.stageDatas));
        DrawReorderableList(nameof(SampleClass.stageDatas2));

    }
}
#endif


public class SampleClass : MonoBehaviour
{

    [HideInInspector]
    public StageData[] stageDatas;

    [HideInInspector]
    public StageData[] stageDatas2;

    [System.Serializable]
    public class StageData
    {
        public string stageName;
        public bool isTutorial;
        public int timeLimit;
        public int life;
    }


    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }
}

 

자동화 버전

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

배열길이 임의로 조정

var property=serializedObject.FindProperty(nameof(SampleClass.folders));
if(property.arraySize > 3)
{
    property.arraySize = 3;
}

 

다중배열 표시

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


#if UNITY_EDITOR
//Production Date 2022-10-10 from Ahzkwid
using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
    public void Draw2DArray(string targetFieldName)
    {
        bool IsArrayCustom(System.Type type)
        {
            return (type.IsArray) || ((type.IsGenericType) && type.GetGenericTypeDefinition() == typeof(List<>));
        }
        void DrawElement(string label,object ilistObject)
        {
            var ilist = (System.Collections.IList)ilistObject;
            EditorGUI.indentLevel--;
            {
                EditorGUILayout.LabelField(label);
            }
            EditorGUI.indentLevel++;
            EditorGUI.indentLevel++;
            {
                if (ilist == null)
                {
                    EditorGUILayout.LabelField("null");
                }
                else
                {
                    EditorGUILayout.TextField($"Size", ilist.Count.ToString());
                    for (int i = 0; i < ilist.Count; i++)
                    {
                        if (ilist[i] == null)
                        {
                            EditorGUI.indentLevel++;
                            EditorGUILayout.LabelField("null");
                            EditorGUI.indentLevel--;
                            continue;
                        }
                        var elementType = ilist[i].GetType();
                        if (IsArrayCustom(elementType))
                        {
                            DrawElement($"▼Element {i}", ilist[i]);
                        }
                        else
                        {
                            EditorGUILayout.TextField($"Element {i}", ilist[i].ToString());
                        }
                    }
                }
            }
            EditorGUI.indentLevel--;
        }
        serializedObject.Update();
        {
            var fields = target.GetType().GetFields();
            GUI.enabled = false;
            {
                foreach (var field in fields)
                {
                    var fieldName = field.Name;
                    if (fieldName != targetFieldName)
                    {
                        continue;
                    }
                    if (field.IsStatic)
                    {
                        fieldName += " (static)";
                    }
                    var fieldType = field.FieldType;
                    if (IsArrayCustom(fieldType))
                    {
                        DrawElement($"▼{field.Name}", field.GetValue(target));
                    }
                }
            }
            GUI.enabled = true;
        }
        serializedObject.ApplyModifiedProperties();
    }
    public override void OnInspectorGUI()
    {

        base.OnInspectorGUI();
        Draw2DArray(nameof(SampleClass.sampleArray2));
        Draw2DArray(nameof(SampleClass.sampleArray3));
        Draw2DArray(nameof(SampleClass.sampleArray4));
    }
}
#endif

public class SampleClass : MonoBehaviour
{
    public string[] sampleArray;
    public string[][] sampleArray2;
    public string[][] sampleArray3 = new string[1][];
    public string[][][] sampleArray4 = new string[2][][] { new string[1][], new string[1][] };
    // Start is called before the first frame update
    void Start()
    {
        sampleArray2 = new string[8][];
        for (int i = 0; i < sampleArray.Length; i++)
        {
            sampleArray2[i] = new string[8];
        }



        sampleArray3 = new string[2][];
        for (int i = 0; i < sampleArray3.Length; i++)
        {
            sampleArray3[i] = new string[3];
            for (int j = 0; j < sampleArray3[i].Length; j++)
            {
                sampleArray3[i][j] = $"text[{i}][{j}]";
            }
        }


        sampleArray4 = new string[2][][];
        for (int i = 0; i < sampleArray4.Length; i++)
        {
            sampleArray4[i] = new string[2][];
            for (int j = 0; j < sampleArray4[i].Length; j++)
            {
                sampleArray4[i][j] = new string[2];
                for (int k = 0; k < sampleArray4[i][j].Length; k++)
                {
                    sampleArray4[i][j][k] = $"text[{i}][{j}][{k}]";
                }
            }
        }


    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

 

 

오른쪽으로 한칸 들여쓰기

EditorGUI.indentLevel++;
{
    //들어갈 내용
}
EditorGUI.indentLevel--;

 

 

박스 레이아웃 두르기

GUILayout.Space(50);
EditorGUILayout.LabelField("제목", EditorStyles.boldLabel);
GUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUI.indentLevel++;
{
    //들어갈 내용
}
EditorGUI.indentLevel--;
GUILayout.Space(20);
GUILayout.EndVertical();

 

 

 

 

 

자기 자신 스크립트를 표시

GUI.enabled = false;
{
    var script = MonoScript.FromMonoBehaviour((MonoBehaviour)target);
    EditorGUILayout.ObjectField("Script", script, typeof(MonoScript), false);
}
GUI.enabled = true;

 

 

SerializedProperty To Type

    public static System.Type GetType(SerializedProperty property)
    {
        var parentType = property.serializedObject.targetObject.GetType();
        var fieldInfo = parentType.GetField(property.propertyPath);
        return fieldInfo.FieldType;
    }

 

 

 

 

커스텀 드롭다운

하기는 레이어 드롭다운 예제이다.

var sampleClass = (SampleClass)target;

GUILayout.BeginHorizontal();
{
    GUILayout.Label("Layer");

    int layer = sampleClass.gameObject.layer;
    if (EditorGUILayout.DropdownButton(new GUIContent( LayerMask.LayerToName(layer)), FocusType.Passive))
    {
        GenericMenu menu = new GenericMenu();
        for (int i = 0; i < 32; i++)
        {
            var layerName = LayerMask.LayerToName(i);
            if (string.IsNullOrWhiteSpace(layerName))
            {
                continue;
            }
            menu.AddItem(new GUIContent($"{i}: {layerName}"), false, Callback, i);
        }
        menu.ShowAsContext();


        void Callback(object obj)
        {
            sampleClass.gameObject.layer = (int)obj;
        }
    }
}
GUILayout.EndHorizontal();

 

레이어마스크 드롭다운 예제

근데 내장변수 LayerMask 쓰면 된다 (뻘짓함)

인줄 알았는데 Unsupported type BitField버그때문에 다시 씀

더보기
#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(Crosshair))]
public class CrosshairEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();


        var targetClass = (Crosshair)target;

        serializedObject.Update();
        {
            GUILayout.BeginHorizontal();
            {
                GUILayout.Label("Layer Mask");

                var layerMask = targetClass.layerMask;

                var layerNames = new List<string>();
                for (int i = 0; i < 32; i++)
                {
                    if ((layerMask & (1<<i))==0) //해당 레이어마스크가 미체크면 스킵
                    {
                        continue;
                    }
                    var layerName = LayerMask.LayerToName(i);
                    if (string.IsNullOrWhiteSpace(layerName))
                    {
                        layerName = i.ToString();
                    }
                    layerNames.Add(layerName);
                }
                var name = "";
                switch (layerMask)
                {
                    case 0: //Nothing
                        name = "Nothing";
                        break;
                    case -1: //Everything (-1,~0)
                        name = "Everything";
                        break;
                    default:
                        if (layerNames.Count>3)
                        {
                            name = "Mixed...";
                        }
                        else
                        {
                            name = string.Join(", ", layerNames);
                        }
                        break;
                }
                if (EditorGUILayout.DropdownButton(new GUIContent(name), FocusType.Passive))
                {
                    var menu = new GenericMenu();

                    menu.AddItem(new GUIContent($"Nothing"), layerMask == 0, Callback, 0); //Nothing
                    menu.AddItem(new GUIContent($"Everything"), layerMask == -1, Callback, -1); //Everything
                    for (int i = 0; i < 32; i++)
                    {
                        var layerName = LayerMask.LayerToName(i);

                        var on = (layerMask & (1 << i)) != 0;
                        if (string.IsNullOrWhiteSpace(layerName))
                        {
                            continue;
                        }
                        menu.AddItem(new GUIContent($"{i}: {layerName}"), on, Callback, 1 << i);
                    }
                    menu.ShowAsContext();


                    void Callback(object obj)
                    {
                        switch ((int)obj)
                        {
                            case 0: //Nothing
                            case -1: //Everything (-1,~0)
                                targetClass.layerMask = (int)obj;
                                break;
                            default:
                                targetClass.layerMask ^= (int)obj;
                                break;
                        }
                    }
                }
            }
            GUILayout.EndHorizontal();

        }
        serializedObject.ApplyModifiedProperties();
    }
}
#endif

 

 

 

 

enum to dropdown 예제

아주 가끔 쓰인다

#if UNITY_EDITOR

    using UnityEditor;
    [CustomEditor(typeof(PickupSystem))]
    public class PickupSystemInspecter : Editor
    {
        public enum Piece
        {
            None, Pawn, Knight, Bishop, Rook, Queen, King
        }
        public override void OnInspectorGUI()
        {
            GUI.enabled = false;
            {
                var script = MonoScript.FromMonoBehaviour((MonoBehaviour)target);
                EditorGUILayout.ObjectField("Script", script, typeof(MonoScript), false);
            }
            GUI.enabled = true;


            serializedObject.Update();
            {
                DrawPropertiesExcluding(serializedObject, "m_Script", nameof(PickupSystem.piece));

                GUILayout.BeginHorizontal();
                {
                    GUILayout.Label(nameof(PickupSystem.piece));
                    var pickupSystem = target as PickupSystem;
                    var names = System.Enum.GetNames(typeof(Piece));
                    if (EditorGUILayout.DropdownButton(new GUIContent(names[pickupSystem.piece]), FocusType.Passive))
                    {
                        var menu = new GenericMenu();
                        for (int i = 0; i < names.Length; i++)
                        {
                            if (string.IsNullOrWhiteSpace(names[i]))
                            {
                                continue;
                            }
                            menu.AddItem(new GUIContent($"{i}: {names[i]}"), false, Callback, i);
                        }
                        menu.ShowAsContext();


                        void Callback(object obj)
                        {
                            pickupSystem.piece = (int)obj;
                        }
                    }
                }
                GUILayout.EndHorizontal();


            }
            serializedObject.ApplyModifiedProperties();
        }
    }
#endif

 

 

 

 

 

 

텍스처 표시

        {
            var preview = AssetPreview.GetAssetPreview(loadComputeShader.renderTexture);
            GUILayout.Label(preview);
            EditorGUILayout.LabelField(nameof(LoadComputeShader.renderTexture));
        }

 

 

 

MinMax슬라이더

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


#if UNITY_EDITOR

using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();//기본 인스펙터
        serializedObject.Update();
        {
            var sampleClass = (SampleClass)target;
            var minLimit = sampleClass.minLimit;
            var maxLimit = sampleClass.maxLimit;
            EditorGUILayout.LabelField($"MinMaxSlider ({minLimit},{sampleClass.minVal},{sampleClass.maxVal},{maxLimit})");
            EditorGUILayout.MinMaxSlider(ref sampleClass.minVal, ref sampleClass.maxVal, minLimit, maxLimit);
        }
        serializedObject.ApplyModifiedProperties();


    }
}

#endif


public class SampleClass : MonoBehaviour
{
    public float minVal = -10;
    public float minLimit = -20;
    public float maxVal = 10;
    public float maxLimit = 20;
    // Start is called before the first frame update
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

'Unity > C#' 카테고리의 다른 글

SQLite 유니티 사용방법  (0) 2020.05.24
유니티 어트리뷰트  (0) 2020.05.19
UGui 관련  (0) 2020.05.19
posted by 모카쨩

  • total
  • today
  • yesterday

Recent Post

저사양 유저용 블로그 진입