게임 씬에 추가할 수 있는 다양한 물체들이 있다. 이전 포스트에서 예제로 만들어본 큐브도 하나의 오브젝트다. 씬에 배치되는 하나의 객체를 세는 단위라고 생각해도 된다. 이러한 오브젝트들은 사용자가 원하는 상황에 맞춰 입력을 받거나 자체적인 알고리즘으로 회전, 이동, 크기에 변화를 줄 수 있다.

물체가 생성되면 우리는 씬에서 물체의 위치를 조정하고 회전을 하거나 크기를 조정할 수 있다.
하지만 게임으로 들어가면 실제로 이러한 것들을 조절할 방법이 없다.
그래서 사용자의 입력을 받아 물체를 이동하도록 만들어 볼 것이다.
오브젝트에 적용가능한 컴포넌트로 게임이 시작될 때와 매 프레임마다 업데이트되는 것에 대한 메소드가 포함되어 있어 이를 이용해 오브젝트를 움직여 볼 것이다.
Scene을 추가한 것 처럼 패키지 윈도우에 우클릭을 하면 Create -> C# Script가 있다. 이를 생성하고 스크립트를 열면 다음과 같다

void Start()
// 게임이 시작되고 오브젝트가 생성될 때 실행되는 메소드이다.
void Update()
// 게임이 실행되고 매 프레임마다 호출되는 메소드로 컴포넌트의 상태변화를 만들어 낼 수 있다.
이제 여기서 방향키에 맞춰 큐브가 위치를 움직일 수 있도록 만들어 보겠다.
public class Cube_test : MonoBehaviour
{
[SerializeField] private float mvSpeed = 10.0f;
// 오브젝트의 이동속도의 정의
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.UpArrow)) {
transform.Translate(Vector3.forward*mvSpeed*Time.deltaTime);
}
if (Input.GetKey(KeyCode.DownArrow)) {
transform.Translate(Vector3.back * mvSpeed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.LeftArrow)) {
transform.Translate(Vector3.left * mvSpeed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.RightArrow)) {
transform.Translate(Vector3.right * mvSpeed * Time.deltaTime);
}
}
}
SerializeField
private를 선언해서 mvSpeed 변수는 외부에서 접근할 수 없지만
[SerializeField] 선언으로 인스펙터 창에서 이 변수로 접근하여 값을 변경할 수 있다.
그리고 엔진에서 오브젝트를 움직이다보면 정수 값으로 움직이면 부드럽지 못하거나 세세한 이동이 안될 것이다.
그래서 대부분 float을 사용해서 정밀한 계산을 하여 위치와 값을 나타낸다.
transform.Translate(Vector3.forward mvSpeed Time.deltaTime);
객체가 가지고 있는 위치, 회전, 크기 값을 저장하는 컴포넌트로 모든 오브젝트가 가지고 있다. 이를 제거하거나 트랜스폼이 없는 객체 생성은 불가능하다.
Translate 메소드는 객체를 한 방향으로 움직이는 것으로 mvSpeed의 값만큼 움직이며 Time.deltaTime으로 매 프레임마다의 시간을 받아 값을 조절하여 적용시켰다. Vector3.forwards는 x 방향으로 1만큼의 크기를 가진 단위벡터이다. 이를 적용하면 x 방향으로 매 프레임 마다 10씩 움직일 수있다.
다음과 같이 작성해서 스크립트 파일을 큐브에 적용시키면 잘 움직이는 것을 확인할 수 있다.
적용시킬때는 스크립트를 드래그해서 큐브의 인스펙터창에 가져다 놓을 수 있다.
인스펙터창 addComponent 부근에 갖다 놓으면 자동으로 적용된다.

패키지 창에서 오른쪽 마우스키를 누르고 Create를 누르면 다음과 같이 뜨게 된다.
여기서 C# script를 선택한다.
이러한 스크립트는 패키지에 저장해놨다가 필요한 오브젝트에 사용할 때 컴포넌트 추가를 통해 적용할 수 있다.

public class Object_move_test: MonoBehaviour {
void Start(){
// 게임이 시작되면 호출되는 메소드로 오브젝트의 위치값을 초기화하거나
다른 초기화가 필요할때 사용한다.
}
void Update(){
// 매 프레임마다 호출되는 메소드로 오브젝터의 상태를 변화시켰을때
매 프레임마다 변화를 확인할 수 있다.
}
}
public class Object_move_test : MonoBehaviour{
[SerializeField] private float O_Speed = 10.0f;
//SerializeField는 데이터나 오브젝트 상태를 Unity 에디터에 저장하고 나중에
다시 사용할 수 있게 재구성할 수 있는 형태로 포맷하는 것으로 O_Speed 변수에 대해서
inspect 창에서 다른 값을 줄 수 있다.
void Start(){
}
void Update(){
if(Input.GetKey(KeyCode.W)){
transform.Transfer(Vector3.up*O_Speed*Time.deltaTime);
}
if(Input.GetKey(KeyCode.S)){
...
}if(Input.GetKey(KeyCode.A)){
...
}if(Input.GetKey(KeyCode.D)){
...
}
}
}
엔진하면 당연하게도 물체들이 서로 부딛쳐서 생기는 물리작용을 나타낼 수 있다. 물리작용을 이용
해서 현실에서 볼 수 있는 자연스러운 현상을 나타낼 수 있다.
https://docs.unity3d.com/kr/2019.4/Manual/ExecutionOrder.html
위 큐브에서 인스펙터 창에서 AddComponent를 누르면 작은 창이 뜬다.

여기서 rigid라고 입력하면 rigidbody와 rigidbody 2d가 나온다. 2D는 말그대로 2D 환경에서 사용하는 것으로 3차원 환경인 현재에서는 사용할 수 없다.
적용하면 다음과 같은 컴포넌트가 인스펙터에 추가된다.

여기서 Use Gravity가 체크된 채로 적용되있는데 이 항목은 오브젝트에 중력을 적용시킬지에 대한 여부를 조절할 수 있다. 지금은 잠깐 해제하도록 한다.
이제 다른 오브젝트를 만들어서 충돌했을 때 잘 적용되었는지 확인할 것이다.

다음과 같이 만들어서 실행해서 확인하면 물체가 충돌하면서 튕겨져 나오는 것을 확인 할 수 있다.
https://docs.unity3d.com/kr/2019.4/Manual/ExecutionOrder.html
위 문서는 unity 이벤트 함수의 실행 순서이다.
여길 보면 현재 unity에서 어떤 순서로 물리와 이벤트 함수 처리, 렌더링을 처리하는지 알 수 있다.
collision : collider를 포함한 물체가 충돌하는 것을 감지하는 것으로 여기에는 두 충돌체의 접촉점 및 상대 속도와 같은 충돌에 대한 정보가 포함된다.
collider : 오브젝트에 대한 충돌범위, 혹은 경계를 정의해주는 것으로 실체를 가지지 않지만 대부분 물체와 동일한 혹은 상황에 맞는 영역을 가진다. 이러한 영역에 두 물체가 서로 충돌하는 것을 감지하며 충돌할 시 onCollision 과 onTrigger를 호출한다.
rigid body : 오브젝트가 물리적인 힘을 받았을 때 제어를 통해 사실적인 움직임을 보여줄 수 있는 기능이다.
실제로 유니티에서 오브젝트가 움직이는 함수들의 작동은 다음에서 확인할 수 있다.
https://docs.unity3d.com/kr/2022.3/Manual/ExecutionOrder.html

위 도면을 보면 Trigger를 먼저 확인하고 Collision을 확인하는 것을 볼 수 있다.
Trigger는 collider 범위 내에 있는지 없는지를 확인한다.
그리고 Collision으로 범위를 확인해서 물리연산을 통해 밀어낸다.
충돌 판정을 하기 위해서는 여러가지 알고리즘이 필요하지만 unity에서는 기본적으로 지원을 한다. 하지만 세부적으로 들어가야하는 상황이 발생할 수 있으므로 알아두는 것이 좋다.
https://blog.naver.com/PostView.nhn?blogId=jerrypoiu&logNo=221172549241
모든 오브젝트들은 각자 고유의 좌표들을 가지고 있다. 우리가 씬에서 오브젝트들을 잡고 움직이면 오브젝트의 좌표가 움직이게 된다.
transform.Translate(Vector3.forward*Time.deltaTime);
// OR
transform.position = transform.position+Vector3*forward*Time.deltaTime;
위 코드는 스크립트 상으로 오브젝트를 일정한 방향으로 움직일려고 할 때 사용하는 코드이다.
두 코드 모두 현재 위치에서 벡터방향에 맞춰 일정거리를 이동한다. 하지만 실제로 엔진의 내부에서는 좀 더 복잡한 연산이 이루어진다.
우리는 프로그램을 통해 3차원 공간이라고 인식하고 있지만 실제로는 모니터 화면의 2차원 공간을 통해 인식한다. 여기서 잠깐 멈춰서 생각해보면 컴퓨터는 3차원에서 2차원으로 보고있지만
화면을 통해 연산을 한다면 우리는 2차원에서 3차원을 만들어야하는 것이다.
그렇다면 이러한 연산을 위해 단순히 좌표값만 가지고는 위치와 크기, 회전에 대한 연산을 하기에는 벅차다. 그래서 이러한 연산을 하기 위해 행렬Matrix 이라는 것을 사용한다. 행렬은 선형대수학이라는 학문에서 제일 기초적으로 사용하는 요소로 이 행렬에 대한 연산을 통해 물체의 위치, 회전, 크기를 조금 더 세세하게 나타낼 수 있다.
위치의 경우
회전의 경우
크기의 경우
위 식들은 OpenGL의 경우를 가져왔다. DirectX는 연산식이 다르다고 하니 만약 사용한다면 미리 알아두는것이 좋을 듯 하다.
위와 같은 연산을 통해 컴퓨터 내부에서 오브젝트의 극점들의 위치를 계산해서 오브젝트의 상태를 나타낸다. 그리고 이렇게 나타낸 극점Vertex들의 정보와 어떤 극점들을 연결해줄 것인지에 대한 정보가 담긴 Index들의 정보를 버퍼에 넣어서 연결해주고 컴퓨터가 GPU에게 넘겨주면 GPU는 이러한 정보를 토대로 화면 상의 픽셀들을 어떻게 나타낼 것인지 모니터로 보내는 과정을 거친다.
이러한 일렬의 과정을 마치 파이프라인 같다고 하여 그래픽 파이프라인이라고 한다.
좀 더 세세하게 들어가면 많은 설명이 필요하기에 따로 확인해볼 필요가 있다.
https://youtu.be/C8YtdC8mxTU?si=ASS_87ESNWszOTtY
https://www3.ntu.edu.sg/home/ehchua/programming/opengl/CG_BasicsTheory.html