내일배움캠프/프로젝트

팀 프로젝트 - 맵 에디터 기반 제작

서보훈 2024. 12. 11. 20:58

맵 제작을 위한 맵 에디터의 기반을 만들어주었습니다.

 

파란색이 교수측 플레이어 캐릭터의 생성위치, 노란색이 학생측 플레이어의 생성위치를 표기하며, 붉은색은 유령의 생성위치를 표기합니다.

 

Generate players spawn 버튼으로 2명의 플레이어 스폰 위치를 생성 할 수 있으며, Generate ghost spawn 으로 유령이 스폰될 위치를 정해줄 수 있습니다.

 

플레이어는 2명으로 고정되어있으며, 현재로써는 스폰 위치가 고정될 예정이기때문에 각각 하나씩의 스폰위치가 생성되면 이후로는 생성되지 않습니다.

 

유령의 경우 다수의 스폰 위치를 사용할 수 있으며, 이후 모든 위치에서 스폰이 될지, 일부 위치에서만 스폰시킬지 결정하게될듯 합니다.

 

Set spawn positions as Inspactor 와 Set spawn positions as Hierarchy 버튼의 경우 컴포넌트에 저장된 위치정보와 씬에 올라와있는 위치정보를 동기화 시켜주는 버튼입니다.

as Inspactor 를 누를경우 컴포넌트에 적어준 좌표로 씬에 위치한 오브젝트들의 실제 위치가 변경되며

as Hierarchy 를 누르면 씬의 오브젝트들 좌표로 컴포넌트의 정보가 변경되게 됩니다.

 

Remove players 와 Remove Ghosts 는 각각 씬의 플레이어 스폰위치, 유령 스폰위치를 전부 삭제시키는 역할을 합니다

Undo Ghost Spawn 의 경우, 마지막에 생성된 유령 스폰위치를 지우는 역할을 합니다.

 

아래는 에디터 제작에 사용된 코드 내용입니다.

더보기

에디터의 전체 내용입니다.

public class MapEditManager : MonoBehaviour
{
    public SpawnPointObject spawnPointPrefab;

    [Header("Map Setting")]
    public int mapNumber = 1;
    public string mapSavePath = "Assets/Maps";

    [Header("Player Spawn Positions")]
    public Vector3 professorPosition;
    public Vector3 studentPosition;

    private bool isPlayersCreated = false;
    private Transform professroTransform;
    private Transform studentTransform;

    [Header("Ghost Spawn Positions")]
    public List<Vector3> ghostPositions = new List<Vector3>();

    private List<Transform> ghostTransforms = new List<Transform>();

    //스폰포인트용
    private Color professorSpawnPointColor = Color.blue;
    private Color studentSpawnPointColor = Color.yellow;
    private Color ghostSpawnPointColor = Color.red;

    //플레이어 스폰 위치 생성
    public void CreatePlayerStartPosition()
    {
        if (isPlayersCreated) return;

        SpawnPointObject professor = Instantiate(spawnPointPrefab, new Vector3(0, 0, 0), Quaternion.identity, transform);
        professor.OnCreated("Professor Spawn Point", professorSpawnPointColor);

        SpawnPointObject student = Instantiate(spawnPointPrefab, new Vector3(5,0,0), Quaternion.identity, transform);
        student.OnCreated("Student Spawn Point", studentSpawnPointColor);

        professorPosition = professor.transform.position;
        studentPosition = student.transform.position;

        professroTransform = professor.transform;
        studentTransform = student.transform;

        isPlayersCreated = true;
    }

    //인스펙터창 기준으로 위치 변경
    public void SetPlayersPositonAsInspector()
    {
        if(!isPlayersCreated) return;

        professroTransform.localPosition = professorPosition;
        studentTransform.localPosition = studentPosition;
    }

    //씬 기준으로 위치 변경
    public void SetPlayersPositionAsHierarchy()
    {
        if (!isPlayersCreated) return;

        professorPosition = professroTransform.localPosition;
        studentPosition = studentTransform.localPosition;
    }

    public void ClearPlayers()
    {
        if (!isPlayersCreated) return;

        professorPosition = Vector3.zero;
        studentPosition = Vector3.zero;

        DestroyImmediate(professroTransform.gameObject);
        DestroyImmediate(studentTransform.gameObject);

        professroTransform = null;
        studentTransform = null;

        isPlayersCreated = false;
    }

    //유령 생성 위치
    public void CreateGhostPosition()
    {
        SpawnPointObject ghost = Instantiate(spawnPointPrefab, new Vector3(0, 0, 0), Quaternion.identity, transform);
        ghost.OnCreated("Ghost Spawn Point", ghostSpawnPointColor);

        ghostPositions.Add(ghost.transform.position);
        ghostTransforms.Add(ghost.transform);
    }

    public void SetGhostPositionsAsInspector()
    {
        for(int i = 0; i < ghostPositions.Count; i++)
        {
            ghostTransforms[i].localPosition = ghostPositions[i];
        }
    }

    public void SetGhostPositionsAsHierarchy()
    {
        for(int i = 0; i < ghostPositions.Count; i++)
        {
            ghostPositions[i] = ghostTransforms[i].localPosition;
        }
    }

    public void DestroyLastGhostPosition()
    {
        if (ghostPositions.Count == 0) return;

        int lastIndex = ghostPositions.Count - 1;
        ghostPositions.RemoveAt(lastIndex);
        Transform ghost = ghostTransforms[lastIndex];
        ghostTransforms.RemoveAt(lastIndex);

        DestroyImmediate(ghost.gameObject);
    }

    public void ClearGhostPositions()
    {
        if (ghostPositions.Count == 0) return;

        int count = ghostPositions.Count;
        for (int i = 0; i < count; i++)
        {
            DestroyLastGhostPosition();
        }
    }
}

 

Editor 를 상속받는, 커스텀 에디터 클래스입니다

[CustomEditor(typeof(MapEditManager))]
public class MapEditor : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        GUILayout.Space(10);

        MapEditManager manager = (MapEditManager)target;

        if(GUILayout.Button("Generate players spawn"))
        {
            manager.CreatePlayerStartPosition();
        }

        if(GUILayout.Button("Generate ghost spawn"))
        {
            manager.CreateGhostPosition();
        }

        GUILayout.Space(10);

        if(GUILayout.Button("Set sapwn positions as Inspactor"))
        {
            manager.SetPlayersPositonAsInspector();
            manager.SetGhostPositionsAsInspector();
        }

        if(GUILayout.Button("Set spawn positions as Hierarchy"))
        {
            manager.SetPlayersPositionAsHierarchy();
            manager.SetGhostPositionsAsHierarchy();
        }

        GUILayout.Space(10);

        if(GUILayout.Button("Remove players"))
        {
            manager.ClearPlayers();
        }

        if(GUILayout.Button("Remove Ghosts"))
        {
            manager.ClearGhostPositions();
        }

        if(GUILayout.Button("Undo Ghost spawn"))
        {
            manager.DestroyLastGhostPosition();
        }
    }
}

이후 만들어준 내용을 전부 json으로 저장하고, 저장된 내용을 기반으로 맵을 생성해줄 예정입니다.

[System.Serializable]
public class MapData
{
    //플레이어들 시작 위치
    public Vector3 professorSpawnPos;
    public Vector3 studentSpawnPos;

    //유령, 아이템 생성 위치
    public Vector3 ghostSpawnPos;
    public List<Vector3> itemSpawnPos;

    //퍼즐 정보...TODO

    //구조물 관련
    public List<string> structureResourcePaths;
    public List<Vector3> structurePositions;
}

 


위 내용에서 구조물에 대한 내용을 아직 어떻게 해야할지 고민중인 상태입니다.

별도 씬에서 구조물을 배치한 후, 해당 내용을 묶은 뒤 프리팹화 시켜서 저장을 시켜줄지,

 

에디터상에서 맵을 제작할 수 있도록 리소스 파일에 구조물을 프리팹으로 저장한 뒤, 프리팹의 주소와 배치 위치를 저장하여 씬이 시작될 때 리소스에서 똑같이 만들어줄 수 있도록 만들지

 

또한 이러한 내용을 어떻게 구현해줄것인지 고민중입니다.

 

제작중 크게 막히는 내용은 없었기 때문에, 트러블슈팅은 없습니다.