Q.Unity TMP에 있는 폰트로 한글을 쓰려니 자꾸 깨져서 나와

A.

한글이 적용되는 폰트로 TMP Asset을 만들어주면된다.

 

1.Window - TextMeshPro - Font Assets Creator 로 들어가 설정창을 연다.

 

2. Font Assets Creator의 위에서부터 빨간상자 쳐진 부분을 채워준다.(제일 위 다운받은 폰트를 적용시켜준다.)

다 적용 했으면 Font Atlas를 생성 후 Save 해준다.

 

3.만들어진 TMP_Font Asset Inspector에서 AtlasPopulation Mode가 Static을 되있는것을 Dynamic으로 변경하고 Sampling Point Size를 60정도로 설정후 Apply를 눌러적용 시킨다.

 

4.그리고 적용시킬 TMP 오브젝트에 폰트에셋을 적용시켜 사용한다.

 

Q.TMP 한글 폰트 적용까진 다했는데 스크립트에서는 어떡해 사용해?

A.

using UnityEngine;
using TMPro;

public class DebugText : MonoBehaviour)
{
    public TMP_Text tmpT;
    public TextMeshProUGUI tmpUgui;

    tmpT.text = "원하는 텍스트 적어넣기";
    tmpUgui.text = "원하는 텍스트 적어넣기2";
}

TMP_Text , TextMeshProUGUI 자료형을 선언하고 사용하면 된다. 

개인 프로젝트를 제출하고 금일 하였던 꾸준실습 강의와 인강으로 들었던 내용을 복습하는겸 정리하였다.

 

https://01149.tistory.com/102

 

Unity :: 비트연산자와 레이어마스크

Q.비트연산자는 뭐야?A.컴퓨터가 나타내는 제일 작은 단위인 비트(bit = 0 또는 1)비트 연산자는 bit(0,1)을 이동하거나 논리 연산을 할떄 사용하는 것이다.논린 연산을 사용하면 이런식의 결과값을

01149.tistory.com

https://01149.tistory.com/103

 

C# :: 메모리 구조

Code 영역 : 프로그래머가 작성한 코드를 보관 Data 영역 : Static, Const 프로그램/어플리케이션 전반에 필요한 데이터 저장생성 시점 : 프로그램/어플리케이션 실행시삭제 시점 : 프로그램/어플리케

01149.tistory.com

 

 

 

Code 영역 : 프로그래머가 작성한 코드를 보관  
Data 영역 : Static, Const
프로그램/어플리케이션 전반에 필요한 데이터 저장
생성 시점 : 프로그램/어플리케이션 실행시
삭제 시점 : 프로그램/어플리케이션 종료시
Heap 영역 : 참조 데이터(객체) 저장 생성 시점 : new(C#), Instaniate(Unity)
삭제 시점 : GC(가비지 컬렉터) 동작 시점
가비지는 해당 데이터가 사용하지 않는 시점
(참조가 끊긴 시점)GC에 수집됨

p.s
Unity Editor의 Destroy()라는 오브젝트를 삭제해주는 메서드가 있는데 이것도 눈에 보이는 오브젝트가 삭제된거지 Heap영역에 있는 Data들은 가비지 데이터가 되어버린다.
Stack 영역 : 로컬 변수,매개 변수 저장
프로그램/어플리케이션 실행순서에 필요한 데이터 보관
생성 시점 : 함수 내에서 선언시, 변수 할당시 생성
종료 시점 : 함수가 종료될때

 

priavte void Start()
{
    int x = 10;
    int y = x;
    x = 5;
}

 

해당 코드가 있을때 x,y의 최종 데이터는 어떡해 되는가?

x = 5 , y = 10이 될것이다.

 

class Person()
{
	public int Age;
	public string Name;
}

private void Start()
{
	Person personA = new Person(32,"BirdHead");
    
	Person personB = personA;
    
	personA.Age++;
}

해당 코드에서 PersonA와 PersonB의 Age의 데이터는 몇인가?

결과는 둘다 33 이다.

 

Q.왜 위의 코드와 아래의 코드의 데이터는 왜 다른식으로 저장돼?

A.

로컬 변수(지역변수) 및 매개변수는 Stack영역에 첫 메모리부터 저장되기 시작한다.

해당 데이터에 접근하기 위해 0000 0000 주소값을 가져오는게 아닌 변수 x로 이름을 대체하여 사용하게 되는것이다.

해당 변수(주소 이름)을 호출하여 그 안에 데이터를 꺼내서 사용하는 것이다.

 

 

해당 코드는 Class로 객체 A,B를 생성하고 A의 Age 데이터를 1 증가시키는 코드이다.

1번 : 체(인스턴스)는 Stack 영역에 저장되고 객체의 데이터들은 Heap영역으로 저장된다.

이때 Heap의 저장된 Data는 Stack에 참조 되어 있는 상태라고 표현한다.

2번 : B 객체에 A를 대입 하게되면 , 객체인 B는 Stack 영역에 저장되고 B 객체의 데이터는 A에 참조되어 데이터를 사용하게 된다.

3번 : A 객체의 Age 데이터를 호출할때  Stack 영역의 A객체에 접근 후 참조된 데이터값에 접근하여 수정 할 수 있다.

 

이런 과정을 걸치면 B도 기존의 A데이터 Heap영역에 참조되어있기에 데이터가 변경되면 B도 같이 변경되는 모습을 볼 수 있다.

 

Q.객체를 삭제하게 되면 Heap영역의 Data는 어떡해 돼?

A.

Stack 영역의 객체만 삭제되고 , Heap영역의 Data는 참조가 끊기며 사용하지 않는 데이터가 된다.

즉, 가비지(쓰레기)데이터가 되어 메모리 누수를 발생시킨다.

하지만 C#에서는 이런 가비지 데이터들을 수집하여 처리해주는 GC(가비지 컬렉터)가 존재한다.

GC는 어느 시점에 동작하는지는 알 수 없다.

 

Q. GC가 있으니 메모리 걱정없이 사용해도 되는거 아닌가요?

A. 절대 아니다

private voud Update()
{
    Monster orc = new Monster();
    //Unity Editor인경우 Instaniate가 new와 똑같다.
    
    Debug.Log("몬스터 이름" + Orc.name);
}

===============================================================

IEnumrator Coroutine
{
	while(true)
    {
        Monster orc = new Monster();
        //Unity Editor인경우 Instaniate가 new와 똑같다.

        Debug.Log("몬스터 이름" + Orc.name);
        
        yield return new WaitForSeconds(1.0f);
    }
}

위에 적힌 두 코드들의 공통점은 반복되는 루프문이라는 것이다.

 

 

Update()는 1프레임에 한번씩 동작하는 함수이다. 60프레임이면 1초에 60번 동작하는 함수라는 것인데,

해당 함수는 Stack 영역에 객체를 생성하고 함수의 종료로 파괴되어 가비지 데이터를 생성하게되는데

지금 해당 함수로 1초면 벌써 가비지 데이터가 60개가 생성된것이다. 

 

그리고 GC가 호출되어 가비지데이터들을 수집할떄 수집 갯수가 많게되면 결국 우리가 잘아는 프리징,렉 이 발생하고 심각하면 프로그램이 종료되는 현상도 발생하는 것이다. 

 

유니티 오브젝트의 Inspector의 컴포넌트를 들을 보면 상위 클래스인 Monobehaviour로 구성된 것을 알수 있다.

즉, 객체 생성 후 파괴되거나 사라지게 되면 가비지가 생성 된다. 

 

유니티로 개발시에 오브젝트 생성 및 파괴를 목적이나 이유없이 절대 반복문에 작성하면 안된다.

 

Q. 그럼 가비지데이터를 생성 안하는 방법이 있나?

A. 아니 없다. 개발할때 무조건 가비지 데이터는 만들어 질 수 밖에 없다.

하지만 가비지 데이터를 적게 만들어내는 방법들이 있고 이런 방법들이 바로 최적화를 의미한다.

 

가비지 데이터를 적게 만들어내는 방법

1. 데이터를 한번만 준비하고 재활용 

public class MonsterDisplay : Monobehaviour
{

    private voud Update()
    {
        Monster orc = new Monster();
        //Unity Editor인경우 Instaniate가 new와 똑같다.

        Debug.Log("몬스터 이름" + Orc.name);
    }
}

============= 재활용 =======================

public class MonsterDisplay : Monobehaviour
{
    private Monster orc;
     
    private void Awake()
    {
    	//Awkae() 메서드로 오브젝트가 활성활될때 한번만 실행
    	orc = new Monster();
    }
     
    private voud Update()
    {
        Debug.Log("몬스터 이름" + Orc.name);
    }
}

위의 코드처럼 Monster 객체를 Class내의 변수로 선언하고 Awake()에서 한번만 생성 이후

Update()에서는 객체가 동작하는 코드를 작성하면 된다.

 

해당 코드에 내용 및 기능이 더 추가되면 오브젝트 풀링이라는 설계패턴이 만들어진다.

 

2.큰 규모의 데이터가 아니거나 자주 사용해야되는 데이터인 경우 구조체 사용하기

 

구조체는 데이터가 Stack 영역에 생성

즉 , 구조체는 파괴되어도 Stack 영역에서 파괴되기에 가비지 데이터가 남지 않게된다.

또한 Stack 메모리가 Heap 메모리보다 속도도 빠르기에 자주 사용되는 데이터들은 구조체가 최적화에 좋다.

 

Unity 내에서도 Vector2, Vector3는 Class가 아닌 구조체 타입이다.

 

그렇다고 모든 데이터를 구조체로 작성하면 문제가 생긴다.

1.큰 데이터를 구조체로 생성시 문제 발생

2.외부데이터를 호출해서 사용해야 될경우 문제 발생

이러한 문제점이 발생하기에 적절히 사용하여야한다.

Q.그럼 데이터들이 어떤게 참조되어 사용되는지? 어떤게 Stack에 만들어지는지 알아?

A.

Stack 영역 ( Value 형식 ) Heap 영역 ( Ref 형식)
C#의 기본 타입
(int,float,bool,char ….)
String
구조체 Class
enum interface
  Delegate

이 표를 통해 이렇게 나눠진다고 보면된다.

Stack 영역에 생성되는 데이터들은 Value(값) 형식이라고 부른다.

Heap 영역에 생성되는 데이터들은 Ref(참조) 형식이라고 부른다.

Heap 영역의 객체는 Stack에 생성되고 객체의 데이터는 Heap영역에 생성되고 객체에 참조되어진다.

 


결론 :

0, Stack 영역은 Value 형식들의 데이터가 저장 , Heap 영역은 Ref 형식들의 데이터가 저장됨

1. 구조체는 Value 형식 , Class Ref 형식이다. Ref 형식의 데이터들은 Stack 데이터들이 참조하여 사용되고 있다.

2. 객체 생성시에 미리 선언하여 저장해놓고 사용한다.

3.Ref 형식 데이터들은 참조가 끊기면 가비지 데이터가 되어 GC에 의해 처리된다.

Q.비트연산자는 뭐야?

A.

컴퓨터가 나타내는 제일 작은 단위인 비트(bit = 0 또는 1)

비트 연산자는 bit(0,1)을 이동하거나 논리 연산을 할떄 사용하는 것이다.

논린 연산을 사용하면 이런식의 결과값을 얻을수 있다.

AND : 1000 & 0001 = 0000 
OR : 1000 & 0001 = 1001 
XOR : 1000 & 0001 = 1001 
NOT : 1000 -> 0111 

0010  << 1 = 0100 

0010  >> 1 = 0001

(<< , >> 이동시 처음과 끝 bit 는 0으로 채워진다.)

 

Q.비트연산자는 왜 쓰는거야?

A.

효율적이고 빠른 데이터 처리와 메모리 절약이 목적

Unity에서는 레이어마스크 처리 할 때 사용이 됨

 

사용하는 이유를 정확히 설명하면

1.메모리 효율성 :하나의 정수 값으로 각각의 비트를 개별적인 상태로 관리 할수 있기 때문에

예를 들이 32bit 정수 (4byte = int)는 32개의 서로 다른 상태를 한번에 표현 할 수 있기 때문

2.빠른 성능 : 비트연산은 CPU에 최적화 된 연산이며 덧셋,곱셈 보다 비트 연산이 더 빠르게 수행되기 떄문에

그래픽 처리시 매우 높은 최적화 효율을 보인다.

3.복잡한 조건을 단순하게 표현 : 메모리 효율성과 좀 겹치는 내용이지만, 32개의 정수 단일값으로 다중 상태를 관리하기에 가독성이 매우 좋아짐 

 

Q.레이어마스크는 뭐야?

A.

Unity의 레이어비트마스크(Bit Mask)를 사용하여 여러 레이어를 하나의 값으로 표현하는 방식

오브젝트의 레이어 설정 및 32개의 레이어(int 정수= 32bit)

 

 p.s 비트마스크?

bit의 이진수 표현을 활용하여 나타내는 방법

 

Q.레이어마스크는 왜 쓰는거야?

A.

일반적으로 내 공격이 나한테 맞거나 , 적의 공격이 적에 맞거나 하는건 게임에서는 적용되면 않된다.

(물론 적용되는 게임들도 있다) 이러한 문제를 대처하기 위해 레이어마스크를 사용하는것이다 .

 

1.충돌 검사 : 비트 마스크를 사용하여 특정 레이어에 속한 오브젝트만을 대상으로 충돌 검사 수행이 가능

즉, 태그,이름(문자열)로 검색할 필요가 없으니 최적화에 도움이 됨

2.레이캐스팅 제어 : 레이캐스트가 특정 레이어의 오브젝트에만 반응하도록 비트마스크를 설정 할 수 있음

 

예시1)

객체에서 레이(빛)을 쏴서 부딪히는 객체를 탐지할때 ,장애물 오브젝트는 무시하고 탐색을 하고 싶을때

해당 장애물의 레이어를 제외하여 레이가 해당 장애물 오브젝트에 부딪힐때 투과해서 쏠수 있게 하는기능

레이(빛)이 장애물(8번레이어)에 닿은경우 투과하여 사람(5번 레이어)에 접촉하여 사람 오브젝트의 Data를 얻게됨

해당 내용을 코드로 작성하면 

public int ObstcalseLayer = 5;
public int PeopleLayer = 8;

LayerMask layerMask = 1 << ObstcalseLayer; //장애물(5번레이어)를 비트마스크로 생성후 레이어마스크에 초기화
raycastObj = ~raycastObj; //반전비트를 이용하여 장애물 레이어를 0으로 변경
 
 //레이캐스트에 layerMask를 인자값으로 넣어 장애물 레이어를 투과하여 레이캐스트를 발사
 Physics.Raycast (transform.position, transform.TransformDirection (Vector3.forward), hit, Mathf.Infinity, layerMask);

이렇게 작성된다.

 

3.카메라 렌더링 설정 : 카메라가 특정 레이어의 오브젝트를 렌더링하도록 설정하여, 게임의 시각적 요소를 세밀하게 제어를 가능하게 한다. 

 

Q.레이어마스크를 어떡해 쓰는거야?

A.우선 오브젝트에 있는 레이어를 코드내에서 사용하기위해 비트마스크로 생성해야한다.

 

1 << n // 1을 n번째 비트 위치로 시프트(이동)한다는 의미
//즉 n은 레이어(0~32 layer정수값을 의미), n번쨰 레이어를 나타내는 비트마스크로 생성한다는 의미

//예시
//충돌된 객체의 게임오브젝트에서 레이어를 초기화
int layer =  collision.gameObject.layer;

1 << layer; //호출받아온 레이어를 비트마스크로 생성하여 사용

 

해당 코드처럼 1 << n 을 사용하여 비트마스크로 생성하는것이다. 

 

그럼 비트마스크로 생성까지 완료했으면 그 이후에는 어떡해 해야되나?

해당 비트마스크를 이용하여 검사를 진행하면된다.

 

위에서 설명한 논리 연산자들을 활용하는 것이다.

AND (&): 특정 레이어의 존재 여부 확인할떄 사용
OR (|): 새로운 레이어를 추가할 때 사용
XOR (^):  두 레이어의 차이를 찾을떄 사용 ( 비교하는 대상이 다르면 1 )
NOT (~):  특정 레이어를 제외시킬때 사용 ( 모든비트 반전 )

 

보통 이 논리연산자들 이 특징들을 사용한다. 

 

public LayerMask levelCollisonLayer; //비교할 기준 레이어

private void OnTriggerEnter2D(Collider2D collision)
{
	//LayerMask의 value 프로퍼티는 해당레이어의 비트마스크를 십진수를 반환해준다.
    if (IsLayerMatched(levelCollisonLayer.value, collision.gameObject.layer))
    {
        //레이어가 일치하면 true
    }
	//레이어가 불일치하면 false
}



private bool IsLayerMatched(int value, int layer)
{
	//value는 비트마스크이기 때문에 그대로 사용 , layer는 비트마스크로 생성 
    
    //(value | 1 << layer) : | 연산자로 기준 레이어(value)와 객체와 충돌한 레이어(layer) OR연산
    
    //OR 연산된 비트마스크 가 기준 레이어와 일치할경우 True
    return value == (value | 1 << layer);
}

 

해당 코드는 OR 연산을 충돌된 객체 레이어가 기준 레이어와 일치할 경우 동작하는 코드이다.

OR 연산은 레이어를 추가할때 사용 하는데 , 이런식으로도 원하지 않는 레이어는 제외할수 있게 구성 할 수도 있다.

 


해당 테크닉은 유니티 게임 개발시에 정말 많이 사용될것이기 때문에 반드시 익혀둬야한다.

 

해당 애니메이터를 이용하여 설명

Q.StringToHash 메서드는 뭐야?

A.

Animator Class에 있는 메서드애니메이션의 파라미터의 이름(문자열)을 해시값으로 변경하여

최적화 및 성능 개선에 도움을 주는 메서드이다.

 

Q.Animator의 함수라는건 알겠어, 근데 왜 사용하는거야?

A.위에서 말했듯이 최적화 및 성능개선이라고 했다.

애니메이션의 파라미터를 코드로 사용할때

SetBool("isWalking",true);

이런식으로 문자열을 그대로 넣어서 사용하게 되면 파라미터의 문자열을 서로 비교하는 작업을 하는데

이때 처리비용이 크기 때문에 비효율적이라고 하는것이다.

 

하지만 StringToHash를 사용하여 문자열을 Hash값으로 변경해주면

int(정수)는 한 자리만 체크하므로 최적화에 매우 효율적이기 때문이다.

 

또한 문자열 -> 정수( 21억 범위)로 변홤하여 충돌(중복)이 일어날 확률이 매우 낮다!!!
문자열 -> 정수(Hash값)은 항상 동일한 해시값을 반환하기에 다른 파라미터를 건들일 일도 없다.

 

Q.StringToHash 단점이 있을까?

A.

정수 -> 문자열 반환에는 사용하지 못하는 단점이 있다, StringToHash는 일방향으로 작동하는 메서드이기 때문에

 

 p.s

정수(Hash값) -> 문자열은 항상 동일한 문자열을 반환하지 않기에 충돌이 발생 할 수 있다.

 

 

Q.StringToHash는 어떡해 사용해?

public class TopDownAnimationController : AnimationController
{
	//해당 객체에서 사용할 Class 위에 문자열을 해시값으로 변환한후 정적변수로 선언해놓기
    private static readonly int isWalking = Animator.StringToHash("isWalking");
    private static readonly int isHit = Animator.StringToHash("isHit");
    private static readonly int attack = Animator.StringToHash("attack");
 
 	
    //필요할때 해당 변수값을 가져와서 사용하기
 	private void Move(Vector2 vector)
	{
         animator.SetBool(isWalking);
	}

}

이런식으로 미리 문자열을 해시값으로 변환하여 선언한 뒤 (정적으로 생성하여 컴파일하면서 동시에 생성)

필요할때 해당 해시값 변수를 인자값으로 사용하면 된다.

Q.오브젝트 풀링이란?

A. Scene에서 사용할 오브젝트(객체)들을 미리 만들어놓고서 컨테이너에 저장해놓는다.
필요할때 오브젝트 풀링에서 해당 오브젝트를 호출해서 사용한 뒤 다시 컨테이너(오브젝트 풀)에 돌려주는 설계 패턴이다.

 

Q.오브젝트 풀링을 왜 사용하는가?

A.

객체를 생성/파괴하는 메서드인 Instantiate / Destroy는 호출시에 처리비용이 매우 크다.

즉, 처리비용이 매우 크다는 말은 최적화에 안좋다는 뜻이다.

 

미리 객체를 만들어 놓으면 첫 시작할때만 생성을 하기에 실제 인게임시에는 오브젝트를 사용하려고 꺼낼때 처리비용이 크지 않다.

또한 파괴하지 않고 컨테이너(오브젝트 푸)에 돌려주기 때문에 계속 재활용이 가능한 장점도 있다.

 

Q.오브젝트 풀링을 어떡해 사용해?

A.오브젝트 풀링은 프로그래머의 재량에 따라 형태나 많이 바뀐다. 

using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;

public class ObjectPool : MonoBehaviour
{
	//Pool 구조체는 오브젝트 하나를 의미함
    [System.Serializable]
    public class Pool
    {
        public string tag; //오브젝트의 태그
        public GameObject prefab; //오브젝트의 프리팹
        public int size; //Pool에 담을 오브젝트의 갯수
        //TODO CODE : 오브젝트에서 사용할 필드값
    }

    public List<Pool> pools = new List<Pool>(); //사용할 오브젝트를 보관하는 컨테이너
    public Dictionary<string, Queue<GameObject>> PoolDictionary;//실제로 호출하여 사용하게 될 컨테이너

    private void Awake()
    {
        PoolDictionary = new Dictionary<string, Queue<GameObject>>();
		
        //List에 있는 오브젝트를 하나씩 호출하여 n개만큼 생성 후 오브젝트 풀(PoolDictionary)에 담음
        foreach (var pool in pools)
        {
        	//Unity 하이어라키에 오브젝트를 담을 부모 오브젝트 생성
            GameObject poolContainer = new GameObject("Pool_Container_" + pool.tag);
			
            //Queue 자료구조를 사용하여 오브젝트를 관리
            //Queue를 사용한 이유 -> 순서에 상관없는 오브젝트들 사용하기에
            Queue<GameObject> queue = new Queue<GameObject>()
            
            //생서할 오브젝트 갯수만큼 생성
            for (int i = 0; i < pool.size; i++)
            {
            	//오브젝트를 생성 -> 오브젝트를 비활성화 -> 오브젝트 풀(queue)에 담음
                GameObject obj = Instantiate(pool.prefab, poolContainer.transform);
                obj.SetActive(false);
                queue.Enqueue(obj);
            }
			
            //queue에 담은 풀을 Dictionary에 추가 -> 하나의 오브젝트 풀이 완성 
            PoolDictionary.Add(pool.tag, queue);
        }
    }
	
    //외부에서 오브젝트가 필요할때 호출되는 함수
    public GameObject SpawnFromPool(string tag)
    {
    	//오브젝트 풀의 key값을 비교하여 일치하지 않으면 탈출
        if(!PoolDictionary.ContainsKey(tag))
        {
            return null;
        }
		
        //해당 key의 오브젝트를 하나 dequeue
        GameObject obj = PoolDictionary[tag].Dequeue();
        
        //Queue에서 오브젝트 하나가 dequeue됫기에 다시 채워준다.
        PoolDictionary[tag].Enqueue(obj);
		
       	//꺼낸 오브젝트를 활성화
        obj.SetActive(true);
		
        return obj;
    }
}

해당 코드는 총알이나 투사체 및 파티클 같이 순서는 상관없고 재활용을 계속 해야되는 오브젝트에 사용되는 패턴이다.


 

이 코드의 단점은 오브젝트가 하나씩 발사 될때마다 다시 Enqueue로 챙겨주고 있으며 반납을 하지 않고있다.

즉, 투사체들은 오브젝트에 닿을때 해당 조건이되면 Destroy로 파괴되고 있기에 최적화에 좋지 않은 모습을 보이고있다.

 

대처법으로는 외부에서 사용이 완료된 오브젝트를 다시 반납하는 메서드를 만들어 사용하는것이 최적화에 좋다.

 


또 다른 단점은 오브젝트가 많은 숫자를 필요로 만들때 기존 오브젝트 갯수보다 많으면 문제가 생긴다.

예시로 이런 상황이 있다. 

1. 1초에 50개 발사 

2. 오브젝트를 20개만 만들어놓음 
3. 20개를 발사하고 21개쨰를 발사할떄 미리 발사해놧던 20개 중 하나가 사라지고 21개째가 되어 재활용이 되어버리는 문제
4. 총이 나가다가 뒤로 돌아오는 현상이 발생 

이런 부자연스러운 현상이 발생하기에 

 

대처법으로는 Queue대신 List를 활용하여 List 각 하나의 원소에 접근하여 사용중인지를 체크, 만약 모든 오브젝트가 사용 중인경우 추가로 오브젝트 풀에 오브젝트를 추가 생성하는 방식이 있다. 

 


위에 코드는 예시일 뿐이며 , 다른 사람들이 사용한 오브젝트풀링의 좋은 예제가 많이 있다.

여기서는 이런 개념이 있다라는 것을 파악하고 다음 프로젝트 진행시에 사용하는것을 목표로 하자.

[게임 도중 캐릭터 선택 기능]을 구현 중에 데이터를 교체하는게 아닌 Instnate와 Destroy를 이용하여 구현하였다.

나중에 시간이 되면 데이터를 채우는 형식으로 변경하는게 최적화에 더 좋을것 같지만, 우선 과제 제출이 먼저이기떄문에 최적화에 좋지 않지만 이것을 선택했다 하지만. 

 

해당 컴포넌트를 가진 오브젝트를 Destroy 를 하자 문제가 발생하였다.

파괴한 객체에 animator와 InputSystem이 자꾸 접근하려는 문제였다.

 

나는 분명 GameManager에 InputSystem을 컨트롤 하구 있고 Animator는 제대로 사라졌는데 왜 자꾸 사라진 객체에 접근하려는지 이유를 몰랐다.

 

원인은 내가 객체에 AnimationController를 컴포넌트로 추가해놨기 때문이였다.

해당 코드에서 Event함수에 메서드룰 추가 해놨으니 객체가 사라져도

InputSystem은 안사라지고 살아있으나 객체가 사라진 event함수는 갈곳을 잃었기에 문제가 발생하고 있었던것 이였다.

 

 

-대처-

해당 오브젝트가 파괴될떄 갖고 있는 컴포넌트를 전부 null 처리 및 Event함수에 저장된 함수를 제거함으로 트러블슈팅을 해결하였다.

오늘 개발중에 InputActionAsset이 호출이 안되서 알아보니 ScriptableObejct 였다.

 

https://01149.tistory.com/97

 

Unity :: 스크립트에서 InputSystem을 사용하기 위해 InputActionAsset을 호출시 주의할 점

//코드 일부를 가져왔습니다.private InputActionAsset inputActions;inputActions = GetComponent(); 해당 코드는 InputSystem 패키지를 이용하기 위해 스크립트 에서 InputActionAsset을 호출하는 코드이지만계속 Null이 들

01149.tistory.com

 

+ Recent posts