카테고리 보관물: Unity3D

[Unity] TextMeshPro 코드 버전을 Dll 버전으로 변경

이번에 프로젝트의 버전을 2017.4에서 2018.4 버전으로 업데이트 하면서 예전에 구매해서 사용중이던 TextMeshPro의 버전업도 진행하게 되었습니다.
문제는 Unity에서 TextMeshPro를 포함하기 전에 구매한 유저들은 SourceCode가 포함된 형태로 사용 중이었는데 Unity에서 기본 배포하게 되면서 Dll 형태로 배포되기 시작했습니다.
그래서 Unity에서 배포중인 패키지를 설치하게 되면 소스코드와 Dll이 충돌하게 되고 소스코드를 삭제하면 Scene에 사용중이던 TextMeshPro Component들이 전부 Missing 상태로 변경됩니다.
Python으로 Scene, Prefab의 내용을 직접 수정하는 툴을 작업하고 있었는데 알고보니 TextMeshPro에서 기본적으로 그러한 기능을 제공하고 있었습니다.

이번에 버전업을 했던 절차를 정리하면 아래와 같습니다.

  • 2018.4로 버전업을 진행했습니다.
  • Package Manager에서 TextMeshPro를 설치합니다.
  • TextMeshPro Dll과 코드가 중복됨으로 인해서 에러가 엄청나게 발생하고 있습니다. 기존에 사용하던 TextMeshPro 폴더를 삭제합니다.
  • Asmdef 기능을 사용하고 있다면 Dll의 참조가 안됨으로 인해서 에러가 발생하고 있습니다. Asmdef에 새로운 TextMeshPro 참조를 추가해 줍니다.
  • 그래도 에러가 발생하고 있다면 TextMeshPro API 변경으로 인한 것이므로 코드 수정을 통해서 해결해 줍니다.
    • TMP_FontUtilities.SearchForGlyph API가 없어졌습니다. TMP_FontUtilities.SearchForCharacter로 변경해 줬습니다.
    • 임의로 수정해서 사용하던 OnTextChanged 이벤트가 없어서 havePropertiesChanged Property가 true가 되었을 때 LateUpdate() 함수에서 변경을 체크하도록 수정하였습니다.
  • Window - TextMeshPro - Project Files GUID Remapping Tool 을 실행해 줍니다.
  • Scan Project Files 버튼을 클릭하여 Scene과 Prefab, Font Asset, Font Material 파일들을 검색하여 Package Manager로 추가한 새 TextMeshPro 스크립트를 참조하도록 갱신해 줍니다.
  • 검색 후 Save Modified Project Files 를 클릭해 주면 문제가 깔끔하게 사라지면서 새 버전의 TextMeshPro를 사용할 수 있게 되었습니다.

[Firebase] GooglePlay 로그인 실패. the Google id_token is not allowed to be used with this application.

Firebase를 사용한 GooglePlay 인증을 사용하던 중 어느날 갑자기 로그인 실패 오류가 발생하기 시작했습니다.

아무리 검색해 봐도 원인을 파악하기가 힘들어 Firebase Support에 문의를 해보니 다행히 문제를 해결할 수 있었습니다.

문제의 원인은 Firebase Project와 Google Project가 제대로 일치하지 않아서 발생하는 문제였습니다. 그동안은 어째서 잘 동작해왔는지가 오히려 의문스러운 원인...

해결 방법은 아래와 같습니다.

  1. FirebaseConsole로 이동합니다.
  2. 메뉴에서 Authentication -> 로그인방법 -> Google 을 선택합니다.

  3. 외부 프로젝트의 클라이언트 ID 허용(선택사항) 부분에 GooglePlay 인증에 사용한 OAuth 2.0 ClientID를 입력 후 추가해 줍니다.

 

이것으로 문제의 해결이 가능합니다.

잘 되다가 갑자기 문제가 발생한 부분에 대해서는 추가 문의후 기록을 남길 예정.

[Unity3D] Google Play Games Plugin에서 ERROR_NOT_AUTHORIZED 오류로 로그인 실패 또는 IdToken 받아오기 실패

Unity에서 Google 로그인을 구현하기 위해서는 Google Play Games Service Plugin(gpgs)을 사용해야 합니다.

저는 Firebase를 통해서 Google Play, Facebook, Guest 로그인을 구현하는 과정이었고, Firebase에서 Google Play 인증을 위해서는 Google Play에서 IdToken을 받아서 Firebase 인증 모듈에 전달해줘야 합니다.

처음 만난 문제는 gpgs에서 로그인은 성공하여 Social.localUser.id 정보는 들어 있지만 IdToken, Email, ServerAuthCode 등의 정보는 비어있어서 Firebase에 인증 정보를 넘길 수 없었습니다.

이때의 gpgs 초기화 코드는 아래의 상태였습니다.

// GPG를 Activate()하면 Social.localUser가 GPG의 계정 정보로 설정됨
// 안드로이드 빌더 초기화
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
    .RequestServerAuthCode(false)
    .RequestIdToken()
    .RequestEmail()
    .Build();

PlayGamesPlatform.InitializeInstance(config);

// 구글 플레이 로그를 확인할려면 활성화
PlayGamesPlatform.DebugLogEnabled = true;

// 구글 플레이 활성화
PlayGamesPlatform.Activate();

분명히 config를 통해서 RequestIdToken()을 호출하여 요청을 추가하였음에도 불구하고 IdToken 항목이 비어있었습니다.

Unity Log를 확인한 것으로는 원인을 찾을 수 없었고, Android Studio의 Logcat으로 디바이스 로그를 확인하던 중 이상한 부분을 발견할 수 있었습니다.

06-11 22:50:17.497 7610-7663/? I/Unity: Try google play login
06-11 22:50:17.510 7610-7663/? I/Unity: Starting Auth with token client.
06-11 22:50:17.563 7610-7663/? W/Unity: !!! [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 WARNING: Creating new PlayGamesPlatform
 
 (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
06-11 22:50:17.564 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: Activating PlayGamesPlatform.
06-11 22:50:17.565 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: PlayGamesPlatform activated: GooglePlayGames.PlayGamesPlatform
06-11 22:50:17.566 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: Activating PlayGamesPlatform.
06-11 22:50:17.567 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: PlayGamesPlatform activated: GooglePlayGames.PlayGamesPlatform
06-11 22:50:17.568 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: Creating platform-specific Play Games client.
 [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: Creating Android IPlayGamesClient Client
06-11 22:50:17.578 7610-7663/? D/TokenFragment: Creating fragment
06-11 22:50:17.579 7610-7610/? D/TokenFragment: onStart()
06-11 22:50:17.585 7610-7610/? D/TokenFragment: onResume called
 Building client for: a98c718 (a:false e:true i:true wc: 619017498880-4eeh5dlhl8jso58368gv4tf0s2qs4s8i.apps.googleusercontent.com f: false)
06-11 22:50:17.587 7610-7663/? I/Unity: ---- [0] -- 105
06-11 22:50:17.592 7610-7610/? W/PopupManager: You have not specified a View to use as content view for popups. Falling back to the Activity content view. Note that this may not work as expected in multi-screen environments
06-11 22:50:17.617 5637-6375/? D/[WeatherGearO(220318052351)]: {[EF07ED23AAA1263A1FC0BD061AAAD81CFEF15A971ABD51417EE09B7660DD4FB818A9462C7397822725FFF2738B19ADA7]}
06-11 22:50:17.648 7610-7610/? D/TokenFragment: No connected Games API
06-11 22:50:17.649 7610-7610/? E/TokenFragment: Setting result error code to: 13
06-11 22:50:17.653 5637-5637/? D/[WeatherGearO(220318052351)]: {[20CE3D41AD5C8B99E86585ABAB0BF03E702BEB2FC2CA5F53D66FB562B77F4529B627A611ACF593EFCAECBBA4E800B8A9081BC429F8CCD72700680F6A1D6A48CC]}
06-11 22:50:17.657 5637-5637/? D/[WeatherGearO(220318052351)]: {[E5B16E2CDC4F169AEF5B4319C5BDF7110156A5C36B5B4FD8C066ADFAD237FF3B8D85C1771AE8244AE78CE8BB9C6079741D01C71F84AB90238AB7DED8E9D360BC]}
06-11 22:50:17.658 5637-5637/? D/[WeatherGearO(220318052351)]: {[E5B16E2CDC4F169AEF5B4319C5BDF711320D5E9C4351D1F5E6CB8FF1DF59CF2C64BE1F0F10EBA2467C1DD93AB2A27A40A65FAEFDFFB43F75B64AAEDA7C3A5B59]}
06-11 22:50:17.659 5637-5637/? D/[WeatherGearO(220318052351)]: {[20CE3D41AD5C8B99E86585ABAB0BF03EE5160ED4AAE0971C0D182570D628C6CD0253855F5B1B3CDBA2912A6CEF37A6C4]}
06-11 22:50:17.668 5637-5637/? D/[WeatherGearO(220318052351)]: {[20CE3D41AD5C8B99E86585ABAB0BF03E856D7DF2593883BBCF72D0E06237841289C8E379B2D1A1A78B856DF6D06ACDC0]}
06-11 22:50:17.672 5637-5637/? D/[SA_SDK]SAAgent: SAAgent - onDestroy:SAWeather_Service
06-11 22:50:17.673 5637-6370/? W/[SA_SDK]SAAgent: Performing agent cleanup
06-11 22:50:17.673 5637-6370/? I/[SA_SDK]SASocket: Connection is already closed
06-11 22:50:17.675 5637-5637/? D/[WeatherGearO(220318052351)]: {[EF07ED23AAA1263A1FC0BD061AAAD81CA3F446FA4E808076CF16D0F746E839D242804AFF8476C203988BABCD46197438]}
06-11 22:50:17.676 5637-6370/? I/[SA_SDK]SAAgent: Agent ID retrieved successfully for com.samsung.accessory.saweather.service.SAWeather_Service Agent ID:37723
06-11 22:50:17.676 5909-5947/? I/SAFrameworkConnection: Clean up fwk connection agentId:37723 com.samsung.android.gearoplugin
06-11 22:50:17.680 5909-6078/? V/SAFrameworkConnection: Removed incremental update callback for:/system/weather2_0
06-11 22:50:17.680 5637-6370/? D/[SA_SDK]SAAdapter: Agent callback removed. Current size - 10
06-11 22:50:17.685 7610-7610/? D/TokenPendingResult: Calling onResult for callback: GooglePlayGames.Android.TokenResultCallback result: Status: Status{statusCode=ERROR, resolution=null} email: <null> id:<null> access: <null>
06-11 22:50:17.708 5173-30288/? I/AuthChimeraService: Executing send connection operation
06-11 22:50:17.715 5173-28166/? W/FirebaseAuth: [PhoneNumberAuthPostProcessor] postProcess starts
 [PhoneNumberAuthPostProcessor] postProcess ends
06-11 22:50:17.716 7610-7858/? D/FirebaseAuth: Notifying id token listeners about user ( tY2DUCs391aFgwIVLufabvlvSwP2 ).
06-11 22:50:17.732 7610-7610/? D/GamesUnitySDK: Performing Android initialization of the GPG SDK
06-11 22:50:17.771 7610-7610/? I/Unity: Building GPG services, implicitly attempts silent auth
06-11 22:50:17.773 7610-7610/? I/GamesNativeSDK: Using existing jar.
06-11 22:50:17.776 7610-7610/? I/GamesNativeSDK: Writing 1941 bytes to jar file
06-11 22:50:17.779 7610-7610/? W/zygote: Skipping duplicate class check due to unrecognized classloader
06-11 22:50:17.784 7610-7610/? I/GamesNativeSDK: Using existing jar.
 Writing 1066 bytes to jar file
06-11 22:50:17.787 7610-7610/? W/zygote: Skipping duplicate class check due to unrecognized classloader
06-11 22:50:17.793 7610-7610/? W/PopupManager: You have not specified a View to use as content view for popups. Falling back to the Activity content view. Note that this may not work as expected in multi-screen environments
06-11 22:50:17.794 7610-7897/? I/GamesNativeSDK: Auth operation started: SIGN IN
 Connecting to Google Play...
06-11 22:50:17.814 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: Starting Auth Transition. Op: SIGN_IN status: 13
06-11 22:50:17.815 7610-7610/? D/TokenFragment: Done with processRequest, result is pending.
06-11 22:50:17.815 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: Invoking callbacks, AuthState changed from silentPending to Unauthenticated.
06-11 22:50:17.816 7610-7610/? D/FirebaseApp: Notifying auth state listeners.
 Notified 0 auth state listeners.
06-11 22:50:17.850 5173-10655/? W/GamesServiceBroker: Client connected with SDK 12211000, Services 12685025, and Games 59040048
06-11 22:50:17.850 5173-5886/? W/GamesServiceBroker: Client connected with SDK 12211000, Services 12685025, and Games 59040048
06-11 22:50:17.861 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:17 +09:00 DEBUG: there are pending auth callbacks - starting AuthUI
06-11 22:50:17.880 7610-7663/? I/Unity: Token[0:8] = eyJhbGci
06-11 22:50:17.909 5173-3007/? W/GamesServiceBroker: Client connected with SDK 13004000, Services 12685025, and Games 59040048
06-11 22:50:17.974 5173-7898/? I/AsyncOpDispatcher: 44-AuthAccountWithNoServerAuthCallback
06-11 22:50:18.085 5112-8279/? I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
06-11 22:50:18.181 3254-3254/? I/android.hardware.wifi@1.0-service: getLinkLayerStats
06-11 22:50:18.200 3788-3811/? D/StorageManagerService: getExternalStorageMountMode : 2
06-11 22:50:18.201 3788-3811/? D/StorageManagerService: getExternalStorageMountMode : 3
06-11 22:50:18.208 3788-3811/? D/StorageManagerService: getExternalStorageMountMode : final mountMode=2, uid : 10220, packageName : com.skt.prod.phonebook
06-11 22:50:18.217 3788-3811/? I/ApplicationPolicy: isApplicationExternalStorageWhitelisted:com.skt.prod.phonebook user:0
06-11 22:50:18.217 3788-3811/? D/ApplicationPolicy: isApplicationExternalStorageWhitelisted: DO is not enabled on user 0. Allowed.
06-11 22:50:18.217 3788-3811/? D/ActivityManager: package com.skt.prod.phonebook, user - 0 is SDcard whitelisted
06-11 22:50:18.217 3788-3811/? I/ApplicationPolicy: isApplicationExternalStorageBlacklisted:com.skt.prod.phonebook user:0
06-11 22:50:18.217 3788-3811/? D/ApplicationPolicy: isApplicationExternalStorageBlacklisted: DO is not enabled on user 0. Allowed.
06-11 22:50:18.241 5112-8279/? W/Auth: [GetToken] GetToken failed with status code: NeedPermission
06-11 22:50:18.257 3788-3811/? W/ActivityManager: Slow operation: 60ms so far, now at startProcess: returned from zygote!
 Slow operation: 60ms so far, now at startProcess: done updating battery stats
06-11 22:50:18.258 3788-3811/? W/ActivityManager: Slow operation: 60ms so far, now at startProcess: building log message
06-11 22:50:18.258 3788-3811/? I/ActivityManager: Start proc 7899:com.skt.prod.phonebook/u0a220 for service com.skt.prod.phonebook/.sync.SyncManagerService
06-11 22:50:18.258 3788-3811/? W/ActivityManager: Slow operation: 61ms so far, now at startProcess: starting to update pids map
 Slow operation: 61ms so far, now at startProcess: done updating pids map
 Slow operation: 63ms so far, now at startProcess: done starting proc!
06-11 22:50:18.260 7899-7899/? E/Zygote: isWhitelistProcess - Process is Whitelisted
06-11 22:50:18.273 7899-7899/? W/SELinux: SELinux selinux_android_compute_policy_index : Policy Index[2], Con:u:r:zygote:s0 RAM:SEPF_SM-G950N_8.0.0_0005, [-1 -1 -1 -1 0 1]
06-11 22:50:18.276 7899-7899/? I/SELinux: SELinux: seapp_context_lookup: seinfo=trustonicpartner, level=s0:c512,c768, pkgname=com.skt.prod.phonebook 
06-11 22:50:18.301 5173-7321/? W/Auth: [GoogleAuthUtil] GoogleAuthUtil
06-11 22:50:18.326 5173-3007/? W/GamesServiceBroker: Client connected with SDK 13004000, Services 12685025, and Games 59040048
06-11 22:50:18.330 7610-7610/? I/TokenFragment: onConnected called
06-11 22:50:18.339 3788-4468/? I/ClientCertificateManager Service: fixContextInfoForMP() returning Caller uid: 10220 ,Container id: 0
 fixContextInfoForMP() returning Caller uid: 10220 ,Container id: 0
 ClientCertificateManager.isPremiumContainer() : false for user : 0
06-11 22:50:18.341 5173-3007/? W/GamesServiceBroker: Client connected with SDK 13004000, Services 12685025, and Games 59040048
06-11 22:50:18.355 3788-4622/? I/ClientCertificateManager Service: fixContextInfoForMP() returning Caller uid: 10220 ,Container id: 0
06-11 22:50:18.356 3788-4622/? I/ClientCertificateManager Service: fixContextInfoForMP() returning Caller uid: 10220 ,Container id: 0
 ClientCertificateManager.isPremiumContainer() : false for user : 0
06-11 22:50:18.371 7899-7899/? D/ActivityThread: Added TimaKeyStore provider
06-11 22:50:18.372 5173-7898/? I/AsyncOpDispatcher: 44-AuthAccountWithNoServerAuthCallback
06-11 22:50:18.375 3788-4323/? I/ActivityManager: DSS on for com.skt.prod.phonebook and scale is 1.0
06-11 22:50:18.457 3788-4323/? W/AppOps: Bad call: specified package com.google.android.play.games under uid 10382 but it is really 10250
 java.lang.RuntimeException: here
 at com.android.server.AppOpsService.getOpsRawLocked(AppOpsService.java:1402)
 at com.android.server.AppOpsService.checkPackage(AppOpsService.java:1106)
 at com.android.internal.app.IAppOpsService$Stub.onTransact(IAppOpsService.java:169)
 at android.os.Binder.execTransact(Binder.java:682)
06-11 22:50:18.487 7899-7916/? I/vndksupport: sphal namespace is not configured for this process. Loading /vendor/lib/egl/libGLES_mali.so from the current namespace instead.
06-11 22:50:18.525 5173-3007/? W/GamesServiceBroker: Client connected with SDK 13004000, Services 12685025, and Games 59040048
06-11 22:50:18.528 7899-7899/? D/FirebaseApp: com.google.firebase.auth.FirebaseAuth is not linked. Skipping initialization.
06-11 22:50:18.544 7899-7899/? D/FirebaseApp: com.google.firebase.crash.FirebaseCrash is not linked. Skipping initialization.
06-11 22:50:18.551 7899-7916/? D/libEGL: loaded /vendor/lib/egl/libGLES_mali.so
06-11 22:50:18.557 7899-7899/? I/FA: App measurement is starting up, version: 11020
 To enable debug logging run: adb shell setprop log.tag.FA VERBOSE
06-11 22:50:18.571 7610-7610/? V/GamesNativeSDK: Play Games callback indicates connection.
06-11 22:50:18.593 7610-7897/? I/GamesNativeSDK: Successfully connected to Google Play.
06-11 22:50:18.598 7899-7899/? I/FA: To enable faster debug mode event logging run:
 adb shell setprop debug.firebase.analytics.app com.skt.prod.phonebook
06-11 22:50:18.604 3788-4323/? D/SamsungAlarmManager: Cancel Alarm calling from uid:10220 pid :7899 / op:PendingIntent{bddad2c: PendingIntentRecord{42e53f5 com.skt.prod.phonebook broadcastIntent}}
06-11 22:50:18.611 7899-7899/? I/FirebaseInitProvider: FirebaseApp initialization successful
06-11 22:50:18.619 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Starting Auth Transition. Op: SIGN_IN status: VALID
06-11 22:50:18.632 5173-10310/? I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
06-11 22:50:18.664 3788-4468/? D/ConnectivityService: filterNetworkStateForUid() uid: 10220 networkInfo: [type: WIFI[] - WIFI, state: CONNECTED/CONNECTED, reason: (unspecified), extra: "starter_5G", failover: false, available: true, roaming: false, metered: false]
06-11 22:50:18.674 3788-4468/? W/StorageManager: getStorageLowBytes lowPercent : 5, lowBytes : 2913012121, maxLowBytes : 524288000
06-11 22:50:18.679 3788-5348/? W/AppOps: Bad call: specified package com.google.android.play.games under uid 10382 but it is really 10250
 java.lang.RuntimeException: here
 at com.android.server.AppOpsService.getOpsRawLocked(AppOpsService.java:1402)
 at com.android.server.AppOpsService.checkPackage(AppOpsService.java:1106)
 at com.android.internal.app.IAppOpsService$Stub.onTransact(IAppOpsService.java:169)
 at android.os.Binder.execTransact(Binder.java:682)
06-11 22:50:18.680 3788-4468/? W/StorageManager: getStorageLowBytes lowPercent : 5, lowBytes : 2913012121, maxLowBytes : 524288000
06-11 22:50:18.681 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Entering internal callback for PlayerManager#InternalFetchSelfCallback
06-11 22:50:18.682 3788-4468/? W/AppOps: Bad call: specified package com.google.android.play.games under uid 10382 but it is really 10250
 java.lang.RuntimeException: here
 at com.android.server.AppOpsService.getOpsRawLocked(AppOpsService.java:1402)
 at com.android.server.AppOpsService.checkPackage(AppOpsService.java:1106)
 at com.android.internal.app.IAppOpsService$Stub.onTransact(IAppOpsService.java:169)
 at android.os.Binder.execTransact(Binder.java:682)
06-11 22:50:18.682 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Populating User
06-11 22:50:18.687 3788-4468/? W/StorageManager: getStorageLowBytes lowPercent : 5, lowBytes : 2913012121, maxLowBytes : 524288000
06-11 22:50:18.691 3788-4468/? W/StorageManager: getStorageLowBytes lowPercent : 5, lowBytes : 2913012121, maxLowBytes : 524288000
06-11 22:50:18.696 3788-4468/? W/AppOps: Bad call: specified package com.google.android.play.games under uid 10382 but it is really 10250
 java.lang.RuntimeException: here
 at com.android.server.AppOpsService.getOpsRawLocked(AppOpsService.java:1402)
 at com.android.server.AppOpsService.checkPackage(AppOpsService.java:1106)
 at com.android.internal.app.IAppOpsService$Stub.onTransact(IAppOpsService.java:169)
 at android.os.Binder.execTransact(Binder.java:682)
06-11 22:50:18.713 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Found User: [Player: 'PlayerID' (id g44786123847612328794)]
06-11 22:50:18.714 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Maybe finish for User
06-11 22:50:18.714 3788-4468/? W/StorageManager: getStorageLowBytes lowPercent : 5, lowBytes : 2913012121, maxLowBytes : 524288000
06-11 22:50:18.715 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Auth not finished. User=[Player: 'PlayerID' (id g44786123847612328794)] achievements=
06-11 22:50:18.719 3788-5348/? W/AppOps: Bad call: specified package com.google.android.play.games under uid 10382 but it is really 10250
 java.lang.RuntimeException: here
 at com.android.server.AppOpsService.getOpsRawLocked(AppOpsService.java:1402)
 at com.android.server.AppOpsService.checkPackage(AppOpsService.java:1106)
 at com.android.internal.app.IAppOpsService$Stub.onTransact(IAppOpsService.java:169)
 at android.os.Binder.execTransact(Binder.java:682)
06-11 22:50:18.720 3788-4468/? W/AppOps: Bad call: specified package com.google.android.play.games under uid 10382 but it is really 10250
 java.lang.RuntimeException: here
 at com.android.server.AppOpsService.getOpsRawLocked(AppOpsService.java:1402)
 at com.android.server.AppOpsService.checkPackage(AppOpsService.java:1106)
 at com.android.internal.app.IAppOpsService$Stub.onTransact(IAppOpsService.java:169)
 at android.os.Binder.execTransact(Binder.java:682)
06-11 22:50:18.748 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Entering internal callback for AchievementManager#InternalFetchAllCallback
06-11 22:50:18.749 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Populating Achievements, status = VALID
06-11 22:50:18.793 7899-7899/? W/Notification: Use of stream types is deprecated for operations other than volume control
 See the documentation of setSound() for what to use instead with android.media.AudioAttributes to qualify your playback use case
06-11 22:50:18.814 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Found 1 Achievements
06-11 22:50:18.815 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Maybe finish for Achievements
06-11 22:50:18.816 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Auth finished. Proceeding.
06-11 22:50:18.817 7610-7663/? I/Unity: [Play Games Plugin DLL] 06/11/18 22:50:18 +09:00 DEBUG: Invoking Callbacks: System.Action`2[System.Boolean,System.String]
06-11 22:50:18.820 7610-7663/? I/Unity: Gpgs authenticate success.

위의 로그에서 아래의 한줄을 발견할 수 있었습니다.

06-11 22:50:18.241 5112-8279/? W/Auth: [GetToken] GetToken failed with status code: NeedPermission

Permission이라는 힌트를 가지고 두가지 해결방법을 검색해 볼 수 있었습니다.

첫번째는 AndroidManifest.xml 파일에
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
권한을 추가해서 해결이 되었다는 내용이었습니다만 이걸로는 해결되지 않았습니다.

두번째는 config에 AddOauthScope를 추가해 보라는 내용이었고 이걸으로 해결할 수 있었습니다.
이 해결책을 보고 AddOauthScope 함수의 선언부를 보았더니 아래와 같은 주석이 달려 있었습니다.

/// <summary>
/// Requests an Oauth scope from the user.
/// </summary>
/// <remarks>
/// Not setting one will default to 'games_lite' and will not show a consent
/// dialog to the user. Valid examples are 'profile' and 'email'.
/// Full list: https://developers.google.com/identity/protocols/googlescopes
/// To exchange the auth code with an id_token (or user id) on your server,
/// you must add at least one scope.
/// </remarks>
/// <returns>The builder.</returns>
public Builder AddOauthScope(string scope)
{
    if (mScopes == null) mScopes = new List<string>();
    mScopes.Add(scope);
    return this;
}

아무것도 설정하지 않을경우 'games_lite' 라는 scope가 설정된다고 하는데... 주석에 달려 있는 링크를 따라가보니 games_lite라는 scope는 존재하지 않았습니다.

https://developers.google.com/identity/protocols/googlescopes

그래서 위 링크에 있는 scope에서 Google Play Game Service와 관련된 scope를 추가하여 초기화 코드를 아래와 같이 수정하였습니다.

// GPG를 Activate()하면 Social.localUser가 GPG의 계정 정보로 설정됨
// 안드로이드 빌더 초기화
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
    .AddOauthScope("profile")
    .AddOauthScope("email")
    .AddOauthScope("https://www.googleapis.com/auth/games")
    .AddOauthScope("https://www.googleapis.com/auth/plus.login")
    .RequestServerAuthCode(false)
    .RequestIdToken()
    .RequestEmail()
    .Build();
PlayGamesPlatform.InitializeInstance(config);

// 구글 플레이 로그를 확인할려면 활성화
PlayGamesPlatform.DebugLogEnabled = true;

// 구글 플레이 활성화
PlayGamesPlatform.Activate();

이 코드를 통해서 해결하긴 했는데... 문제는 이 코드를 수정한 업데이트를 디바이스에 적용하고 즉시 수정되진 않고 또다른 에러가 발생하면서 로그인에 실패합니다.

이제는 Unity Log에서도 볼 수 있는 ERROR_NOT_AUTHORIZED 에러가 발생하게 됩니다. 이 경우에 캐싱된 로그인 정보를 강제로 SignOut 처리를 해준 후 다시 로그인을 시도하거나 게임을 삭제 후 다시 설치하여 실행하면 됩니다.

그러면 Google Play 로그인과 함께 권한을 물어보는 윈도우가 열리게 되고 허용을 선택하면 로그인에 성공하게 됩니다.

Document랑 예제 좀 탄탄하게 정리해주면 안되나...

[Unity3D] SceneVew에서 targetparametercountexception이 발생할 때

Unity에서 Editor 기능으 구현하다보니 SceneView에서 TargetParameterCountException이 발생하면서 SceneView에 아무것도 보이지 않는 현상이 발생했습니다.

CustomEditor에서 SceneView에 뭔가 표시하기 위한 방식은 아래와 같이 두가지가 있습니다.  이 두가지 스타일을 섞어서 사용할 경우 Exception이 발생합니다. 한가지 형태로 통일하면 Exception을 해결할 수 있습니다.

void OnEnable() {
    SceneView.onSceneGUIDelegate += OnSceneGUI;
}

void OnSceneGUI(SceneView sv) {}
[CustomEditor(typeof(SomeType))]
class SomeType : Editor {
    void OnSceneGUI() {}
}

 

[Unity3D] Destroy 함수와 OnDestroy

로직을 만들다보니 특정 조건일 때 Start() 함수에서 Destroy를 호출할 일이 생겼습니다. 그런데 문득 OnDestroy는 어느 타이밍에 호출되는 것인지 정확히 알고 싶어졌습니다.

그래서 아래와 같은 코드로 테스트를 해봤습니다.

using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour {
    // Use this for initialization
    void Start () {
        Destroy(gameObject);
        Debug.Log("Test Start");
    }

    void OnDestroy() {
        Debug.Log("Test Destroy");
    }
}

 

Destroy 함수를 호출하여 Object를 제거하면 즉시 제거되는 것이 아니라 해당 Frame의 마지막에 제거됩니다. 이런식으로 동작하는 것은 복잡한 로직 처리 중 오브젝트가 제거되어 null 참조가 일어나는 일을 줄여줍니다. Destroy 호출하고나서 해당 객체를 참조하는게 잘못된게 아니냐고 볼 수도 있으나 Coroutine이나 Animator등 복잡하게 연계된 로직에서는 null참조를 없애는게 더 힘듧니다.

그러면 OnDestroy는 Destroy 함수를 호출했을 때 호출될지 아니면 실제로 소멸이 일어나는 타이밍에 호출될지 테스트를 해보기 위해서 위와같은 코드를 실행해 보니 콘솔창에 아래와 같이 출력되었습니다.

Test Start
Test Destroy

OnDestroy는 실제로 소멸이 일어나는 타이밍에 객체를 소멸시키기 직전에 호출하는 모양입니다.

Project에 저장된 Json 스크립트 읽기

Unity에서 Project에 포함된 Json 스크립트를 읽어들이는 방법입니다.

첫번째는 C#에서 기본적으로 지원해주지 않는 Json 파서를 구해야 합니다.

Json 파서는 여러가지가 있으나 C#에서 편리하게 사용할 수 있는 파서일 수록 iOS에서 문제가 발생하기 때문에 사용하기가 어렵습니다. 그 이유는 C#의 Reflection 기능에 의한 것이라고 하는데 자세한 설명은 다음의 링크를 참고하면됩니다.

http://www.unitystudy.net/bbs/board.php?bo_table=dustin&wr_id=365&sca=&sfl=wr_subject&stx=ios&sop=and

그래서 저는 SimpleJSON을 사용하기로 결정하였습니다. SimpleJSON 관련 링크는 아래와 같습니다.

http://wiki.unity3d.com/index.php/SimpleJSON#Download

 

이제 Project에 Json 파일을 추가하고 읽어서 사용하는 방법입니다.

게임에서 외부 파일을 가져다 사용하기 위해서는 Project에 Resources 라는 폴더를 생성해야 합니다. 이 폴더는 Asset 폴더 아무곳에나 생성해도 된다고 합니다. 그에 관련한 내용은 다음의 링크를 참고하면 됩니다.

http://docs.unity3d.com/ScriptReference/Resources.html

Resources 폴더를 추가한 후 내부에 폴더를 서 생성하거나 직접 파일을 추가하면 됩니다. 사용 시 Resources 폴더를 기준으로 Path가 시작되며 파일의 확장자는 생략해야 합니다.

즉 Resources/Data/Level.txt 라는 파일이 존재한다면

Resources.Load("Data/Level") as TextAsset

이런 형태로 사용할 수 있습니다.

2D에서 Raycast를 통한 오브젝트 선택

Unity3D에서 2D 환경에서의 Object 선택을 위한 방법을 소개합니다.

    void Update() {
        if (Input.GetMouseButtonDown(0))
        {
            Vector2 wp = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            Ray2D ray = new Ray2D(wp, Vector2.zero);
            RaycastHit2D[] hits = Physics2D.RaycastAll(ray.origin, ray.direction);

            foreach (var hit in hits)
            {
                if (!hit.collider.gameObject.CompareTag("Coin"))
                    continue;

                GameObject can = hit.collider.gameObject;

                can.GetComponent<Animator>().SetTrigger("Disapear");
            }
        }
    }