Posted in: Unity, 메서드 & 팁 정리

복합 데이터 형식 클래스 -1- (유니티 게임 프로그래밍)

복합 데이터 형식은 기본 데이터 형식이 다양하게 이루어져 있는 데이터 형식입니다. 기본 데이터 형식이 데이터 하나를 다룬다고 생각하면 복합 데이터 형식은 데이터 여러 개를 다룹니다.

해당 강좌에서 자주 언급되었던 클래스인데, 직접적으로 설명하진 않았죠. 개발자에 따라 다르게 생각하겠지만 저는 유니티에서 가장 많이 사용되는 복합 데이터 형식은 클래스라고 생각합니다. 오늘은 글 2개에 걸쳐 클래스가 왜 필요한지 이해하기 위해 실습을 같이 해보고 클래스를 만드는 방법을 배워봅시다.

예시로 게임 세상의 NPC를 만들어보자


유니티 사용 능력을 키우기 위해 오늘도 조금 억지 상황을 만들어보겠습니다. 복잡해 보이는 코드가 있을 수 있는데 모르는 코드는 넘어가도 됩니다. 제 강좌를 그대로 따라오신 분은 지금 C# 문법을 모르는 상태이기 때문에 내용이 어려울 겁니다. 차근차근 알아갈 테니 걱정하지 않으셔도 됩니다.

아래 그림은 클래스 다이어그램(class diagram)입니다. 클래스에 들어있는 모든 메서드들이 목록 형태로 나열되어 있어서 그 클래스에서 무슨 일을 하는지 한눈에 파악할 수 있습니다.

저희는 NPC 3명을 만들어야 합니다. 마을 이장(Headman), 대장장이(Blacksmith), 재봉사(Seamster)를 만들어야 하는데 NPC는 플레이어와 대화를 통해 상호작용을 합니다. 각 NPC마다 대화 대사는 달라야 합니다. 대화 기능은 메서드로 구현하겠습니다. 마을 이장은 말하기(Talk)와 퀘스트(Quest) 메서드만 있습니다. 대장장이와 재봉사는 말하기(Talk), 퀘스트(Quest), 판매(Sell), 구매(Buy)와 수리(Repair) 메서드가 있습니다.

버튼 UI 게임 오브젝트 5개를 만들어서 대화하길 원하는 NPC 오브젝트를 선택하고 버튼을 선택하면 대사가 화면에 나오도록 만들어야 합니다. 버튼에 연결되는 클래스는 하나입니다. 버튼을 클릭하면 onClick 이벤트 함수에 의해 NPC 오브젝트의 메서드를 호출하는 상황을 만들어봅시다.

이제 실습을 위해 새로운 Scene을 만듭시다. 해야 하는 과정이 좀 많아요.

실습 환경 구축


Class Scene을 만들었습니다. Class Scene을 사용하여 실습 환경을 디자인하겠습니다.

Button 오브젝트 5개를 생성하여 위 사진처럼 보기 좋게 배치합니다. 각 버튼의 Rect Transform Anchor Presets을 이용하면 쉽게 배치할 수 있습니다. alt를 누른 상태로 Anchor Presets을 선택하면 Button의 위치가 바로 바뀝니다. 밑으로 위치시키고 Scene에서 X축만 조금 수정하면 됩니다.

각 버튼의 Text를 말하기, 퀘스트, 판매, 구매 그리고 수리로 바꿉니다. 해당 버튼을 클릭하면 onClick 이벤트 함수가 호출되면서 미리 선택돼있는 NPC 오브젝트의 메서드를 호출하는 게 목표입니다.

NPC 오브젝트를 만들겠습니다. 사람 모양은 아니지만 일단 NPC라고 칩시다. Cube, Sphere, Capsule 오브젝트를 만듭니다.

Cube는 Headman(마을 이장), Sphere는 Blacksmith(대장장이), Capsule는 Seamster(재봉사)로 오브젝트 이름을 바꿉시다.

우리(개발자)는 누가 마을 이장인지 대충 알지만 게임 안에서 플레이어는 누가 마을 이장인지 모릅니다. 그러니 각 오브젝트마다 어떤 역할을 하는지 보여줘야 합니다. 오브젝트 위에 텍스트를 띄워서 어떤 역할을 하는지 보이게 만들어 봅시다.

Headman 오브젝트 위에 마우스 포인트를 올린 상태에서 UI > Text를 생성합니다.

Canvas와 Text 오브젝트가 Headman 오브젝트의 자식이 되었습니다. Text를 Headman 오브젝트 위에 띄워서 “마을 이장”을 보여주면 플레이어는 누가 마을 이장인지 알 수 있겠죠.

Headman 오브젝트의 자식인 Canvas 오브젝트의 컴포넌트 프로퍼티를 수정하겠습니다. Rect Transform 컴포넌트의 위치 값을 (0, 1, 0)으로 수정합니다. Canvas 컴포넌트의 Render Mode를 World Space로 수정합니다. Canvas Scaler 컴포넌트의 Dynamic Pixels Per Unit을 1000으로 수정합니다. 그 외에 다른 프로퍼티는 수정하지 않습니다.

Headman 오브젝트 자식인 Canvas 오브젝트의 자식으로 있는 Text 오브젝트의 컴포넌트 프로퍼티도 수정하겠습니다. Rect Transform 컴포넌트의 위치 값은 (0, 0, 0)입니다. Text 컴포넌트의 Text를 마을 이장으로 수정합니다. Alignment를 중간 값으로 수정합니다. Horizontal Overflow와 Vertical Overflow를 Overflow로 바꿉니다.

위 과정을 거치면 게임 오브젝트 위에 텍스트가 보입니다. 텍스트로 해당 게임 오브젝트의 역할을 나타낼 수 있습니다. 대장장이와 재봉사도 비슷한 방법으로 텍스트를 띄워줍시다.

Canvas 오브젝트의 자식으로 Text 오브젝트를 생성합니다. Text 오브젝트의 이름은 MainText로 수정합니다. 우리는 MainText 오브젝트로 NPC 오브젝트의 대사를 화면에 띄울 것입니다.

MainText 오브젝트의 위치를 잘 보이는 곳으로 위치시키고 잘 보이도록 Font Size 올리기, Alignment 중간, Overflow로 수정합시다.

완성된 실습환경 모습입니다. 목표는 원하는 NPC 오브젝트를 클릭하고 버튼을 선택하면 버튼 텍스트에 해당하는 메서드를 호출하여 플레이어와 대화할 수 있는 것입니다.

스크립트 작성


스크립트를 작성하기 전에 클래스 다이어그램을 다시 보겠습니다. 각 NPC마다 클래스를 작성하여 NPC 오브젝트에 연결시켜야 합니다. 가장 간단한 Headman 클래스부터 만들어 봅시다.

Headman 스크립트

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

public class Headman: MonoBehaviour
{
    public Text mainText;

    void Start()
    {
        mainText = GameObject.Find("MainText").GetComponent<Text>();
    }

    public void Talk()
    {
        mainText.text = "안녕하시오. 나는 앤글 블로그 마을 이장이오.";
    }

    public void Quest()
    {
        mainText.text = "내가 그대에게 부탁할게 있소.";
    }
}

Headman 스크립트입니다. 하나하나 설명해드리겠습니다.

using UnityEngine.UI;

using 키워드를 사용해 UnityEngine.UI 네임스페이스가 묶고 있는 클래스를 Headman 스크립트에서 사용하겠다고 알립니다. 네임스페이스는 클래스 및 메서드를 묶고 있는 범위입니다. UnityEngine.UI 안에 유니티 UI 관련된 클래스와 메서드가 생성돼있습니다.

엄밀히 말하면 굳이 using 키워드로 네임스페이스에 접근 안 해도 UI 관련 클래스를 사용할 수 있습니다. 하지만 UnityEngine.UI에 생성된 클래스와 메서드를 사용할 때마다 UnityEngine.UI를 앞에 명시해야 합니다. 코드가 길어지니 매우 귀찮으니 using 키워드로 네임스페이스에 접근하는 게 좋습니다.

public Text mainText;

void Start()
{
    mainText = GameObject.Find("MainText").GetComponent<Text>();
}

public void Talk()
{
    mainText.text = "안녕하세요. 나는 앤글 블로그 마을 이장이오.";
}

public void Quest()
{
    mainText.text = "내가 그대에게 부탁할게 있소.";
}

Headman은 말하기와 퀘스트 기능만 구현되면 됩니다. 해당 메서드들이 호출되면 MainText 오브젝트의 text 프로퍼티를 바꿔서 게임 화면에 텍스트를 띄워야 합니다. 그러기 위해선 MainText 오브젝트의 Text 컴포넌트를 레퍼런스로 연결된 변수가 필요합니다. Text 형식의 변수 mainText를 선언하고 Start 메서드에서 MainText 게임 오브젝트를 찾아 MainText 게임 오브젝트에 연결돼있는 Text 컴포넌트를 대입합니다. 그리고 Talk 메서드와 Quest 메서드에서 mainText의 text 프로퍼티를 수정하면 게임 화면에 텍스트를 바꿀 수 있겠죠.

Blacksmith 스크립트

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

public class Blacksmith : MonoBehaviour
{
    public Text mainText;

    void Start()
    {
        mainText = GameObject.Find("MainText").GetComponent<Text>();
    }

    public void Talk()
    {
        mainText.text = "안녕하시오. 나는 앤글 블로그 대장장이오.";
    }

    public void Quest()
    {
        mainText.text = "내가 그대에게 부탁할게 있소.";
    }

    public void Sell()
    {
        mainText.text = "팔고 싶은 것이 있소?";
    }

    public void Buy()
    {
        mainText.text = "구매하고 싶은 것이 있소?";
    }

    public void Repair()
    {
        mainText.text = "모두 고쳤소.";
    }
}

Blacksmith 스크립트입니다. Headman 스크립트와 비슷합니다. 굳이 설명할 필요 없겠지요? 클래스 다이어그램에서 봤던 것처럼 Sell 메서드, Buy 메서드, Repair 메서드가 추가되었습니다.

Seamster 스크립트

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

public class Seamster : MonoBehaviour
{
    public Text mainText;

    void Start()
    {
        mainText = GameObject.Find("MainText").GetComponent<Text>();
    }

    public void Talk()
    {
        mainText.text = "안녕하시오. 나는 앤글 블로그 재봉사오.";
    }

    public void Quest()
    {
        mainText.text = "내가 그대에게 부탁할게 있소.";
    }

    public void Sell()
    {
        mainText.text = "팔고 싶은 것이 있소?";
    }

    public void Buy()
    {
        mainText.text = "구매하고 싶은 것이 있소?";
    }

    public void Repair()
    {
        mainText.text = "모두 고쳤소.";
    }
}

Seamster 스크립트입니다. 역시 앞 스크립트와 비슷합니다. 이쯤 되면 굳이 똑같은 내용을 작성해야 되나 싶습니다.

NPC 스크립트를 모두 생성했다면 각 NPC 오브젝트에 스크립트를 연결합시다.

그리고 Button 오브젝트에 연결되는 스크립트도 작성해야 합니다. 버튼은 대화하고 싶은 NPC 오브젝트를 클릭했을 때 NPC 오브젝트를 대화 상대로 인식해야 하고 그 이후에 버튼을 클릭하면 버튼 텍스트에 해당하는 NPC의 메서드를 호출해야 합니다.

버튼을 클릭하면 onClick에 등록돼있는 메서드가 호출되는 건 다들 아실 겁니다. 미리 Inspector 창에서 메서드를 등록시켜 놓으면 안 됩니다. 왜냐하면 플레이어가 어떤 NPC와 대화할지 모르기 때문에 미리 등록시켜 놓으면 잘못된 대사가 나올지도 모릅니다. 유니티 Inspector 창에서 onClick에 메서드를 등록하는 것보다 스크립트에서 앞서 클릭한 NPC 오브젝트의 메서드를 onClick 이벤트 함수에 메서드를 등록, 호출과 제거를 처리해야 합니다.

이 과정은 복잡할 수 있는데 모든 걸 이해하려고 할 필요는 없습니다. C#과 유니티를 계속 배우다 보면 자연스럽게 이해하고 사용하게 될 거예요.

버튼이 사용하는 GetMethod 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class GetMethod : MonoBehaviour, IPointerDownHandler
{
    public Button button;
    public Text text;
    public GameObject target;

    void Start()
    {
        button = GetComponent<Button>();
        text = GetComponentInChildren<Text>();
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        if(eventData.button == PointerEventData.InputButton.Left &&
            target != null)
        {
            if(text.text == "말하기")
            {
                print("말하기 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Talk"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }

            if(text.text == "퀘스트")
            {
                print("퀘스트 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Quest"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }

            if(text.text == "판매")
            {
                if (target.gameObject.name == "Headman")
                    return;
                print("판매 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Sell"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }

            if (text.text == "구매")
            {
                if (target.gameObject.name == "Headman")
                    return;
                print("구매 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Buy"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }

            if (text.text == "수리")
            {
                if (target.gameObject.name == "Headman")
                    return;
                print("수리 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Repair"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }
        }
    }
}

버튼 오브젝트가 NPC 오브젝트가 가지고 있는 메서드를 가져온다는 의미에서 GetMethod라고 이름을 정했습니다. 하나하나 설명해드리겠습니다.

using UnityEngine.EventSystems;

public class GetMethod : MonoBehaviour, IPointerDownHandler

using 키워드로 위 네임스페이스에 접근하면 IPointerDownHandler 인터페이스를 사용할 수 있습니다. IPointerDownHandler 인터페이스는 UI 오브젝트를 클릭했을 때 호출되는 이벤트 함수 OnPointerDown을 반드시 클래스에서 사용하도록 강제합니다. 인터페이스는 상속받는 클래스에 선언된 요소를 반드시 사용하도록 강제하는 문법입니다.

public Button button;
public Text text;
public GameObject target;

void Start()
{
    button = GetComponent<Button>();
    text = GetComponentInChildren<Text>();
}

Button 형식으로 button 변수를 선언하고 Start 메서드에서 Button 오브젝트의 Button 컴포넌트를 대입합니다. 중요한 것은 GetComponent 메서드는 게임 오브젝트에 연결되어 있고 < > 안에 명시된 컴포넌트를 가져오는데 스크립트가 연결되어 있는 게임 오브젝트의 컴포넌트를 가져옵니다. 지금 Scene에서 버튼 오브젝트는 5개가 있습니다. 버튼 오브젝트 5개 모두 스크립트가 연결된다면 스크립트 또한 각각의 버튼 오브젝트에 생성되고 Start 메서드가 호출될 때 버튼 오브젝트 각자가 자신의 Button 컴포넌트를 button 변수에 대입합니다.

위 코드를 그림으로 표현한다면 이렇게 표현할 수 있겠네요. 버튼 오브젝트가 5개가 있으니 모두 각자 Button 형식의 변수를 만드는 겁니다. 컴포넌트를 변수로 가져와서 이벤트 함수에서 컴포넌트의 기능을 호출할 수 있는 겁니다. 이 과정을 잘 이해해야 합니다. 계속 유니티를 사용하다 보면 자연스럽게 이해하게 될 거예요.

GameObject 형식으로 선언한 target 변수는 선택한 NPC 오브젝트를 받아오기 위한 변수입니다. target 변수로 선택한 NPC 오브젝트 안에 생성돼있는 메서드를 사용할 수 있습니다.

public void OnPointerDown(PointerEventData eventData)
{
    if(eventData.button == PointerEventData.InputButton.Left &&
        target != null)
    {
        if(text.text == "말하기")
        {
            print("말하기 버튼 클릭");
            button.onClick.AddListener(delegate { target.SendMessage("Talk"); });
            button.onClick.Invoke();
            button.onClick.RemoveAllListeners();
        }

        if(text.text == "퀘스트")
        {
            print("퀘스트 버튼 클릭");
            button.onClick.AddListener(delegate { target.SendMessage("Quest"); });
            button.onClick.Invoke();
            button.onClick.RemoveAllListeners();
        }

        if(text.text == "판매")
        {
            if (target.gameObject.name == "Headman")
                return;
            print("판매 버튼 클릭");
            button.onClick.AddListener(delegate { target.SendMessage("Sell"); });
            button.onClick.Invoke();
            button.onClick.RemoveAllListeners();
        }

        if (text.text == "구매")
        {
            if (target.gameObject.name == "Headman")
                return;
            print("구매 버튼 클릭");
            button.onClick.AddListener(delegate { target.SendMessage("Buy"); });
            button.onClick.Invoke();
            button.onClick.RemoveAllListeners();
        }

        if (text.text == "수리")
        {
            if (target.gameObject.name == "Headman")
                return;
            print("수리 버튼 클릭");
            button.onClick.AddListener(delegate { target.SendMessage("Repair"); });
            button.onClick.Invoke();
            button.onClick.RemoveAllListeners();
        }
    }
}

OnPointerDown 메서드는 UI 오브젝트를 클릭했을 때 호출되는 이벤트 함수입니다. 이 메서드에서 버튼을 클릭했을 때 onClick 이벤트 함수에 호출할 메서드를 등록하고 제거해야 합니다. 버튼이 5개가 있는데 각 버튼을 구별하는 방법은 버튼 오브젝트 자식으로 있는 Text 오브젝트의 text 프로퍼티입니다. if 문으로 구별을 하는데 if 문의 정확한 기능은 다음에 알아봅시다.

button.onClick.AddListener 메서드로 호출할 메서드를 등록한 다음 button.onClick.Invoke 메서드로 등록한 메서드를 호출하고 button.onClick.RemoveAllListeners 메서드로 등록한 메서드를 제거합니다. 등록한 메서드를 제거하는 이유는 onClick 이벤트 함수에 메서드가 여러 개 등록돼도 등록한 메서드를 모두 호출합니다. 그렇게 되면 나중에 호출되는 메서드가 엄청 많아질 수 있으므로 매번 제거해야 합니다.

이제 모든 버튼에 GetMethod 스크립트를 연결시킵니다.

게임매니저 오브젝트 생성


하지만 아직 끝나지 않았습니다. GetMethod 스크립트에서 NPC 오브젝트를 마우스 클릭에 의해 target 변수에 대입해야 하는데 대입하는 기능이 없죠. 결론만 놓고 보면 GetMethod 스크립트에서 사용해야 하므로 GetMethod 스크립트 안에 NPC 오브젝트를 대입하는 기능을 만드는 게 좋겠지만 조금 다르게 해봅시다.

빈 게임 오브젝트를 생성하고 이름을 GameManager로 바꿉니다. GameManager는 대부분 개발자가 사용하는 방법으로 게임 관리에 필요한 중요한 기능이나 게임 시스템 상으로 대부분 게임 오브젝트가 알아야 하는 게임 오브젝트가 있다면 GameManager에서 관리합니다.

GameManager 오브젝트가 사용할 스크립트를 생성하고 이름을 GameManager로 바꾸면 신기하게도 아이콘이 톱니바퀴로 바뀝니다. 톱니바퀴는 게임 안에서 설정을 의미하죠. 그만큼 개발자들 사이에서 GameManager을 많이 사용한답니다. 저렇게 만든다고 스크립트 구조가 특별히 달라지고 하는 건 없습니다. 구별하기 편해라고 톱니바퀴 아이콘으로 바뀌는 것 같아요.

GameManager 스크립트 작성


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

public class GameManager : MonoBehaviour
{
    public GameObject target;
}

GameManager 스크립트는 위 코드가 전부입니다. 어떤 NPC를 선택한 것인지 알기만 하면 되므로 NPC 오브젝트를 참조할 변수만 선언합니다. 스크립트 작성이 끝나면 GameManager 오브젝트에 연결합시다.

다른 스크립트에서 GameManager를 사용하자


GameManager 스크립트를 만들었으니 모든 스크립트에서 GameManager를 사용하기 위해 스크립트를 수정하겠습니다.

Headman

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

public class Headman: MonoBehaviour
{
    public Text mainText;
#-------------       [수정]       -------------#
    public GameManager gameManager;
#----------------------------------------------#

    void Start()
    {
        mainText = GameObject.Find("MainText").GetComponent<Text>();
#-------------       [수정]       -------------#
        gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
#----------------------------------------------#
    }

    public void Talk()
    {
        mainText.text = "안녕하시오. 나는 앤글 블로그 마을 이장이오.";
    }

    public void Quest()
    {
        mainText.text = "내가 그대에게 부탁할게 있소.";
    }

#-------------       [수정]       -------------#
    private void OnMouseDown()
    {
        gameManager.target = this.gameObject;
    }
#----------------------------------------------#
}

Headman 스크립트의 수정된 부분을 표시했습니다. gameManager 변수를 선언한 이유는 GameManager 스크립트 안에 선언된 target 변수에 접근하기 위해서입니다.

OnMouseDown 메서드는 이벤트 함수인데 스크립트가 연결된 오브젝트를 클릭하면 호출되는 이벤트 함수입니다. Headman 오브젝트를 클릭하면 GameManager 스크립트 안에 선언된 target 변수에 NPC 오브젝트를 대입합니다.

Blacksmith

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

public class Blacksmith : MonoBehaviour
{
    public Text mainText;
#-------------       [수정]       -------------#
    public GameManager gameManager;
#----------------------------------------------#

    void Start()
    {
        mainText = GameObject.Find("MainText").GetComponent<Text>();
#-------------       [수정]       -------------#
        gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
#----------------------------------------------#
    }

    public void Talk()
    {
        mainText.text = "안녕하시오. 나는 앤글 블로그 대장장이오.";
    }

    public void Quest()
    {
        mainText.text = "내가 그대에게 부탁할게 있소.";
    }

    public void Sell()
    {
        mainText.text = "팔고 싶은 것이 있소?";
    }

    public void Buy()
    {
        mainText.text = "구매하고 싶은 것이 있소?";
    }

    public void Repair()
    {
        mainText.text = "모두 고쳤소.";
    }

#-------------       [수정]       -------------#
    private void OnMouseDown()
    {
        gameManager.target = this.gameObject;
    }
#----------------------------------------------#
}

변경점은 Headman 스크립트와 같습니다. 설명은 생략하겠습니다.

Seamster

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

public class Seamster : MonoBehaviour
{
    public Text mainText;
#-------------       [수정]       -------------#
    public GameManager gameManager;
#----------------------------------------------#

    void Start()
    {
        mainText = GameObject.Find("MainText").GetComponent<Text>();
#-------------       [수정]       -------------#
        gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
#----------------------------------------------#
    }

    public void Talk()
    {
        mainText.text = "안녕하시오. 나는 앤글 블로그 재봉사오.";
    }

    public void Quest()
    {
        mainText.text = "내가 그대에게 부탁할게 있소.";
    }

    public void Sell()
    {
        mainText.text = "팔고 싶은 것이 있소?";
    }

    public void Buy()
    {
        mainText.text = "구매하고 싶은 것이 있소?";
    }

    public void Repair()
    {
        mainText.text = "모두 고쳤소.";
    }

#-------------       [수정]       -------------#
    private void OnMouseDown()
    {
        gameManager.target = this.gameObject;
    }
#----------------------------------------------#
}

역시 변경점은 Headman 스크립트와 같습니다.

GetMethod

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class GetMethod : MonoBehaviour, IPointerDownHandler
{
    public Button button;
    public Text text;
    public GameObject target;
#-------------       [수정]       -------------#
    public GameManager gameManager;
#----------------------------------------------#

    void Start()
    {
        button = GetComponent<Button>();
        text = GetComponentInChildren<Text>();
#-------------       [수정]       -------------#
        gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
#----------------------------------------------#
    }

#-------------       [수정]       -------------#
    void Update()
    {
        if(gameManager.target != null)
        {
            target = gameManager.target;
        }
    }
#----------------------------------------------#

    public void OnPointerDown(PointerEventData eventData)
    {
        if(eventData.button == PointerEventData.InputButton.Left &&
            target != null)
        {
            if(text.text == "말하기")
            {
                print("말하기 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Talk"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }

            if(text.text == "퀘스트")
            {
                print("퀘스트 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Quest"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }

            if(text.text == "판매")
            {
                if (target.gameObject.name == "Headman")
                    return;
                print("판매 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Sell"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }

            if (text.text == "구매")
            {
                if (target.gameObject.name == "Headman")
                    return;
                print("구매 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Buy"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }

            if (text.text == "수리")
            {
                if (target.gameObject.name == "Headman")
                    return;
                print("수리 버튼 클릭");
                button.onClick.AddListener(delegate { target.SendMessage("Repair"); });
                button.onClick.Invoke();
                button.onClick.RemoveAllListeners();
            }
        }
    }
}

다른 스크립트와 마찬가지로 gameManager 변수를 선언하고 대입합니다. 그리고 특이한 것이 Update 메서드인데요. GameManager 스크립트에 선언돼있는 변수 target에 레퍼런스가 연결된다면 GetMethod 스크립트에 선언된 변수 target에 레퍼런스를 대입합니다. Update 메서드는 매 프레임 호출되므로 매 순간 target 변수에 레퍼런스를 넣을 것인지 판별합니다.

target 변수에 레퍼런스가 연결된다면 OnPointerDown 메서드에 명시된 if 문의 조건이 성립됩니다. target 변수에 레퍼런스가 연결돼야 버튼 오브젝트가 클릭 됐을 때 스크립트에 작성한 버튼 기능이 동작하는 것이죠.

잘 동작하는지 테스트


스크립트 작성을 마쳤으면 플레이 버튼을 클릭해서 게임이 잘 동작하는지 테스트해봅시다. 재봉사 NPC를 클릭하고 말하기 버튼을 클릭하니 위 사진처럼 Seamster 스크립트의 Talk 메서드가 호출됐습니다. 잘 작동하는군요! 하지만 우리는 똑같은 코드를 여러 번 작성해야 했습니다. 코드를 수정해야 했을 때도 똑같은 코드를 반복해서 수정했죠. 굉장히 불편하지 않았나요?

글이 너무 길어지므로 다음 게시글에서 그 문제를 해결하고 클래스의 특징을 배워봅시다. <복합 데이터 형식 클래스 -2->에서 계속됩니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다