Unity Chapter 4-2. 소코반 게임 만들기 : 플레이어 조작
카테고리: Unity Lesson 1
태그: Unity Game Engine
인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!
Chapter 4. 소코반 게임 만들기 : 플레이어 조작
start 함수
: 게임이 처음 시작되었을 때 딱 한번 실행됨. start 함수를 가진 스크립트가 붙어있는 모든 오브젝트들은 게임 시작시 각자의 start 내용대로 실행하게 된다.
update 함수
: 화면이 한번 깜빡일 때 한번 실행. 즉 매 프레임마다 계속 실행된다. update 함수를 가진 스크립트가 붙어있는 모든 오브젝트들은 게임 내내 매 프레임마다 각자의 update 내용대로 실행하게 된다.
- 게임은 매 프레임마다 상황이 변한다. 이러한 정보를 받아들이고 없뎃할 수 있는 내용을
update
에 넣어야 한다.- *ex) 인공지능이 플레이어를 추적할 때 플레이어 위치를 계속 업뎃해서 받아와야함, 유저의 입력이 들어올 때마다 받아서 처리
update 함수 안에서 유저 입력(키보드) 처리하기
- 유저 입력을 받는 함수는 매 프레임마다 계속 실행된다.
- 매 프레임마다 1초에 수십번씩 계속 실행되다가 마침 딱 알맞는 유저입력이 들어오면 그게 맞는 처리를 해주는 것.
- 예를 들어 스페이스바를 눌렀으면 발사 처리
- 매 프레임마다 1초에 수십번씩 실행되야 하므로
update()
함수에 유저 입력 받는 함수를 작성해줄 것이다.- 유니티는 매 프레임마다 게임이 종료될 때 까지 계속해서 Update 메세지를 날린다.
- update 함수 안에 Debug.Log 콘솔 출력을 넣고 실행해보면 계속 무한히 콘솔 출력 메세지가 뜨는 것을 확인할 수 있다.
- 매 프레임마다 1초에 수십번씩 계속 실행되다가 마침 딱 알맞는 유저입력이 들어오면 그게 맞는 처리를 해주는 것.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 10f;
public Rigidbody playerRigidbody;
void Start()
{
}
void Update()
{
if(Input.GetKey(KeyCode.W))
playerRigidbody.AddForce(0, 0, speed); // z축
if(Input.GetKey(KeyCode.A))
playerRigidbody.AddForce(-speed, 0, 0); // x축
if(Input.GetKey(KeyCode.S))
playerRigidbody.AddForce(0, 0, -speed); // z축
if(Input.GetKey(KeyCode.D))
playerRigidbody.AddForce(speed, 0, 0); // x축
}
}
- W,A,S,D 키 입력이 들어오면 playerRigidbody는 현재 참조하는 Rigidbody 컴포넌트가 붙어있는 오브젝트가 힘을 받도록 한다.
Input
- Input
- UnityEngine에서 제공하는 입력과 관련된 집합.
- 키보드, 모바일, 조이스틱의 입력을 받을 수 있는 여러 함수들의 집합.
- 함수
GetKey(뫄뫄)
- 뫄뫄 키보드 입력이 들어오면 True를 리턴한다.
- 뫄뫄 키보드 입력이 들어오지 않는 상태면 False를 리턴한다.
- ex) Input.GetKey(KeyCode.W) : W키가 입력되면 True 리턴
KeyCode
- 키보드의 각 자판들은 정수와 매칭된다.
- 그러나 자판에 매칭되는 정수를 모두 외우기는 힘듬. 119는 W.. 이런식으로 외울 수가 없음.
- KeyCode 클래스에 각 키보드 자판들과 정수가 연결되어 있으니 예를 들어
KeyCode.W
이런식으로 갖다 쓰기만 하면 된다.
- Game창을 한번 마우스로 클릭해준 후
WASD
입력을 해보자.
Input.GetKey의 불만 사항
- 키보드를 일일이 명시하고 싶지 않다.
- ex) 일일이 KeyCode.W, KeyCode.S, …
- 꼭 방향을 딱 4가지로만 나누고 싶지 않다.
- 이렇게 4가지.
W -> z축+
,A -> x축-
,S -> z축-
,D -> x축+
,
- 이렇게 4가지.
Input.Axix를 사용해보자
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 10f;
public Rigidbody playerRigidbody;
void Start()
{
}
void Update()
{
float inputX = Input.GetAxis("Horizontal");
float inputZ = Input.GetAxis("Vertical");
playerRigidbody.AddForce(inputX * speed, 0, inputZ * speed);
}
}
Input.GetAxis("Horizontal");
GetKey
와 다르게 문자열을 매개변수로 받으며 float 값을 리턴한다.- ex) “Horizontal”
- 수평 방향에 대해서 키보드나 조이스틱으로 입력을 했을때 -1 ~ +1 사이의 float 값을 리턴한다.
- 즉 키보드 수평방향에 대응되는 키들이 “Horizontal”에 맵핑되어 있다.
- A와 D가 맵핑 되어 있다. - “Fire”, “Jump”, “Crunch” 등등..
- ex) “Horizontal”
GetKey
보다 좋은 점- 만약 점프 키를 Space bar가 아닌 윗쪽 방향키로 바꾸고 싶다면?
GetKey
의 경우 직접 코드로 찾아가서 GetKey(KeyCode.SpaceBar)를 Getkey(KeyCode.Up)으로 바꾸는 수고를 감수해야 한다.- 그런데
GetAxix
를 사용한다면 점프 동작에 대한 유니티 상에서 "Jump"에 Space bar가 할당 되있는 것을 그냥 Up으로 바꿔주면 땡이다.- 직접 GetKey처럼 코드 찾아가서
Input.GetAxis("Jump")
코드를 수정하지 않아도 되는 것이다! - 그냥 유니티 Project Settings 에서 바꾸기만 하면 된다.
- 직접 GetKey처럼 코드 찾아가서
- 문자열에 맵핑된거 바꾸는 방법
- 유니티 - 메뉴 - Edis - Project Settings - Input 에서 바꿀 수 있다.
- 현재 Horizontal은 왼쪽 방향키(left), 오른쪽 방향키(right), 그리고 보조로 A키, D키가 설정 되어있다.
- Negative :
left
,A
- Positive :
Right
,D
- float 값을 리턴하는 이유 : 조이스틱 때문
Negative
키는-1.0f
ex) “Horizontal”에서 A키Positive
키는1.0f
ex) “Horizontal”에서 D키- 아무것도 안누를 땐
0.0f
- 이렇게 -1 ~ 1 사이의 float 실수값을 반환하는데 이렇게 하는 이유는 조이스틱 입력 때문!
- 조이스틱은 키보드와 다르게 딱 눌렀다 뗏다 같이 이분법적으로 생각할 수가 없기 때문.
- 조이스틱은 아주 살짝만 밀거나 많이 밀거나 미는 정도에 따라 입력의 정도가 다른 것!
- 오른쪽으로 살짝 밀면 0.2 이런게 가능
관성 없애기
- Rigidbody의
AddForce
는 말 그대로 힘을 주는 함수라 매개변수로 받은 힘과 내부적으로 관성, 마찰력 등등 이런 것들이 종합적으로 계산되어작용되게 된다 - 따라서
AddForce
를 사용하게 되면 방향키를 눌러도 오브젝트가 즉시 움직이는게 아니라 살짝 지연이 있은 후에 움직이게 된다.- Rigidbody 컴포넌트가 관성 또한 처리해주었기 때문
Rigidbody의 velocity 변수 값을 직접 바꿈
- 관성 없이 빠릿빠릿하게 움직이게 하고 싶다면.
- 바로 바로 내가 넣은 매개변수 값만큼 움직이게 하고 싶다면
힘
말고속도 값
에 바로 지정해 주면 됨.AddForce
함수 대신-
Rigidbody의
velocity
변수의 값을 직접 바꿔주기.playerRigidbody.velocity = new Vector3(inputX, 0, inputZ);
- Vector3 를 사용한다.
- x, y, z 를 가지는 집합
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 10f;
public Rigidbody playerRigidbody;
void Start()
{
}
void Update()
{
float inputX = Input.GetAxis("Horizontal");
float inputZ = Input.GetAxis("Vertical");
Vector3 velocity = new Vector3(inputX, 0, inputZ);
velocity = velocity * speed; // 한방에 각 x, y, z 마다 speed를 곱함
playerRigidbody.velocity = velocity;
}
}
- 만약 A 키와 W 키를 동시에 누르는 순간!
- inputX 에 -1가 들어오고 inputZ 에 1이 들어온다.
- velocity = Vector3(-10.0f, 0, 10.0f) 가 된다.
playerRigidbody.velocity
에 이를 대입해 주면 오브젝트가 관성 마찰력 그런거 계산 안된체로 그냥 바로 이 속도 값을 가지게 된다.- 입력이 들어오게 되는 순간마다 (-10.0f, 0, 10.0f) 만큼 x, y, z축 방향으로 이동하게 될 것이다 ~
- 입력이 들어오지 않는다면 inputX 와 inputZ 값은 0이므로 매 프레임마다 speed 곱해도 속도는 (0, 0, 0)을 유지하니 아무 변화도 일어나지 않는다.
- 근데 이렇게
AddForce
함수를 거치지 않고 속도 변수값을 직접 지정해주니까 관성에 더하여 중력까지 작용하지 않는다..!- 입력이 들어오게 되는 순간마다 (-10.0f, 0, 10.0f) 만큼 x, y, z축 방향으로 이동할 뿐 y 값은 고려하지 않기 때문에.
AddForce
함수는 지 혼자 알아서 중력 가속도, 마찰력, 관성 등등 여러 물리 법칙들을 고려하여 힘을 적용시켜 줬었다.
- 입력이 들어오게 되는 순간마다 (-10.0f, 0, 10.0f) 만큼 x, y, z축 방향으로 이동할 뿐 y 값은 고려하지 않기 때문에.
문제점
AddForce
함수는 지 혼자 알아서 중력 가속도, 마찰력, 관성 등등 여러 물리 법칙들을 고려하여 힘을 적용시켜 줬었다.- 근데 이렇게
AddForce
함수를 거치지 않고 속도 변수값을 직접 지정해주니까 생기는 문제점..- 바닥을 벗어나면
- 입력을 계속 하는 중일 때 -> 아예 중력을 받지 않음
- 입력이 들어오게 되는 순간마다 (-10.0f, 0, 10.0f) 만큼 x, y, z축 방향으로 이동할 뿐 y 값은 고려되지 않아 0으로 유지되기 때문에.
- 입력 안 하는 중일 때 -> 확 떨어지는게 아닌 이상하게 아주 천천히 떨어지는 요상한 모양이 된다.
RigidBody
컴포넌트 특성 자체가 중력 가속도가 디폴트로 적용되어 있다.Use Gravity
체크 해제하면 중력 영향 안받게 됨- velocity.y 즉 y 속도값은
RigidBody
컴포넌트 자체에 의해 중력의 영향을 받음.
- 따라서 중력 가속도를 받아 속도가 떨어졌다가 바로 이내
playerRigidbody.velocity = Vector3(0, 0, 0)
으로 덮어씌워져서.. - 그래서 떨어지다 말고 떨어지다 말면서 아주 천천히 떨어지는 모양이 되는 것.
- 입력을 계속 하는 중일 때 -> 아예 중력을 받지 않음
- 바닥을 벗어나면
- 해결 방법
- 떨어지는 속도는 유지시켜 주어야 한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 10f;
public Rigidbody playerRigidbody;
void Start()
{
}
void Update()
{
float inputX = Input.GetAxis("Horizontal");
float inputZ = Input.GetAxis("Vertical");
float fallSpeed = playerRigidbody.velocity.y; // 💛원래 y의 속도를 보존시켜 주어야 한다.💛
Vector3 velocity = new Vector3(inputX, 0, inputZ);
velocity = velocity * speed;
velocity.y = fallSpeed; // 💛입력에 따라 결정된 속도값의 y는 기존에 보존해 두었던 원래 y로💛
playerRigidbody.velocity = velocity; // 최종
}
}
- 최종적으로
playerRigidbody.velocity
에Vector3(inputX * speed, fallspeed, inputZ * speed)
가 대입되는 것이나 마찬가지다.
GetComponent()
Rigidbody playerRigidbody;
public
를 빼면 이제 이 변수들은 유니티에서 슬롯이 열리지 않는다. 그럼 변수 playerRigidbody
가 오브젝트에 붙어있는 Rigidbody 컴포넌트를 어떻게 참조할 것인가? 슬롯이 열리지 않는데 어떻게 할 수 있을까?
GetComponent<컴포넌트 이름>()
을 사용하면 된다.- UnityEngine에서 지원하는 함수다.
- 이 스크립트가 붙어 있는 오브젝트에
<>
안에 적혀 있는 컴포넌트가 실존하여 오브젝트에 붙어 있는 상태라면 그 붙어 있는 컴포넌트를 리턴해준다. - 이 방법을 사용하면 슬롯 없이 그냥 코드에서 바로 해당 컴포넌트를 변수에 연결시킬 수 있다.
public
을 써서 슬롯을 이용하여 직접 드래그 앤 드롭으로 연결시켜 주는 방식은 편하긴 하지만 다른 개발자들이 건드려서 게임이 터질 수도 있고 여러 위험을 안고 있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 10f;
Rigidbody playerRigidbody; // public 뗀 상태
void Start()
{
playerRigidbody = GetComponent<Rigidbody>(); // Rigidbody 컴포넌트가 붙어 있다면 찾아서 리턴 해줌.
}
void Update()
{
float inputX = Input.GetAxis("Horizontal");
float inputZ = Input.GetAxis("Vertical");
float fallSpeed = playerRigidbody.velocity.y;
Vector3 velocity = new Vector3(inputX, 0, inputZ);
velocity = velocity * speed;
velocity.y = fallSpeed;
playerRigidbody.velocity = velocity;
}
}
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기