일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- vue jwt
- Transform
- vue 로그인
- 이기적
- for문
- Scanner
- Vue.js
- 반복문
- 노드
- 자바
- Node.js
- package.json
- java
- 리액트
- quasar
- vue 독학
- vue 앱만들기
- 이기적 리눅스 마스터 2급
- JWT
- Vue
- Rigidbody
- 리눅스마스터2급
- 템플릿 리터럴
- 형변환
- npm start
- axios
- 유니티
- Unity
- React
- 컴포넌트
- Today
- Total
나아가기
[Unity] 프리팹과 충돌 판정 본문
<플레이어를 움직여 떨어지는 화살을 피하는 게임>
1단계. 프로젝트와 씬 만들기
1. CatEscape 파일명으로 2D 프로젝트 파일 생성하기.
2. 프로젝트에 필요한 리소스 추가하기.
3. File에서 SaveAs를 선택하고, 씬 이름을 GameScene으로 저장하기.
2단계. 씬에 오브젝트 배치하기.
1. 플레이어 배치하기.
Player 이미지를 Project 창에서 Scene뷰로 드래그&드롭합니다.
그리고 Inspector 창에서 원하는 위치로 position을 지정해줍니다.
2. 배경 이미지 넣기.
배경 이미지를 Project 창에서 Scene뷰로 드래그&드롭합니다.
그리고 Inspector 창에서 원하는 위치와 Scale을 지정해줍니다.
플레이어를 먼저 넣고, 후에 배경을 넣었기 때문에 플레이어가 표시되지 않는 상황이 발생할 수도 있습니다.
유니티 2D 게임에서 각 게임 오브젝트는 레이어 번호를 갖고 있고, 이 번호에 따라 화면에 놓일 앞뒤 위치가 결정됩니다.
Player와 Backgound의 Inspector의 가장 하단에 보면,
Order in Layer가 모두 0인 것을 볼 수 있다.
이것이 레이어 번호이고,
Player를 앞으로 댕기기 위해 0을 1로 변경해줍니다.
그럼 다음과 같이 Player가 Backgrund 앞으로 오게 됩니다.
3단계. 키를 조작해서 플레이어 움직이기.
1. Player 스크립트를 작성합니다.
최종적으로 만들고자 하는 것은 화면에 있는 버튼을 누르면 좌우로 이동하는 것이지만,
UI나 화살 등 다양한 요소가 더해지면 헷갈리므로 화살표 키보드로 움직이는 법을 먼저 구현합니다.
1) PlayerController라는 이름의 Script를 만들어줍니다.
2) 스크립트 작성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
void Start()
{
}
void Update()
{
//왼쪽 화살표를 눌렀을 때
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
//왼쪽으로 3만큼 움직인다.
transform.Translate(-3, 0, 0);
}
//오른쪽 화살표를 눌렀을 때
if (Input.GetKeyDown(KeyCode.RightArrow))
{
transform.Translate(3, 0, 0);
}
}
}
키를 눌렀을 때 검출하기 위해서 Input 클래스의 GetKeyDown 메서드를 사용합니다.
이 메서드는 매개변수로 전달한 키가 눌리는 순간 true를 한번 반환합니다.
왼쪽 화살표 키를 누르면 if문의 조건이 true가 되면서 transform.Translate(-3, 0, 0)을 실행합니다.
마찬가지로 오른쪽 화살표 키를 누르면 if문의 조건이 true가 되면서 transform.Translate(3, 0, 0)을 실행합니다.
▶ GetKeyDown : 키를 눌렀을 때
GetKey : 키를 누고 있는 순간
GetKeyUp : 키를 뗀 순간
2. 플레이어에 스크립트 적용하기.
PlayerController 스크립트를 Hierarchy창의 Player 오브젝트로 드래그&드롭합니다.
3. 게임을 실행하면, 좌우 화살표 키를 누르면 플레이어가 좌우로 조금씩 이동합니다.
4단계. Physics를 사용하지 않고 화살 떨어뜨리기.
유니티에 내장되어 있는 Physics를 사용하면 직접적인 움직임을 처리하기 어렵습니다.
따라서 "움직이는 오브젝트를 만드는 방법"으로 화살을 위에서 아래로 떨어뜨리겠습니다.
1. 화살 배치하기.
Project 창에서 arrow 이미지를 Scene뷰로 드래그&드롭합니다.
Hierarchy 창에서 arrow를 클릭하고, Inspector 창에서 Transform 항목의 Position을 설정합니다.
앞서서 다뤘던 것처럼, 화살이 Background 뒤에 숨겨지면 Order in Layer를 1로 바꿉니다.
2. 화살이 위에서 아래로 떨어지는 스크립트 작성하기 -> ArrowController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowController : MonoBehaviour
{
void Start()
{
}
void Update()
{
//프레임마다 등속으로 낙하시키기.
transform.Translate(0, -0.1f, 0);
//화면 밖으로 나오면 오브젝트를 소멸시킨다.
if(transform.position.y < -5.0f)
{
Destroy(gameObject);
}
}
}
1) Update 메서드에서 Translate(0, -0.1f, 0)을 사용해 등속으로 화살을 위에서 아래로 떨어뜨립니다.
Translate 메서드의 y좌표에 -0.1f를 지정하면 오브젝트를 프레임마다 조금씩 아래로 움직이게 합니다.
※ Translate는 게임 오브젝트를 현재 좌표에서 인수 값만큼 이동시키는 메서드입니다.
Translate(0, 3, 0) 은 (0, 3, 0)의 좌표로 이동을 시키는 것이 아니라, Y축 방향으로 3만큼 이동한다는 의미입니다.
2) 화면 밖으로 나온 화살은 소멸.
화살을 내버려두면 눈에는 보이지 않지만 화살이 계속 떨어지고 있습니다. 따라서 메모리가 낭비됩니다.
화살의 Y좌표 값이 화면 아래쪽(y = -5.0)보다 작아지면 Destroy 메서드를 써서 자신을 소멸!
카메라가 있는 중앙이 (0, 0)
따라서 5칸 아래는 (0, -5)
게임을 실행 했을 때 화살이 화면 밖으로 나가면,
Hierarchy 창에서도 arrow가 사라지는지 확인합니다.
5단계. 충돌 판정하기.
충돌하면 특정한 처리를 합니다.
특정한 처리를 둘로 나누어 충돌을 감지하는 부분을 충돌 판정, 충돌을 감지한 다음의 움직임을 정하는 부분을 충돌 반응
이 게임에서 충돌 판정을 해야하는 오브젝트는 '화살과 플레이어' 뿐 입니다.
한번에 출현할 수 있는 최대의 화살 개수도 생각하며 충돌 판정을 구현해봅니다.
1. 간단한 충돌 판정 알고리즘
오브젝트끼리 닿았는지 엄밀하게 감지하려면 오브젝트의 윤곽선이 닿았는지 검사해야 하지만, 계산량이 아주 많고 복잡
그래서 간단한 충돌 판정을 한다면, 오브젝트의 형상을 단순히 원형이라고 가정합니다.
원형은 오브젝트의 윤곽을 확인하지 않아도 원의 중심 좌표와 반경을 알면 간단히 할 수 있습니다.
원의 중심 좌표와 반경을 사용해 충돌 판정을 해 보겠습니다.
1) 화살표를 둘러싼 원의 반지름을 r1, 중심 좌표를 p1
2) Player를 둘러싼 원의 반지름을 r2, 중심 좌표를 p2라고 합니다.
3) p1에서 p2까지의 거리를 d라고 할때, 피타고라스의 정리를 이용하면 다음과 같이 구할 수 있습니다.
1) 두 원의 중심 간의 거리 d가 r1+r2보다 크면,
두 원은 충돌하지 않습니다.
2) 반대로 d가 r1+r2보다 작으면,
충돌합니다.
2. 충돌 판정 스크립트 작성하기
충돌 판정 알고리즘을 화살이 움직이는 ArrowController 스크립트에 구현합니다.
따라서 ArrowController를 다음과 같이 수정합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowController : MonoBehaviour
{
GameObject player;
void Start()
{
this.player = GameObject.Find("player");
}
void Update()
{
//프레임마다 등속으로 낙하시키기.
transform.Translate(0, -0.1f, 0);
//화면 밖으로 나오면 오브젝트를 소멸시킨다.
if(transform.position.y < -5.0f)
{
Destroy(gameObject);
}
//충돌 판정
//1. 화살의 중심 좌표
Vector2 p1 = transform.position;
//2. 플레이어의 중심 좌표
Vector2 p2 = this.player.transform.position;
Vector2 dir = p1 - p2;
float d = dir.magnitude;
//화살의 반경
float r1 = 0.5f;
//플레이어의 반경
float r2 = 1.0f;
if(d < r1 + r2)
{
//충돌한 경우 화살을 지운다.
Destroy(gameObject);
}
}
}
ArrowController 스크립트이기 때문에,
Vector2 p1 = transform.position;은 자기 자신인 화살의 좌표를 p1에 대입하는 식입니다.
Vector2 p2 = this.player.transform.position;는 플레이어의 좌표입니다.
그리고 dir의 길이 d는 magnitude를 통해서 계산합니다.
▶ magnitude는 Vector2 클래스를 갖는 멤버 변수입니다.
이렇게 Vector 클래스에서는 백터 계산에 편리한 멤버변수가 여러개 있습니다.
6단계. 프리팹과 공장 만들기
화살 오브젝트를 1초에 한 개씩 만들어내는 공장을 만듭니다.
유니티에서 설계도를 프리팹이라고 합니다. 프리팹은 게임 중인 오브젝트를 만드는데 필요한 정보가 쓰여있고, 프리팹이 있으면 몇 개라도 같은 오브젝트를 만들 수 있습니다.
이러한 특징 때문에 같은 오브젝트를 많이 만들고 싶을 때 주로 프리팹을 사용합니다.
프리팹을 만드는 방법은 1) 이미 있는 오브젝트를 사용해 프리팹을 만든다.
2) 제너레이터 스크립트를 만든다.
3) 빈 오브젝트에 제너레이터 스크립트를 적용한다.
4) 제너레이터 스크립트에 프리팹을 전달한다.
1. 화살 프리팹 만들기
1) 설계도로 쓰고 싶은 오브젝트를 Hierarchy 창에서 Project 창으로 드래그&드롭 합니다.
지금은 화살 프리팹을 만들고 싶으므로 arrow를 Project 창으로 옮겨줍니다.
그리고 알기쉽게 arrow를 arrowPrefab으로 바꿔줍니다.
2) 원래 Hierarchy 창에 있던 arrow를 지워줍니다.
2. 제너레이터 스크립트 작성하기 → ArrowGenerator로 스크립트를 하나 만들어줍니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowGenerator : MonoBehaviour
{
public GameObject arrowPrefab;
float span = 1.0f;
float delta = 0;
void Update()
{
this.delta += Time.deltaTime;
if(this.delta > this.span)
{
this.delta = 0;
GameObject go = Instantiate(arrowPrefab);
int px = Random.Range(-6, 7);
go.transform.position = new Vector3(px, 7, 0);
}
}
}
제너레이터 스크립트에서는 앞에서 작성한 프리팹을 바탕으로 화살 인스턴스를 1초마다 한개씩 생성합니다.
1) public GameObject arrowPrefab;
화살 설계도를 넣는 변수를 선언합니다.
그리고 이 설계도의 실체는 그 전에 작성한 arrowPrefab입니다.
따라서 이 변수에 arrowPrefab을 대입해야 합니다.
2) Update 메서드는 프레임마다 실행되고 있는 앞 프레임과 현재 프레임 사이의 시간 차이는 TIme.deltaTime에 대입.
프레임과 프레임 사이의 시간 차이를 delta변수에 모으고, 그 시간의 합계가 1초 이상이 되면 delta변수를 비운다.
따라서 delta 변수를 비우는 시점인 1초에 한 번씩 화살이 생겨납니다.
3) 화살 인스턴스는 Instantiate메서드를 사용해 생성합니다.
Instantiaate 메서드는 매개변수로 프리팹을 전달하면, 반환값으로 프리팹 인스턴스를 돌려줍니다.
4) 화살의 x좌표는 -6에서 6사이에 불규칙하게 위치하도록 Random클래스의 Range 메서드를 사용합니다.
3. 빈 오브젝트에 제너레이터 스크립트 적용하기.
게임 오브젝트에 제너레이터 스크립트를 적용해 '공장 오브젝트'를 만들었습니다.
감독 스크립트와 마찬가지로 빈 오브젝트에 '제너레이터 스크립트'를 적용하면 '공장 오브젝트'가 됩니다.
4. 제너레이터 스트립트에 프리팹 전달하기
앞에서 작성한 제너레이터 스크립트의 프리팹 변수와 프리팹의 실체를 연결하는 방법으로 아웃렛 접속이 있습니다.
아웃렛은 콘센트에서 플러그를 꼽는 구멍을 의미합니다.
따라서 Unity에서는 Inspector 창에서 해당하는 플러그를 만들고 스크립트의 콘센트에 오브젝트를 대입합니다.
Arrow Generator 스크립트 안에
none으로 되어있는 Arrow Prefab에
arrowPrefab오브젝트를 대입합니다.
7단계. UI 표시하기
마지막으로 게임의 진행 상황을 한번에 볼 수 있도록 UI를 배치하고, 게임 진행 상황에 맞게 표시를 바꿉니다.
<UI 만드는 방법>
1. UI 부품을 Scene 뷰에 배치한다.
2. UI를 갱신하는 감독 스크립트를 작성한다.
3. 빈 오브젝트를 만들고, 작성한 스크립트를 적용한다.
1. HP 게이지 배치하기.
UI 오브젝트인 Image를 사용해 HP 게이지를 만듭니다.
Hierarchy 창에서 + 를 클릭하고 Image 오브젝트를 생성한 후, 파일명을 hgGauge로 바꿔줍니다.
만든 hpGauge에 게이지 이미지를 설정할 예정입니다.
hgGauge를 클릭하고 Project 창에 있는 hg_gauge를 image 항목의 Source Image로 드래그&드롭합니다.
Source Image 안에
hp_gauge를 대입한 것을 확인!
2. 앵커 포인트 설정
화면의 크기가 바뀌어도 화면 오른쪽 위에 HP 게이지가 표시되도록 앵커 포인트 변경
즉, 화면 크기가 바뀔 떄마다 어디를 원점으로 해서 UI부품 좌표를 다시 계산하는가? 입니다.
UI 오브젝트의 위치는 앵커 포인트를 원점(0, 0)으로 하는 값으로 지정됩니다.
예를들면 앵커 포인트를 화면 중앙에 두고 화면크기를 줄이면, 오른쪽 위에 있는 HP게이지 표시가 화면에서 이탈..
앵커포인트를 와면 오른쪽 위에 두면 HP 게이지는 항상 오른쪽 위에 표시되고 화면에서 이탈하지 않습니다.
앵커포인트를 적절히 설정하면, 실행하는 기기의 화면 크기에 의존하지 않는 UI를 만들수 있습니다.
따라서 화면크기가 바뀌어도 HP게이지가 항상 오른쪽 위에 표시되도록 앵커 포인트를 화면 오른쪽으로 설정합니다.
Hierarchy 창에서 hgGauge를 선택하고 Inspector 창에서 앵커포인트 아이콘을 클릭합니다.
표시된 Anchor Presets에서 오른쪽 위에 고정 아이콘을 클릭합니다.
RectTransform 항목의 pos x, y, z 그리고 width와 height를 다음과 같이 설정합니다.
3. HP 게이지 줄여나가기.
HP게이지를 줄여나가려면, UI 오브젝트의 Image에서 제공하는 Fill기능을 사용해야 합니다.
Fill Amount 변수값을 바꾸면 이미지 표시 영역을 줄이거나, 늘릴 수 있습니다.
게이지를 줄이는 방법은 원형으로 이미지를 잘라내는 방식 외에도 상하방향, 좌우방향 등 여러가지가 있습니다.
[ Fill Method ] 1) Horizontal : 가로 방향으로 이미지를 잘라 낸다.
2) Vertical : 세로 방향으로 이미지를 잘라 낸다.
3) Radial 90 : 90도 선형으로 이미즈를 잘라 낸다.
4) Radial 180 : 반원형으로 이미지를 잘라 낸다.
5) Radial 360 : 원형으로 이미지를 잘라 낸다.
여기서는 원형 게이지를 이용함으로 Radial 360으로 지정하겠습니다.
Hierarchy 창에서 hgGauge를 선택하고, Image 항목의 Image Type을 Filled, Fill Method를 Radial 360으로 설정합니다.
1. Fill Origin은 잘라내기를 시작하는 위치입니다.
우리는 위에서부터 잘라내기 때문에 top으로 설정
2. Fill Amount는 HP게이지의 초기값을 의미함으로,
가득찬 상태인 "1"로 설정해줍니다.
지금까지 잘 따라했다면, 실행 시 다음과 같은 화면이 나와야합니다.
8단계. UI를 갱신하는 감독 만들기
플레이어와 화살의 충돌 판정은 화살 컨트롤러가 했습니다.
따라서 플레이어가 화살에 맞는 시점에 화살 컨트롤러는 감독에게 HP게이지가 줄었다고 알려야합니다.
감독은 알림을 전달받아 HP 게이지의 표시를 갱신합니다.
순서는,
감독스크립트 작성하기 → 빈 오브젝트 만들기 → 빈오브젝트에 감독 스크립트 적용하기
1. 감독 스크립트 작성하기.
C# 스크립트를 만든 후, 파일명을 GameDirector로 변경합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameDirector : MonoBehaviour
{
GameObject hpGauge;
void Start()
{
this.hpGauge = GameObject.Find("hgGauge");
}
public void DecreaseHp()
{
this.hpGauge.GetComponent<Image>().fillAmount -= 0.1f;
}
}
1) using UnityEngine.UI; 는 UI오브젝트를 스크립트에서 조작할 때 필요합니다.
2) 감독 스크립트를 사용해 HP 게이지를 갱신하려면 감독 스크립트가 HP게이지의 실체를 조작할 수 있어야 합니다.
Start 메서드에서는 Find 메서드를 사용해 씬 중에서 hp 게이지의 오브젝트를 찾고 hgGauge 변수에 대입합니다.
3) 화살 컨트롤러에서 HP게이지 표시를 줄이는 처리를 호출할 것을 고려해 HP게이지의 처리는 public 메소드로 작성
4) 화살과 플레이어가 충돌했을 때, 화살 컨트롤러가 호출하는 메서드로 Image 오브젝트의 fillAmount를 줄여 HP 게이지를 표시하는 비율을 낮춥니다.
2. 빈오브젝트를 만들어 이름을 GameDirector로 바꿉니다.
그리고 Project 창의 GameDirector 스크립트를 Hierarchy창의 GameDirector 오브젝트로 드래그&드롭 해줍니다.
3. HP가 줄었다고 감독에게 알리기.
플레이어가 화살을 맞으면 화살 컨트롤러에서 감독 스크립트의 DecreaseHp 메서드를 호출하는 부분을 만듭니다.
플레이어가 화살에 맞은 시점에 HP가 줄어들도록 DecreaseHp 메서드를 호출하는 처리를 화살컨트롤러의 충돌판정처리에서 합니다. 따라서 화살 컨트롤러인 "ArrowController"에 코드를 추가합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowController : MonoBehaviour
{
GameObject player;
void Start()
{
this.player = GameObject.Find("player");
}
void Update()
{
//프레임마다 등속으로 낙하시키기.
transform.Translate(0, -0.1f, 0);
//화면 밖으로 나오면 오브젝트를 소멸시킨다.
if(transform.position.y < -5.0f)
{
Destroy(gameObject);
}
//충돌 판정
//1. 화살의 중심 좌표
Vector2 p1 = transform.position;
//2. 플레이어의 중심 좌표
Vector2 p2 = this.player.transform.position;
Vector2 dir = p1 - p2;
float d = dir.magnitude;
//화살의 반경
float r1 = 0.5f;
//플레이어의 반경
float r2 = 1.0f;
if(d < r1 + r2)
{
//감독 스크립트에 플레이어와 화살이 충돌했음을 알린다.
GameObject director = GameObject.Find("GameDirector");
director.GetComponent<GameDirector>().DecreaseHp();
//충돌한 경우 화살을 지운다.
Destroy(gameObject);
}
}
}
마지막에 "감독 스크립트에 플레이어와 화살이 충돌했음을 알리는 코드" 추가!
1) Find 메서드를 사용하는 이유는, ArrowController에서 GameDirector 오브젝트에 있는
DecreaseHp 메소드를 호출하기 때문입니다.
2) 다음으로 GetComponent 메서드를 사용해 GameDirector 오브젝트의 GameDirector 스크립트를 구하고
DecreaseHp 메소드를 호출합니다.
3) 게임을 실행하면, 플레이어가 화살을 맞았을 때 HP 표시가 줄어드는 것을 볼 수 있습니다.
'Unity_Metaverse' 카테고리의 다른 글
[Unity] 1인칭 시점 카메라 설정하기 (0) | 2022.07.20 |
---|---|
[Unity] Physics와 애니메이션 (0) | 2022.07.19 |
[Unity] 2D 자동차 게임 만들기 (0) | 2022.06.29 |
[Unity] 2D 룰렛 게임 만들기 (0) | 2022.06.29 |
[Unity] 플레이어 만들고, 오브젝트 이동시키기 (키보드 입력 값) (0) | 2022.06.22 |