Unity Chapter 4-2. 소코반 게임 만들기 : 플레이어 조작

Date:     Updated:

카테고리:

태그:

인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!


Chapter 4. 소코반 게임 만들기 : 플레이어 조작

start 함수 : 게임이 처음 시작되었을 때 딱 한번 실행됨. start 함수를 가진 스크립트가 붙어있는 모든 오브젝트들은 게임 시작시 각자의 start 내용대로 실행하게 된다.

update 함수 : 화면이 한번 깜빡일 때 한번 실행. 즉 매 프레임마다 계속 실행된다. update 함수를 가진 스크립트가 붙어있는 모든 오브젝트들은 게임 내내 매 프레임마다 각자의 update 내용대로 실행하게 된다.

  • 게임은 매 프레임마다 상황이 변한다. 이러한 정보를 받아들이고 없뎃할 수 있는 내용을 update에 넣어야 한다.
    • *ex) 인공지능이 플레이어를 추적할 때 플레이어 위치를 계속 업뎃해서 받아와야함, 유저의 입력이 들어올 때마다 받아서 처리


update 함수 안에서 유저 입력(키보드) 처리하기

  • 유저 입력을 받는 함수매 프레임마다 계속 실행된다.
    • 매 프레임마다 1초에 수십번씩 계속 실행되다가 마침 딱 알맞는 유저입력이 들어오면 그게 맞는 처리를 해주는 것.
      • 예를 들어 스페이스바를 눌렀으면 발사 처리
    • 매 프레임마다 1초에 수십번씩 실행되야 하므로 update() 함수에 유저 입력 받는 함수를 작성해줄 것이다.
      • 유니티는 매 프레임마다 게임이 종료될 때 까지 계속해서 Update 메세지를 날린다.
      • update 함수 안에 Debug.Log 콘솔 출력을 넣고 실행해보면 계속 무한히 콘솔 출력 메세지가 뜨는 것을 확인할 수 있다.
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의 불만 사항

  1. 키보드를 일일이 명시하고 싶지 않다.
    • ex) 일일이 KeyCode.W, KeyCode.S, …
  2. 꼭 방향을 딱 4가지로만 나누고 싶지 않다.
    • 이렇게 4가지. W -> z축+, A -> x축-,S -> z축-,D -> x축+,

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");
    1. GetKey와 다르게 문자열을 매개변수로 받으며 float 값을 리턴한다.
      • ex) “Horizontal”
        • 수평 방향에 대해서 키보드나 조이스틱으로 입력을 했을때 -1 ~ +1 사이의 float 값을 리턴한다.
      • 즉 키보드 수평방향에 대응되는 키들이 “Horizontal”에 맵핑되어 있다.
      • A와 D가 맵핑 되어 있다. - “Fire”, “Jump”, “Crunch” 등등..
    2. GetKey보다 좋은 점
      • 만약 점프 키를 Space bar가 아닌 윗쪽 방향키로 바꾸고 싶다면?
      • GetKey의 경우 직접 코드로 찾아가서 GetKey(KeyCode.SpaceBar)를 Getkey(KeyCode.Up)으로 바꾸는 수고를 감수해야 한다.
      • 그런데 GetAxix를 사용한다면 점프 동작에 대한 유니티 상에서 "Jump"에 Space bar가 할당 되있는 것을 그냥 Up으로 바꿔주면 땡이다.
        • 직접 GetKey처럼 코드 찾아가서 Input.GetAxis("Jump") 코드를 수정하지 않아도 되는 것이다!
        • 그냥 유니티 Project Settings 에서 바꾸기만 하면 된다.
    3. 문자열에 맵핑된거 바꾸는 방법
      • 유니티 - 메뉴 - Edis - Project Settings - Input 에서 바꿀 수 있다.
      • 현재 Horizontal은 왼쪽 방향키(left), 오른쪽 방향키(right), 그리고 보조로 A키, D키가 설정 되어있다.
      • Negative : left, A
      • Positive : Right, D
    4. 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 함수 대신
    • Rigidbodyvelocity 변수의 값을 직접 바꿔주기.

      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 함수는 지 혼자 알아서 중력 가속도, 마찰력, 관성 등등 여러 물리 법칙들을 고려하여 힘을 적용시켜 줬었다.

문제점

  • 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.velocityVector3(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;   
    }
}



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Unity Lesson 1 카테고리 내 다른 글 보러가기

댓글 남기기