[개발일지] 29. 맵 프리팹 수정 & 스크립트 수정

2025. 1. 27. 16:16·유니티 프로젝트/점토게임

0. 들어가기 전에

이번에는 맵 프리팹과 스크립트를 수정했다. 기존 맵 프리팹은 그냥 맵에 압정의 위치가 한 곳에만 있고 변경되지 않았다. 이번에는 맵에 압정 3개를 놓은 후, 랜덤한 확률로 압정을 활성화 여부를 결정해서 압정이 다양한 곳에 생성되는 것처럼 보이도록 했다.

 

압정이 활성화 될 확률은 쉬움, 보통, 어려움 단계에 맞게 설정해야 했는데 이를 위해 Random.Range 를 이용했다.

 

 


 

 

1. 맵 프리팹

일단 쉬움 단계의 맵의 종류로는 4개를 만들어놨는데 더 추가할 예정이다.

 

일단 맵은 압정이 있는 경우, 몬스터가 있는 경우, 아무것도 없는 경우로 나뉜다. 

더보기

1. 인스펙터 창

모든 맵의 인스펙터 창 컴포넌트 상태는 다음과 같다. 맵 마다 다른 부분이 있다면 Map Controller 스크립트이다. 위에서 맵은 장애물이 있는 경우와 없는 경우로 나뉜다고 했는데 그에 따라 Spikes 와 Monsters 에 들어가는 것들도 달라진다.

 

아래 사진의 경우는 맵에 압정이 3개 있는 경우이다.

 

 


 

 

2. 스크립트

이번에 수정한 스크립트는 MapLoop 와 Monster 이다.

 

2.1 MapLoop 스크립트

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Tilemaps;

public class MapController : MonoBehaviour
{
    [Header("Map Info")]
    public float curTime = 0;
    public float targetTime = 2f; // 활성화된지 2초가 지나면 활성화 끄기
    public int mapLevel; // 맵 단계에 따라 압정이 최대로 활성화 되는 개수가 달라짐

    // 맵에 장애물 없으면 활성화 안 됨
    public GameObject[] spikes; // 압정 개수(맵이 활성화 될 때마다 압정의 위치 변경)

    public GameObject[] monsters; // 몬스터 개수

    private void OnEnable()
    {
        // 활성화 되면 자동으로 호출됨
        ResetMap();
    }

    private void Update()
    {
        curTime += Time.deltaTime;

        if (curTime >= targetTime)
            gameObject.SetActive(false); // 비활성화!
    }

    public void ResetMap()
    {
        curTime = 0;
        transform.localPosition = Vector3.zero; // 지역 위치를 (0, 0, 0) 으로 설정해주기..

        // 장애물을 랜덤으로 활성화하기
        for (int i =0; i<spikes.Length; i++)
        {
            int count = 0;

            // 쉬움 단계
            if (mapLevel == 0)
                count = 5;
            // 보통 단계
            else if (mapLevel == 1)
                count = 3;
            // 어려움 단계
            else
                count = 2;

            int tmp = Random.Range(0, count); // 0, 1, 2, ... count-1 중 랜덤
            if (tmp == 0)
            {
                // 활성화
                spikes[i].SetActive(true);
            }
            else
            {
                // 비활성화
                spikes[i].SetActive(false);
            }
        }

        for (int i=0; i<monsters.Length; i++)
        {
            monsters[i].SetActive(true); // 몹 활성화하기
        }
    }
}

 

2.2 MapLoop 스크립트 변경 사항 설명

더보기

1. 변수

새로 추가한 변수는 다음과 같다. 맵 단계에 따라 압정이 최대로 활성화 되는 개수가 달라지기 때문에 mapLevel 변수를 만들었다.

 

그리고 맵의 자식 오브젝트인 압정과 몬스터를 할당하기 위한 배열을 선언했다.

public int mapLevel; // 맵 단계에 따라 압정이 최대로 활성화 되는 개수가 달라짐

// 맵에 장애물 없으면 활성화 안 됨
public GameObject[] spikes; // 압정 개수(맵이 활성화 될 때마다 압정의 위치 변경)

public GameObject[] monsters; // 몬스터 개수

 

 

2. OnEnable()

새로 추가한 메서드이다. 게임 오브젝트가 활성화될 때 자동으로 호출되는 메서드이다.

 

맵이 활성화 되면 정보를 초기화 하도록 ResetMap 메서드를 호출했다.

private void OnEnable()
{
    // 활성화 되면 자동으로 호출됨
    ResetMap();
}

 

 

3. ResetMap()

맵의 정보를 초기화하는 메서드이다. 기존 메서드에 로직을 더 추가했다.

 

현재 야생 난이도에 따라 압정이 활성화 되는 확률이 달라져야 한다. 지금은 임의로 설정한 값이고 다음에 직접 플레이해보면서 난이도 조정을 할 생각이다.

 

일단 맵 레벨에 맞게 count 값을 설정해준 후 Random.Range 로 확률 계산을 했다. 일단 count = 5 면 각 압정이 활성화 될 확률이 20% 이다. 쉬움 단계인데도 불구하고 압정이 활성화 될 확률이 높은 것 같아서 다음에 변경해야 할 것 같다.

 

몬스터는 딱히 활성화 여부를 확률로 계산하지 않고 그냥 맵에 자식으로 존재하는 모든 몹을 활성화 하도록 했다. 얘는 알아서 움직이니까 이렇게 해도 될 것 같다는 생각이 들었다..

public void ResetMap()
{
    curTime = 0;
    transform.localPosition = Vector3.zero; // 지역 위치를 (0, 0, 0) 으로 설정해주기..

    // 장애물을 랜덤으로 활성화하기
    for (int i =0; i<spikes.Length; i++)
    {
        int count = 0;

        // 쉬움 단계
        if (mapLevel == 0)
            count = 5;
        // 보통 단계
        else if (mapLevel == 1)
            count = 3;
        // 어려움 단계
        else
            count = 2;

        int tmp = Random.Range(0, count); // 0, 1, 2, ... count-1 중 랜덤
        if (tmp == 0)
        {
            // 활성화
            spikes[i].SetActive(true);
        }
        else
        {
            // 비활성화
            spikes[i].SetActive(false);
        }
    }

    for (int i=0; i<monsters.Length; i++)
    {
        monsters[i].SetActive(true); // 몹 활성화하기
    }
}

 

2.3 Monster 스크립트

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

public class Monster : MonoBehaviour
{
    public enum MoveDir
    {
        Left = -1, Idle = 0, Right = 1
    }

    [Header("Monster info")]
    public float speed = 1f; // 이동 속도
    public MoveDir dir = MoveDir.Idle; // 점토 몬스터가 움직이는 방향
    public Coroutine monsterMoveCoroutine; // 몬스터는 계속해서 움직이는데 그 동작을 코루틴으로 만들 것. 코루틴 시작한 거 저장하기 위한 용도

    public bool isDie = false;

    private Animator anim;
    private Rigidbody2D rigid;
    private SpriteRenderer spriteRenderer;

    private AudioSource monsterAudio;

    private void Start()
    {
        // 컴포넌트 할당 
        anim = GetComponent<Animator>();
        rigid = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        monsterAudio = GetComponent<AudioSource>();
    }

    private void OnEnable()
    {
        // 활성화 될 때 몬슽터 정보 리셋
        MonsterReset();
    }

    private void Update()
    {
        if (!isDie)
        {
            // 몬스터가 땅에 있으면 이동 방향에 따라 움직일 수 있도록
            transform.Translate(new Vector2((int)dir, 0) * speed * Time.deltaTime * 0.5f);

            // 애니메이션 수행 & 스프라이트 관리
            SetMonsterSprite();

            // 몬스터 레이캐스트
            MonsterRaycast();
        }
    }

    private void MonsterReset()
    {
        isDie = false;

        if (monsterMoveCoroutine != null)
            StopCoroutine(monsterMoveCoroutine); // 코루틴 종료
        monsterMoveCoroutine = StartCoroutine(MonsterMove()); // 코루틴 다시 시작
    }

    private void MonsterRaycast()
    {
        // 방향에 맞게 레이 쏴야함
        // 근데 아래 방향으로 쏴야함 -> 몬스터보다 조금 더 앞에서 밑에 방향으로 레이 쏠 것
        Vector2 front = new Vector2(rigid.position.x + (int)dir, rigid.position.y);
        Debug.DrawRay(front, Vector2.down, new Color(0, 1, 0));
        RaycastHit2D rayHit = Physics2D.Raycast(front, Vector3.down, 1, LayerMask.GetMask("Ground"));

        if (rayHit.collider == null)
        {
            Debug.Log("밑에 땅이 업어여;;");

            // 자기 앞이 낭떠러지면 move 코루틴 중지하고 GotoReverse 시작
            StopCoroutine(monsterMoveCoroutine);
            StartCoroutine(GotoReverse());
        }
    }

    private IEnumerator MonsterMove()
    {
        while (true)
        {
            dir = (MoveDir)Random.Range(-1, 2);

            yield return new WaitForSeconds(2f); // 2초마다 방향 바꿀 수 있도록..
        }
    } 

    private IEnumerator GotoReverse()
    {
        // 이동 방향을 반대로 바꾸는 코루틴

        int reverseDir = -(int)dir; // 현재 이동 방향에 반대

        // 점토가 가는 방향의 밑이 낭떠러지면 방향 바꿔야함
        dir = (MoveDir)reverseDir;

        yield return new WaitForSeconds(2f); // 2초 기다령
        monsterMoveCoroutine = StartCoroutine(MonsterMove()); // 코루틴 다시 시작
    }

    private void SetMonsterSprite()
    {
        // 애니메이션 수행
        if ((int)dir == 0)
            anim.SetBool("Move", false);
        else
        {
            anim.SetBool("Move", true);

            // 움직이는 방향이 오른쪽이면 스프라이트 뒤집도록..
            if ((int)dir == -1)
                spriteRenderer.flipX = false;
            else
                spriteRenderer.flipX = true;

        }
    }

    public void Die()
    {
        StartCoroutine(DieAnim()); // 코루틴 시작
    }

    private IEnumerator DieAnim()
    {
        monsterAudio.Play(); // 오디오 재생

        isDie = true; // 죽은 상태 알리기
        anim.SetTrigger("Die"); // 죽는 애니메이션 수행

        yield return new WaitForSeconds(1f); // 2초 기다려여

        gameObject.SetActive(false); // 활성화 꺼야함
    }
}

 

2.4 Monster 스크립트 변경 사항 설명

더보기

1. OnEnable()

새로 추가한 메서드이다. 게임 오브젝트가 활성화될 때 몬스터 정보를 리셋하기 위한 메서드를 호출하도록 했다.

private void OnEnable()
{
    // 활성화 될 때 몬슽터 정보 리셋
    MonsterReset();
}

 

 

2. MonsterReset()

새로 만든 메서드이다. isDie 변수의 값을 다시 false 로 설정해주고, 기존 코루틴을 종료한 후 다시 시작하도록 했다. 

private void MonsterReset()
{
    isDie = false;

    if (monsterMoveCoroutine != null)
        StopCoroutine(monsterMoveCoroutine); // 코루틴 종료
    monsterMoveCoroutine = StartCoroutine(MonsterMove()); // 코루틴 다시 시작
}

 

 


 

 

3. 결과물

 

 

 

'유니티 프로젝트/점토게임' 카테고리의 다른 글
  • [개발일지] 31. 야생 판넬 제작 & 체력바 애니메이션 적용 & 씬 이동
  • [개발일지] 30. 야생 난이도에 따른 맵 생성 & 배경 설정
  • [개발일지] 28. 맵 이동 & 생성
  • [개발일지] 27. 플레이어 & 장애물 상호작용
dubu0721
dubu0721
dubu0721 님의 블로그 입니다.
  • dubu0721
    dubu0721 님의 블로그
    dubu0721
  • 전체
    오늘
    어제
    • 분류 전체보기 (335)
      • 프로그래밍언어론 정리 (0)
      • 컴퓨터네트워크 정리 (5)
      • 알고리즘&자료구조 공부 (64)
        • it 취업을 위한 알고리즘 문제풀이 입문 강의 (60)
        • 학교 알고리즘 수업 (3)
        • 실전프로젝트I (0)
      • 백준 문제 (193)
        • 이분탐색 (7)
        • 투포인트 (10)
        • 그래프 (7)
        • 그리디 (24)
        • DP (25)
        • BFS (15)
        • MST (7)
        • KMP (4)
        • Dijkstra (3)
        • Disjoints Set (4)
        • Bellman-Ford (2)
        • 시뮬레이션 (3)
        • 백트래킹 (15)
        • 위상정렬 (5)
        • 자료구조 (25)
        • 기하학 (1)
        • 정렬 (11)
        • 구현 (8)
        • 재귀 (8)
        • 수학 (8)
      • 유니티 공부 (11)
        • 레트로의 유니티 게임 프로그래밍 에센스 (11)
        • 유니티 스터디 자료 (0)
        • C# 공부 (0)
      • 유니티 프로젝트 (48)
        • 케이크게임 (13)
        • 점토게임 (35)
      • 언리얼 공부 (10)
        • 이득우의 언리얼 프로그래밍 (10)
      • 진로 (1)
      • 논문 읽기 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    언리얼
    이득우
    레트로의 유니티 프로그래밍
    골드메탈
    자료구조
    유니티 프로젝트
    투포인터
    수학
    dp
    BFS
    재귀
    그리디
    맵
    백준
    티스토리챌린지
    스택
    백트래킹
    유니티 공부 정리
    오블완
    시뮬레이션
    우선순위큐
    해시
    유니티
    그래프
    이벤트 트리거
    큐
    바킹독
    정렬
    C#
    이분탐색
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
dubu0721
[개발일지] 29. 맵 프리팹 수정 & 스크립트 수정
상단으로

티스토리툴바