ScriptableObject는 Unity에서 데이터를 저장하고 관리하는 유연한 데이터 컨테이너이다.

 

ScriptableObject의 특징을 나열하면

1.MonoBehaviour 보다 경령화 되었고 GameObject에 컴포넌트로 추가 할 수 없다.

2.유니티 CallBack 중에서 OnEnable(),OnDisable(),OnDestroy()만 호출이 가능하다.

3.인스턴스화를 하지 않기 떄문에 사본이 여러개가 생성되지 않아 메모리 할당 최적화에 용이하다. 

 

json,xml,csv 등으로 관리하던 데이터를 Unity에서 쉽게 사용 할 수 있도록 편의적인 기능으로 제공한 것이 ScriptableObject 이다.

 

그럼 ScriptableObject는 어떡해 사용하는가?

1. ScriptableObject를 어떡해 설정할건지 선언이 필요하다.

파일 폴더링(폴더 분류 및 정리)는 필수이다.

Class 기반이기에 똑같이 .cs 로 작성을 해주면된다.

 

1.CreataAssetMenu 라는 Attribute 를 통해 포멧에 맞춰 작성해주어야 프로젝트 창에서 문제없이 호출하여 생성이 가능해진다.

2.ScriptableObject로 사용할 Class는 ScriptableObject를 상속해줘야 된다.

3.Header라는 Attribute를 통해 Inspector창에 표시될때 항목을 나눠줄 수 있다.

4. ScriptableObject Class 기반이기에 상속이 가능하다 ( RangedAttackSO는 AttackSO를 상속하고있다)

 

2. ScriptableObject를 .cs로 설정을 다했는데 어떡해 생성해?

제대로 .cs를 작성하였다면 , 프로젝트 창에서 생성시에 위에 이미지처럼 제대로 표시될것이다.

RangedAttackSO를 생성하면 Inspector에서 부모 데이터까지 다 Inspector에서 표시되며 쉽게 데이터 값이 수정 할수있다. 

 

3. ScriptableObject를 생성했으면 어떤식으로 코드에서 사용돼?

CharaterStat 클래스에 attackSO를 멤버변수로 가지게 한상태

캐릭터 스텟에 AttackSO 라는 ScriptableObject를 추가하여 사용하며 필요할때 데이터를 가져와서 호출할 수있다.

 

위의 CharaterStat을 SerializeField화 해놓고 Inspector에서 수정 할수 있게 선언
Charater Stats Handler 컴포넌트에 ScriptableObject(PlayerDefaultAttackSO)를 설정하고 스텟값을 설정하는 모습

 

이런식으로 해당 객체에게 능력치 데이터를 선언할 수 있게 된다.

자식 Class로 형변환하여 사용도 가능

투사체를 발사하는 메서드인데 공격에 관련된 데이터를 ScriptableObject에 담아놔서 이렇게 꺼내 쓸수도 있다.

 

4. 스텟 능력치 말고도 쓸 수 있어?

물론이다.

1.다양한 패턴을 분리하여 ScriptableObject로 만들수도있고

2.FSM 구조같은 형태를 만들어 해당 캐릭 모드를 ScritptableObject로 만들수도 있고

3.미연시 게임의 대화 로직 같은 것도 .csv가아닌 ScritptableObject로도 만들 수있다.

 

위에 예시 든것 뿐만아니라 생각해서 활용하면 어떡해든 만들어 사용 할 수 있다. 

오늘은 두 벡터사이의 거리를 측정 할수있는 Atan2(y,x)메서드 Unity에서 다루는 회전에 대해 TIL을 작성했습니다.

 

https://01149.tistory.com/90

 

게임수학 :: 아크탄젠트( Atan2(y,x) 함수에 대해 ) , 두 객체 사이의 각도 알기

삼각함수에 아크(arc)는 삼각함수의 역함수를 나타내는 것이다.삼각함수는 각도 -> 변의 비율 을 알수 있다면역삼각함수는  변의 비율 -> 각도 를 알수 있다. 그럼 역삼각함수중 왜 아크탄젠트를

01149.tistory.com

https://01149.tistory.com/91

 

Unity :: 오일러각 vs 쿼터니언 , 쿼터니언과 벡터 곱셈

게임 개발을 하면 오브젝트를 회전시켜야 될 일이 정말 많다.회전을 시키면 두가지 개념을 보게 되는데오일러 각과 쿼터니언이다. 오일러 각(Euler Angle)-3차원 공간의 절대적 좌표를 기준으로 물

01149.tistory.com

 

게임 개발을 하면 오브젝트를 회전시켜야 될 일이 정말 많다.

회전을 시키면 두가지 개념을 보게 되는데

오일러 각과 쿼터니언이다.

 

오일러 각(Euler Angle)

-3차원 공간의 절대적 좌표를 기준으로 물체의 회전을 측정하는 방법

-X,Y,Z 축을 기준으로 회전하기 때문에 직관적이고 조작이 용이함

-180도가 넘는 회전의 표현이 가능

-3번에 나누어 계산하기 떄문에 비용으 크며 짐벌락 현상이 발생할수 있음 

 

짐벌락 : 두개의 회전 축이 서로 겹쳐지는 현상

https://www.youtube.com/watch?t=97&v=zc8b2Jo7mno&feature=youtu.be

 

쿼터니언(Quaternion)

-방향과 회전을 표현하는 방식

-4차원 복소수를 이용한 회전표현 방법 ( x,y,z,w)로 이루어져 있고 , 각 성분은 축이나 각도가 아니다.

-180도가 넘는 회전을 표현하지 못한다 ( 180도 넘는 표현은 179 + 130 이런식으로 더해서 표현한다)

-각 축을 한번에 계산하여 비용이 적고 짐벌락이 발생하지 않는다.


위의 각 오일러 각 , 쿼터니언의 특징을 알고있으면 게임 개발시 많은 도움을 받을 수있다.

 

유니티는 Inspector 에서 나오는 Rotation(회전)은 vector3로 표현이 된다. 즉, 오일러 각을 의미한다.

하지만 유니티 내부에서는 회전을 계산시에 오일러각이 아닌 쿼터니언을 사용하게된다. 

 

즉, 코드내부에서 회전을 시킬경우 쿼터니언 메서드들을 사용해서 회전을 시켜줘야한다.

 

쿼터니언은 많은 메서드를 가지고 있지만 자주 사용하는것을 몇개 기록하였다.

 

1. Quaternion.Euler(x,y,z) : 오일러각을 쿼터니언으로 변경 


두 벡터의 거리(Vector2 direction)를 인자값으로 아크탄젠트를 이용하여 각도(rotZ)를 반환받은뒤

해당 각도를 이용하여 객체(armPivot)을 회전시키고 있다.


2. Quaternion.LookRotation : 머리를 회전하는 것처럼, 앞과 위를 특정한 방향으로 하는 회전 쿼터니언을 만듬
3. Quaternion,Slerp : 쿼터니언과 다른 쿼터니언 사이 내분점을 알수 있음,
구형보간 기법(Spherical Linear Interpolation)을 활용해서 쿼터니언에 쓰면 더 정확해짐
ex)두 쿼터니언 Q1,Q2가 있을때 30% 지점은 어디일까? 같은 문제를 풀기 유용해짐

 


쿼터니언과 벡터의 곱셈

 

쿼티니언을 활용해 객체의 회전뿐만아닌 벡터의 회전도 가능하다.

즉, 오른쪽으로 이동중인 객체를 위쪽으로 회전하는 구현이 가능하다.

유니티에서는 쿼터니언과 벡터의 곱을 지원하기에 순서만 맞춰서 코드를 작성해주면된다. 

쿼터니언 * 벡터 이 순서대로 계산해주면된다. 

삼각함수에 아크(arc)는 삼각함수의 역함수를 나타내는 것이다.

삼각함수는 각도 -> 변의 비율 을 알수 있다면

역삼각함수는  변의 비율 -> 각도 를 알수 있다.

 

그럼 역삼각함수중 왜 아크탄젠트를 알아야 되는것인가??

우선 탄젠트에 대해 알아보자 

Atan를 사용해도 음수값을 알수 없기에 범위가 한정적이다.

 

탄젠트는 높이(y) 밑변(x)라는 직관적인 값 덕분에 사용하고 싶었지만 음수값이 나오지않기에 사용을 못했지만

개발자들이 사용하기 위해 x,y자체가 양수인지 음수인지를 통해 360도를 다 구분할 수 있게 구현한 것이다.

 

Atan2(y,x) 라는 메서드로 구현한 것이다.

이를 통해 (x,y)에서 Math.Atan2(y,x) 함수를 사용하여 벡터가 x축과 이루는 각도를 [ -pi , pi ] 까지의 값을 얻을수있다.

(p.s pi는 180도가 되도록 표현하는 것을 호도법 , 그리고 그 단위를 라디안이라고 부름 , 즉 라디안에서 pi = 180 이라고 생각하면됨)


그럼 Atan2(y,x) 메서드를 어디에 사용해야되느냐?

2D에서 점 또는 벡터에 대한 각도를 찾는데 사용된다.

즉,  객체가 특정 방향을 향하도록 만들거나 두 점 사이의 각도를 계산할때 유용해진다.

 

예시) 몬스터가 90도 범위의 시야각을 가지고 있을때, 캐릭터가 범위 안에 있는지 없는지 판정을 어떡해 구현해야되는가?

우선 몬스터-캐릭터의 거리를 구해야하기에 벡터의 뺼셈을 이용해야 된다.

(벡터의 뺼셈을 이용하여 두 벡터간의 거리를 구할 수 있다. [목표 지점의 벡터 - 이동 및 바라보는 객체의 벡터] )

 

구해진 벡터(x,y)를 Atan2(y,x) 메서드에 인자값으로 넣을경우 각도가 반환되는데 -45 ~ 45도 사이에 있으면 몬스터 시야에 캐릭터가 있다고 판단하면된다.

(중요 :: Atan2(y,x)로 반환된 각도의 단위는 라디안이기에 degree로 바꿔줘야한다)


 

예시2) 2D 게임에서 마우스 위치를 기준으로 좌우반전 구현

마우스 위치를 기준으로 캐릭터가 좌우 반전 하고 있는 모습

 

두 벡터의 거리를 알기위해 [ 마우스의 위치 - 캐릭터의 위치 ] 벡터 뺼셈을 이용하여 거리(방향벡터)를 구하고

해당 벡터를 Mathf.Atan2(y,x) * Mathf.Rad2Deg 를 이용하여 각도값을 반환한다.

 

반환된 각도값은 두 객체간의 각도를 나타낸다. 

Atan2의 반환 각도 범위는 [ -pi , pi ]여서 좌표계에 이렇게 각도를 표시하였다.

이 그림을 보면 1사분면,4사분면에 있을때는 절대값이 90도를 안넘기에 스프라이트를 반전하지 않아도 되고

2사분면,3사분면에 있을때는 절대값이 90도를 넘기에 스프라이트를 반전하게 된다.

이런식으로 flip을 적용시킨다.

 


결론 :

1. Atan2는 각도를 알기 위해 y와 x의 비율을 통해 각을 알아내는 함수이고 [-180 , 180] 범위에서 각도 반환값을 제공한다.

2. Atan2를 사용한뒤 객체에 회전값을 입력할떄 Rad2Deg를 사용해야한다.

(유니티 내부는 쿼터니언 inspector는 오일러이기 때문에)

'게임수학' 카테고리의 다른 글

게임수학 :: 삼각함수 기초 (작성중)  (0) 2024.10.08

우리가 흔히 알고 있는 삼각함수 이다.

학교에서 가르켜주니까 그냥 배웠던 경우가 더욱 많을것이고 나도 그렇게 삼각함수를 배웠다.

 

게임개발시 수학은 막 전공자들 급으로 깊게 파고 들지않아도 되지만 기본은 알고있어야한다.

왜냐하면 이 삼각함수로 적이 내 시야각에 있는지, 적과 나의 거리가 얼마나 되는지 정말 많은것에 사용되기 때문이다.

 

삼각함수 : 각도를 통해 삼각형들의 각 변의 비율을 알아 낼 수 있는 함수

말이 딱딱하게 들릴 수 있는데 간단히 생각해보면 이해가 간다.

 

예시로 sin(30) = 1/2 인것은 알고 있을것이다. 또한 sinA = 높이 / 빗변 로 표현 할수있다.

sin(30) = 1/2 , sinA = 높이 / 빗변 2개의 정보를 알고 있으면 

빗변의 길이는 높이의 2배이구나! 라는 것을 알수 있기 때문이다. 

 

 

오늘 정리한 내용도 많고 따로 찾아보기 위해 글 하나에 정리하는것보다

따로 분리하는게 나을것 같아 이렇게 작성했습니다. 

 


https://01149.tistory.com/84

 

Unity :: 충돌 처리,OnCollison~() , OnTrigger~()의 인자값 차이

Unity로 작업을 할때 충돌처리를 하기 위해서 많이 쓰던 메서드이다. 하지만, 수업을 듣는 도중 Unity 패키지의 자동완성으로 나오는 충돌처리 메서드의 인자값의 타입이 다르다는 말을 듣고나서

01149.tistory.com

https://01149.tistory.com/85

 

Unity :: Unity 입력처리, InputManager vs InputSystem

Unity 작업시 기존부터 사용되어왔던 입력처리 패키지로 비교적 쉽게 사용가능하다는 장점이 있다.*Inspect를 통한 간단한 키매핑 public KeyCode Up; public KeyCode Down; void Update() { movement = 0f; if(Input.GetKey(

01149.tistory.com

https://01149.tistory.com/86

 

설계 :: OPP(객체 지향 프로그래밍)에서 SRP(단일 책임 원칙) 설계하기

SRP : Single Responsibility Principle(단일 책임 원칙)단일 책임 원칙(SRP)는 는 원칙을 말한다. 여기서 '책임'이라는 의미는 하나의 '기능 담당'으로 보면된다.하나의 클래스는 하나의 기능을 담당하여 하

01149.tistory.com

https://01149.tistory.com/87

 

Unity2D :: 타일맵을 이용한 오브젝트 꾸미기

https://0x72.itch.io/dungeontileset-ii 16x16 DungeonTileset II by 0x72A free tileset + animated characters + weapons + others.0x72.itch.io타일맵 공부를 위해 해당 에셋으 사용하였습니다. Q.게임 월드에 타일맵을 어떡해 생성

01149.tistory.com

 

 

https://0x72.itch.io/dungeontileset-ii

 

16x16 DungeonTileset II by 0x72

A free tileset + animated characters + weapons + others.

0x72.itch.io

타일맵 공부를 위해 해당 에셋으 사용하였습니다.

 

Q.게임 월드에 타일맵을 어떡해 생성해요?

A.하이어라키 창 우클릭 - 2D Object - TileMap - 원하는 타일맵 선택(해당 글에서는 Rectangular)

Rectangular : 흔히 아는 직각 모양의 타일맵

Hexagonal : 문명 게임의 육각형 타일맵을 제공

IsoMetric : 대각선 방향 + 위에서 아래로 바라보는 방향의 타일맵 제공

하이어라키창 우클릭 후 타일맵 생성

 

생성시에 Grid와 그 자식으로 Tilemap이 생성된다.

위의 Tilemap 오브젝트하나의 레이어 처럼 사용 될것이다. 

Grid 컴포넌트
Tilemap컴포넌트 , Tilemap Renderer에서 Order in Layer 를 변경하여 레이어 순서를 변경 가능

 

Q.어떡해 Tile을 생성해?

A.우리가 가지고 있는 Asset을 이용해서 Tile Palette를 만들어줘야한다.

말그대  물감을 짜서 그림그릴떄 사용하는 파렛트를 말하는것이다.

 

Tile Palette 생성은 프로젝트 창 우클릭 - Create - 2D - Tile Palette - 원하는 타일맵 파레트 선택

(해당 글은 Rectangular Palette를 선택)

Tile Palette를 Project 창에 생성된 모습

 

Tile Pallete를 생성 했으면 그 안에 스프라이트(물감)를 넣어야 된다.

 

팔렛트에 스프라이트 타일을 추가하는 방법

 

타일 팔렛트에 추가한 경우 프로젝트 창에 타일 스프라이트들이 추가 되있는 모습

 

>>실수로 스프라이트를 중복으로 넣었거나 잘못 배치한 경우

더보기
현재 똑같은 스프라이트가 2개가 들어가 있는 경우

 

팔렛트 배치를 잘못하거나 중복 스프라이트가 들어가거나 아니면 월드에 타일을 배치할때 잘못했을떄가 있을것이다.

 

그럴떄 당황하지 말고 타일 팔렛트의 펜 도구를 한번 클릭 후

타일 팔렛트의 펜 도구

 

Shift + 지우고 싶은 타일 클릭을 하게 되면 해당 타일이 사라지게 된다.

중복 스프라이트가 지워진 모습

Q.그럼 타일맵 레이어는 어떡해 추가해요?

A.Grid에 자식으로 타일팔렛트 오브젝트를 추가하고, 타일 팔렛트에서 설정하고 배치하면된다.

 

BackDeco를 선택 후 배치하면

Back과 BackDeco가 배치된 범위가 주황색 선으로 표시가 된다.

 

Q.타일에 충돌처리는 어떡해 해요?

A.Tile Collider 컴포넌트를 추가하면된다. 

Collider 레이어를 만들고 해당 TileMap 오브젝트의 Renderer를 비활성화 시키면된다.

 

Collider 타일맵 오브젝트를 생성하고 Tilemap Collider2D 컴포넌트를 추가하면 Scene 뷰에 콜라이더가 표시된다.

 

일반 오브젝트에 적용되는 콜라이더와 똑같이 스프라이트 크기에 맞춰 콜라이더가 생성된다.

물론 offset을 건들여 수정도 가능하다.

 

Render를 비활성화하면 이런 모습이 된다.
벽에 충돌하여 뚫지 못하고 지나가는 모습을 보여준다.

SRP : Single Responsibility Principle(단일 책임 원칙)

단일 책임 원칙(SRP)는 <객체는 단 하나의 책임만 가져야 한다> 는 원칙을 말한다.

 

여기서 '책임'이라는 의미는 하나의 '기능 담당'으로 보면된다.

하나의 클래스는 하나의 기능을 담당하여 하나의 책임을 수행하는데 집중되어야 한다

 

 

현재 TopDownGame 프로젝트 수업을 듣는 중에 캐릭터의 이동을 담당하는 설계를 하는 중에 로직 순서를 간단하게라도 정리 해보았다.

1번은 생성자에서 함수를 event 함수에 추가 후, 동작하지 않는다.

2번은 PlayerInputController에서 InputValue(입력값을 받음)를 받아서 Move event함수를 호출하여 부모클래스로 접근한다.

3번은 부모에있는 TopDownController의 event Action에 등록된 함수를 Invoke()로 동작시키면

TopDownMoveMent에 있는 함수를 동작시키게 한다.

 

그 이후로, 입력값을 받을때마다 2 ~ 3번을 반복하게 된다.

 

이 로직은 상위 부모 클래스로 Controller로 선언 후 각 기능(이동,공격등등)마다 Class로 설계하여 SRP 원칙을 지켰고

event 함수를 사용하여 확장성을 챙겼다고 생각한다.

 

이렇게 구성하게 되면 

'기능 변경(수정)이 일어 났을때의 파급 효과가 최대한 적게 일어난다.

즉, 커플링의 영향이 적어진다고 볼 수 있는것이다. 

 

해당 구조를 UML로 표현하면 이렇게 된다.

 

기능을 분리하였고 TopDownController를 참조하여서 그 안에 사용할 메서드를 Event로 저장합니다.

그럼 PlayerInputController는 자신의 부모클래스에 저장된 event들을 호출하여 사용이 가능해집니다.

 

Unity에서 컴포넌트로 적용한 모습

PlayerInputController 부모 클래스에 해당 기능들을 저장시키고 inputController에서 호출한다.

 

SRP(단일 책임 원칙)에 대해서는 해당 블로그 글을 참고 하였습니다.

 

https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EC%95%84%EC%A3%BC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-SRP-%EB%8B%A8%EC%9D%BC-%EC%B1%85%EC%9E%84-%EC%9B%90%EC%B9%99

 

💠 완벽하게 이해하는 SRP (단일 책임 원칙)

단일 책임 원칙 - SRP (Single Responsibility Principle) 단일 책임 원칙(SRP)는 객체는 단 하나의 책임만 가져야 한다는 원칙을 말한다. 여기서 '책임' 이라는 의미는 하나의 '기능 담당'으로 보면 된다. 즉,

inpa.tistory.com

 

+ Recent posts