https://www.youtube.com/watch?v=EzQbMtk1hZ0&list=PLiSlOaRBfgkcPAhYpGps16PT_9f28amXi&index=16

오늘은 장비메뉴 UI를 만들어보자

1.메뉴 UI제작

UI는 유저인터페이스/위젯블루프린터/사용자위젯으로 만들어주었다.

 

UI구성요소

UI는 켄버스와 왼쪽위에 장비 메뉴라는 글자,

캐릭터 이미지가 들어올 이미지칸, 양쪽에 인벤토리 장비가 들어올 Vertical Box를 만들어주었다. 

이때 Vertical Box의 앵커는 각 Vertical 박스의 모서리에 위치하도록했다. 이때 앵커의 위치가 정확히 모서리는 아니여도 괜찮다.

 

2. 메뉴 호출

메뉴호출은 IA_EquipmentMenu라는 키를 추가해주고 이 키에 이벤트를 추가하는 방식으로 만들었다.

메뉴호출 이벤트

주요한 내용은

메뉴창이 열려있는지 확인할 Bool 변수와 Equipment Widget을 생성하고 이를 변수로 지정해준 뒤 제거할 때는 Remove from Parent를 통해 제거해 준다. 

메뉴가 열릴때는 마우스커서 표시를 하게 해주고 Set Input Mode Game and UI를 켜서 위젯 클릭을 할 수 있게 해주고 Set Movement mode 를 None으로 만들어서 움직이지 못하게 해준다.

메뉴가 닫힐때는 이와 반대로 마우스 커서표시를 끄고 Set Input Mode Game 만을 작동하게해 UI 클릭을 못하게 하고 Set Movement mode를 Walking으로 변경해준다.

 

3.슬롯UI만들기

슬롯또한 유저인터페이스/위젯블루프린터/사용자위젯으로 만들어주었다.

내부에는 Border box/Button으로 구성하여 사용자가 클릭했을 때 아이템 리스트가 뜰 수 있도록 하였다.

슬롯UI

4.메뉴 UI완성

메뉴 UI에 Vertical Box에 Slot UI를 두개 씩 넣어준다.

이때 첫번째 꺼에는 중앙정렬을 하고 위쪽 패딩을 50 넣어주고

두번째 꺼에도 중앙정렬을 하고 위쪽 패딩은 250을 넣어준다.

밑은 패딩값과 완성된 UI이다.

UI구
패딩값(위, 아래)

https://www.youtube.com/watch?v=ugiNsb9pc8k&list=PLiSlOaRBfgkcPAhYpGps16PT_9f28amXi&index=15

인벤토리 시스템

1. 무기 구조체 만들어주기 -S_Weapons

무기는 이름, 데미지, 필요레벨,아이콘 등을 가지고 있어야한다.

※무기 타입은 enum으로 선언해주기

2.아이템데이터(S_Slots)- 데이터 테이블 열 핸들 타입으로 구조체 만들기

각 아이템 객체를 가지고있음

3. 데이터 테이블 만들어주기 DB_Weapons

무기에 대한 정보를 가지고있을 데이터 테이블 만들기

4. 무기 객체 만들어주기 BP_Weapon

- 스태틱 매쉬와 아이템데이터 변수를 가지고 있음

이를 통해 아이템 정의해준다. 

Construction Script 게임동안 계속 실행된다.

객체의 정보에 따라 데이터 테이블의 아이템의 정보를 가져와서 그에 맞는 스태틱매쉬로 바뀌게 해준다

계층으로 접근하는 것보다 2번 구조체로 분할하는것이 좋다.
아이템마다 달라지는 스태틱 매쉬

5.Equipment System 만들어주기 - 액터컴포넌트

Add Weapon 함수로 무기 가져올 수 있게 

변수- weaponSlots - S_slots 구조체 -  키 눌렀을 때 가져올 수 있도록

고르는 키 추가해주기 E 

5-1. 무기 줍는로직 만들기

키를 눌렀을 때 만약 무기가 탐지된다면 추가해주기

아이템을 줍는 로직

 

아이템이 성공적으로 배열에 추가되었다!

 

https://www.youtube.com/watch?v=ETDgyFRyT_8&list=PLWKwcHKTXy5T5v_qSsvUnjFZG85pDOZPq&index=2

[Tesselator 단계]

테셀레이터는 3단계로 구성되어 있다.

HullShader → Tesselation → DomainShader 단계를 거친다.

 

Hullshader는 테셀레이터 작업의 첫단계

->VertexShader에서 공간변환을 진행하지 않고 Hullshader 정점정보들을 전달

Hullshader 는 폴리곤을 어떻게 분활 할 것인가? 폴리곤을 얼마나 분할 할 것인가? 를 결정하는 단계

조금 더 사실적으로 표현하기 위한 방법- 정점의 개수를 늘림

[적용전]

[적용후]

테셀레이터는 다시 말해 다각형을 겹치지 않고 작게 만들어 빈틈을 없애 게임등에서 사물이나 인물등을 실제에 보다 가깝게 표현할수 있게 도와주는 기술

 

DomainShader는 테셀레이터가 출력한 정점마다 한 번씩 함수(셰이딩 언어) 호출을 해주게 된다.

테셀레이션이 활성화 되면 기존의 정점쉐이더에서 수행한 것들을 도메인 셰이더에서 수행하게 된다.

예를들어 공간변환(월드 → 뷰포트 → 투상)이 될수 있다.

 

실제 게임을 제작 할떄는 정적이 적은 로우폴리곤과 정점이 많은 하이폴리곤 모델 두개를 따로 지원하는 경우 많다.

-> 자주 사용되는 기술은 아님.

여러가지 매쉬

[Geometry Shader]- 필수는 x

기본 폴리곤에서 정점을 추가하거나 또는 삭제 하거나 하는 연산을 할수 있다.

정점 정보를 추가하여 표현 할수 있는 모델이라면 그만큼의 정점정보를 뺴고 저장가능

-> 메모리적으로 용량을 적게 차지 할수도 있고 GPU 도움을 받아서 정점을 추가해주기 때문에 연산속도가 빨라 질수 있다.

같은 모델 2개 - 지오메트리 shader로 복사해오기
파티클 이벤트- 하나만 올려두고 지오메트리로 색, 위치바꾸기

[Rasterization]

정점처리 단계를 지난 정점은 다음 단계인 레스터라이저 단계로 넘어갑니다.

정점 - 삼각형 - 하나의 독자적 도형으로 처리된다.

우선 화면에 그려질 2차원 삼각형의 세 정점이 결정되면 다음과 같은 일이 일어납니다.

레스터라이저

  1. 이 삼각형이 포함하는 모든 픽셀마다 pixelShader(fragment shader-opengl)가 실행
  2. 삼각형의 세 정점에 할당 되었던 여러 데이터(pos, uv, normal, color)가 보간되어 삼각형 내부에 각 픽셀셰이더로 넘어옵니다.

정점 - 도형을 이루는 기본 - 나머지는 보간 

Directx에서는 이러한 과정을 통틀어서 레스터라이제이션

-> 고정 파이프라인 단계로 프로그래머 이러한 로직들을 임의 바꿀수 없는 파이프라인 단계

자체 알고리즘으로 알아서 동작을 한다.

대표적 레스터라이제이션의 역할을 나열해 보자면

  1. 클리핑
  2. 원근 나눗셈(perpective division)
  3. 후면 제거
  4. 스캔변환 
  5. 뷰포트 변환

[클리핑]

투영변환 이후의 클립공간 볼륨 바깥에 놓인 폴리곤들을 잘라내는 작업

가시부피안에 있는 것만 보이게

[원근 나눗셈]

현재 단계에서 투영변환을 통해 원근법이 적용된 3차원 물체들을 직육면체 클리핑 공간에서 정의되어 있다.

단순히 생각하면 3차원에서 2차원으로 차원을 줄이면 된다. 

동차좌표계 -> 일반좌표계

바로 Z좌표로 모든 성분을 나붜버리는거죠. 투영변환을 마친 정점데이터는 (x, y, z, w)에서 w성분에 z값이 저장된다. 원근 나눗셈이 적용 된 이후에는 x,y,z,w -> x,y,z의 좌표계로 변환되는데 이를 NDC(normailize device coordinate) 공간(동차좌표계 ->일반 좌표계 - 정규화 기기 좌표계 )이라고 부릅니다. 여기서 정규화라는 이름이 붙는 이 좌표의 xy 범위는 [-1 ~ 1] z의 범위는 [0~1]이기 때문

[후면 제거]

카메라가 바라보고 있는 방향에 물체에 가려진 면적은 굳이 연산을 할 필요가 없다.

외적(Cross product) 삼각형의 바라보고있는 면의 방향을 구하여 뒷면일 경우에 연산에서 제외 시킨다.

[뷰포트 변환]

컴퓨터 화면상의 윈도우 스크린 공간을 갖는데 이 스크린 공간 내에 2차원 이미지가 그려질 뷰포트가 정의되는데

NDC공간의 물체들을 스크린 공간으로 이전시키는변환을 뷰포트 변환

*[스캔 변환]

이전의 변환들은 자세한 사항을 몰라도 프로그래밍하는데 문제가 없었지만 이 스캔 변환은 렌더링 프로그램에서 직접적인 영향을 미치기 떄문에 꽤 중요하다.

삼각형 하나가 내부에 차지하는 모든픽셀(fragment)들을 생성하는 작업이다.

이때 정점데이터에 들어온 데이터들은 보간(선형 보간)되어서 픽셀셰이더로 넘어간다.

[Pixel Shader]

레스터화된 도형에 원하는 색을 입혀서 출력하게끔 도와주는 셰이더

텍스처매핑 , 노말매핑, 등등 기법으로 색을 입혀서 표현도 가능하다.

조명 처리나 이미지 처리를 할 때 유용하게 사용된다.

정점 데이터가 보간된 값이 넘어온다.

[Output merger]-알파테스트, 깊이테스트

깊이 - 스텐실 테스트와 블렌딩이 일어나서 최종적인 화면(텍스처)에 물체를 그려준다.

 

어느게 앞에 있는지에 따라 색이 달라질 것이다.
투명하다면 뒤에 색과 합쳐서

[Compute shader]- 부수적인 단계

병렬연산지원

일반 렌더링 파이프라인과 별도로 그래픽카드를 사용할때 실행할수 있도록 도와주는 셰이더

대량 병렬 GPGPU 알고리즘 또는 게임 렌더링의 일부를 가속시키기 위해서 사용 가능

효율적으로 사용 하려면 GPU 아키텍처와 병렬 알고리즘에 대한 지식뿐만 아니라 DirectxComput, Opengl Compute, CUDA, 또는 OpenCL에 대한 지식도 필요

'게임공부 > Directx11' 카테고리의 다른 글

[Directx11]4. 삼각형 그리기  (1) 2024.07.01
[Directx11]3.장치초기화  (0) 2024.06.27
[Directx11]2.초기설정  (0) 2024.06.26
[Directx11]그래픽스OT  (0) 2024.06.25
[Directx11]1. 렌더링 파이프라인-1  (0) 2024.06.19

https://www.youtube.com/watch?v=Wry5ltdrDQI&list=PLWKwcHKTXy5T5v_qSsvUnjFZG85pDOZPq&index=1

다이렉스x11 강의

파이프라인

여러 명령어가 중첩되어서 프로그램이나 하나의 작업을 실행하게 도와주는 과정(연산들의 집합)이다. 한 사이클 안에 들어가야 해서 과정들이 많기 때문에 한사이클이 복잡해지거나 사용자 입장에서 어려워질수 있는 단점이 존재한다.

렌더링 파이프라인(레스터라이제이션)

파이프라인

엔진을 먼저 학습하고 관련된 파이프라인을 학습해 나가자

 GPU

ALU가 CPU보다 많다 - 작은 데이터를 동시에 처리하기 좋다 - 무수히 많은 점 처리하기에 좋음 - RGBA(각 32bit)

[Input Assembler]

3차원 모델 하나를 3차원 세상에 나타내기 위해서는 가장 먼저 해줘야 할것은 무언인가?

여기서 모델은 점(vertex)로 이루어져 있다. 이것을 우리는 폴리곤(점들의 집합)이라고 한다.

주로 게임에서는 삼각형을 가지고 3D 폴리곤을 정의하는데, 이떄 이 정점데이터들을 운반하는 자료구조를 우리는 Vertex Buffer라고 한다.

정점(RAM)->GPU(Vertex Buffer) 

정점 버퍼와 같이 등장하는 용어로 Index buffer라는 것이 있다.

인덱스 버퍼는 쉽게 생각하면 정점들의 인덱스를 저장하고 있는 버퍼라고 할수 있는데

6개 정점활용

 

사각형을 예를 들면 사각형을 (삼각형 기반으로) 그리기 위해서는 2*3 = 6개의 정점이 필요하다.

사각형을 구성하는 정점을 4개만 두고 4개를 한 붓그리기 처럼 중복해서 그려주는 방식을 사용하는 걸 인덱스 버퍼의 기능이라고 생각할 수 있다.(하지만 없어도 상관은 없다)

한붓그리기

 

추가적으로 인덱스 버퍼를 사용하는데, 그것도 결국 메모리를 사용하니까 똑같다고 생각 할 수도 있다.

인덱스 버퍼 - 4바이트 숫자써서 효율적일 수 있다

정점은 위치데이터 말고도, 색깔, 법선, 텍스처좌표(UV), 애니메이션에 필요한 정보등등 여러가지 프로그래머가 원하는 데이터를 추가하여 사용하기 떄문에 단순히 정수만 저장하는 인덱스버퍼가 훨씬 메모리적으로 효율적이다.

인덱스 버퍼

정점버퍼는 그냥 정점들의 연속적인 메모리 구조에 불과 하기때문에 실제로 GPU에서는 이러한 정점들을 이용하여 어떤 도형을 만들어야 하는지 정보가 필요하다. 해당 도형정보를 Primitve Topology 라고 한다.

결국은 점을 어떻게 묶을 것인지 정하는것이다.

Primitve Topology

결론적으로 Input Assembler 는 이러한 정점들의 데이터를 읽고, 삼각형과 같은 도형으로 조립하는 단계의 일을 한다고 생각하면된다.

게임 - 무수히 많은 오브젝트

[Vertex Shader]

Input Assembler 에서 받은 정점정보들의 정보로 도형은 생성이 되었지만 로컬 좌표계에 있기 때문에

해당 데이터들을 화면에 그대로 출력해버리면 화면에 중심부에 전부 그대로 출력되어버려서 공간좌표계(World)로 변환할 필요가 있습니다. Local space에서 World space 로 변환이 필요하고,

실제 플레이어가 바라보는 카메라가 중심이 되는 공간 View Spcae(뷰포트변환)변환을 해준다. 그리고 마지막으로 Projection 변환(투상변환)을 거쳐 최종적으로 정의된 ClipSpace라는 공간으로 변환을 해준다.

옛날게임- 카메라라는 개념이 없었음(바이오하자드2)

최초카메라-마리오64

[월드공간 변환]

Local Space라고도 불리는 오브젝트 공간은 3차원 세상에서 효현될 각각의 개인의 공간에 정의된 영역이다.

Model Space
World Space

[카메라 공간 변환(뷰포트 변환)]

월드 변환이 완료되어 모든 물체가 한공간(World space)에 모아지면 이제 우리가 원하는, 시점에서 물체를 관찰 할 수 있게 해줘야한다.

이 때 관찰자로써 가상의 카메라가 필요하고, 이 카메라가 볼 수 있는 영역의 공간을 뷰 공간(뷰포트)이라고 합니다.

월드 공간의 모든 물체를 카메라 공간으로 변화하게 된다면 효율적으로 여러가지 효과나 렌더링등을 진행할수 있다.

뷰포트변환

가상의 카메라는 컴퓨터의 성능의 한계 때문에 실제 세상과는 다르게 시야가 제한 될수 있다.

FOV(시야각), ASPECT(종횡비)에 의해 결정 -> 가시공간 , 가시부피

이렇게 생성된 뷰볼륨에 Near(전방 절단면), Far(후방 절단면)정보가 전달되어 절두체의 영역을 다시 정의 한다.

가시부피

[View Frustrum]

절두체 공간 밖에 있는 물체는 그리지 않는데, 우리가 살고 있는 3차원 세상은 모든걸 보여준다. 이렇게 밖에 없는 이유는 계산상의 효율성을 위해 어쩔수 없이 도입된 개념

만약 물체가 절두체의 경계를 걸치게 되면 바깥쪽 부분은 잘려서 버리게 된다.->클리핑(Clipping)

이 클리핑은 카메라변환에서 이뤄지지 않고 나중에 클립공간에서 ->레스터라이저로 넘겨질때 수행

[투영변환]

투영변환

원근법을 활용 -> 원근변환

원근감(Depth Feeling)

동일한 크기의 물체라도 시점으로부터 멀리 있는 것은 작게 보이고 가까운 것은 크게 보임

●소실점(VP: Vanishing Point)

원근투상 결과 평행선이 만나는 점(시점 높이)

소실점의 수 -> 일점투상(One-point Projection), 이점투상(Two-point Projection), 삼점투상(Three-point Projection)

● 원근 변환(Perspective Transformation) 

직선->직선, 평면->평면

물체 정점 간의 거리에 대한 축소율이 달라짐(cf.어파인 변환)

투영 변환은 이러한 원근 법을 구현하기 위해 카메라 공간에서 정의된 절두체를 3차원 클립공간으로 변환하는것을 의미한다.

여기서 투영변환이라는 이름과는 다르게 3차원 공간의 물체를 2차원 평면으로 바꾸는것이 아니라 3차원 물체로 변형됨에 주목해야한다.

-> 아직 2차원평면이 아니다.

 

투영 변환을 거친 물체들을 관찰해보면 절두체 뒤쪽에 있던 영역의 폴리곤은 상대적으로 작아지는 것을 볼 수 있는데 우리가 원했던 원근법을 적용 된 것이라고 볼수 있다.

 

추가적으로 한개더 생각해 보면 원근법을 3차원 공간에서 실현하기 위해 직육면체 볼륨으로 물체들을 변환 시켰는데 여기서 얻는 이점이 있다. 좀 더 간단한 공식으로 쉽게 Clipping(클리핑)작업을 할 수 있다.

'게임공부 > Directx11' 카테고리의 다른 글

[Directx11]4. 삼각형 그리기  (1) 2024.07.01
[Directx11]3.장치초기화  (0) 2024.06.27
[Directx11]2.초기설정  (0) 2024.06.26
[Directx11]그래픽스OT  (0) 2024.06.25
[Directx11]2.랜더링 파이프라인2  (0) 2024.06.21

1. Manager 관련 코드에는 monobehavior은 안쓰는게 좋다.

-굳이 필요가 없다

2. UI요소들의 크기를 조정할 때는 Scale을 조정하기보다 Anchor와 width height로 조절하는게 맞다

- Scale은 곱연산으로 중첩되어 들어가기 때문에 각 요소들의 크기를 결정하기 쉽지 않지만 Anchor와 width, height를 조정하면 지정한 값으로 들어가기 때문에 훨씬 좋다.

3. 코드를 잘 읽고 ctrl + 12 + 함수 및 매서드 정의 잘 보기

 해당 함수가 사용된 곳을 찾으면서 어떻게 함수가 쓰이는지 어떤 방식으로 구현되어 있는 지 확인하고 이를 활용하여 자신만의 코드를 만드는게 효율적이다.

 

계속 업데이트 예정...

 

1. 입력액션 지정

특정 키를 눌렀을 때, 해당 행동을 하게 하기 위해서는 일단 키를 눌렀을 때 작동하게 할 캐릭터의 Input/Actions 폴더에 들어가서 우클릭 후 입력 / 입력액션을 클릭하고 원하는 이름으로 지정해주면 된다.

만든 후 기본 설정으로 두면 된다. 

1. 입력액션 지정

2. 매핑 컨텍스트에 키 추가

Input 폴더의 입력 매핑 컨텍스트를 더블클릭하여 들어간 뒤 +키를 눌러 매핑을 추가해주고 키 값은 키보드 버튼을 누른뒤 원하는 키를 입력하면 된다.

3. 움크리기 애니메이션 추가

애니메이션에 움크리는 애니메이션을 추가해서 동작할 수 있도록한다.

 

 

4. 동작 블루 프린터 추가

키를 추가해주었다면 키를 통해 작동할 캐릭터의 블루프린터로 들어 간 후, 이벤트 그래프에서 커스텀 이벤트를 추가해서 동작하도록 만들어 주면 된다. 

 

5. 함수 내부 동작

함수는  커스텀이벤트를 추가해서 구현해두었는데 이 때 전에 추가해둔 IA_Crouch 를 불러온다음 키가 눌릴떄, 즉 Started에서 만약 달리는 중과 움크리는 중이 아니라면 움크린 불린값을 True로 만들어준다. 이때, 애니메이션 그래프에서 Crouched Locomotion으로 애니메이션이 넘어가지게 된다. 그 이후 걷는 속도를 낮춰주고 카메라의 거리를 조금 멀리로 바꿔주는데 이때, 400 -> 500의 값을 Lerp하게 즉, 스무스하게 넘어가도록 해준다.

언리얼 공부를 하면서 무작정 유투브강의를 찾아 보며 어떤걸 따라 만들어 볼 지 고민했는데 내가 만들고 싶은 게임인 RPG게임을 따라 만드는게 도움이 많이 될 것 같아서 유투브에 있는 RPG 게임 Tutorial를 보면서 따라 만들어 보기로 했다. 영어로만 된 강의라 이해하기 어려웠지만 천천히 듣고 보면서 배워 나갔다. 

https://www.youtube.com/watch?v=WcDj4uZygyE&list=PLiSlOaRBfgkcPAhYpGps16PT_9f28amXi&index=2

 

나는 이 강의를 보고 시작했다. 

일단 기본적인 걷고 뛰는 것을 만들어 보았는 데 이때 애니메이션이 값에 따라 자연스럽게 이어지도록 해주는 것이 Blend Space 1D 이다. 

1. FBX import

일단 서있고 걷고 달리는 애니메이션 을 넣어줘야한다.

이때 애니메이션에 사용될 FBX파일 언리얼 프로젝트에 넣어주면되는데 혹시 모를 경우를 대비해 디폴트로 리셋하고

메시를 자신이 사용할 스켈레톤을 넣어줘야한다. 여기서 나는 스켈레톤인 SK_Mannequin으로 플레이를 만들것이라 SK_Mannequin으로 선택하고 임폴트 해주었다. 

2. Blend Space 1D

Blend Space 1D는 변수의 값에 따라 애니메이션이 자연스럽게 이어지도록 하는 애니메이션 흐름도라고 보면 될 것 같다.

애니메이션 변환을 위한 변수선

이때, 가로축의 이름을 본 사진과 같이 Speed로 해주면되는데 이것은 애니메이션이 Speed의 값에 따라 바뀌게 하기위한 변수선언이라고 보면 되며 이때 750정도를 max값으로 해서 달리기값의 최대를 정해주면 된다. 

애니메이션 삽입

그리고 위의 화면에 보이는 곳에 왼쪽이 Speed값이 0 오른쪽이 최대값인 750 이여서 이에 맞는 애니메이션을 넣어주면 된다. 

3.Animation Blueprint 

그 다음 Animation Blueprint를 만들어 주면 된다. 밑의 사진은 다 만들어진 상태이다. 

중요한 것은 state machine이다. state machine은 Cache pose를 사용하기 위해 애니메이션을 하나의 객체처럼 만드는 것이다. transition은 어떨때 애니메이션이 다른 애니메이션으로 변환되는 지 설정하는 것으로 지금에서는 Speed 값을 넣어 주면 된다. 

Locomotion State machine & Transition
Main State machine 및 흐름도

밑의 사진은 Animation Blue print 안의 Event graph에서 실제로 캐릭터가 움직임에 따라 캐릭터의 스피드를 구하는 Blue print이다. 

 

그동안 협업으로 프로젝트를 진행 해본적은 있지만 애자일방법론 같은 정말 협업에 사용되는 방법을 따라 개발해본적은 없어 이번 프로젝트를 통해 기획단계에서 부터 클래스 다이어그램, 시퀸스 다이어그램, 요구사항 명세서등을 어떻게 작성하는 지 익히고 사용해보기로 하였다.

1. 클래스 다이어그램

클래스 다이어그램의 정의를 보자

클래스 다이어그램은 구조 다이어그램으로 클래스 내부 구성요소 및 클래스 간의 관계를 도식화하여 시스템의 특정 모듈이나 일부 및 전체를 구조화한다.

클래스 다이어그램에서 클래스는 이름, 속성(변수), 메소드 순으로 나열하고 속성과 메소드 사이에 선을 그어 분류한다.

변수 선언

+ public
-  private
#  protected 
~  default 
{readonly} final
밑줄 static
[*] or [0..1] 리스트 사이즈 

 

클래스 간 관계

이미지 참조:  https://en.wikipedia.org/wiki/Class_diagram

●Association

Association은 참조 관계일때 사용하고  A -> B 이면 A가 B를 참조 A - B 이면 A가 B를 B가 A를 참조할 수 도 있고 둘 다 참조 이거나 둘 다 참조가 아니다.

 

Inheritance

Inheritance는 상속관계로 부모 클래스와 자식 클래스 간의 상속관계에 사용

 

Realization

Realization은 인터페이스를 상속하여 클래스에서 실제 기능을 구현할 때 사용

 

Dependency

Dependency는 클래스간의 참조관계에서 사용하지만 Association은 변수로 다른 클래스와 연관이 있을 때 사용하지만 Dependency는 메소드의 파라미터나 반환에 사용되는 클래스 관계에서 사용

 

Aggregation

Aggregation은 집합관계에서 사용

 

Composition

Aggregation과 비슷하게 전체 - 부분의 집합 관계를 나타낼 때 사용하지만 Aggregation 보다는 더 강력한 집합을 의미할 때 사용합니다. 합성 관계에서는 부분이 전체에 종속적이고 라이프 사이클을 관리한다 라고 볼 수 있다.

우리는 유투브 강의를 보고 따라서 Vampire Survivor라이크류 게임을 만들어보았다. 이제 우리만의 스테이지와 몹을 만들고 이를 통해 데이터를 모아서 AI모델을 통해 유저의 생존시간과 시도 횟수를 예측해보기로 하였다.
 
1. 우리만의 스테이지
기존의 강의에서는 스테이지가 하나밖에 없어서 여러 스테이지를 깨고 그 다음스테이지를 예측해야하는 우리에게는 여러가지 스테이지가 필요했다. 그렇게 여러 스테이지를 생성하는 법을 공부하였는데 이때 사용하는 것이 맵을 게임오브젝트로 만들어 두고 GameManager에서 Stages라는 Gameobject 배열로 만들고 이곳에 여러 스테이지를 저장한 뒤 기존의 스테이지가 끝나면 스테이지맵의 게임오브젝트 Active를 false로 바꿔주고 다음  스테이지맵의 게임 오브젝트 Active를 true로 만들어 주면 된다. 
Stages[stageIndex].SetActive(false);
stageIndex++;
Stages[stageIndex].SetActive(true);
 
이러한 코드를 GameManager 코드에 NextStage() 메서드로 구현해두었다.  또한 Player의 위치를 다시 원점으로 돌려서 스테이지를 플레이할 수 있게 
player.transform.position = Vector3.zero;
 
이 코드도 함께 넣어 구현해 주었다. 

Backyard Top-Down Tileset(FREE)
https://assetstore.unity.com/packages/2d/environments/backyard-top-down-tileset-53854

 

Backyard Top-Down Tileset | 2D 주변환경 | Unity Asset Store

Elevate your workflow with the Backyard Top-Down Tileset asset from Kittens and Elves at Work. Find this & more 주변환경 on the Unity Asset Store.

assetstore.unity.com

https://assetstore.unity.com/packages/2d/characters/top-down-2d-rpg-assets-pack-188718

 

Top-Down 2D RPG Assets Pack | 2D 캐릭터 | Unity Asset Store

Elevate your workflow with the Top-Down 2D RPG Assets Pack asset from Goldmetal. Find this & more 캐릭터 on the Unity Asset Store.

assetstore.unity.com

 
 
골드메탈님의 강의에서 무한맵처럼 보이게 맵을 이동하는 것을 코드로 구현했었는데 이 코드는 그대로 사용하고 각 스테이지별 기존 맵과 동일한 크기의 타일맵을 생성하여서 스테이지가 달라지는 것을 알 수 있도록 구현하였다. 
타일맵을 생성하는데에는 위의 두 Asset을 활용하여 만들었다.
 
private void OnTriggerExit2D(Collider2D collision)
{
 
    switch (transform.tag)
    {
        case "Ground":
            float diffX = playerPos.x - myPos.x;
            float diffY = playerPos.y - myPos.y;
            float dirX = diffX < 0 ? -1 : 1;
            float dirY = diffY < 0 ? -1 : 1;
            diffX=Mathf.Abs(diffX);
            diffY=Mathf.Abs(diffY);
            if (diffX > diffY)
            {
                transform.Translate(Vector3.right * dirX * 40);
            }
            else if (diffX < diffY)
            {
                transform.Translate(Vector3.up * dirY * 40);
            }
            break;
    }

}

만약 플레이어 밑에 있는 Area 오브젝트가 Ground의 끝을 만났을때 맵을 그 길이만큼 왼쪽이나 오른쪽 또는 위 아래로 움직여주는 코드이다. 
 
2.우리만의 몹 만들기
기존의 강의에서는 몹이 2개만 생성가능했기 때문에 우리는 총 4개의 스테이지에 맞는 여러 몬스터를 더 넣고자 했다. 

https://assetstore.unity.com/packages/2d/characters/2d-monster-undead-spum-premium-addon-pack-200884

 

2D Monster Undead - SPUM Premium Addon Pack | 2D 캐릭터 | Unity Asset Store

Elevate your workflow with the 2D Monster Undead - SPUM Premium Addon Pack asset from soonsoon. Find this & more 캐릭터 on the Unity Asset Store.

assetstore.unity.com

Undead Survivor에 맞게 좀비 Asset을 찾아서 넣어주었다. 가격은 $9.99정도이다. 이 Asset을 처음 받았을 때 기존에 강의에서 활용하던 몬스터 Asset과 구조가 많이 달라서 사용법을 찾는데 오래 걸렸다. 기존에 Asset은 제일 상위 오브젝트에 Animator을 붙이고 이를 통해 Animation이 작동했었는데 이 Asset은 하위에 UnitRoot가 있고 이 아래에 몬스터의 몸을 이루는 부분들이 있어서 이 UnitRoot를 통해 해당 몬스터의 Animation이 작동하는 것을 알았다. 그래서 이 UnitRoot에 기존에 가지고 있던 Animator에 Hit라는 Trigger과 Dead라는 Bool 값을 넣어서 만약 유저에게 데미지를 입었을때의 Animation과 몬스터의 체력이상의 공격을 당했을 때 Dead에 해당하는 Animation이 작동하도록 했다.
void OnTriggerEnter2D(Collider2D collision)
{
    if (!collision.CompareTag("Bullet"))
        return;
    health -= collision.GetComponent<Bullet>().damage;
    StartCoroutine(KnockBack_1());
    if (health > 0)
    {
        anim.SetTrigger("Hit");
        AudioManager.instance.PlaySfx(AudioManager.Sfx.Hit);
    }
    else
    {
        isLive = false;
        coll.enabled = false;
        rigid.simulated = false;
        anim.SetBool("Dead", true);
        StartCoroutine(Dead());
        GameManager.instance.kill++;
        GameManager.instance.GetExp();

        if (GameManager.instance.isLive)
            AudioManager.instance.PlaySfx(AudioManager.Sfx.Dead);
    }
}
 
여기서 Bullet은 유저의 무기이다. 

 
 
Enemy에 들어갈 Animator도 수정하였다. 또한 Spawner에 spawn 메서드를 통해 몬스터가 생성되는데 Stage에 따라 시작 몬스터와 몇초마다 다른 몬스터가 생성되는지를 코드로 구현하여 스테이지마다 몬스터가 바뀔수 있도록 구현하였다.
 
 void spawn()                        //0번째부터시작 1,2번째는 총알이라 놔두기 
 {
     GameObject enemy;
     if (GameManager.instance.stageIndex == 0)           //1스테이지는 레벨에 따라
     {
         if (level == 0)
         {
             enemy = GameManager.instance.pool.Get(level);    //SpawnData 인스펙터창
         }
         else
         {
             enemy = GameManager.instance.pool.Get(level + 2);    //SpawnData 인스펙터창
         }
         enemy.transform.position = spawnPoint[Random.Range(1, spawnPoint.Length)].position;
         enemy.GetComponent<Enemy_new>().Init(spawnData[level]);
     }
     else
     {                                                           //2스테이지부터는 레벨+스테이지 인덱스번째의 몹부터 소환
         if (level == 0)
         {
             enemy = GameManager.instance.pool.Get(level+2+GameManager.instance.stageIndex);    //SpawnData 인스펙터창
         }
         else
         {
             enemy = GameManager.instance.pool.Get(level +2+ GameManager.instance.stageIndex-1);    //SpawnData 인스펙터창
             if((level + 2 + GameManager.instance.stageIndex) == GameManager.instance.pool.prefabs.Length)
             {
                 enemy = GameManager.instance.pool.Get(GameManager.instance.pool.prefabs.Length-1);    //SpawnData 인스펙터창
             }
         }
         enemy.transform.position = spawnPoint[Random.Range(1, spawnPoint.Length)].position;
         enemy.GetComponent<Enemy_new>().Init(spawnData[level]);
     }
 }
 
또한 몬스터마다 체력, 스폰시간, 체력, 속도등을 정하여 입력해 주었다. 

 

내가 코딩을 배우고 코딩으로 무엇인가를 만드는 걸 목표로 하게 되었을 때부터 내 오랜 목표는 누구나 쉽고 재밌게 즐길 수 있는 게임(RPG)를 만드는 것이다. 그리고 나는 현실처럼 많은 상호작용을 게임안에서 할 수 있고 또 다른 세상을 유저가 만들어가기도 하고 게임을 진행하는 데 있어서 하나의 루트만 존재하는 것이 아니라 유저에 따라 여러루트가 존재할 수 있는 그런 게임을 만들고 싶다.

그렇게 꿈을 향해서 나아가던 중에 몰입에 있어서 중요한 역활을 하는 게임 난이도를 AI가 바꿀 수 있으면 어떨까라는 생각을 친구와 나누게 되었고 이 아이디어를 실제로 학기 중에 공부하고 구현해보자 라는 계획을 세우게 되었다. 그래서 나와 친구는 학교에 직접 한 학기의 수업을 만들어서 진행할 수 있는 것이 있어서  유저의 게임실력을 받아서 게임의 난이도를 유저에 맞게 변경해주는 것을 만들기로 하였다. 

처음에는 rpg게임으로 구현을 해보려 했지만 생각보다 시간이 많이 소요될 것 같고 게임보다는 실제로 작동하는 AI모델을 만들어 보려고했기때문에 이러한 데이터를 더 잘 받을 수있는 뱀서라이크 게임을 만들어서 모델에 사용하기로 하였다. 그래서 뱀서라이크 게임을 만드는 영상을 찾다가 골드메탈님의 영상을 보게 되었고 이것을 바탕으로 우리의 팀만의 차별성(여러스테이지, 몬스터종류의 차별성)을 조금 넣어서 모델에 넣어보자는 결론이 나왔다. 

 

밑에는 해당 강좌의 시작 영상이 있다.

https://www.youtube.com/watch?v=MmW166cHj54&list=PLO-mt5Iu5TeZF8xMHqtT_DhAPKmjF6i3x&index=1&t=12s

 

 

 

+ Recent posts