일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 내배캠
- 티스토리챌린지
- Til
- Action
- 프로그래머스
- 최종프로젝트
- 마크다운
- projectl
- 스탠다드
- 내주말
- 텍스트게임
- 유니티
- 스파르타
- 분반별
- string배열과 char
- Input Field
- 코테
- 분반별학습
- 스파르타부트캠프
- 코테풀기
- 내일배움캠프
- 스파르타내일배움캠프TIL
- 피셔예이츠
- input system
- 스파르타내배캠til
- 코딩테스트
- 알아볼것
- 백준
- 스파르타내일배움캠프
- 오블완
- Today
- Total
Ottoman
내일배움캠프 5주 4일차(23) TIL - 이슈해결 본문
오늘의 목표
1. 현재 방해물이 고정된 위치에 나타나고 있다. 해상도가 바뀌는것에 대비해 플레이화면의 구석에 자리잡도록 고정된 좌표값을 계산된 수식으로 바꾸기.
2. 현재 고정된 방해물만 호출한다. 여러 방해물 중 랜덤으로 하나를 골라 호출하는 함수 만들기.
3. 방해물 한 개 더 만들기.
// 이슈
상위클래스에서 뷰포트 좌표를 월드 좌표로 변환한 값을 필드에 저장하고 있다. 하위클래스에서 이 월드좌표값을 가져다 쓰려는데 상위클래스에서는 좌표를 잘 땄는데 하위클래스 실행될 때가 되면 이 좌표값들이 모두 0이 되어있다.
의도
Disruptor클래스에 방해물에서 자주 가져다 쓸 정보를 저장해놓고 자식클래스에서 가져다 쓴다.
관찰
OnEnable이 Awake보다 먼저 작동하고있다. 그로인해 fourEdges가 초기화되지 않아 null 이었다. 새로운이슈다.
void OnEnable()
{
if(fourEdges == null)
{
fourEdges = new FourEdge[4];
fourEdges[0] = new FourEdge(leftEdgeWorldPos, bottomEdgeWorldPos, false, false);
fourEdges[1] = new FourEdge(rightEdgeWorldPos, bottomEdgeWorldPos, true, false);
fourEdges[2] = new FourEdge(rightEdgeWorldPos, topEdgeWorldPos, true, true);
fourEdges[3] = new FourEdge(leftEdgeWorldPos, topEdgeWorldPos, false, true);
}
int i = Random.Range(0, 4);
NewPos(fourEdges[i]);
m_Coroutine = CoroutineMethod();
StartCoroutine(m_Coroutine);
}
Awake()에서 진행하던 리스트 초기화를 OnEnable에서 하도록 했다. 이렇게해서 fourEdges가 null이 되는건 막았다.
이래도 원래 이슈는 그대로다.
문제이유
위와 같이 상위클래스스크립트 Disruptor는 매니저에 캐싱되어있고 값을 얻으려는 Disruptor게임오브젝트에는 없다.
스크립트의 필드값은 게임오브젝트마다 고유하게 저장된다. static이 아니기 때문.
하위클래스의 스크립트에서 값을 접근하면 같은 게임오브젝트의 상위클래스에 접근한것이다. 실제 원하는 값은 다른 게임오브젝트에 저장되어 있는데 말이다.
대처방법
내가 원하는 값을 담고있는 게임오브젝트를 인스펙터에서 직접 캐싱해서 가리키고 그곳을 통해서 값을 가져왔다.
public class Disruptor_Kotori : Disruptor
{
// 플레이 스크린 구석에서 랜덤하게 등장하여 화면을 가리는 방해물입니다.
[SerializeField] private DisruptorMgr disruptorMgr;
private FourEdge[] fourEdges;
...
...
private void Awake()
{
...
}
void OnEnable()
{
if(fourEdges == null)
{
Disruptor disruptor = disruptorMgr.GetComponent<Disruptor>();
fourEdges = new FourEdge[4];
fourEdges[0] = new FourEdge(disruptor.GetleftEdgeWorldPos(), disruptor.GetbottomEdgeWorldPos(), false, false);
fourEdges[1] = new FourEdge(disruptor.GetrightEdgeWorldPos(), disruptor.GetbottomEdgeWorldPos(), true, false);
fourEdges[2] = new FourEdge(disruptor.GetrightEdgeWorldPos(), disruptor.GettopEdgeWorldPos(), true, true);
fourEdges[3] = new FourEdge(disruptor.GetleftEdgeWorldPos(), disruptor.GettopEdgeWorldPos(), false, true);
}
int i = Random.Range(0, 4);
NewPos(fourEdges[i]);
m_Coroutine = CoroutineMethod();
StartCoroutine(m_Coroutine);
}
...
}
이슈에 대한 소감
이전 TIL에서 게임오브젝트와 스크립트 필드값의 종속성을 GPT에 질문한 적이 있었다. 직접 겪어보니 더 잘 알겠다. 질문한 적이 없었다면 원인과 해결방법을 알아내는데 더 많은 시간이 들어갔을거라 생각한다.
따라서 생기는 의문
상위클래스의 값을 그냥 가져다 쓸 수는 없는걸까? 상위클래스 필드에 저장해둘 값은 static으로 하면 좋을까? 혹시 상위클래스 Disruptor를 매니저에 캐싱해둬서 생긴일은 아닐까? 지금 Disruptor의 Init()을 매니저에 맡겨놔서 이어지는 나비효과 아닌가?
개선점
SetActive를 통해서 실행되기때문에 OnEnable에 구속된다. 게임오브젝트는 항상 활성화 해두고 spriteRenderer를 enable 체크 하면 구속에서 많이 벗어날 것 같다.
// 추상클래스 abstract class 는 게임오브젝트에 캐싱할 수 없다.
- 추상클래스 자체는 인스턴스화될 수 없기 때문에 MonoBehavior의 메서드( Awake, Start, Update등)가 직접 실행되지 않는다.
- 추상클래스를 상속받은 자식클래스는 인스턴스화될 수 있다.
- 자식클래스에서 추상클래스에서 정의된 추상 메서드와 가상 메서드를 구현 및 호출할 수 있다.
- 자식 클래스의 인스턴스는 추상 클래스에서 정의되고 구현된 일반 메서드도 호출할 수 있습니다.
자주 맞닥뜨린 문제
초기화를 대부분 Awake에서 하다보니 여러 클래스의 Awake 순서가 꼬여 실행되지 않는 경우가 있다.
- 순서가 안맞은 코드는 Start()로 옮겨서 실행순서를 보장했다.
GPT의 제안
Unity에서 여러 클래스의 초기화 순서가 문제될 때, Awake와 Start 메서드의 순서로 인해 발생하는 문제를 해결하기 위한 다양한 방법이 있습니다. 여기 몇 가지 대안을 소개합니다:
1. Script Execution Order 설정
Unity는 특정 스크립트의 실행 순서를 설정할 수 있는 기능을 제공합니다. 이를 통해 Awake나 Start 메서드의 실행 순서를 명시적으로 지정할 수 있습니다.
Edit 메뉴에서 Project Settings를 선택합니다.
Script Execution Order를 클릭합니다.
원하는 스크립트를 추가하고 실행 순서를 지정합니다.
이를 통해 특정 스크립트가 다른 스크립트보다 먼저 실행되도록 할 수 있습니다.
2. Lazy Initialization
필요한 순간에 초기화를 진행하는 방식입니다. 예를 들어, 다른 클래스의 초기화가 필요할 때까지 초기화를 연기하는 것입니다.
3. Singleton Pattern
Singleton 패턴을 사용하여 클래스의 인스턴스를 전역적으로 관리하고 초기화를 보장할 수 있습니다.
4. Manual Initialization
각 스크립트의 초기화 순서를 명시적으로 지정하는 방법입니다. 특정 초기화 메서드를 만들어 필요할 때 호출합니다.
5. Using Unity's RuntimeInitializeOnLoadMethod Attribute
이 속성을 사용하여 특정 메서드가 모든 씬이 로드된 후 실행되도록 할 수 있습니다.
요약
각 방법은 특정 상황에 따라 유용할 수 있습니다. 가장 적합한 방법을 선택하여 초기화 순서를 보장하는 것이 중요합니다. Script Execution Order 설정이 가장 간단한 방법일 수 있으며, 복잡한 초기화 로직이 필요한 경우 Lazy Initialization 또는 Singleton Pattern을 사용할 수 있습니다. Manual Initialization은 명시적인 초기화 순서가 필요한 경우에 유용합니다.
유니티 강좌4. 게임 오브젝트의 회전 : 네이버 블로그 (naver.com)
유니티 강좌4. 게임 오브젝트의 회전
회전을 하는 방식은 크게 3가지가 있다. transform.Rotate transform.RotateAround Quaternion 먼저 tr...
blog.naver.com
'TIL' 카테고리의 다른 글
내일배움캠프 5주 일요일 TIL - 메모이제이션, 효과음, 오일러 각의 계층구조 (0) | 2024.05.19 |
---|---|
내일배움캠프 5주 토요일 TIL - (GPT) 상위클래스의 인터페이스, 게임오브젝트(상위,자식) 가리키기, Animation트러블슈팅, HasExitTime (0) | 2024.05.18 |
내일배움캠프 5주 3일차(22) TIL - 팀프로젝트 생성자 vs Awake() (0) | 2024.05.16 |
내일배움캠프 5주 2일차(21) TIL - 유니티 스크립트 주의사항... (1) | 2024.05.14 |
내일배움캠프 5주 1일차(20) TIL (0) | 2024.05.13 |