유니티 다른 오브젝트 위치값 가져오기 - yuniti daleun obeujegteu wichigabs gajyeoogi

컴포넌트를 사용하여 게임 오브젝트 제어

Unity 에디터에서는 인스펙터를 사용하여 컴포넌트 프로퍼티를 변경합니다. 예를 들어 Transform 컴포넌트의 포지션 값을 변경하면 게임 오브젝트의 포지션이 변경됩니다. 마찬가지로 렌더러 머티리얼의 컬러나 리지드바디의 질량을 변경하여 게임 오브젝트의 형상이나 동작에 원하는 효과를 적용할 수 있습니다. 대부분의 경우 스크립트 역시 컴포넌트 프로퍼티를 수정하여 게임 오브젝트를 조작하는 것입니다. 스크립트는 시간이 지남에 따라, 또는 사용자의 입력에 따라 차츰 프로퍼티 값을 변경할 수 있다는 점에서 차이가 있습니다. 적절한 시점에 오브젝트를 변경, 생성, 파괴하여 모든 종류의 게임플레이를 구현할 수 있습니다.

컴포넌트 액세스

가장 간단하고 가장 일반적인 사례는 스크립트로 같은 게임 오브젝트에 연결된 다른 컴포넌트에 액세스해야 하는 경우입니다. 개요 섹션에서 언급했듯이 컴포넌트는 실질적으로 클래스의 인스턴스이므로, 첫 번째 단계는 작업할 컴포넌트 인스턴스에 대한 레퍼런스를 가져오는 것입니다. 이 작업은 GetComponent 함수를 사용하여 수행됩니다. 일반적으로 컴포넌트 오브젝트를 변수에 할당합니다. 이 작업은 C#에서 다음과 같은 구문을 사용하여 수행합니다.

void Start () 
{
    Rigidbody rb = GetComponent<Rigidbody>();
}

컴포넌트 인스턴스에 대한 레퍼런스가 있으면 인스펙터에서와 마찬가지로 프로퍼티 값을 설정할 수 있습니다.

void Start () 
{
    Rigidbody rb = GetComponent<Rigidbody>();
    
    // Change the mass of the object's Rigidbody.
    rb.mass = 10f;
}

인스펙터에서는 사용할 수 없는 추가 기능으로서 컴포넌트 인스턴스에서 함수를 호출할 수 있습니다.

void Start ()
{
    Rigidbody rb = GetComponent<Rigidbody>();
    
    // Add a force to the Rigidbody.
    rb.AddForce(Vector3.up * 10f);
}

같은 오브젝트에 둘 이상의 커스텀 스크립트를 첨부하지 못할 이유는 없습니다. 한 스크립트에서 다른 스크립트에 액세스해야 할 때에는 평소와 마찬가지로 GetComponent를 사용하고 스크립트 클래스의 이름(또는 파일 이름)을 사용하여 원하는 컴포넌트 타입을 지정할 수 있습니다.

게임 오브젝트에 추가되지 않은 컴포넌트를 검색하여 가져오려고 하면 GetComponent는 null을 반환합니다. null 오브젝트의 값을 변경하려는 모든 시도는 런타임 시점에 null 참조 오류를 발생시킵니다.

기타 오브젝트 액세스

스크립트는 때때로 단독으로 작동하기도 하지만 다른 오브젝트를 계속 추적하는 것이 더 일반적입니다. 예를 들어 적군이 플레이어를 추격하려면 플레이어의 위치를 ​​알아야 합니다. Unity 에디터는 특정 상황에 알맞은 다른 오브젝트를 검색하여 가져오는 다양한 방법을 제공합니다.

게임 오브젝트와 변수 연결

관련된 게임 오브젝트를 찾는 가장 간단한 방법은 public GameObject 변수를 스크립트에 추가하는 것입니다.

public class Enemy : MonoBehaviour
{
    public GameObject player;
    
    // Other variables and functions...
}

이 변수는 다음과 같이 인스펙터에서 볼 수 있습니다.

유니티 다른 오브젝트 위치값 가져오기 - yuniti daleun obeujegteu wichigabs gajyeoogi

이제 씬이나 계층 구조 패널에서 오브젝트를 이 변수로 끌어다 놓아 오브젝트를 변수에 할당할 수 있습니다. 이렇게 하면 다른 오브젝트와 마찬가지로 GetComponent 함수와 컴포넌트 액세스 변수를 이 오브젝트에 사용할 수 있게 됩니다. 따라서 다음과 같은 코드를 사용할 수 있습니다.

public class Enemy : MonoBehaviour {
    public GameObject player;
    
    void Start() {
        // Start the enemy ten units behind the player character.
        transform.position = player.transform.position - Vector3.forward * 10f;
    }
}

또한 스크립트에서 컴포넌트 타입의 public 변수를 선언하면 해당 컴포넌트가 연결된 게임 오브젝트를 드래그할 수 있습니다. 이렇게 하면 게임 오브젝트 자체가 아닌 컴포넌트에 직접 액세스합니다.

public Transform playerTransform;

변수와 함께 오브젝트를 연결하면 영구적으로 연결되어 있는 개별 오브젝트를 처리할 때 매우 유용합니다. 배열 변수를 사용하여 같은 타입의 오브젝트를 여러 개 연결할 수 있지만, 런타임 시점이 아닌 Unity 에디터에서 연결을 해야 합니다. 런타임 시점에 오브젝트를 찾는 것이 편리할 수 ​​있으며, 아래에서 설명하는 것처럼 Unity 에디터는 이를 수행하는 두 가지 기본 방법을 제공합니다.

자식 게임 오브젝트 찾기

때로는 게임 씬에 적군, 이동 지점, 장애물 등과 같이 같은 타입의 게임 오브젝트가 여러 개 사용됩니다. 게임 오브젝트를 관리하거나 게임 오브젝트에 반응하는 스크립트는 이러한 게임 오브젝트를 추적해야 할 필요가 있습니다. 예를 들어 길찾기 스크립트는 모든 이동 지점을 사용할 수 있어야 합니다. 변수를 사용하여 이러한 게임 오브젝트를 연결하는 것은 가능하지만, 모든 새로운 이동 지점을 스크립트의 변수에 끌어다 놓은 일은 매우 지루한 작업입니다. 마찬가지로 이동 지점이 삭제될 때마다 누락된 게임 오브젝트에 대한 변수 참조를 제거해야 하는 불편함이 있습니다. 이와 같은 때에는, 한 세트의 게임 오브젝트를 하나의 부모 게임 오브젝트 밑에 자식 오브젝트로 두고 관리하는 것이 더 낫습니다. 모든 게임 오브젝트는 Transform 컴포넌트를 포함하므로 부모의 Transform 컴포넌트를 이용하여 자식 오브젝트를 검색하여 가져올 수 있습니다.

using UnityEngine;

public class WaypointManager : MonoBehaviour {
    public Transform[] waypoints;
    
    void Start() 
    {
        waypoints = new Transform[transform.childCount];
        int i = 0;
        
        foreach (Transform t in transform)
        {
            waypoints[i++] = t;
        }
    }
}

또한 Transform.Find 함수를 사용하여 이름별로 특정 자식 오브젝트를 찾을 수 있습니다. transform.Find("Gun");

게임플레이 중에 자식 오브젝트가 추가되거나 제거되는 부모 오브젝트가 있을 때 유용합니다. 집어 들었다가 내려 놓을 수 있는 무기를 예로 들 수 있습니다.

이름이나 태그로 게임 오브젝트 찾기

게임 오브젝트를 식별할 정보가 어느 정도 있다면 씬 계층 구조에서 어디에서나 항상 게임 오브젝트를 찾을 수 있습니다. GameObject.Find 함수를 사용하여 개별 오브젝트를 이름으로 검색하여 가져올 수 있습니다.

GameObject player;

void Start() 
{
    player = GameObject.Find("MainHeroCharacter");
}

오브젝트 또는 오브젝트 컬렉션은 GameObject.FindWithTag 및 GameObject.FindGameObjectsWithTag 함수를 사용하여 태그로도 찾을 수 있습니다.

GameObject player;
GameObject[] enemies;

void Start() 
{
    player = GameObject.FindWithTag("Player");
    enemies = GameObject.FindGameObjectsWithTag("Enemy");
}

키보드 입력으로 오브젝트 움직이기

오브젝트를 움직이게 만들 수 있는 방법은 여러 방법이 있다. 

이동하는 방법뿐만 아니라 여러 가지로 Vector를 많이 사용하게 되는데, 

학생 때 꼴도 보기 싫었던 Vector를 유니티 공부하면서 원없이 본 것 같다. 

우선 코드를 작성해서 연습해보기 전에 유니티를 켜고,

3D 프로젝트를 하나 만들어서 아래 그림과 같이 Plane과 Capsule을 하나 생성해 보자.

그리고 지난번 블로그에 나온 것처럼 Capsule을 알아보기 쉽게 파란색을 입혔다.

나는 아래와 같이 Plane의 크기로 Capsule의 position을 변경했다.

Plane Scale (X : 5, Y : 1, Z : 5)

Capsule Position ( X : 0, Y : 1, Z : 0)

유니티 다른 오브젝트 위치값 가져오기 - yuniti daleun obeujegteu wichigabs gajyeoogi
연습을 시작할 준비가 되었다.

■ 지정된 좌표로 이동시키기(순간이동)

먼저 Tranform을 이용하여 이동시켜보자.

오브젝트를 어떤 키를 누르면 미리 지정된 좌표로 이동하도록 해보자.

아래 그림처럼 처음 Capsule 이 있던 지점으로 사각형 꼭지점으로 이동하는 스크립트를 작성해보자.

유니티 다른 오브젝트 위치값 가져오기 - yuniti daleun obeujegteu wichigabs gajyeoogi
1번부터 4번 키를 누르면 동그라미 원 안으로 이동해보자.

C# Script를 열고, MovePosition이라고 스크립트 이름을 수정하고, Capsule에 드래그하여 넣자.

아래와 같이 코드를 입력해보자.

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class MovePosition : MonoBehaviour

{

void Update()

{

if (Input.GetKey(KeyCode.Alpha1) == true)

{

gameObject.transform.position = new Vector3(-212);

}

if (Input.GetKey(KeyCode.Alpha2) == true)

{

gameObject.transform.position = new Vector3(212);

}

if (Input.GetKey(KeyCode.Alpha3) == true)

{

gameObject.transform.position = new Vector3(21-2);

}

if (Input.GetKey(KeyCode.Alpha4) == true)

{

gameObject.transform.position = new Vector3(-21-2);

}

}

}

cs

코드를 살펴보면 숫자키 1~4번을 누를 때마다 사각형 꼭지점으로 이동하도록 좌표값을 넣었다.

그럼 아래와 같이 오브젝트가 이동하는 것을 볼 수 있다.

Alpha0~9를 넣으면 숫자 0~9까지의 키보드 자판을 인식하고, 

영문도 알파벳을 넣으면 알파벳을 인식하도록 되어 있다.

예를 들어 위의 소스코드에서 Alpha1~Alpha4 대신 A, B, C, D를 넣고

A, B, C, D를 누르면 아래와 동일하게 이동하는 것을 볼 수 있다.

유니티 다른 오브젝트 위치값 가져오기 - yuniti daleun obeujegteu wichigabs gajyeoogi
사각형 꼭지점으로 순간이동하듯 움직이는 것을 볼 수 있다.

이렇게 지정된 좌표가 아닌 화살표 방향키를 눌렀을 때 이동하고 싶을 때는 어떻게 할까?

■ Translate를 이용한 이동

화살표를 눌렀을 때 일정한 간격으로 이동하는 방법은 어떻게 할까?

Transform에서는 Translate라는 function을 제공한다.

이것은 지정된 거리로 오브젝트를 이동시켜 주는 역할을 한다. 

그럼 바로 코드를 아래와 같이 수정해 보자.

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class MovePosition : MonoBehaviour

{

void Update()

{

if (Input.GetKey(KeyCode.LeftArrow) == true)

{

gameObject.transform.Translate(new Vector3(-100));

}

if (Input.GetKey(KeyCode.RightArrow) == true)

{

gameObject.transform.Translate(new Vector3(100));

}

if (Input.GetKey(KeyCode.UpArrow) == true)

{

gameObject.transform.Translate(new Vector3(001));

}

if (Input.GetKey(KeyCode.DownArrow) == true)

{

gameObject.transform.Translate(new Vector3(00-1));

}

}

}

cs

잘 이동하는지 확인을 해보자.

유니티 다른 오브젝트 위치값 가져오기 - yuniti daleun obeujegteu wichigabs gajyeoogi
잘 움직이기는 하지만 뭔가 부자연스럽고 엄청 빠르다.

움직이기는 잘하는 것 같지만 뭔가 부자연스럽다. 

유니티에는 Update라는 특별한 메서드가 있다. 이 메서드는 매 프레임마다 실행되는 함수이다. 

보통 컴퓨터는 1초당 프레임이 60번 실행이 된다. Update 메서드가 1/60초에 한 번씩 실행된다는 이야기다.

자세히 보면 화살표를 누르고 있는 동안 1 만큼씩 순간이동을 하는데 이게 1/60초라는 워낙 짧은 시간에 다음 좌표로 이동하기 때문에 연속된 것처럼 보이는 것이다.

그럼 위의 코드에서 Vector3 안의 숫자를 1 → 3으로 변경하면 어떻게 보일까?

유니티 다른 오브젝트 위치값 가져오기 - yuniti daleun obeujegteu wichigabs gajyeoogi
3의 거리만큼 이동하기 때문에 좀더 끊겨서 이동하는 것이 잘 보인다.

빨리 움직이는 것처럼 보이기는 하지만 3의 거리만큼 순간 이동하면서 움직이고 있는 것이다.

일반 사람들이 게임을 플레이할 때 이렇게 움직이는 것을 원한 것이 아니었을 것이다.

그럼 연속된 움직임을 가지기 위해서는 어떻게 해야 할까?

Rigidbody라는 물리적 속성을 이용하여 움직이면 자연스러운 움직임을 만들어 낼 수 있다.

오늘은 여기까지 하고 다음 글에서 이어서 설명하도록 하겠다.