Lighting Intensity Multiplier : 실제 환경의 빛을 조절한다.Reflecting Intensity Multiplier : 실제 오브젝트에 반사되는 정도를 조절한다.Card , Cash , QR 등 상대방의 결제 수단에 맞는 클래스를 받아와야 하고, 또 새로운 결제 수단이 생기면 그 클래스까지 추가해주어야 함. (=수정이 오래 걸림)Payment 로 묶는다면 훨씬 간편하게 결제 메서드를 구현할 수 있다.public interface Payment { public void Pay(); } public class Card : Payment { public void Pay(){} } public class Cash : Payment { public void Pay(){} } public class QR : Payment { public void Pay(){} } //인터페이스를 사용하지 않았을 경우 public class Store { Card card; Cash cash; QR qr; } //인터페이스를 사용했을 경우 public class Store { Payment payment; payment.Pay(); }
ScriptableObjectScriptableObject란?
ScriptableObject 는 데이터를 저장하고 관리하는 데 특화된 Unity의 클래스MonoBehaviour와 다르게, 씬(Scene)에 존재하지 않아도 된다.ScriptableObject를 만드는 방법
- ScriptableObject 을 상속받는 데이터 클래스를 만든다. (ItemData.cs)
using System.Collections; using System.Collections.Generic; using UnityEngine; public enum ItemType { Equipable, //장착 아이템 Consumable, //소비 아이템 Resource //자원 아이템 } public enum ConsumableType { Health, //체력 회복 아이템 Hunger //배고픔 회복 아이템 } public class ItemDataConsumable { public ConsumableType type; //무엇을 회복시키는 소비 아이템인지 public float value; //얼마만큼 회복시켜 주는지 } //ScriptableObject를 만들 때 빠르게 만들 수 있도록 에셋 생성 메뉴창에 추가 [CreateAssetMenu(fileName = "Item", menuName = "New Item")] public class ItemData : ScriptableObject { [Header("Info")] public string displayName; //아이템 이름 public string description; //아이템 정보 public ItemType type; //아이템 타입 (장착/소비/자원) public Sprite icon; //아이템 이미지 public GameObject dropPrefab; //아이템 프리팹 [Header("Stacking")] public bool stackable; //여러개 가지고 있을 수 있는 아이템인지 체크하는 변수 public int maxStackAmount; //얼마나 많이 한꺼번에 가지고 있을 수 있는지 [Header("Consumable")] public ItemDataConsumable[] consumables; }
Project > 마우스 오른쪽 버튼 클릭 > Create > New Item으로 ScriptableObject 생성 (지금은 New Item이지만, 스크립트에서 menuName 을 무엇으로 정해주었는지에 따라 이름이 바뀐다.)
생성한 ScriptableObject 에 데이터를 넣어준다.
생성한 ScriptableObject 는 다음과 같은 방식으로 활용할 수 있다.
ItemObject.cs :
- 상호작용을 위한 ray 와 아이템 오브젝트가 만나면 실행시켜줄 메서드들이 들어가있다.
using System.Collections; using System.Collections.Generic; using UnityEngine; public interface IInteractable //상호작용 인터페이스 { public string GetInteractPrompt(); //화면에 띄워줄 프롬포트 함수를 작성 public void OnInteract(); //인터렉트 했을 때 어떤 효과를 발생 시킬 것인지 } public class ItemObject : MonoBehaviour, IInteractable { public ItemData data; public string GetInteractPrompt() { string str = $"{data.displayName}\n{data.description}"; return str; } public void OnInteract() { //Player 스크립트 먼저 수정 CharacterManager.Instance.Player.itemData = data; CharacterManager.Instance.Player.addItem?.Invoke(); //E키를 누르면 인벤토리로 이동시킬 것이므로 맵에 있는 Object는 삭제한다. Destroy(gameObject); }
Legacy Text와 달리 TextMeshPro Text는 일반 폰트 파일을 넣어줄 수 없다.
가지고 있는 폰트 파일을 TextMeshPro Text에 적용하려면 다음과 같은 과정을 거쳐야 함:
Window > TextMeshPro > Font Asset Creator 선택
준비한 폰트를 Source Font File에 끌어넣는다.
Atlas Resolution과 Character Set을 알맞게 설정해주었는지 체크한다.
- Atlas Resolution :
- 텍스트 폰트의 글리프(Glyph, 문자 이미지)를 저장하는 텍스처(Atlas)의 크기를 설정하는 옵션
- 값이 클수록 더 많은 글자를 포함할 수 있지만, 메모리 사용량이 증가함.
- 만약 한글, 중국어 같은 많은 글자를 포함해야 한다면 4096 x 4096 이상의 해상도가 필요하다!
- Character Set:
- 폰트 에셋에 포함할 문자(Character) 범위를 설정하는 옵션.
- 어떤 문자들을 폰트 파일에 포함할지 선택할 수 있다.
- Character Set 옵션 :
옵션 설명 ASCII 영어 알파벳 + 숫자 + 특수문자 (기본 128자) Extended ASCII ASCII + 유럽어 문자 포함 (256자) Unicode 모든 유니코드 문자 포함 (메모리 사용 증가) Custom Range 사용자가 직접 원하는 문자만 포함
Character Sequence에 32-126,44032-55203,12593-12643,8200-9900 를 입력해준다.
Font Asset에 저장할 건지 숫자로 범위를 지정해주는 것.
32-126: 영어 범위 (알파벳)44032-55203: 한글 범위12593-12643: 자음 모음8200-9900: 특수 문자
다 입력했다면 Generate Font Atlas 버튼을 누르고 변환이 완료될 때 까지 기다린다. (꽤 오래걸린다.)
끝나면 Save를 누르고, 저장한 Font Asset을 TextMeshPro Text에 적용해준다!
UpdateLighting 메서드에 대한 이해void UpdateLighting(Light lightSource, Gradient colorGradiant, AnimationCurve intensityCurve) { float intensity = intensityCurve.Evaluate(time); lightSource.transform.eulerAngles = (time - (lightSource == sun ? 0.25f : 0.75f)) * noon * 4.0f; lightSource.color = colorGradiant.Evaluate(time); lightSource.intensity = intensity; GameObject go = lightSource.gameObject; if (lightSource.intensity == 0 && go.activeInHierarchy) go.SetActive(false); else if (lightSource.intensity > 0 && !go.activeInHierarchy) go.SetActive(true); }
처음 위 코드를 접했을 때, 왜 lightSource 가 sun일 때는 0.25f를 빼주어야 하는지, moon일 때는 왜 0.75f를 빼주어야 하는지, 마지막에는 왜 4.0f를 곱해야 하는지 이해하지 못함.
Q1. 왜 하필 0.25f 와 0.75f 인가?
먼저 이걸 이해하려면 time 이 어떤 변수인지 이해해야 함.
time 값은 0~1 범위를 반복하며 하루의 흐름을 표현하는데, 대략적으로 표현하자면 다음과 같다.
- time = 0.0 → 새벽
- time = 0.25 → 아침
- time = 0.5 → 정오
- time = 0.75 → 저녁
- time = 1.0 → 자정 (0.0과 같음)
해가 뜨기 시작하는 것은 0.25초인 아침이고, 달이 뜨기 시작하는 것은 0.75초인 저녁임.
때문에 0.25초(아침)에는 해가, 0.75초(저녁)에는 달이 떠오를 수 있도록 보정값을 넣어주는 것.
Q2. 왜 +가 아니라 -인가?
여기서 많이 해맸는데, 이건 직접 식을 계산해보면 답이 나온다.
아래 표는 해의 위치를 위해 -0.25의 보정을 넣어준 결과이다:
time값계산 결과 결과 (eulerAngles)태양 위치 0.00(00:00)(-0.25) * 90 * 4(-90, 0, 0)태양이 지평선 아래 0.25(06:00)(0.00) * 90 * 4(0, 0, 0)태양이 떠오르기 시작 0.50(12:00)(0.25) * 90 * 4(90, 0, 0)태양이 하늘 꼭대기 0.75(18:00)(0.50) * 90 * 4(180, 0, 0)태양이 지평선으로 내려감 1.00(00:00)(0.75) * 90 * 4(270, 0, 0)태양이 반대쪽 지평선 아래
만약 0.25를 빼주지 않거나, 0.25를 빼는 대신 더해버린다면 해는 자정부터 떠버리거나, 저녁무렵부터 떠오르는 등 해가 너무 일찍 떠오르게 된다.
0.5초에 태양이 하늘 한가운데(90, 0, 0)에 있으려면 무조건 -0.25를 해주어야 하는 것.
달의 경우도 마찬가지로 자정에 하늘 한가운데(90, 0, 0)에 위치할 수 있도록 -0.75의 보정값을 넣어주는 것.
time값계산 결과 결과 (eulerAngles)달 위치 0.00(00:00)(-0.75) * 90 * 4(-270, 0, 0)달이 지평선 아래 0.25(06:00)(-0.50) * 90 * 4(-180, 0, 0)달이 지평선 근처 0.50(12:00)(-0.50) * 90 * 4(-90, 0, 0)달이 떠오르기 시작 0.75(18:00)(-0.25) * 90 * 4(0, 0, 0)달이 떠오름 1.00(00:00)(0.00) * 90 * 4(90, 0, 0)달이 정점
Q3. 왜 4.0f를 곱해주어야 하는가?
4f를 제외하고 0.25 * 90 부분만 계산한다면, 결과 값은 -90이 아니라 -22.5가 나온다.time이 1.0이 되더라도 우리가 (90, 0, 0)인 noon을 곱해주는 한 90도 이상의 각도는 나올 수 없다.4.0f를 곱해주어 360도로 회전할 수 있도록 보정값을 넣어주는 것!!UpdateLighting 메서드를 이해한 것 처럼 적어두긴 했지만 아직 완벽하게 이해한 것 같지는 않다. 어디까지나 아, 이게 이런 뜻이구나! 정도로 납득한 느낌...UpdateLighting 부분은 여유가 된다면 내일 튜터님께 여쭤볼 예정!