기록 보관소

[Unity/유니티] 기초-뱀서라이크: 몬스터 만들기[05] 본문

유니티 프로젝트/뱀서라이크

[Unity/유니티] 기초-뱀서라이크: 몬스터 만들기[05]

JongHoon 2023. 5. 22. 21:50

개요

유니티 독학을 위해 아래 링크의 골드메탈님의 영상들을 보고 직접 따라 해보면서 진행 상황을 쓰고 배웠던 점을 요약한다.

https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2 

 

📚유니티 기초 강좌

유니티 게임 개발을 배우고 싶은 분들을 위한 기초 강좌

www.youtube.com


뱀서라이크: 몬스터 만들기[05]

1. 오브젝트 만들기

몬스터 Enemy 1의 스프라이트 Run 0을 Hierarchy 창에 드래그 & 드랍해서 오브젝트 생성
그림자 추가를 위한 Order in Layer 2로 변경
그림자 스프라이트 Shadow를 자식 오브젝트로 추가하고, 위치를 조정한다
Animator 컴포넌트 추가
에셋에 만들어진 Animation Controller가 있으므로, 이를 이용하면 된다
Enemy 1이므로 AcEnemy 1을 추가
Rigidbody 2D 컴포넌트 추가
Gravity Scale 0으로 변경하고, Freeze Rotation Z축을 체크해서 중력과 회전을 제거한다
Capsule Collider 2D 컴포넌트 추가
스프라이트에 맞게 Collider 크기를 조정해준다
완성된 몬스터 오브젝트를 복사
복사한 오브젝트는 Enemy 2(왼쪽)의 스프라이트와 애니메이션으로 변경해준다.

  • 완성된 몬스터 오브젝트의 애니메이션 테스트를 위해 실행하면 된다.
  • 실행 해보면 골드메탈님의 강의 영상처럼 이전 시간에 플레이어의 Rigidbody 2D 컴포넌트에는 Z축 고정이 없어서 몬스터와 부딪히면 회전하는 문제가 발생할 것이다.
  • 나는 배우고 따라하는 입장이라 영상에서 보여줘서 아래처럼 변경한 후에 테스트해봤다.

회전 방지를 위해 Player의 Rigidbody 2D에서 Freeze Rotation Z를 체크해주었다.

  • 추가로 플레이어가 몬스터들에게 쉽게 밀려나지 않도록 Mass도 늘려준다. 위 캡쳐에서는 5로 늘렸는데, 후술할 챕터에서 몬스터 숫자를 늘리니 여전히 쉽게 밀려나서, 15정도로 더 늘려주었다.

몬스터 오브젝트 테스트. 애니메이션도 문제 없고, 플레이어가 부딪혀도 회전하는 문제가 발생하지 않았다.


2. 플레이어 추적 로직

Enemy C# 스크립트 파일 생성

// Enemy Script

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

public class Enemy : MonoBehaviour {
    public float speed;
    public Rigidbody2D target;

    bool isLive;

    Rigidbody2D rigid;
    SpriteRenderer spriter;

    void Awake() {
        rigid = GetComponent<Rigidbody2D>();
        spriter = GetComponent<SpriteRenderer>();
    }

    void FixedUpdate() {
        Vector2 dirVec = target.position - rigid.position;  // 방향 = 위치 차이의 정규화(Normalized). 위치 차이 = 타겟 위치 - 나의 위치.
        Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime;
        rigid.MovePosition(rigid.position + nextVec);   //플레이어의 키 입력값을 더한 이동 = 몬스터의 방향 값을 더한 이동
        rigid.velocity = Vector2.zero; //물리 속도가 이동에 영향을 주지 않도록 속도 제거
    }
}

앞 챕터에서 완성한 Enemy 오브젝트 2개에 Enemy 스크립트 파일을 추가해준다
speed는 Player보다 조금 느린 2.5로 설정하고, Target은 Player 오브젝트를 끌어 넣는다
로직 테스트. 시작 직후부터 문제없이 플레이어를 잘 따라온다. 그러나 스프라이트 Flip을 설정하지 않아서 문워크를 한다.

  • 스프라이트 방향은 목표(플레이어)의 X축 값과 자신(몬스터)의 X축 값을 비교해 작으면 Flip X를 체크(true)하도록 만들면 된다.
  • 변수 중 isLive를 사용하지 않았고, 앞으로 플레이어의 공격 구현에 있어 필요해질테니 미리 Enemy 스크립트에 해당 조건을 추가해주자.
  • 강의 영상에서 isLive는 앞의 Flip 테스트 이후에 진행했으나, 나는 시간 아낄겸 로직에 큰 영향을 주지 않으니 한꺼번에 작성한 후에 테스트했다.
// Enemy Script

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

public class Enemy : MonoBehaviour {
    public float speed;
    public Rigidbody2D target;

    bool isLive = true; //테스트용 변수 초기화

    Rigidbody2D rigid;
    SpriteRenderer spriter;

    void Awake() {
        rigid = GetComponent<Rigidbody2D>();
        spriter = GetComponent<SpriteRenderer>();
    }

    void FixedUpdate() {
        if (!isLive)
            return;
        Vector2 dirVec = target.position - rigid.position;  // 방향 = 위치 차이의 정규화(Normalized). 위치 차이 = 타겟 위치 - 나의 위치.
        Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime;
        rigid.MovePosition(rigid.position + nextVec);   //플레이어의 키 입력값을 더한 이동 = 몬스터의 방향 값을 더한 이동
        rigid.velocity = Vector2.zero; //물리 속도가 이동에 영향을 주지 않도록 속도 제거
    }

    void LateUpdate() {
        if (!isLive)
            return;
        spriter.flipX = target.position.x < rigid.position.x;
    }
}

Flip 테스트. 플레이어 이동 방향에 맞춰서 몬스터도 방향을 바꾸며 따라온다.


3. 몬스터 재배치

  • 플레이어와 몬스터의 거리가 너무 멀어지게 되면, 다시 만나게되기는 힘들 것이고 몬스터 숫자는 계속 증가만 할것이다. 이런 상황이 반복되면 분명 게임 플레이와 성능에 악영향을 미칠 수 있을 것이다(필자의 생각).
  • 이를 위해서 앞선 강의에서 진행했던 배경 재배치처럼 몬스터도 재배치를 구현해서 재활용할 수 있게, 플레이어를 계속 쫓을 수 있게 한다.
  • 그래서 기존의 재배치 로직을 활용하기 위해 Reposition 스크립트 파일을 수정한다.
//Reposition Script

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

public class Reposition : MonoBehaviour {
    Collider2D coll;    //모든 모양의 Collider를 포함

    void Awake() {
        coll = GetComponent<Collider2D>();
    }

    void OnTriggerExit2D(Collider2D collision) {    //IsTrigger가 체크된 Collider에서 나갔을 때 동작
        if (!collision.CompareTag("Area"))
            return;

		//거리를 구하기 위해 플레이어 위치와 타일맵 위치 미리 저장
        Vector3 playerPos = GameManager.instance.player.transform.position;
        Vector3 myPos = transform.position;
        float diffX = Mathf.Abs(playerPos.x - myPos.x);
        float diffY = Mathf.Abs(playerPos.y - myPos.y);

        Vector3 playerDir = GameManager.instance.player.inputVec;   //Player 스크립트에서 inputVec을 public으로 바꿔줘야함
        float dirX = playerDir.x < 0 ? -1 : 1;
        float dirY = playerDir.y < 0 ? -1 : 1;

        switch (transform.tag) {
            case "Ground":
                if (diffX > diffY) {    //X축 이동시
                    transform.Translate(Vector3.right * dirX * 40); //X축으로 2칸 이동
                }
                else if (diffX < diffY) {   //Y축 이동시
                    transform.Translate(Vector3.up * dirY * 40);    //Y축으로 2칸 이동
                }
                break;
            case "Enemy":
                if (coll.enabled) { //몬스터가 죽으면 collider2D 컴포넌트를 disable 시키도록 구현할 예정
                    transform.Translate(playerDir * 20 + new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), 0f)); //플레이어 이동 방향에 따라 맞은 편에서 등장하도록 이동
                }
                break;

        }
    }
}

Enemy에 수정한 Repositon 스크립트 파일을 넣어준다
로직 발동을 위한 Enemy 태그 추가. 이후 Enemy 오브젝트들의 태그를 변경해준다.
테스트에 앞서 몬스터도 이렇게 늘려주자. 그리고 Player 이동 속도도 5로 잠시 늘려주었다.
재배치 테스트. 화면 밖으로 사라진 몬스터가 플레이어 이동 반대 방향에서 다시 나타난다.