World Space의 UI 클릭 이벤트 발생하지 않는 문제 해결
두가지 퍼즐 구현 마무리를 하고 다른 파트와 합쳐보다가 문제가 발생했다.
현상 : Player를 추가하고나면 World Space에 있는 UI에 클릭이 안된다.
World Space에 있는 UI 캔버스의 EventCamera는 스크립트에서 동적으로 할당하기 때문에 이 문제는 아닌 것 같았다.
찾은 원인 : 커서 Lock모드
PlayerController에서 커서의 lockState를 Locked로 세팅해주고 있는데,
EventSystem은 보통 마우스가 움직일 때 UI 감지를 업데이트 하는데 Locked 상태에서는 마우스 이동이 없다고 판단될 수 있다고 한다.
해결 : 클릭 감지 시 UI 이벤트 수동으로 실행
이벤트 감지를 못하는 것이 원인이기 때문에 마우스 클릭 시 현재 클릭에 걸리는 UI를 체크해서 이벤트를 실행시켜주기로
했다.
- 클릭 이벤트 실행 시 넣어줘야 하는 PointerEventData의 생성자에 현재 이벤트 시스템을 넣어주고 position은 마우스 포지션(대부분의 상황에서 화면의 중심임)으로 세팅해준다.
- EventSystem의 RaycastAll은 UI 요소에 별도의 Raycast를 수행한다.
- Ray에 걸린 애들의 클릭 이벤트를 모두 실행시킨다.
마우스 좌클릭 시 실행하던 로직이 있어서, 그곳에서 WorldUIClick 메서드를 추가로 호출해줘서 해결하였다.
+ 이곳의 Raycast에서 최상위 UI만 검출되는 것이 아니라 UI가 걸리기 때문에, 쓸데없는 검출을 막고자 배경이미지같은 UI들은 Raycast Target 옵션을 비활성화 해줬다.
제네릭, return T, default
두 번째 역할 분담으로 게임 데이터 저장 기능을 맡게 되었다. 지난번 팀플에서도 데이터 로더를 만들었었는데, 튜터님 피드백 시간에 로더는 다 같은 코드를 갖고 있으니 제네릭으로 만들면 보완이 될 것이라고 말씀해주셨었다. 그래서 이번에는 제네릭으로 DataManager를 구현했다.
그런데 제네릭 함수에서 return null을 하면 오류가 뜬다. T가 nullable이 아닐 수 있기 때문이다. 값 타입이라든가..
그래서 에러 메세지에 뜬대로 dafault를 사용했다. dafault는 각 타입의 기본값을 반환해준다. int라면 0, 클래스라면 null 등.
기본 제공 형식의 기본값 - C# reference
bool, char, int, float, double 등과 같은 C# 형식의 기본값을 알아봅니다.
learn.microsoft.com
dafault(T)로 써줘도 되고, default만 써줘도 된다.
암호화, 복호화 적용된 SaveData(), LoadData() 코드
(Encrypt, Decrypt 함수 내용은 글 아래에서)
public static T LoadData<T>(string filePath)
{
if(File.Exists(Application.persistentDataPath + filePath))
{
string loaded = File.ReadAllText(Application.persistentDataPath + filePath);
if (loaded == null)
{
throw new System.NullReferenceException();
}
string context = Decrypt(loaded);
T Data = JsonConvert.DeserializeObject<T>(context);
return Data;
}
else
{
return default;
}
}
public static void SaveData<T>(T data, string filePath)
{
string context = JsonConvert.SerializeObject(data);
context = Encrypt(context);
File.WriteAllText(Application.persistentDataPath + filePath, context);
}
데이터 암호화 / 복호화
데이터를 저장할 때 json 파일 그대로 저장되는 것을 방지하기 위해 데이터의 간단한 암호화 / 복호화를 적용시켜 구현해보았다.
암호화 코드
Key와 초기화벡터 IV를 이용해서 암호화하는 코드이다. 암호화한 내용물 앞에 IV값을 덧붙여서 내보내는 것을 볼 수 있다.
- IV는 대칭키 암호화에서 사용되는 매개변수인데, 키가 같아도 IV는 항상 달라야 한다. IV가 같으면 같은 데이터에 대해 항상 같은 결과물이 나오게 되고, 그로인해 어떤 문자가 어떻게 암호화되는지 패턴을 찾을 수 있어지기 때문에 암호화의 의미가 없어진다. 그리고 사용한 IV를 키와 함께 알고 있어야 복호화가 가능하기 때문에 보통 암호문과 함께 저장한다고 한다.
- TransformFinalBlock() 으로 암호화가 이루어지고 있는데, 이는 암호화할 데이터가 블록 크기의 배수에 딱 떨어지지 않아도 마지막 블록에 패딩을 적용해서 올바른 암호화가 이루어지도록 한다.
public static string Encrypt(string plainText)
{
using (Aes aes = Aes.Create())
{
aes.Key = key;
aes.GenerateIV();
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] bytesToEncrypt = Encoding.UTF8.GetBytes(plainText);
// 암호화
byte[] encryptedData = encryptor.TransformFinalBlock(bytesToEncrypt, 0, bytesToEncrypt.Length);
// IV를 데이터 앞에 덧붙임
byte[] result = new byte[aes.IV.Length + encryptedData.Length];
Array.Copy(aes.IV, 0, result, 0, aes.IV.Length);
Array.Copy(encryptedData, 0, result, aes.IV.Length, encryptedData.Length);
return Convert.ToBase64String(result);
}
}
그런데!! using문 저렇게 사용하는 거 처음 봤다. 이 문법은 IDisposable 인터페이스를 구현한 객체를 사용할 때, using 블록을 벗어나면 자동으로 Dispose()가 호출되고 리소스 정리가 자동으로 이루어지게 하는 문법이라고 한다.댕신기.
Aes의 부모 클래스에 IDisposable이 구현되어 있다.
저장할 때 암호화 전과 후를 비교한 모습
복호화 코드
암호화할 때 앞에 붙여뒀던 IV를 복구하고, 그 IV와 키를 사용해서 복호화를 진행한다. 앞에 붙여둔 IV를 구할 때 aes.IV.Length 길이만큼 구하는데, 이는 항상 AES의 기본 블록 크기이기 때문에 항상 동일하다. (AES의 블록 크기는 16바이트 고정임.)
public static string Decrypt(string encryptedString)
{
byte[] encryptedData = Convert.FromBase64String(encryptedString);
using(Aes aes = Aes.Create())
{
aes.Key = key;
//IV 복구
byte[] iv = new byte[aes.IV.Length];
byte[] realEncrytedContext = new byte[encryptedData.Length - iv.Length];
Array.Copy(encryptedData, 0, iv, 0, iv.Length);
Array.Copy(encryptedData, iv.Length, realEncrytedContext, 0, realEncrytedContext.Length);
// 복호화
aes.IV = iv;
ICryptoTransform decrytor = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] decryptedData = decrytor.TransformFinalBlock(realEncrytedContext, 0, realEncrytedContext.Length);
return Encoding.UTF8.GetString(decryptedData);
}
}
다 만들고서 봤는데 공식 문서에도 예시와 메서드 등이 잘 나와있따.
https://learn.microsoft.com/ko-kr/dotnet/api/system.security.cryptography.aes?view=net-8.0
Aes 클래스 (System.Security.Cryptography)
모든 AES(Advanced Encryption Standard) 구현에서 상속해야 하는 추상 기본 클래스를 나타냅니다.
learn.microsoft.com
많이 도움 받은 블로그 : https://ljhyunstory.tistory.com/349
[Unity] 유니티 데이터 저장하기[1] - (PlayerPrefs + 암호화)
유니티를 이용해서 게임/ 어플리케이션을 제작할 경우 생성되는 데이터들...저 역시 개발을 진행 할 수록 어플리케이션이 종료되어도 유지되는 데이터들을어딘가에 저장해야하는 상황이 발생
ljhyunstory.tistory.com
'프로젝트 일지 > Unity' 카테고리의 다른 글
[Unity/TIL] Addressable 연구 (3) | 2025.04.08 |
---|---|
[Unity/TIL] 트러블슈팅 - 싱글턴 인스턴스에 Action 구독할 때 주의해야 함!!! (0) | 2025.04.02 |
[Unity/TIL] 3D 퍼즐 플랫폼 게임 - 팀 프로젝트 일지 (2) (0) | 2025.03.14 |
[Unity/TIL] 3D 퍼즐 플랫폼 게임 - 팀 프로젝트 일지 (1) (0) | 2025.03.13 |
[Unity/TIL] 3D 개인 프로젝트 일지 (3) - 짭테런 후회와 마무리 (0) | 2025.03.11 |