mooni
[Unity] Chap04. 사이드뷰 게임의 기본 시스템 만들기 본문
4-1. 사이드뷰 게임
사이드뷰 : 옆에서 바라본 시점의 게임 시스템
목표 : "왼쪽부터 시작하여 오른쪽에 있는 골인 지점까지 이동하는 사이드뷰 '런&점프' 게임 만들기"
Object : 플레이어 캐릭터, 지면 & 블록, 골인 지점 & 게임 오버, 상태 표시 & 재시작
Script : 좌우 이동 & 점프 + 애니메이션, 골인 지점 접촉 시 Clear, 떨어지면 Game Over, Game Over 시 재시작
4-2. 샘플 게임 실행해보기
실행 파일 모두 삭제된 이슈로 못해봄 그냥 만들어볼게요
4-3. 게임 스테이지 만들기
1) Layer 설정
→ 지면과 블록에 Ground Layer 적용, 이를 스크립트에서 받아 Ground 접촉 시에만 점프 가능하도록 활용
Layer : 게임 오브젝트를 그룹으로 다루는 구조
지면에 닿았을 때만 점프가 가능한 것을... Layer : ground로 해결함,,, 난 여태 무엇ㅇ를,,,,
2) 골인 지점 설정
→ Box Collider 2D - is Trigger 설정을 통해 접촉 시 물리적인 충돌이 발생하지 않지만 충돌 이벤트는 script에서 받음
3) Tag 설정
→ 목표 지점에 Goal Tag 적용
Tag : 게임 오브젝트에 문자를 할당해 구별
4-4. 게임 오브젝트 재사용하기
프리팹(Prefab) : 게임 오브젝트 복제 시스템
프리팹 사용
1) 오브젝트 생성 후 필요한 컴포넌트 설정
2) 해당 오브젝트를 계층 뷰에서 프로젝트 뷰로 옮기면 프리팹 생성 완료, 쉽게 말하면 설정값 복사
3) 생성된 프리팹을 필요한 곳에 사용, 이후에 개별로 변경하는 것은 프리팹에 영향을 주지 않음
프리팹 수정
1) 개별로 변경 시 다른 오브젝트에 영향을 주지 않음
2) 프로젝트 뷰에서 프리팹을 수정 시 씬에 배치된 오브젝트가 전체 수정됨
화면 밖 충돌 판정 만들기

1) 빈 오브젝트 Wall Object 생성
2) Wall Object에 Box Collider 2D를 두개 생성하여 화면 양 옆에 배치
→ 이때 Transform으로 위치를 변경하지 않고 Offset으로 위치 조정
게임 오버 판별하기

1) 마찬가지로 빈 오브젝트 DeadObject 생성
2) 화면 아래에 Box Collider 2D 생성, is Trigger로 충돌 이벤트 판정
4-5. 플레이어 캐릭터 만들기
1) Player 오브젝트에 Palyer Tag 설정
2) 점프 구현
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
Rigidbody2D rbody;
float axisH = 0.0f;
public float speed = 3.0f;
public float jump = 9.0f; //점프력
public LayerMask groundLayer; //LayerMask : Layer 자료형, unity editor에서 지정할 수 있도록 public 선언
bool goJump = false; //jump 여부
bool onGround = false; //지면접촉 여부
// Start is called before the first frame update
void Start()
{
rbody = this.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
axisH = Input.GetAxisRaw("Horizontal");
if (axisH > 0.0f)
{
Debug.Log("오른쪽 이동");
transform.localScale = new Vector2(1, 1);
}
else if(axisH < 0.0f)
{
Debug.Log("왼쪽 이동");
transform.localScale = new Vector2(-1, 1);
}
if(Input.GetButtonDown("Jump")) //키입력 여부
{
Jump();
}
}
void FixedUpdate()
{
onGround = Physics2D.Linecast(transform.position, transform.position - (transform.up * 0.1f), groundLayer);
//Physics2D 컴포넌트(Class)의 Linecast 함수로 접촉 여부 확인
//Linecast(시작점, 끝점, 대상)
if(onGround || axisH != 0) //지면접촉O | 좌우 입력O : 공중에서도 이동 속도 갱신 가능
{
rbody.velocity = new Vector2(speed * axisH, rbody.velocity.y);
}
if(onGround && goJump) //지면접촉O & 점프키O
{
Debug.Log("점프!");
Vector2 jumpPw = new Vector2(0, jump); //jumpPw = (x, y) : 수직 방향의 Vector 값
rbody.AddForce(jumpPw, ForceMode2D.Impulse); //AddForce 사용하여 순간적 힘 적용
goJump = false; //점프 후 false으로 갱신
}
}
public void Jump() //키입력 시 goJump = True, 터치스크린으로 조작 시 대응을 위해 public 선언
{
goJump = true;
Debug.Log("점프 버튼 눌림!");
}
}
위의 script 작성 후 유니티 에디터에서 public 변수 Ground Layer의 값을 Ground로 설정하면 적용 가능
변수
float jump : 0.9의 점프력 설정, 유니티 에디터로 수정가능하도록 public 설정
goundLayer : 앞부분에서 추가했던 Ground 레이어를 설정하기 위한 변수, LayerMask 자료형으로 레이어를 다룸
goJump : 스페이스 키 눌렸는지의 여부를 저장
onGround : 지면에 접촉중인지 나타내는 플래그
Layer는 LayerMask 자료형으로 다룸
Input Class의 Input Method
Key
bool down = Input.GetKeyDown(KeyCode.Space) : 키 눌림
bool press = Input.GetKey(KeyCode.Space) : 키가 눌려있는 상태
bool up = Input.GetKeyUp(KeyCode.Space) : 키를 떼었을 때
인수는 KeyCode형으로 받음
Mouse
bool down = Input.GetMouseButtonDown(0) : 마우스 눌림
bool press = Input.GetMouseButton(0) : 마우스가 눌려있는 상태
bool up = Input.GetMouseButttonIUp(0) : 마우스를 떼었을 때
인수는 0(좌), 1(우), 2(휠버튼)
스마트폰의 터치 패널도 처리할 수 있음
여러가지 입력 기기의 버튼
bool down = Input.GetButtonDown("Jump")
bool press = Input.GetButton("Jump")
bool up = Input.GetButtonIUp("Jump")
인수는 InputManager에 설정된 문자열을 받음
Jump에 여러 키를 할당하면 여러 버튼을 게임에서 Jump를 위해 활용 가능
가상 축
float axisH = Input.GetAxis("Horizontal")
float axisV = Input.GetAxisRaw("Vertical")
인수는 마찬가지로 InputManager의 문자열을 받음
가상축 : 키보드의 화살표, 컨트롤러의 아날로그 스틱
GetAxis : -1~1연속 값을 가짐
GetAxisRaw : -1, 0, 1 세가지 중 하나의 값을 가짐
FixedUpdate method
onGround
Physics2D component(class)의 Linecase Method를 사용하여 Layer와의 접촉 여부를 판별
Linecase(시작점, 끝점, 대상 Layer) : bool형 반환
→ transform.position - (transform.up * 0.1f) : 게임 오브젝트의 pivot(bottom) - 게임 오브젝트의 pivot의 y축으로 0.1
속도 & 이동
if(onGround || axisH != 0) : 속도 갱신에 조건 분기 처리
1) onGround : 지면 접촉 여부
2) axisH != 0 : 속도가 0이 아님 == 좌우 이동이 있는 경우
→ 공중에 존재할 때의 진행 방향의 속도가 갱신될 수 있도록 설정
→ 해당 조건이 없으면 점프 시 물리 시뮬레이션에 맡겨져 공중에서 그대로 떨어짐
if(onGround && goJump) : 점프 가능 조건
1) jumpPw(0, jump) : jumpPw(x(0), y(0.9f)), 수직 방향의 Vector 값
2) AddForce(jumpPw, ForceMode2D.Impulse) : AddForce(힘의 방향 * 힘, 힘의 종류)
transform.up = Vector(0, 1, 0) : y축으로 0.1 아래 지점
3) Physics Material 2D로 마찰 조정 : 컴포넌트X, 파라미터O, 프로젝트 뷰에서 생성

Friction(마찰 계수) : 0 ~ 1 (미끄러짐 ~ 미끄러지지 않음)
Physics Material 2D를 Player - Capsule Collider2D - Material에 적용시킴
4) 중력 적용
rigidbody2D의 Gravity Scale 값 조정
1 = 1G(지구) 값이 커질 수록 중력이 강하게 작용, 0은 무중력
5) 애니메이션 만들기 : Mecanim 기능 활용
4가지 데이터를 만든 후 Mecanim에서 활용
1) Sprite : 이미지 데이터, Sprite Renderer 컴포넌트를 사용하여 스프라이트 데이터로 이미지를 표시
2) Animation Clip : 여러 스프라이트를 사용하여 이미지를 전달, 애니메이션 적용 데이터, 재생시간, 재생속도 관리
3) Animator Controller : 여러 애니메이션 클립을 관리하는 데이터, 캐릭터의 상태에 따른 애니메이션 클립 관리
4) Animator Component : 오브젝트에 어태치 후 애니메이션시키는 컴포넌트
6) 애니메이션을 위한 추가 스크립트
//추가된 부분만 작성
public class PlayerController : MonoBehaviour {
Animator animator; //애니메이터
public string stopAnime = "PlayerStop";
public string moveAnime = "PlayerMove";
public string jumpAnime = "PlayerJump";
public string goalAnime = "PlayerGoal";
public string deadAnime = "PlayerOver";
string nowAnime = "";
string oldAnime = "";
}
void Start() { //애니메이션 가져오기, 기본 애니메이션은 stop
animator = GetComponent<Animator>();
nowAnime = stopAnime;
oldAnime = stopAnime;
}
void FixedUpdate() {
if(onGround) { //지면 위에서
if(axisH == 0) { //이동이 없을 경우 stopAnime
nowAnime = stopAnime;
}
else { //이동이 있는 경우 moveAnime
nowAnime = moveAnime;
}
}
else { //공중에서 jumpAnime
nowAnime = jumpAnime;
}
if(nowAnime != oldAnime) { //상태 전환 시 애니메이션 재생
oldAnime = nowAnime;
animator.Play(nowAnime);
}
}
//접촉 시작
private void OnTriggerEnter2D(Collider2D collision) {
if(collision.gameObject.tag == "Goal") { //해당 태그에 접촉 시 아래 함수(애니메이션) 실행
Goal();
}
else if(collision.gameObject.tag == "Dead") {
GameOver();
}
}
public void Goal() { //골
animator.Play(goalAnime);
}
public void GameOver() { //게임오버
animator.Play(deadAnime);
}
7) 게임 종료 판정 스크립트
//추가된 부분만 작성
public class PlayerController : MonoBehaviour {
public state string gameState = "playing";
}
void Start() { //게임 초기 상태 playing
gameState = "playing";
}
void Update() {
if(gameState != "playing") { //playing 상태가 아니면 플레이어 조작 금지
return;
}
}
void FixedUpdate() {
if(gameState != "playing") {
return;
}
}
public void Goal() { //GameStop() 호출
gameState = "gameclear";
GameStop();
}
public void GameOver() { //GameStop() 호출
gameState = "gameover";
GameStop();
GetComponent<CapsuleCollider2D>.enabled = false; //충돌 판정 비활성화
rbody.AddForce(new Vector(0, 5), ForceMode2D.Impulse); //Dead 시 튀어오르는 연출
}
void GameStop() { //속도를 0으로 하여 강제 정지
Rigidbody2D rbody = GetComponent<Rigidbody2D>();
rbody.velocity = new Vector2(0, 0);
}
Game State
1) playing : 게임 진행 중, 플레이어 조작 가능
2) gameclear : Goal tag가 부여된 오브젝트에 접촉, 플레이어 조작 불가
3) gameover : Dead tag가 부여된 오브젝트에 접촉, 플레이어 조작 불가
4) gameend : 2와 3이후 상태
와 정말 길었다,,,
*교재 : 누구나 할 수 있는 유니티 2D 게임 제작
'Unity' 카테고리의 다른 글
[Unity] 3차 Study : PalyerController Script (0) | 2024.03.11 |
---|---|
[Unity] Chap05. 버튼과 UI 만들기 (0) | 2024.03.07 |
[Unity] 1차 Study : 점프, 닉네임 달기 완성! (1) | 2024.02.27 |
[Unity] Chap03. 스크립트 작성하기 (2) | 2024.02.26 |
[Unity] Chap02. 유니티로 첫 게임 만들기 (2) | 2024.02.26 |