내일배움캠프 35일차 TIL <Unity 게임개발 숙련 프로젝트 2일차> 05/27

정광훈(Unity_9기)·2025년 5월 27일

TIL (Today I Learned)

목록 보기
45/110
post-thumbnail

오늘 한 것

1. 장애물 모델링 적용



모델링은 이렇게 5개
처음에는 장애물에 모델링만 입혔었는데 기둥이 하나만 나오는게 이상해서
길게 나오도록 바꿔보았다.

여기서는 캐릭터가 크지만 실제 캐릭터 모델링은 작기 때문에
그 크기에 맞춘 것임

장애물 모델링을 프로젝트 창에 이동 후
전에 만들었던 장애물과 똑같은 방법으로 제작.

그리고 다시 프리팹화 시킴


2. 긴 장애물 추가

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

public class Obstacle : MonoBehaviour
{
    [SerializeField] private GameObject[] obstaclePrefab; // 장애물 프리팹 넣기
    [SerializeField] private GameObject[] longObstaclePrefab; // 장애물이 한 줄에 길게 나옴

    [Header("장애물 생성 설정")] private float[] spawnXPosition = { -2.6f, 0.1f, 2.6f }; // 장애물 가로 간격
    public float spawnYPosition; // 높이
    public float spawnZPosition; // 화면 뒷부분
    public float minSpawnDelay; // 장애물 소환되는데 걸리는 최소 시간
    public float maxSpawnDelay; // 장애물 소환되는데 걸리는 최대 시간

    [Header("긴 장애물 설정")] public float obstacleCount; // 한 번에 몇 개씩 나오게 할 건지
    public float betweenObstacle; // 길게 장애물이 나올 때 장애물간 간격 (겹치기 방지)
    private float LongSpawnDelay; // 긴 장애물 소환되는데 걸리는 시간


    void Start()
    {
        // 레인 별 독립적으로 장애물 소환
        for (int i = 0; i < spawnXPosition.Length; i++)
        {
            // 코루틴 무한 반복 
            StartCoroutine(SpawnObstacle(i));
        }
    }

    IEnumerator SpawnObstacle(int laneIdx) // 장애물 소환 (여기서 소환 처리)
    {
        while (true)
        {
            // 장애물 소환되는 시간 간격
            yield return new WaitForSeconds(Random.Range(minSpawnDelay, maxSpawnDelay));
            // 장애물 타입
            float obstacleType = Random.Range(1, 101);

            if (obstacleType <= 70) // 일반 장애물 소환 - 70% 확률로 소환
            {
                yield return StartCoroutine(RandomSpawnObstacle(laneIdx));
            }
            else // 긴 장애물 소환 - 30% 확률로 소환
            {
                yield return StartCoroutine(RandomSpawnLongObstacle(laneIdx));
            }
        }
    }

    IEnumerator RandomSpawnObstacle(int laneIdx) // 랜덤으로 일반 장애물 소환
    {
        // 장애물 모델링 중 하나를 랜덤으로 선택
        int randObstaclePrefabs = Random.Range(0, obstaclePrefab.Length);
        // 위 코드에서 뽑힌 장애물을 선택된 장애물로 지정
        GameObject selectedObstaclePrefab = obstaclePrefab[randObstaclePrefabs];
        // 장애물 생성 x좌표 가져오기 - 레인 별로 독립적으로 장애물 소환
        float spawnX = spawnXPosition[laneIdx];

        // 장애물 소환 위치 - 장애물 모델링이 90도 돌아간 상태여서 인스펙터에서 설정한 값으로 불러옴
        GameObject newObstacle = Instantiate(selectedObstaclePrefab,
            new Vector3(spawnX, spawnYPosition, spawnZPosition), selectedObstaclePrefab.transform.rotation);

        // 장애물 오브젝트들이 ObstacleSpawner 오브젝트의 안에 생성됨
        // 생성된 장애물 오브젝트들이 ObstacleSpawner를 부모 오브젝트로 설정 - hierarchy 창을 정리하는 용도
        newObstacle.transform.parent = this.transform;

        yield break; // 코루틴 종료
    }

    IEnumerator RandomSpawnLongObstacle(int laneIdx) // 랜덤으로 긴 장애물 소환
    {
        for (int i = 0; i < obstacleCount; i++) // 장애물을 연속으로 생성하는 조건문
        {
            // 장애물 모델링 중 하나를 랜덤으로 선택
            int randObstaclePrefabs = Random.Range(0, longObstaclePrefab.Length);
            // 위 코드에서 뽑힌 장애물을 선택된 장애물로 지정
            GameObject selectedObstaclePrefabs = longObstaclePrefab[randObstaclePrefabs];
            // 장애물 생성 x좌표 가져오기 - 레인 별로 독립적으로 장애물 소환
            float spawnX = spawnXPosition[laneIdx];

            // 장애물 소환 위치 - 장애물 모델링이 90도 돌아간 상태여서 인스펙터에서 설정한 값으로 불러옴
            GameObject newObstacle = Instantiate(selectedObstaclePrefabs,
                new Vector3(spawnX, spawnYPosition, spawnZPosition), selectedObstaclePrefabs.transform.rotation);

            // 장애물 오브젝트들이 ObstacleSpawner 오브젝트의 안에 생성됨
            // 생성된 장애물 오브젝트들이 ObstacleSpawner를 부모 오브젝트로 설정 - hierarchy 창을 정리하는 용도
            newObstacle.transform.parent = this.transform;

            // 각 장애물 사이의 간격을 조절
            ObstacleMove obstacleMove = newObstacle.GetComponent<ObstacleMove>();
            
            // 소환 시간 = 거리 / 속도 
            float LongSpawnDelay = betweenObstacle / obstacleMove.obstacleSpeed;

            if (i < obstacleCount - 1)
            {
                // 장애물이 하나씩 나오는데 걸리는 시간
                yield return new WaitForSeconds(LongSpawnDelay);
            }
        }
    }
}

문제 발생:

긴 장애물을 만들 때 RandomSpawnObstacle(), RandomSpawnLongObstacle()
이렇게 두 개로 나누고 start()에 랜덤 값과 if문을 추가하여
0이면 일반 장애물, 1이면 긴 장애물이 소환되도록 했다.

플레이 하니 긴 장애물과 일반 장애물이 겹쳐서 나오는 현상이 발생했다.

둘 중에 하나만 나오게 하고 싶었는데
장애물이 길어서 다 나오기 전에 소환된 것 같다.

해결 방안:
SpawnObstacle()을 만들어서 하나만 나오도록 통제하도록 하였다.

yield return new WaitForSeconds(Random.Range(minSpawnDelay, maxSpawnDelay)); 를
제일 위에 적어서 확실하게 일정 시간동안 멈췄다가 함수를 나가고,

다시 호출 되면 yield 이후부터 코드가 실행되어
둘 중 하나만 실행 되도록 수정하였다.

0개의 댓글