HYNGNG | 알곡사료

3D 환경에서 플레이어의 이동 스크립트 본문

유니티/유니티 기초

3D 환경에서 플레이어의 이동 스크립트

hyngng 2022. 6. 16. 20:36
728x90
반응형

🙂블로그 이전했습니다!🙂

 

유니티에서 WASD 입력에 따라 오브젝트가

가속을 받아 움직이도록 하는 C# 스크립트는 다음과 같다.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerBall : MonoBehaviour
{
    Rigidbody rigid;

    void Awake()
    {
        rigid = GetComponent<Rigidbody>();
    }
 
    void FixedUpdate()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");

        rigid.AddForce(new Vector3(h0v), ForceMode.Impulse);
    }
}
 

이에 코드에 대한 자세하지만 완벽하지는 않은 설명을 덧붙인다.


using이 사용된 첫 세 줄부터 시작한다.

 

유니티는 여러 개의 프로그램 함수들을 모아 하나의 특정한 기능을 수행할 수 있도록 만든 컴포넌트(Component)를 기반으로 작동하며, 이에 (컴포넌트 기반 && 객체 지향 언어)인 C#를 사용한다.

말인즉, 우리가 필요에 따라 함수를 묶어 만든 컴포넌트를 그때그때 상황에 맞게 끼워 사용할 수도 있고, 마찬가지로 다른 사람이 만든 컴포넌트를 가져와 사용할 수 있다는 것인데

 

객체 지향 언어 답게 C#에서 클래스를 기반으로 구성되는 컴포넌트는 간혹, 각기 다른 사람이 작성한 클래스 이름이 서로 같아 충돌하는 상황이 생길 수 있다.

내가 만든 컴포넌트와 다른 사람이 만든 컴포넌트를 구성하는 여러 개 클래스 사이 이름이 겹치면 충돌하는 것이다.

그래서 대안으로 나온 것이 유사한 클래스들을 구분지어 묶는 새로운 단위인 네임스페이스이고,

 

첫 줄부터 등장하는 using 문법은 클래스명만으로 네임스페이스에 정의된 하위 클래스를 사용할 수 있게 하는 명령어다.

 

따라서 using System.Collections System.Collections 라는 네임스페이스를 구성하는 클래스와 인터페이스를 사용하겠다는 의미이다.

 

https://docs.microsoft.com/ko-kr/dotnet/api/system.collections?view=net-6.0

 

C#는 마이크로소프트에서 제작된 언어이고, 따라서 마이크로소프트에서 가이드를 제작, 배포하고 있는데

마이크로소프트에 따르면 System.Collections를 구성하는 것(클래스, 구조체, 인터페이스)은 위와 같다고 한다.

 

System.Collections.Generic도, UnityEngineusing으로 묶여 동일하게 작동한다.


다음의 public class PlayerBall : MonoBehaviour 부분에 대해서,

PlayerBall : MonoBehaviourMonoBehaviour라는 부모클래스PlayerBall라는 자식클래스에게 상속하여 MonoBehaviour 클래스 내의 기능(Start(), Update()...) 들을 사용하겠다는 의미이며

public class PlayerBallPlayerBall 클래스를 공개(public)로 선언하겠다는 의미이다.

 

MonoBehaviour클래스에 구체적으로 어떠한 기능들이 몇 개까지 있는지는 공식 메뉴얼에 자세히 나와 있으니 참조할 수 있다. (대략 기초적인 상당수의 뼈대를 MonoBehaviour가 제공하고 있다고 이해하면 편하다.)

 

맛보기로 보여주면 이런 느낌이다.

 


다음의 Rigidbody rigid에 대해서,

 

Rigidbody는 물리학에서의 용어 강체(剛體)를 뜻하는 영단어로, '강체'는 외력이 가해져도 모양이나 크기가 변하지 않는 물체를 말한다.

유니티 내의 오브젝트는 별도로 설정을 해주지 않는 이상 벽이나 땅과 충돌하더라도 오브젝트의 모양이나 크기는 변함이 없으니 어느 정도 직관적인 용어라 할 수 있겠다.

 

유니티에서 Rigidbody는 힘과 토크 등의 물리 제어로 오브젝트가 사실적으로 움직이게 해 주는 3D 물리 컴포넌트로, 이를 오브젝트에 적용하게 되면 오브젝트가 현실과 비슷한 물리적 운동을 할 수 있게 된다.

 

 

Rigidbody가 적용된 오브젝트를 클릭하면 Inspector 창에서 Mass(질량)이나 Drag(공기 저항), 중력 적용 여부(Use Gravity) 등 해당 오브젝트의 물리값을 변경할 수 있으며,

가령 10으로 설정되어 있는 질량값을 500으로 늘린다면 같은 힘이 주어졌을 때의 물체의 가속력이 낮아 오브젝트의 움직임이 비교적 굼뜨게 된다.

 

 

더불어 유니티 창에서 Edit > Project settings > Physics 창에 들어가면 가장 위에 Gravity값을 확인할 수 있는데,

중력 값이 (X: 0), (Y: -9.81), (Z:0)으로 설정되어 있었으므로 약 9.80665m/s²으로 계산되는 실제 중력가속도를 소숫점 아래 셋째 자리에서 반올림하여(9.81)로 쓰고 있음을 확인할 수 있었다.

 

코드의 Rigidbody rigid 이 부분이 C언어와 Python의 기초적인 일부분만을 다룬 나는 다소 생소했는데,

알아보니 rigid 라는 이름의 Rigidbody형 변수를 선언하라는 의미이다.

(예를 들어 C#에서 f라는 이름의 float형 변수를 선언하고 싶다면 "float f"라고 입력해야 한다.

마찬가지로 rigid라는 이름의 Rigidbody형 변수를 선언하고 싶을 때 Rigidbody rigid라는 코드가 나온다.)

 

+++ 2D 개발 환경에서는 컴포넌트로 일반 Rigidbody가 아닌 Rigidbody 2D를 사용해야 한다.


다음의 void Awake(), rigid = GetComponent<Rigidbody>(); 부분에 대해서,

 

void 함수는 기초 C언어에서도 자주 등장하기에 많이 궁금했는데,  리턴할 값이 없을 때 쓰는 함수이다.

 

"입력값과 출력값이 있는 것을 함수라고 부르지 않나? 왜 리턴값이 없는데도 함수라고 부르지?" 하는 의문이 들어 찾아보니,

수학이 아닌 프로그래밍 분야에서 쓰는 함수는 영미권에서 쓰는 function의 딱딱한 번역어이고

쓰기는 "함수"라고 쓰지만 그 의미는 경우에 따라 유연하게 function의 여러 뜻 중 하나인 "기능" 정도의 의미를 가진다고 한다.

 

이어, Awake 함수는 게임을 시작하기 전 변수의 초기화를 위해 사용하며

Awake() 안에 작성된 rigid = GetComponent<Rigidbody>()는 이 스크립트가 적용된 오브젝트가 갖고 있는 컴포넌트 중 Rigidbody를 추출하여 앞에서 선언한 rigid라는 변수에 저장하라는 의미이다.


다음의

FixedUpdate()

float h = Input.GetAxisRaw("Horizontal")

float v = Input.GetAxisRaw("Vertical")

rigid.AddForce(new Vector3(h0v), ForceMode.Impulse)

부분에 대해서,

 

FixedUpdate 함수는 Update 함수의 변형이다.

Update 계열 함수에는 Update(), FixedUpdate(), LateUpdate()로 그 종류가 3가지가 있는데

각각 1프레임마다, 주기적인 물리적 시간마다, Update함수의 호출이 마쳤을 때마다 호출된다.

 

이 중 FixedUpdate()함수는 Edit > Project settings > Time > Fixed Timestep에 설정된 일정한 주기값에 따라 반복적으로 호출되며 물리값을 계산하는 용도에 알맞다.

가령, Update함수에 물리량 계산 주기를 맡기면 계산이 각 프레임마다 이루어지게 되고, 컴퓨터에 불규칙적인 부하에 따라 계산 주기가 불안정해지며 물리엔진 충돌검사에 문제가 생긴다.

 

이 때문에 Rigidbody를 통해 물리를 계산하는 경우 FixedUpdate함수를 사용해야 한다.

 

이어, Input은 유니티 엔진에서 제공하는 입력과 관련된 함수 집합이다. 마우스의 위치값을 받아오는 mousePosition 함수, 특정한 키가 눌렸는지 확인할 수 있는 KeyCode 함수 등이 있으며

이 함수 모두 사용할 때 Input.mousePosition() 또는 Input.KeyCode()의 형태로 사용한다.

 

GetAxisRawInput 집합에서 정의되어 있는 함수 중 GetAxis에서 파생된 함수이다.

GetAxis는 조이스틱 입력까지도 받을 수 있도록 [-1, 1]의 범위 중 하나의 숫자를 실수형(float)으로 리턴하는 형태로 되어 있는데, 키보드 입력의 경우 그럴 필요 없이 단순히 -1, 0, 1만을 삼분법의 형태로 취급하면 더욱 간단하다는 필요에 의해 만들어진 함수가 GetAxisRaw함수이다.

 

GetAxisRaw 함수는 문자열(String)을 매개변수로 받는데, 문자열 Horizontal(※수평)은 키보드 A&D에 맵핑되어 있고, 문자열 Vertical(※수직)은 키보드 W&S에 맵핑되어 있는 식이다.

 

그러니까 float h = Input.GetAxisRaw("Horizontal")는 W&S 입력에 따른 값(1&-1)을 float변수 h에 저장하라는 의미이고,

float v = Input.GetAxisRaw("Vertical")은 A&D 입력에 따른 값(-1&1)을 float변수 v에 저장하라는 의미가 된다.

 

rigid.AddForce(new Vector3(h, 0v), ForceMode.Impulse)

AddForce함수는 Rigidbody에게 힘을 전달하는 함수로 AddForce(방향&힘 값, 힘의 종류)로 구성된다. 여기에서 방향&힘 값Vector3라는 이름의 데이터형으로 주었고, 이 형식에 대해 간단히 알 필요가 있어 정리해 둔다.

Vector3는 이름 그대로 방향과 크기를 가지는 벡터값을 나타내며 지금의 유니티는 3차원 공간을 다루므로 x, y, z 세 개의 원소로 표현된다. (마찬가지로 2D환경에서는 x, y 두 개의 원소값을 가지는 Vector2가 사용된다.)

(이 부분을 찾아보면서 되게 재미있는 내용을 발견했는데, 유니티는 기본적으로 3D엔진이라 2D 공간은 실제로 2차원 공간이 아니라 3차원에서 단순히 모든 오브젝트의 Z좌표값을 0으로 고정한 상태라는 내용이었다. 흥미가 돋아 좀 더 알아보니 사실이었다.)

 

시작점 없이 끝점의 좌표만을 사용하는 것은 일반적인 벡터 사용법이 그렇듯 원점을 시작점으로 두기 때문이며,

Vector3는 int, float, string과 같은 하나의 데이터형이지만 C#이 기본적으로 제공하는 형식은 아니고 구조체이다.

(구조체가 무엇인가 마이크로소프트의 가이드에서 찾아보니 데이터와 관련 기능을 캡슐화할 수 있는 값 형식이라고 소개하고 있었다. 다른 자료를 더 찾아보았더니 대략 "직접 만드는 값 형식" 정도의 의미였다.)

 

힘의 종류에는 Accelation, Force, Impulse, VelocityChange가 있는데 이 중 Impulse는 충격량(※F*Δt)을 가해 속도를 순간적으로 변화시키는 방식으로, 타격이나 폭발 등에 쓰기 적합한 방식이다. 이곳에서는 마찬가지로 연속적인 가속이 필요없는 Impulse 방식을 통해 오브젝트의 이동을 구현하였다.

 

new는 메모리에 인스턴스를 할당하라는 명령어이다.

 

정리하면, rigid.AddForce(new Vector3(h, 0v), ForceMode.Impulse)는 (수직, 0, 수평) 입력을 받아 특정한 방향과 크기로 오브젝트에 충격량을 가하라는 의미가 되겠다.


정리하여, "물리 계산을 위한 Rigidbody가 적용된 오브젝트에게 WASD 입력 각각에 할당된 일정한 충격량을 가해 오브젝트를 움직여라"가 되겠다.

 

728x90
반응형