본문 바로가기
개발 일지

CrowTale - 개발일지 2

by PrintedLove 2022. 5. 3.

안녕하세요 Printed입니다.

이제 대충 블록 작업이 끝나가는것 같습니다.

여기서 몇 개 더 추가하고 이제 NPC 대화와 전투쪽에 집중해 볼까 합니다.

 

이번 업데이트로 큰 변경점이 있었는데, 제가 골머리를 썩였던 부분이라 다른 분들은 저처럼 삽질 안하셨으면 하는 바램에서 설명을 추가해 봅니다.

 

일단 영상입니다.

 

 

영상을 보시면 플레이 동시에 버퍼링과 함께 타일맵에 동적으로 바위, 수풀, 나무와 같은 장식 레이어 타일들이 배치되는 것을 확인하실 수 있습니다.

유니티 내에서 랜덤 룰 타일과 그룹 타일을 커스텀하면 어떻게든 이런 기능을 만들 순 있을 것 같은데, 여러 타일맵에 배치된 타일들의 정보를 가져오는(플랫폼 위에 정상적으로 있는지, 데코 타일끼리 겹치진 않았는지) 방법을 몰라서

그냥 무식하게 룰타일로 오브젝트를 깔고, 첫 업데이트문에서 검사를 돌려서 타일과 쉐도우 프리팹을 까는 방법을 사용했습니다.

 

그림자 프리팹도 동적할당 함수가 없어서 커스텀으로 하나 만들어서 뜯어 고쳤구요...

아래는 그 노가다의 결과물입니다.

 

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

// 타일 그룹 종류
enum Tiletype:int
{
    Stone_1, Stone_2, Stone_3, Stone_4, Bush_1, Bush_2, Bush_3, Bush_4
    ,Tree_1, Tree_2, Tree_3, Tree_4, Tree_5, Tree_6
}

public class DecoratiionTileGenerator : MonoBehaviour
{
    //타일 그룹 종류에 따른 타일 리스트 저장용 구조체
    struct DecoratiionTile
    {
        public Tiletype name;
        public Tile[] tileList;

        public DecoratiionTile(Tiletype name, Tile[] tileList)
        {
            this.name = name;
            this.tileList = tileList;
        }

        public DecoratiionTile(Tiletype name, Tile tileList)
        {
            this.name = name;
            this.tileList = new Tile[1];
            this.tileList[0] = tileList;
        }
    }

    public GameObject tilemap_Ground, tilemap_Stones, tilemap_Bushs, tilemap_Trees, tilemap_Objects;
    public Tile[] tiles;
    List<DecoratiionTile> Tileset = new List<DecoratiionTile>();

    private void Awake() {
        //타일 그룹 리스트에에 타일리스트 저장
        Tileset.Add(new DecoratiionTile(Tiletype.Stone_1, tiles[0]));
        Tileset.Add(new DecoratiionTile(Tiletype.Stone_2, tiles[1]));
        Tileset.Add(new DecoratiionTile(Tiletype.Stone_3, tiles[2]));
        Tileset.Add(new DecoratiionTile(Tiletype.Stone_4, tiles[3]));
        Tileset.Add(new DecoratiionTile(Tiletype.Bush_1, tiles[4]));
        Tileset.Add(new DecoratiionTile(Tiletype.Bush_2, tiles[5]));
        Tileset.Add(new DecoratiionTile(Tiletype.Bush_3, tiles[6]));
        Tileset.Add(new DecoratiionTile(Tiletype.Bush_4, tiles[7]));
        Tileset.Add(new DecoratiionTile(Tiletype.Tree_1
        , new Tile[8] {tiles[34], tiles[35], tiles[22], tiles[23], tiles[12], tiles[13], tiles[8], tiles[9]}));
        Tileset.Add(new DecoratiionTile(Tiletype.Tree_2
        , new Tile[8] {tiles[36], tiles[37], tiles[24], tiles[25], tiles[14], tiles[15], tiles[10], tiles[11]}));
        Tileset.Add(new DecoratiionTile(Tiletype.Tree_3
        , new Tile[6] {tiles[38], tiles[39], tiles[26], tiles[27], tiles[16], tiles[17]}));
        Tileset.Add(new DecoratiionTile(Tiletype.Tree_4
        , new Tile[6] {tiles[40], tiles[41], tiles[28], tiles[29], tiles[18], tiles[19]}));
        Tileset.Add(new DecoratiionTile(Tiletype.Tree_5
        , new Tile[6] {tiles[42], tiles[43], tiles[30], tiles[31], tiles[20], tiles[21]}));
        Tileset.Add(new DecoratiionTile(Tiletype.Tree_6
        , new Tile[4] {tiles[44], tiles[45], tiles[32], tiles[33]}));
    }

    private void LateUpdate() {
        //다른 타일들의 위치정보를 받아오기 위해서 후순위 업데이트문에서 타일 생성
        GenerateTile();
        Destroy(this);
    }

    void GenerateTile()
    {
        //Ground Rule Tile(Assets - Others - Tilemaps)에 의해 상단이 빈공간인 플랫폼 타일위에 생성된 오브젝트 리스트를 받아옴.
        GameObject[] objList = GameObject.FindGameObjectsWithTag("Decoration Tile Generator");

        for (int i = 0; i < objList.Length; i++)
        {
            Vector3 objPosRaw = objList[i].GetComponent<Transform>().position;      //실제 오브젝트 위치
            Vector3Int objPos = new Vector3Int((int)objPosRaw.x, (int)objPosRaw.y, 0);      //float -> Int 처리한 오브젝트 위치
            Vector3Int setPos = new Vector3Int(objPos.x, objPos.y + 1, objPos.z);           //타일 생성 시작 위치

            if (calProbability(35))
            {
                 int tiletype;

                //하단에 플랫폼 타일이 위치하고, 생성할 위치에 플랫폼 타일이 없을 경우.
                if (tilemap_Ground.GetComponent<Tilemap>().GetTile(objPos) != null
                 && tilemap_Ground.GetComponent<Tilemap>().GetTile(new Vector3Int(objPos.x, objPos.y + 1, objPos.z)) == null) {
                    if(calProbability(19))      // 19%확률로 나무 생성
                    {
                        //생성 위치에 다른 데코레이션 타일이 없을 경우
                        if(tilemap_Ground.GetComponent<Tilemap>().GetTile(new Vector3Int(objPos.x + 1, objPos.y, objPos.z)) != null
                         && tilemap_Stones.GetComponent<Tilemap>().GetTile(new Vector3Int(objPos.x + 1, objPos.y + 1, objPos.z)) == null
                         && tilemap_Bushs.GetComponent<Tilemap>().GetTile(new Vector3Int(objPos.x + 1, objPos.y + 1, objPos.z)) == null)
                        {
                            tiletype = Random.Range((int)Tiletype.Tree_1, (int)Tiletype.Tree_6 + 1);
                            bool isGenOk = true;

                            //생성 위치에 같은 타일맵의 타일 그룹이 겹치지 않는지 검사
                            for (int n = 0; n < Tileset[tiletype].tileList.Length; n++)
                            {
                                if(tilemap_Trees.GetComponent<Tilemap>().GetTile(new Vector3Int(setPos.x + (n % 2), setPos.y + n / 2, objPos.z)) != null)
                                    isGenOk = false;
                            }

                            if(isGenOk)
                            {
                                for (int n = 0; n < Tileset[tiletype].tileList.Length; n++)
                                {
                                    tilemap_Trees.GetComponent<Tilemap>().SetTile(
                                        new Vector3Int(setPos.x + (n % 2), setPos.y + n / 2, objPos.z), Tileset[tiletype].tileList[n]);
                                }
                            }
                        }
                    }
                    else 
                    {
                        if(calProbability(38))  //나무가 아닐시 38% 확률로 암석 생성
                        {
                            tiletype = Random.Range((int)Tiletype.Stone_1, (int)Tiletype.Stone_4 + 1);
                            tilemap_Stones.GetComponent<Tilemap>().SetTile(setPos, Tileset[tiletype].tileList[0]);
                        }
                        else
                        {
                            tiletype = Random.Range((int)Tiletype.Bush_1, (int)Tiletype.Bush_4 + 1);
                            tilemap_Bushs.GetComponent<Tilemap>().SetTile(setPos, Tileset[tiletype].tileList[0]);
                        }
                    }
                }
            }

            Destroy(objList[i].gameObject);     //위치 감지용 오브젝트 제거
            objList[i] = null;
        }

        tilemap_Stones.GetComponent<TilemapCollider2D>().enabled = true;
        tilemap_Stones.GetComponent<TilemapShadowCaster2DCustom>().MakeShadow();    //쉐도우캐스터 동적 생성
        tilemap_Bushs.GetComponent<TilemapCollider2D>().enabled = true;
        tilemap_Bushs.GetComponent<TilemapShadowCaster2DCustom>().MakeShadow();
    }

    bool calProbability(byte chance)    //퍼센트 확률 계산용 함수
    {
        if (chance >= Random.Range(1, 101))
            return true;
        else
            return false;
    }
}

 

먼저 룰타일로 깔아둔 플랫폼 타일에서 윗칸이 빈(잔디가 덮인) 타일들에 오브젝트를 생성합니다.

그 후, 생성한 오브젝트들을 리스트로 받아와서 해당 오브젝트 위치에 랜덤한 확률로 타일들을 깔아줍니다. 이때, 쉐도우 캐스터를 필요한 타일들에 동적으로 생성해 줍니다.

 

생성할 위치를 받아올 목적으로 생성한 오브젝트들은 생성과 동시에 제거하고, 해당 코드는 동작후 소멸시켜 리소스 소모를 최대한 줄였습니다.

작동해보니, 처음 시작시에 렉이 좀 걸리지만 그 후에는 직접 브러쉬로 타일을 찍은 정도로 유지되더군요.

 

다음 업데이트도 빠르게 만들어 오겠습니다!

 

 

 

 

아래 깃허브 링크에서 진행중인 프로젝트의 전체 코드를 확인하실 수 있습니다!

 

GitHub - PrintedLove/CrowTale: 유니티 기반 우주하마 팬게임 CrowTale 의 소스코드 및 리소스

유니티 기반 우주하마 팬게임 CrowTale 의 소스코드 및 리소스. Contribute to PrintedLove/CrowTale development by creating an account on GitHub.

github.com

 

'개발 일지' 카테고리의 다른 글

CrowTale - 개발일지 3  (1) 2022.07.03
CrowTale - 개발일지 2.5 - 타이틀 화면  (1) 2022.05.16
CrowTale - 개발일지 1  (0) 2022.04.13
퍼런 하마 이야기 후속작 - CrowTale  (4) 2021.08.24
Pygame RPG Tutorial [Python]  (1) 2021.06.07

댓글