본문 바로가기

IT/유니티

8. 유니티 교육 (유니티 기본3)

1. 무기를 만들고 공격하자


지난번까지는 환경을 만들어 보았다.

동전도 먹고, 불도 만들어서 캠프파어도 만들고!

그럼 이번에는 무기를 만들어서 공격동작을 만들어 보자.

결국 몹들을 때려죽이는게 게임의 기본아니겠는가..ㅋㅋ


무기는 애셋스토어에서 다운받을수도 있겠지만, 우리는 간단하게 실런더를 이용해서

작은 봉을 만들어 보았다.

여기서 중요한것은 실린더로 봉을 만들고 이것을 반드시 FirstPersonCharater의 자식으로 넣어두어야 한다.

나도 강의를 들으면서 알게됬지만, 각 객체의 좌표는 결국 부모를 기준으로 좌표를 잡는거 같다.

봉이 1인칭캐릭터를 부모로 가지게되면, z값만 조금 조정하여서 무기가 앞에 있는거 처럼 나들수 있다.

(보통의 일인칭게임처럼)


봉이 완성되면! 애니메이션을 만들면된다.

애니메이션은 전에도 했듯이 애니메이션 탭에서 작성하면된다.

작성한 애니메이션은 애셋스토어에 저장된다.

(봉을 기준으로 애니메이션을 만들면 자동적으로 애미메이터 컴포넌트가 추가되어서 

계속 자동으로 움직이고 있으니 play automatically를 해제해둔다.)


그리고 마우스 왼쪽버튼을 누른때마다 공격하기 위해서는 스크립트를 작성해야한다.

스크립트는 다음과 같다.


void Update () {

        if (Input.GetButtonDown("Attack"))

        {

            GetComponent<Animation>().Play("attackAnimation");

        }


        if (Input.GetMouseButtonDown(0))

        {

            GetComponent<Animation>().Play("attackAnimation");

        }

}


위의 버튼 다운은 edit->project setting-> input에서 미리정의해둔 공격버튼이고

밑의 마우스다운은 0일때 왼쪽버튼,1이면 오른쪽, 2면 가운데 버튼이다.


작성한 스크립트를 봉에게 드레그해놓으면 오케이!! 





2. 적을 만들고 죽이자!


봉을 만들고 공격모션을 만들었으니 무엇이 필요할까?

그건 바로 우리가 죽일 적이 필요하다!!

우리가 하는 겜처럼 움직이고 공격하는 적은 우선 다음에 만들고, 우선 허수아비같은 몹이라고 만들어보자.

단순히 가만히 있고, hp만 가지고 있는 몹은 캡슐로 만들어 보았다. 상상력이로 몬스터라고 생각하자.ㅋㅋ


그럼 무엇이 필요할까?

혼자서 막코딩으로 게임을 만들때 가장 중요한것은 바로 충동체크 아니겠는가?

모든 게임의 시작과 끝이라고 할수있을것이다.

그럼 유니티에서는 어떻게 이 충동체크를 제어할까?

아마도 여러가지 방법이 있겠지만 오늘 공부해볼 방법은 RaycastHit이다.

프로그래밍 언어들은 다 영어권에서 만들어져서 영어를 안다면 쉽게 이해할수있다. 변수나 함수명만가지고.

이 raycasthit는 단순하다.

ray 레이져를 cast 쏴서 hit 맞는다.

결국 우리의 눈에는 보이지 않지만, 레이져를 쏴서 그 레이져가 어디서 맞는지 체크해보는것이다.


void Update () {

        if (Input.GetMouseButtonDown(0))

        {

            RaycastHit hit;

            if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit))

            {

                totarget = hit.distance;

                if (totarget < range)

                {

                    hit.transform.SendMessage("DeductPoints", hitpoints, SendMessageOptions.DontRequireReceiver);

                }

            }

            

        }

    }


소스코드는 위와 같다.

마우스 왼쪽 버튼을 누를때마다, physics클래스의 raycast를 호출하게 한다.

결국 레이져를 쏜다라는 뜻이다.

raycast의 인자는 레이져의 시작점 그리고 방향, 마지막의 out hit은 레이져가 맞은 대상의 정보

만약 무엇과 충돌했다면 참을 리턴하고

밑의 totarget에 그 대상과의 거리를 알수 있다.


그리고 여기서 재미있는 점은 바로 Sendmessage함수이다.

솔직히 유니티 처음하는 입장에서 엄청 놀란 함수이다.

그냥 뭐든 sendmessage함수로 함수이름과 인자를 넣으면 그 객체의 함수에 접근가능한것이다.

참.......참....쉽다...거지같이 쉬워서 혼자 겜만들었던게 너무 짜쯩난다.


("DeductPoints", hitpoints, SendMessageOptions.DontRequireReceiver)

위의 DeductPoints는 enemyScript안의 함수이름이다.



이렇게 쓰면 쉽게 구현한거 같이 보이지만, 첨에 이걸할때 

아무리 raycast를 해도 distance가 0.5가 나와서 아니 하늘을 보고 쏴도, 땅을 보고 쏴도 똑같은 값이 나와서

좌절하고 있었는데, 알고보니...ㅠㅠㅠㅜㅠㅜㅠ

내가 들고 있는 봉에 충돌체크해서 어딜 찍어도 같은 값이 나온것이였다.

그래서 봉의 위치를 가운데에서 조금 오른쪽으로 옮겨놓았다.


거의 1시간넘게 고생하다가 졸고 일어나서 겨우 발견했다.

유튜브 강사는 암것도 언급해주지 않았다는..ㅠㅠ






3. 진짜 무기를 줘!


무기도 만들고 맞을 대상도 만들었다.

하지만 왠지 현실감 없는 흰봉을 들고 때릴라니 어색하지 않는가?

그래서 이번에는 무기를 함 넣어보자.


남자의 무기라면 뭐겠는가.! 바로 도끼!

도끼 객체를 임포트해서 바로 넣으면 된다.


여기서 그럼! 이 도끼를 가지고 위에서 한거처럼 애니메이션 만들고

캐릭터에 따로 위치시켜야 할까?


엄청 귀찮겠지 않는가?ㅋㅋㅋ 그냥 이전에 만들어 놓은 봉 객체에다가

자식으로 넣어놓는다. 그런후 봉객체의 매쉬랜더러를 지우면!!

짜짠! 바로 적용 가능해진다는거! 쉬워도 너무 쉽다 유니티는..ㅋㅋ



몬스터도 그냥 캡슐이 아니라 쥐 캐릭터를 하나 가지고 와서 위에서 만든 캡슐밑에 넣어두고

캡슐의 매쉬를 지우면 바로 오케!




4. 캔퍼스를 이용한 텍스트출력


게임을 하다보면 플레이어와 시스템과의 의사소통이 필요하다.

퀘스트를 줄때도 그렇고, npc와의 대화를 할때도 그러하다.

이럴때 사용할수 있는 것이 바로 canvas라고 할수있다.


gameObject ->  UI -> Canvas를 클릭하면 추가할수있다.

캔퍼스를 추가하면 캔퍼스안에 패널이 추가되어져있는데, 이것의 크기를 잘 조정하고

색깔도 조정한다음에, gameObject ->  UI -> Text를 추가해서 패널안에 넣어두면

흔히 우리가 게임에서 보는 대화창을 만들수있다.


가운대 아래 넓게 두면 대화창이고, 오른쪽 위에 얇게 생성해두면 퀘스트 창처럼 만들수도 있다.




5. NPC와 퀘스트


4번에서 대화창과 퀘스트 창을 만들어 보았다.

그렇다면 RPG의 기본중에 기본! 바로 NPC와 퀘스트받기를 만들어 보겠다! 오오옷!


이번에는 오직 스크립트 작업을 하면 되니까 쉽다면 쉽고, 어렵다면 어렵다고 할수 있겠다.

소스코드는 다음과 같다.


using UnityEngine.UI;


public int TestBoxOnCheck = 0;

    public GameObject MessageBox;

    public GameObject TextBox;

    public string TextMessage;

    public GameObject QuestBox;

    public GameObject QuestText;

    public string QuestName;


    // Use this for initialization

    void Start () {

}

// Update is called once per frame

void Update () {

        if (Input.GetButtonDown("Submit") || Input.GetButtonDown("Cancel"))

        {

            if (TestBoxOnCheck == 1)

            {

                MessageBox.SetActive(false);

                TestBoxOnCheck = 0;

                TextMessage = "Villager: Get going then!";

            }

            

        }

}


    void OnMouseDown()

    {

        if (TestBoxOnCheck == 0)

        {

            TestBoxOnCheck = 1;

            MessageBox.SetActive(true);

            TextBox.GetComponent<Text>().text = TextMessage;

            QuestName = "Active Quest: 'Recover The Loot'";

            QuestText.GetComponent<Text>().text = QuestName;

        }

        else

        {

            TestBoxOnCheck = 0;

            MessageBox.SetActive(false);

            TextMessage = "Villager: Get going then!";

        }

    }


TestBoxOnCheck는 스위치라고 생각하면 된다. 대화창이나 퀘스트창을 안보여주거나 보여줄때 사용한다.

시작하기전에 메시지패널은 비활성화 된상태이므로, NPC를 클릭하면 활성화 시켜주기위해서 

MessageBox.SetActive(true);

위와 같은 함수를 사용해서 활성화 시켜준다.

전체적으로는 어렵지 않으나, 이번 과정에서 힘들었던 점은 using UnityEngine.UI;를 간과했다는 점이다.

UI를 임포트 하지 않았기 때문에 계속 TextBox.GetComponent<Text>의 Text를 찾지 못한다는 에러가 계속 나왔기 때무에

한시간이상 고생했던거 같다.




6. 퀘스트 달성


퀘스트를 받았으니 퀘스트를 달성까지 해보자.

5에서 우리가 만든 퀘스트는 단순히 물건을 찾아오면 되는 것이였으므로, 

퀘스트아이템을 하나 적당히만들어서 전에 만들어놓은 집안에다가 놓는다.


그리고 퀘스트를 받기 전에는 보이면 안되니가 SetActive(false)로 보이지 않게 해놓고,

퀘스트를 받으때 활성화 시켜서 보여기 한다.

 

5에서는 단순히 퀘스트 스크립트를 하나만 가지고 활용하였는데, 

이번에는 총 3개의 스크립트로 나누어서 진행해보았다.


퀘스트 시작, 퀘스트 활성, 퀘스트완료 이렇게 3개로 나누어서


시작일때는 퀘스트 아이템을 보이게 해놓고, 

활성 스크립트는 퀘스트아이템에 놓고, 찾으면 메시지 박스와 아이템을 사라지게 한다.

완료는 NPC에게 고맙다는 인사와 퀘스트창을 업데이트 하는데 사용한다.


잘하면 하나의 스크립트로도 해결할거 같은데, 무쟈게 어렵게 해놓은 느낌이 드는거 그냥 기분탓일까?

한 5시간 연속으로 하니까 생각하기 싫어서 따라만 했지만, 시간이 나면 곰곰히 생각해 봐야겠다.