Notice
Recent Posts
Recent Comments
Link
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

개발일기

유니티#12 Hierarchy 정리, Tilemap 활용 본문

Unity

유니티#12 Hierarchy 정리, Tilemap 활용

kimjw7815 2025. 1. 1. 16:12

오브젝트를 계속 추가하다보면 Hierarchy가 소란스러울 때가 있다

그게 계속 악화되면 내가 찾고싶은 오브젝트가 어디에 있는지도 모르게 되는데, 그걸 정리하는 방법이 있다

 

 

 

 

 

Empty 오브젝트를 활용해 주는 건데, 임의로 구분할 카테고리를 정해주어서 그 자식 오브젝트로 정리하고 싶은 것을 넣어주는 것이다.

나 같은 경우는 Background 오브젝트 3개, 맵을 구성하는 Grid 오브젝트 3개를 각각 정리해주었다.

Enemy 오브젝트도 종류가 다른 Enemy를 추가하게 된다면 Grid만 따로 모아서 깔끔하게 정리할 수 있을 것이라고 생각한다.

 

 

 

 

 

 

 

추가로, 저번에 Tilemap을 활용해서 맵을 구성했었는데, 이걸 몬스터 소환에도 활용할 수 있지 않을까 생각해보았다.

하지만 생각보다 난항이 있었다.

Tilemap 오브젝트가 아무리 각각의 칸마다 다른 collider를 가진다고 해도, 오브젝트 자체는 하나이고,

배치한 각각의 몬스터들을 따로따로 연산하는 방법도 없는 것이다.

그렇다고 그냥 오브젝트를 쓰려니 아쉬워서, ChatGPT에게 물어보니...

만약 두 가지 방법의 장점을 모두 활용하고 싶다면, 타일맵으로 적의 기본 위치를 지정하고, 배치된 타일에 따라 적 오브젝트를 생성하는 방식을 고려할 수도 있습니다.
예를 들어, 타일맵에서 특정 타일을 "적 스폰 포인트"로 설정하고, 게임 시작 시 이 타일 데이터를 기반으로 적 오브젝트를 생성하는 방법입니다. 이렇게 하면 유지 보수와 유연성을 모두 확보할 수 있습니다.

 

나는 AI가 두렵다. 정말로 언젠간 인간을 뛰어넘어버릴지도 모른다...

 

Tilemap은 전에 설명을 이미 한 바가 있으니 또 설명은 안 하겠다.

https://kimjw7815.tistory.com/18

 

유니티#11 Tilemap

언젠가 유튜브에서 유니티 영상을 보는데 마리오 메이커마냥 마우스로 그리니까 그대로 플랫폼이 그려지는 걸 본 적 있다와 저거 어케하지 싶어서 바로 찾아서 적용을 해 보았다 우선 그 기능

kimjw7815.tistory.com

collider는 안 넣었다.

이제 스크립트를 하나 짜줄 건데, 이 스크립트에선 Tilemap과 Enemy prefab을 받아서 타일이 배치된 위치에 자동으로 Enemy 오브젝트를 생성해 줄 거다.

여기서 Prefab은 오브젝트 5분 밀키트같은 거라고 이해하면 편하다.

 

프리팹이 준비가 되었으면 다음과 같이 스크립트를 짜서 Empty 오브젝트에 넣어준다.

//EnemySpawner.cs
using UnityEngine;
using UnityEngine.Tilemaps;

public class EnemySpawner : MonoBehaviour
{
    public Tilemap enemySpawnTilemap; // EnemySpawn 레이어의 타일맵
    public GameObject enemyPrefab;   // 적 프리팹

    void Start()
    {
        SpawnEnemies();
    }

    void SpawnEnemies()
    {
        // 타일맵의 모든 타일 포지션 확인
        BoundsInt bounds = enemySpawnTilemap.cellBounds;
        TileBase[] allTiles = enemySpawnTilemap.GetTilesBlock(bounds);

        Debug.Log($"Bounds: {bounds}");


        for (int x = 0; x < bounds.size.x; x++)
        {
            for (int y = 0; y < bounds.size.y; y++)
            {
                Vector3Int tilePosition = new Vector3Int(x + bounds.xMin, y + bounds.yMin, 0);
                TileBase tile = enemySpawnTilemap.GetTile(tilePosition);
                Debug.Log($"Tile Position: {tilePosition}, Tile: {tile}");

                if (tile != null)
                {
                    Vector3 worldPosition = enemySpawnTilemap.CellToWorld(tilePosition) + new Vector3(0.5f, 0.5f, -6f);
                    Debug.Log($"Spawning enemy at: {worldPosition}");
                    Instantiate(enemyPrefab, worldPosition, Quaternion.identity);
                }
            }
        }
    }
}

마찬가지로 전능하신 ChatGPT가 짜주신 코드다.

아직 안 봤지만 나중에 자동 스폰으로 바꿀 때 보겠지?

 

이제 public Tilemap에 아까 만든 Tilemap 넣어주고 prefab 넣어주고 하면 끝이 난다.

 

이제 해야할거

  • 주기적으로 몹 확인해서 리젠 (타일마다 넘버 매기기, Update로 에서 Time.time 써서 시간 간격 조절)
  • Enemy HP바 구현
  • NPC 추가, 대화 구현하기 (반경 확인(Overlap), 움직임 제한, UI 설정)
  • UI, 설정창
  • 스킬 봉인, 해금, 아이템 추가

+2025.01.02

더보기

+ 몹 리젠

딕셔너리는 신이야!

//EnemySpawner.cs
using UnityEngine;
using UnityEngine.Tilemaps;
using System.Collections.Generic; //Dictionary 사용

public class EnemySpawner : MonoBehaviour
{
    public Tilemap enemySpawnTilemap; // EnemySpawn 레이어의 타일맵
    public GameObject enemyPrefab;   // 적 프리팹
    public float lastTime;
    public float currentTime;
    public float enemySpawnCoolDownTime=10f;
    private Dictionary<Vector3Int, GameObject> enemyMap = new Dictionary<Vector3Int, GameObject>();

    void Start()
    {
        SpawnEnemies(ignoreCondition:true);
        lastTime=Time.time;
    }

    void Update()
    {
        currentTime=Time.time;
        if (currentTime-lastTime<enemySpawnCoolDownTime) {return;} // 쿨타임 다 안 되면 return
        SpawnEnemies(ignoreCondition:false);
        lastTime=currentTime;
    }

    void SpawnEnemies(bool ignoreCondition)
    {
        // 타일맵의 모든 타일 포지션 확인
        BoundsInt bounds = enemySpawnTilemap.cellBounds;
        TileBase[] allTiles = enemySpawnTilemap.GetTilesBlock(bounds);

        for (int x = 0; x < bounds.size.x; x++)
        {
            for (int y = 0; y < bounds.size.y; y++)
            {
                Vector3Int tilePosition = new Vector3Int(x + bounds.xMin, y + bounds.yMin, 0);
                TileBase tile = enemySpawnTilemap.GetTile(tilePosition);
                // Debug.Log($"Tile Position: {tilePosition}, Tile: {tile}");
                if (tile == null) { continue; }
                if (!ignoreCondition) // 만약 Start가 아니라면=Update라면
                {
                    // 만약 특정 타일의 몹이 죽지 않았다면 = 살아있다면
                    if (enemyMap.ContainsKey(tilePosition) && enemyMap[tilePosition] != null) { continue; }
                    // 만약 죽어있는, 혹은 생성되지 않았다면
                    else { enemyMap.Remove(tilePosition); }
                }


                Vector3 worldPosition = enemySpawnTilemap.CellToWorld(tilePosition) + new Vector3(0.5f, 0.5f, -6f);
                Debug.Log($"Spawning enemy at: {worldPosition}");
                GameObject enemy=Instantiate(enemyPrefab, worldPosition, Quaternion.identity);
                enemyMap[tilePosition]=enemy;
            }
        }
    }
}