목차

반응형

1. 개요

2. 중복 로그인 막는 원리

3. 코드

4. 유의사항

 

 

 

 

 

 

 

 

1. 개요

어느 정도 사이즈가 있어서 파이어베이스 로그인을 필요로 하는 게임을 만드는데 2개의 디바이스에서 동일한 소셜 정보로 순차대로 로그인하면 별도의 처리가 없으면 로그인이 잘만 된다.

악용하는 유저도 있고 수많은 버그를 불러일으키는 행위이기 때문에 예방을 필요로 한다.

골치가 아프다. 어떻게 해야지 중복 로그인을 막을 수 있을까?

파이어베이스 실시간 DB의 onvaluechanged 기능을 사옹하면 쉽게 처리 가능하다.

 

 

 

2. 중복 로그인 막는 원리

중복처리를 막기 위한 플로우는 다음과 같다.

 

1. 유저의 소셜 로그인(구글, 애플)

2. 유저 개인 정보를 보관하는 곳에 유저의 device id를 업데이트

3. 유저의 device id에 onvaluechange 트리거를 연결

4. 트리거 작동시 바뀐 값과 현재 유저의 device id가 같은지 확인, 다르면 중복 로그인 팝업과 함께 게임 종료 처리

 

 

3. 코드

1,2 단계는 생략하고 3,4번 코드만 올리도록 하겠다.

 

3. 유저의 device id에 onvaluechange 트리거를 연결하는 코드

DatabaseReference DBRef;

 

//DBRef는 본인 상황에 맞게 적절한 위치를 가져와야 한다.

DBRef = FirebaseDatabase.GetInstance("DB 이름").RootReference;

 

DBRef.Child("Device ID의 위치").ValueChanged += LogoutFunction;

 

4. 트리거 작동 시 바뀐 값과 현재 유저의 device id가 같은지 확인, 다르면 중복 로그인 팝업과 함께 게임 종료 처리

private void LogoutFunction(object sender, ValueChangedEventArgs args)
{
  if (args.DatabaseError != null) 
  {
      Debug.LogError(args.DatabaseError.Message);
      return;
  }

  //device id와 받은 값이 다른 경우
  if (SystemInfo.deviceUniqueIdentifier != (string) args.Snapshot.Value) 
  {
  	//로그아웃 처리
  }
}

 

 

4. 유의사항

당연하지만 유저가 device id 값을 바꾸기 전에 이벤트를 미리 걸어버리면 안 된다.

반응형
반응형

열심히 일을 하다보면 aab 파일에 문제가 없는지 궁금할때가 있다.

유니티 build and run을 사용하지 않고 aab 파일을 핸드폰에 설치하는 방법에 대해서 알아보자

 

쉬운 방법은 없다 그냥 아래 써있는대로 따라하자

 

본인의 OS에 맞춰 bundletool을 설치하자

github.com/google/bundletool/releases

 

cmd 기준 bundletool을 사용하여 aab파일로부터 apk를 뽑아내는 명령어는 아래와 같다.

 

java -jar /Users/jim/Desktop/workspace/project/bundletool-all-1.3.0.jar  build-apks --bundle=/Users/jim/Desktop/workspace/project/Test/app.aab  --output=/Users/jim/Desktop/workspace/project/Test/result.apks --ks=/Users/jim/Desktop/document/keystore/key.keystore --ks-pass=qlalfqjsgh --ks-key-alias=mycompany --key-pass=qlalfqjsgh --mode=universal

 

빨간색에 해당하는 부분은 본인의 환경에 맞춰서 바꿔넣어야한다.

 

뽑아낸 apks 파일의 확장자를 zip으로 변경하자

 

확장자를 바꾼 파일의 압축을 풀면 한개의 apk가 나올것이다.

해당 파일을 설치하면 된다.

 

adb 설치 명령어까지 친절하게

./adb install /Users/jim/Downloads/reuslt.apk 

반응형
반응형

1. 개요

몰랐는데 사실 유니티 IAP(in app purchase) 상품에 대한 국가별 화폐 단위 및 가격을 하드코딩 없이 손쉽게 불러오고 표출하는 방법이 있었다.

따라서 유니티 클라이언트에 상품별 결제 가격에 대한 정보를 전혀 기입할 필요가 없다!

나도 이 기능은 나중에야 알았다. 이런...!

 

2. 원리

1. IAP 상품 정보 등록 및 Initialize

2. product id로 상품 정보 단일 조회

3. 상품 정보에 내재되어있는 로컬 가격과 로컬 화폐단위 획득 및 표출

 

 

3. 코드

1번은 너무 흔하디 흔하니까 스킵하도록 한다. IAP에 대한 코드가 전혀 없다면 아래 페이지를 참고하자

learn.unity.com/tutorial/unity-iap#5c7f8528edbc2a002053b46e

 

Unity IAP - Unity Learn

Unity IAP (In App Purchases) lets you sell a variety of items directly within your free or paid game including premium content, virtual goods and subscriptions. In this tutorial, we are going to look at how to add IAP to an existing game project. This tuto

learn.unity.com

 

 

2. product id로 상품 정보 단일 조회

일반적인 IAP 스크립트라면 라면 IStoreController 타입의 변수 m_StoreController를 가지고 있을 것이다.

해당 변수로부터 product id를 통하여 원하는 product를 리턴해주는 함수를 하나 만들자

public Product GetProduct(string _productId)
{
	return m_StoreController.products.WithID(_productId);
}

 

 

3. 상품 정보에 내재되어있는 로컬 가격과 로컬 화폐단위 획득 및 표출

if (shopItem.ItemType == ItemType.Package)
{
	textPPrice.text = string.Format("{0} {1}", IAP.instance.GetProduct(shopItem.ProductId).metadata.localizedPrice, IAP.instance.GetProduct(shopItem.ProductId).metadata.isoCurrencyCode);
}

위의 코드에 대한 부가설명을 하겠다.

위의 코드는 상품 UI에 부착되어서 textPPrice라는 text 컴포넌트의 텍스트를 지정해주는 부분이다.

상품 UI는 최초에 초기화 단계에서 표출해주는 상품에 대한 product id를 가지고 있어서 위에서 IAP에 만들어뒀던 product 조회 함수를 통해 플레이스토어 및 앱스토어에 등록된 상품에 대한 정보를 가져올 수 있다.

shopItem.ProductId로 조회한 product의 metadata.localizedPrice는 현지 화폐 단위의 가격을 의미하고 metadata.isoCurrencyCode는 화폐 단위를 의미한다.

예를 들어 10000원짜리 상품을 한국 유저의 경우 text에는 10000 KRW라고 표기된다.

 

4. 특이사항

해당 강좌의 전제 조건은 이미 플레이스토어 및 앱스토어에 상품 정보가 등록되어있다고 가정하에 진행했다. 상품 정보를 등록 안 하면 데이터가 안 나오는 게 당연하다.

에디터에서는 전부다 0.01 USD라고 표기된다. 에디터에서만 그렇게 표기되니까 당황하지말고 디바이스에서 테스트해보자

반응형
반응형

1. 개요

2. 블루스택 설치

3. APK 설치

4. ADB 로그캣 연동

5. 유니티 빌드 오류

 

 

 

 

 

 

 

 

 

1. 개요

사내 QA 팀은 빌드한 게임 테스트를 위해서 녹스나 블루스택을 사용한다. 이를 앱플레이어라고 하는데

만약 당신의 개발 환경이 mac이라면 녹스와 블루스택중 블루스택을 권장한다. 그 이유는 녹스의 경우 mac에서 사용하기 불편하다. 그리고 딱 보면 녹스의 window 버전 하고 mac 버전하고 비교해보면 mac 버전은 정말 초라하다.

여튼 오늘은 블루스택을 설치해서 당신의 게임을 구동하는 부분까지 안내하도록 하겠다.

 

 

 

2. 블루스택 설치

www.bluestacks.com/download.html

 

윈도우 OS에 따라 선택해서 블루스택 설치 파일을 다운로드한다.

참고로 맥과 동일한 환경으로 설치하고 싶다면 Android 64 Bit를 설치하자(mac은 64 bit밖에 없음)

 

 

 

최초 실행 시 위와 같은 화면을 볼 수 있다.

 

 

3. APK 설치

유니티에서 게임을 빌드하면 생기는 APK를 블루스택에 설치하려면 단순히 더블클릭을 해도 되고 또는 APK를 드래그해서 블루스택에 넣어보자.

그러면 내 게임에 설치한 게임이 생기면서 실행이 가능하다.

 

 

4. ADB 로그캣 연동

 

ADB 로그캣 연동하는 방법도 굉장히 쉽다.

 

 

우측 햄버거 버튼 > 설정

 

 

설정 > ADB 사용 체크

 

 

바로 ADB에 에뮬레이터로 잡힌다.

adb logcat -s Unity 해주면 된다.

 

 

5. 유니티 빌드 오류

유니티에서 빌드한 앱이 블루스택에서 그냥 꺼져버리는 경우가 있다.

그런 경우는 빌드 설정을 아래와 같이 해주면 된다.

 

Scripting Backend를 IL2CPP로

Target Architecutres의 두개를 다 체크해주자.

 

그리고 다시 해보면 잘 작동한다.

 

블루스택 bit 환경 Scripting Backend ARMv7 ARM64 구동 여부
32 bit IL2CPP O O O
32 bit IL2CPP X O X
32 bit IL2CPP O X O
32 bit Mono     X
64 bit IL2CPP
O O O
64 bit IL2CPP X O X
64 bit IL2CPP O X O
64 bit Mono     X

빌드별로 구동 되는지 안되는지 여부를 확인해봤는데 위과 같은 결과가 나왔다. 참고하자

반응형
반응형

유니티에서 빌드를 하다 보면 아래와 같은 오류가 발생할 때가 있다.

Failed running C:\Program Files\2019.4.5f1\Editor\Data\il2cpp/build/deploy/net471/il2cpp.exe --convert-to-cpp --emit-null-checks --enable-array-bounds-check --dotnetprofile="unityaot" --compile-cpp --libil2cpp-static --platform="Android" --architecture="ARMv7" --configuration="Release" --outputpath="C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\assets\bin\Data\Native\armeabi-v7a\libil2cpp.so" --cachedirectory="C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Assets\..\Library\il2cpp_android_armeabi-v7a/il2cpp_cache" --additional-include-directories="C:/Program Files/2019.4.5f1/Editor/Data/PlaybackEngines/AndroidPlayer/Tools\bdwgc/include" --additional-include-directories="C:/Program Files/2019.4.5f1/Editor/Data/PlaybackEngines/AndroidPlayer/Tools\libil2cpp/include" --tool-chain-path="C:/Program Files/2019.3.15f1/NDK/android-ndk-r19" --profiler-report --map-file-parser="C:/Program Files/2019.4.5f1/Editor/Data/Tools/MapFileParser/MapFileParser.exe" --directory=C:/Users/user/Desktop/workspace/SuperHeadLeague-master/Temp/StagingArea/assets/bin/Data/Managed --generatedcppdir=C:/Users/user/Desktop/workspace/SuperHeadLeague-master/Temp/StagingArea/Il2Cpp/il2cppOutput 

stdout:
Building libil2cpp.so with AndroidToolChain
	Output directory: C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\assets\bin\Data\Native\armeabi-v7a
	Cache directory: C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Library\il2cpp_android_armeabi-v7a\il2cpp_cache
ObjectFiles: 371 of which compiled: 0
Total compilation time: 259 milliseconds.
il2cpp.exe didn't catch exception: Unity.IL2CPP.Building.BuilderFailedException: C:\Program Files\2019.3.15f1\NDK\android-ndk-r19\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++ @"C:\Users\user\AppData\Local\Temp\tmp5F14.tmp" -o "C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Library\il2cpp_android_armeabi-v7a\il2cpp_cache\linkresult_E7F6115F0E77C4908A52DED5466D075E\libil2cpp.so" -shared -Wl,-soname,libil2cpp.so -Wl,--no-undefined -Wl,-z,noexecstack -Wl,--gc-sections -Wl,--build-id -stdlib=libc++ -static-libstdc++ -target armv7-linux-androideabi19 -Wl,--wrap,sigaction -llog -rdynamic -fuse-ld=gold.exe

C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5065: error: undefined reference to 'Presto_getBatterylife'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5076: error: undefined reference to 'Presto_getCountry'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5076: error: undefined reference to 'Presto_getCountry'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5065: error: undefined reference to 'Presto_getBatterylife'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5899: error: undefined reference to 'Presto_UUID'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5888: error: undefined reference to 'Presto_UnityInit'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5899: error: undefined reference to 'Presto_UUID'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5888: error: undefined reference to 'Presto_UnityInit'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)


   ��ġ: Unity.IL2CPP.Building.CppProgramBuilder.PostprocessObjectFiles(HashSet`1 objectFiles, CppToolChainContext toolChainContext)
   ��ġ: Unity.IL2CPP.Building.CppProgramBuilder.Build(IBuildStatistics& statistics)
   ��ġ: il2cpp.Program.DoRun(String[] args, List`1 foundAssemblies)
   ��ġ: il2cpp.Program.Run(String[] args, Boolean setInvariantCulture)
   ��ġ: il2cpp.Program.Main(String[] args)
stderr:

���� ���� ����: Unity.IL2CPP.Building.BuilderFailedException: C:\Program Files\2019.3.15f1\NDK\android-ndk-r19\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++ @"C:\Users\user\AppData\Local\Temp\tmp5F14.tmp" -o "C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Library\il2cpp_android_armeabi-v7a\il2cpp_cache\linkresult_E7F6115F0E77C4908A52DED5466D075E\libil2cpp.so" -shared -Wl,-soname,libil2cpp.so -Wl,--no-undefined -Wl,-z,noexecstack -Wl,--gc-sections -Wl,--build-id -stdlib=libc++ -static-libstdc++ -target armv7-linux-androideabi19 -Wl,--wrap,sigaction -llog -rdynamic -fuse-ld=gold.exe

C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5065: error: undefined reference to 'Presto_getBatterylife'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5076: error: undefined reference to 'Presto_getCountry'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5076: error: undefined reference to 'Presto_getCountry'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5065: error: undefined reference to 'Presto_getBatterylife'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5899: error: undefined reference to 'Presto_UUID'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5888: error: undefined reference to 'Presto_UnityInit'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5899: error: undefined reference to 'Presto_UUID'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5888: error: undefined reference to 'Presto_UnityInit'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)


   ��ġ: Unity.IL2CPP.Building.CppProgramBuilder.PostprocessObjectFiles(HashSet`1 objectFiles, CppToolChainContext toolChainContext)
   ��ġ: Unity.IL2CPP.Building.CppProgramBuilder.Build(IBuildStatistics& statistics)
   ��ġ: il2cpp.Program.DoRun(String[] args, List`1 foundAssemblies)
   ��ġ: il2cpp.Program.Run(String[] args, Boolean setInvariantCulture)
   ��ġ: il2cpp.Program.Main(String[] args)

UnityEngine.Debug:LogError(Object)
UnityEditorInternal.Runner:RunProgram(Program, String, String, String, CompilerOutputParserBase)
UnityEditorInternal.Runner:RunManagedProgram(String, String, String, CompilerOutputParserBase, Action`1)
UnityEditorInternal.IL2CPPBuilder:RunIl2CppWithArguments(List`1, Action`1, String)
UnityEditorInternal.IL2CPPBuilder:ConvertPlayerDlltoCpp(Il2CppBuildPipelineData, String, String, Boolean)
UnityEditorInternal.IL2CPPBuilder:Run()
UnityEditorInternal.IL2CPPUtils:RunIl2Cpp(String, String, IIl2CppPlatformProvider, Action`1, RuntimeClassRegistry)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

 

위는 오류 전문이다.

 

 

C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5065: error: undefined reference to 'Presto_getBatterylife'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5076: error: undefined reference to 'Presto_getCountry'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5076: error: undefined reference to 'Presto_getCountry'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5065: error: undefined reference to 'Presto_getBatterylife'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5899: error: undefined reference to 'Presto_UUID'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5888: error: undefined reference to 'Presto_UnityInit'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5899: error: undefined reference to 'Presto_UUID'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5888: error: undefined reference to 'Presto_UnityInit'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
C:\Users\user\Desktop\workspace\SuperHeadLeague-master\Temp\StagingArea\Il2Cpp\il2cppOutput/GPresto_iOS.cpp:5910: error: undefined reference to 'Presto_UserType'
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)

오류가 굉장히 길어서 도대체 어디가 문제인가 싶을 텐데 이 부분만 보면 된다. undefiend undefined reference to ~~~

해당 부분이 안드로이드에서 호출하려고하니 정의가 없어서 빌드를 못하겠다는 것이다.

 

나 같은 경우는 경로에 써있는 라이브러리가 문제를 일으키는 것이다.

고맙다 GPresto... 덕분에 제 시간을 날렸어요!

반응형
반응형

1. 개요
2. Transform, Rigidbody 방식 차이
3. Transform 방식으로 포물선 운동 구현

3.1 Tween 
3.2 단순 함수
3.3 떨어지는 방향 보도록 하기

4. Rigidbody 방식으로 포물선 운동 구현

4.1 포물선 운동 물리 공식 이해
4.2 도착지점 알고 발사 각도 알 때
4.3 도착 시간 알고 각도 알 때
4.4 Rigidbody 방식의 한계 - 중력이 정해져 있다
4.5 중력가속도를 바꿔서 투사체마다 다른 발사 방식 적용하기 
4.6 떨어지는 방향 보도록 하기

5. 결론과 요약 

 

 

 

 

 

 

 

 

 

 

 

 

1. 개요 

유니티로 게임을 만드는 사람이라면 한번쯤은 접해보고 포물선 운동 공식을 찾아보고 뭔개소리지 하며 절망했을만한 주제에 대해서 다뤄보도록 하겠다.

다양한 포물선 운동을 나타내는 방식과 코드에 대해서 작성해보았다. 나도 배워가는 단계라서 충분히 부족하지만 그나마 어느정도 이해는 한 상태로서 처음부터 배우고자 하는 사람에게 도움이 되고자 글을 써본다.

포물선 운동에 관한 포괄적인 내용정리를 목표로 글을 써봤다.

 

 

 


2. Transform, Rigidbody 방식 차이 

그 전에 먼저 유니티의 물리 엔진을 사용한다는 것에 대해서 이해를 해야한다.

유니티에서 게임 오브젝트를 이동하는 방식은 크게 두 가지가 있다.

첫번째는 Transform을 기반으로 움직이는 방식

transform.poisiton = transform.poisiton + Vector3.one; 을 행하게 되면 오브젝트가 순간이동을 하게 된다. 이를 반복적으로 행하면 연속적으로 이동하는 것 처럼 보이는데 그 원리를 이용하여 오브젝트를 움직이는 것 이다. 이런 방식을 Transform 기반으로 움직인다고 칭한다.

 

두 번째는 Rigidbody를 이용해서 움직이는 것이다.

오늘의 주제인 유니티 엔진의 물리를 이용해서 움직인다고 생각하면 된다.

Rigidbody.addforce(Vector3.one); 을 행하게 되면 물체가 물리작용을 받아서 움직이게 된다.

 

두 방식은 작게 봤을때는 기호의 차이처럼 보이지만 상당한 차이를 가지고 있으며 여기서 어떠한 방식으로 접근하냐에 따라 추후에 문제가 발생할 수도 있고, 발생한 문제를 해결해나가는 양상은 전혀 다르게 전개된다.(즉 여기서 내가 어떤 방식을 써야하는지 잘 골라라 이말이야)

 

간단한 예를들어 아래 글은 유니티 트레일 작동 방식에 대한 글 인데 Transform 이동방식과 Rigidbody 이동방식 차이에 따라 트레일이 생성되냐 안되냐 차이가 생긴다.

ajh322.tistory.com/265

 

유니티 트레일 움직여도 작동안할때

유니티 트레일에는 2개의 Scaling Mode가 있다. Transform과 Rigidbody이다. Transform Transform은 트레일이 붙어있는 오브젝트가 translate로 움직일 때 트레일이 발생한다. Rigidbody Rigidbody는 트레일이 붙..

ajh322.tistory.com

 

 

 

3. Transform 방식으로 포물선 운동 구현

Transform 방식으로 오브젝트를 움직이는 방식으로 이동하는 경우에 대해서 살펴보고 특징과 어떤 코드를 사용해야 포물선 움직임을 구현할 수 있는지 알아보자.

 

특징 1 : 유니티 물리엔진인 Rigidbody를 사용하지 않고 직접 움직이면 유니티 엔진의 한계없이 게임을 만들 수 있다.(여기서 말하는 한계란 유니티 물리 엔진을 활용하기 시작하면 복잡한 구현을 할 때마다 더욱 큰 난관에 부딪힌다는 말이다. 만약에 움직임을 addforce로 구현한다고 생각해보자, 이후 공기저항에 따른 이동속도 감소, 회전을 줘야할때 자체적인 스크립트가 아닌 유니티 Rigidbody 방식에서 해결하려면 미세한 컨트롤이 힘들어진다.)

 

특징 2 : 이동 단위(속도)가 큰 경우 충돌 판정이 정상적으로 이뤄지지 않음, 일반적으로 생각하는 물리적 결과와는 다른 결과가 나올 수 있음, 트리거 Enter 및 Collision 이벤트가 정상적으로 작동하지 않을 수 있음.

왼쪽과 오른쪽은 단순히 translate 해주는 크기의 차이(속도)로 인해서 발생하는 문제다.

둘다 텔레포트 하느 방식으로 이동하다보니 벽보다 이동속도가 빠른 경우 벽을 뚫고 지나가 버리는 경우가 허다하다.

이런 경우는 자체적으로 Collision 및 Enter를 판별하기 위하여 이동할때마다 물체간의 좌표에 대한 정보를 확인하여 해당 이벤트를 스스로 처리하는 시스템을 구현해야한다.

 

 

 

3.1 Tween

Transfom 이동방식중 한가지인 Tween 방식이다.

Tween을 모르는 분을 위해서 간단히 설명하자면 iTween, Dotween과 같은 라이브러리가 있는데 흔히 오브젝트를 부드럽게 이동하는 것을 구현하기 위해 IEnumerator를 위치를 이동해주는 것을 구현해주는데 그런 기능을 코드 한줄로 구현할 수 있도록 도와주는 유틸리티다.

그리고 Tween에는 보통 포물선 운동을 구현할 수 있는 함수가 내장되어있다.

 

Dotween의 경우 DOPath라는 함수를 사용하여 포물선 운동을 시각적으로 표현할 수 있다.

 

 void Start()
    {
        Vector3 firstPos = transform.position;
        Vector3 secondPos = firstPos + new Vector3(5, 5, 0);
        Vector3 thirdPos = firstPos + new Vector3(10, 0, 0);
        transform.DOPath(new[] {secondPos, firstPos+Vector3.up, secondPos + Vector3.left * 2, thirdPos, secondPos + Vector3.right * 2, thirdPos + Vector3.up}, 1f,PathType.CubicBezier).SetEase(Ease.Unset);
    }

위와 같은 코드를 사용하면 된다. 코드 사용방법은 Tween 라이브러리마다 다르다.

 

위의 운동이 어색해보이는 이유는 속도가 처음에 빨랐다가 최고점에서 가장 느렸다가 마지막에 빨라지는 효과를 넣어줘야하는데 그 부분을 넣어주지 않아서 그렇다.

Easing이라는 옵션을 넣어줘야하는데 포물선에 적합한 Easing이 없어서 함수를 직접 구현해줘야하는데 요청이 있으면 해당 부분을 구현해서 올리도록 하겠다.

 

 

3.2 단순 함수

직접 정의한 공식을 사용하여 함수형태로 움직이는 수식을 만들어서 처리하는 것이다.

어찌보면 Tween과 비슷한 처리 방식이지만 Tween에서 해주는 함수를 직접 정의해서 돌려주는 느낌이다.

 

    private Vector3 startPos, endPos;
    //땅에 닫기까지 걸리는 시간
    protected float timer;
    protected float timeToFloor;
    
    
    protected static Vector3 Parabola(Vector3 start, Vector3 end, float height, float t)
    {
        Func<float, float> f = x => -4 * height * x * x + 4 * height * x;

        var mid = Vector3.Lerp(start, end, t);

        return new Vector3(mid.x, f(t) + Mathf.Lerp(start.y, end.y, t), mid.z);
    }

    protected IEnumerator BulletMove()
    {
        timer = 0;
        while (transform.position.y>=startPos.y)
        {
            timer += Time.deltaTime;
            Vector3 tempPos = Parabola(startPos, endPos, 5, timer);
            transform.position = tempPos;
            yield return new WaitForEndOfFrame();
        }
    }

    private void Start()
    {
        startPos = transform.position;
        endPos = startPos + new Vector3(5, 0, 0);
        StartCoroutine("BulletMove");
    }

 위와 같다. (gist.github.com/ditzel/68be36987d8e7c83d48f497294c66e08)

필요한 변수는 시작지점, 착지지점, 높이, 걸리는 시간이다.(현재 수식에서는 1초로 고정)

 

해석

위의 식을 분석해보고 그에 대한 해석을 나름 해보았는데 궁금하면 읽어보자(패스해도 무방)

위 식에서 필요한 변수를 다시한번 생각해보자.

 

시작지점, 착지지점, 높이, 걸리는 시간

이 식이 갖는 특이한 점이라면 포물선 운동을 구현하는데 사인함수와 코사인함수 그리고 각도 정보가 없다는 것이다.

어떻게 그것이 가능할까?

 

return new Vector3(mid.x, f(t) + Mathf.Lerp(start.y, end.y, t) mid.z); 부분을 보자.

여기서 두 부분으로 나눌 수 있다.

x,z축 그리고 y축이다.

x,z축은 앞과 뒤를 나타내는 평면좌표계고 y축은 높이를 나타낸다.

 

x, z 값은 직선운동처럼 시작지점부터 끝지점까지 Lerp함수를 이용해서 1초를 기준으로 0초면 시작지점, 1초면 끝지점이 되도록 값을 할당받는다.

그리고 y값은 직접 정의한 f(t)함수를 이용해서 받아오는데(y값 뒤의 Mathf.Lerp(start.y, end.y, t)는 높이가 다른 경우를 위해서 만들어 준 것인데 처음의 높이와 끝 부분의 높이를 맞춰주는 것으로 큰 의미는 없다.

 

height 값에 10을 넣으면 위와 같은 식이 나옴

그러면 직접 정의한 함수 f(x)를 살펴보자. height가 10일때 x(시간)에 따른 그래프는 위와 같이 나온다.

함수 내 변수 x는 lerp함수를 거치기 떄문에 무조건 0에서 1까지만 들어가도록 되어 있어서 x가 0.5일때는 무조건 고점 height값을 가지도록 되어있다.

즉 이 수식은 포물선 이동에 대한 물리 함수라기보다는 x이동 따로(직선운동), y 이동은 이차곡선 형태로 이동하도록 된 식이다.

 

3.3 떨어지는 방향 보도록 하기

떨어지는 방향 보는거 코드 짜주기

 

4. Rigidbody 방식으로 포물선 운동 구현

다음은 유니티 물리 엔진을 직접 이용해서 구현하는 방식에 대해서 알아보자.

사용하기 쉽고 직관적이지만 너무 애용하다보면 복잡한 구현이 필요할수록 점차 나락으로 빠질것이다.(나처럼)

 

장점 : 이동 단위(속도)가 큰 경우에도 충돌 판정이 정상적으로 이뤄짐(단 TriggerEnter는 속도가 말도안되게 너무 빠르면 정상적으로 작동하지 않을 수 있음)

오른쪽을 보면 물체의 이동속도가 벽의 두께보다 훨씬 높은데도 정상적으로 못넘어가게 막아주고 있다.

 

단점 : 유니티 엔진이 제공하는 것 이상의 기능을 구현할때 힘들어짐.(이 부분은 직접 경험해봐야 안다.)

 

 

4.1 포물선 운동 물리 공식 이해 

인터넷에 쳐보면 바로 나오는 공식이지만 설명이 친절하지는 않다.

한번 내가 포물선 운동 물리 공식에 대하여 설명해보도록 하겠다.(내가 친절하게 설명해준다고는 안함)

 

그 전에 이해해야하는 개념이 몇 가지 있다. 몰라도 되긴하는데 궁금하면 읽어보자.

 

등속 운동과 가속운동

등속 운동은 말 그대로 속도가 일정한 운동을 의미한다.

총을 초속 10m/s로 쐈다.

해당 총알은 10m/s의 등속 운동을 하게 된다.(공기저항 제외)

t초 뒤 총알의 이동 거리는 10m * t이다.

 

등가속 운동은 일정하게 속도가 증가하는 운동을 의미한다. 짱짱한 자동차가 있다고 했을때 풀악셀을 밟아서 초당 10m/s의 속도로 증가하는 운동을 하는 경우가 그에 해당한다.

해당 자동차의 t초 뒤 속도는 10 * t이다.

그리고 자동차의 t초 뒤 이동 거리는 5t^2 이다.(해당 공식을 유도하는 방법은 정적분을 참고하자)

포물선 운동의 이동거리 공식을 보면 식을 이해하려고 하지말고 따라 붙는 변수의 형태만 확인해보자.

x축 이동은 등속운동 y축 이동은 등가속 운동을 한다는 것이다. 포물선 운동에 대해서 조금 더 이해가 되지않는가? x축 운동은 그냥 평범한 이동인것이다.

y축은 계속해서 중력만큼의 힘이 더해져서 아래로 떨어지는 등가속운동을 하는 것이다.

 

포물선 운동 공식 이해

그렇다면 대망의 포물선 운동 공식을 이해해보자.

중력이 작용하는 세계에서 x와, y축 방향 성분을 가지고 있는 v라는 힘을 주게 되면 아래와 같이 설명할 수 있다.

 

각 축에 대한 속도식은 아래와 같다. V의 벡터분해에 대한 것은 따로 찾아보자

여기서 t에 대한 값을 넣으면 그 시간대에 해당하는 각축별 속도를 의미한다.

x축은 어떤 시간값을 넣더라도 항상 동일한 값을 나타내고 y축은 시간이 값이 작아진다.

 

그리고 이동 거리에 대한 정적분 결과는 아래와 같다.

 

5개의 변수 그리고 2개의 식

포물선 운동 설명

포물선 운동 공식에는 2개의 식과 5개의 변수가 나온다.

V, 세타, t, x위치, y위치

우리는 x위치, y위치를 알고있다.

변수는 3개인데 식은 2개다. 따라서 세 변수중 하나를 결정해주면 나머지 2개의 변수가 스스로 결정된다.

세타를 정해주면 속도와 시간이 정해지고

속도를 정해주면 각도와 시간이 정해지고

시간을 정하면 속도와 세타가 정해진다.

 

그에 따라 생기는 문제 원하는 투사체 발사를 못만든다.

하지만 중력을 변수로 바꿔버리고 세타, t를 지정해주면? V와 중력에 대한 값이 연립해서 나오게 된다.

 

이하 작성하다가 말음 ... ( 아마 쭈욱 작성 안될 가능성 높음 )

 

4.2 도착지점 알고 발사 각도 알 때

인터넷에 가장 많이 나와있는 유형

4.3 도착 시간 알고 각도 알 때 

 

4.4 Rigidbody 방식의 한계 - 중력이 정해져 있다 

일단 그 전에 3차원 포물선 운동을 이해하기 쉽게 쉬운 운동으로 변환해서 설명하겠다. 여기서 말하는 3차원은 유니티라고 가정하겠다.

3차원(x y z 좌표평면) 포물선 운동 -> 2차원(x y좌표평면) x축 운동 + 2차원 y축 운동

3차원 운동 예시 : new Vector3 (10, 10, 0) -> 2차원 운동 : new Vector2(10,0) + new Vector2(0, 10) 이렇게 쪼개진다.

 

그렇다면 포물선 운동 공식에 대해서 얘기하기 전에 직선 운동에 대해서 먼저 얘기해보자

물리에서는 -> 직선운동 설명

 

포물선 운동 설명

포물선 운동 공식에는 2개의 식과 5개의 변수가 나온다.

V, 세타, t, x위치, y위치

우리는 x위치, y위치를 알고있다.

변수는 3개인데 식은 2개다. 따라서 세 변수중 하나를 결정해주면 나머지 2개의 변수가 스스로 결정된다.

세타를 정해주면 속도와 시간이 정해지고

속도를 정해주면 각도와 시간이 정해지고

시간을 정하면 속도와 세타가 정해진다.

 

그에 따라 생기는 문제 원하는 투사체 발사를 못만든다.

하지만 중력을 변수로 바꿔버리고 세타, t를 지정해주면? V와 중력에 대한 값이 연립해서 나오게 된다.

 

4.5 중력가속도를 바꿔서 투사체마다 발사 속도와 각도를 정해주기

 

4.6 떨어지는 방향 보도록 하기

 

5. 결론과 요약 

물리 기반 게임이 아닌 이상 그냥 4.2번 방식으로 가는 게 좋다 
짬밥 높은 개발자가 결국 코어 물리로 가게 되면 이해하기 힘든 영역(drag)이 추가되면 그때부터 나락으로 빠지기 시작함

반응형

'Unity' 카테고리의 다른 글

유니티 블루스택 구동  (1) 2020.09.27
유니티 파이어베이스 이벤트 로깅  (0) 2020.09.22
유니티 파이어베이스 로그 비활성화  (2) 2020.02.05
유니티 꿀팁  (0) 2019.02.27
유니티 물체 따라다니도록 하기  (0) 2019.02.24
반응형

어떤 상황에서 발생하는지는 모르지만 갑자기 위 에러가 발생하면서 유니티가 꺼진다.

프로젝트 경로/Library/metadata 폴더를 삭제하고 재시작하면 해결된다.

 

아래 페이지 참고했음.

answers.unity.com/questions/1117177/the-file-memorystream-is-corrupted-remove-it-and-l.html

 

 

반응형
반응형

유니티 트레일에는 2개의 Scaling Mode가 있다.

 

Transform과 Rigidbody이다.

 

Transform

Transform은 트레일이 붙어있는 오브젝트가 translate로 움직일 때 트레일이 발생한다.

 

Rigidbody

Rigidbody는 트레일이 붙어있는 오브젝트가 rigidbody(물리)를 force나 velocity를 조절해서 움직이는 경우 트레일이 발생한다.

 

당신이 트레일이 달려있는 오브젝트를 translate로 움직여놓고 ScalingMode가 Rigidbody 거나 그 반대 상황의 경우 트레일이 나타나지 않는다.

 

Scaling Mode는 아래 이미지 부분에서 수정 가능하다.

반응형
반응형

유니티 에디터에서 분명 사운드를 출력했는데 소리를 출력하기까지 조금의 딜레이가 생기는 경우가 있다.

 

구글에 쳐보면 다양한 원인을 말해주는데 그중에 해답은 없었다.

 

하루정도 삽질하다가 회사 동료분이 해답을 알려주셨다.

 

원인은 블루투스 이어폰으로 소리를 들어서 생긴 문제였다.

유선 이어폰이나 블루투스 이어폰을 끄고 디바이스로 소리를 들어보면 딜레이가 발생하지 않았다.

 

정말 써놓고도 이상한 트러블슈팅이지만 누군가는 나와 똑같은 삽질을 할 것 같다.(사실 이전 프로젝트 진행하면서 겪었는데 이번에 또 겪음 빡대가리임)

반응형
반응형

1. 개요

2. 파이어베이스 로깅 방법

3. 파라미터 데이터가 안 쌓이는 경우 

 

 

 

 

 

 

 

 

 

1. 개요

파이어베이스의 기능 중 하나인 Analytics 중에서 이벤트 남기는 방법과 이벤트 파라미터가 쌓이지 않는 문제를 겪어서 기록하고자 글을 쓴다.

 

유니티 파이어베이스 애널리틱스 DOCS

유니티 파이어베이스 SDK 다운로드

 

설치하는 방법에 대해서는 너무 쉬워서 따로 기재하지 않겠다.

다만 기존 SDK의 버전과 동일한 버전을 설치하는 것에 유의하자.

 

회사에서는 이런식으로 이벤트에 대한 목록을 짜주면 그에 맞는 이름과 파라미터를 개발자인 내가 알맞게 쏴주는 형식으로 협업한다.

 

2. 파이어베이스 로깅 방법

공식 Docs에는 이렇게 나와있는데 저 코드만으로는 실제 개발에서 사용하기 까다롭다.

아래 코드로 이벤트를 쉽게 날리도록 하자

 

 public void SendFirebaseEvent(string _eventName, params string[] _params)
    {
        Parameter[] parameters = new Parameter[_params.Length];
        for (var i = 0; i < _params.Length; i++) 
            parameters[i] = new Parameter($"param{i + 1}", _params[i]);
        FirebaseAnalytics.LogEvent(_eventName, parameters);
    }

 

위 함수는 이런식으로 호출하면 된다.

SendFirebaseEvent("이벤트 이름", "첫번째 파라미터", "두번째 파라미터", "세번째 파라미터", ...);

 

찍히는 이벤트에 대한 파라미터의 이름은 무조건 param1, param2,... 이런 식으로 찍히니까 유의하자.

나는 굳이 파라미터에 대한 이름을 정하지 않아도 되는데 파라미터 이름까지 정해주면 좋을 것 같긴 하다.

딕셔너리를 이용해서 커스터마이징 해보세요

 

 

4. 파라미터 데이터가 안 쌓이는 경우 

한 가지 치명적인 실수를 하고 있었는데 파이어 베이스의 이벤트는 쌓이지만 파라미터 데이터가 제대로 쌓이지 않는 문제가 발생했다.

 

로그를 보니 아래와 같이 로그캣이 울부짖고 있었다.

Name must start with a letter. Type, name : evvent param, 1

 

파라미터에 대한 변수 이름을 숫자로 지정했더니 발생한 문제였다.

파라미터의 이름의 첫 글자는 문자열로 정하도록 하자(언더바도 작동 안 함)

반응형