2024년 1월 18일 목요일

Unity - 미로 게임

 

미로 생성을 위한 코드는 여기서 가져옴

[유니티] 절차적 생성을 위한 미로 생성 알고리즘 2 (velog.io)



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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System.Collections.Generic;
using UnityEngine;
using static UnityEditor.PlayerSettings;
 
public class MazeRunner : MonoBehaviour
{
 
    [SerializeField] private DragToMoveController m_controller;
    [SerializeField] private GameObject m_wall;//미로 안쪽 벽의 프리펩
 
    private bool[,] m_map;
    private int m_mazeWidth = 11;//벽까지 포함한 크기
    private int m_mazeHeight = 17;//벽까지 포함한 크기
    private Vector2Int m_pos = Vector2Int.zero;
    private Vector2Int[] m_direction = { Vector2Int.up, Vector2Int.down, Vector2Int.left, Vector2Int.right };
    private Stack<Vector2Int> m_stackDir = new Stack<Vector2Int>(); //지나온 길의 방향 저장
    private float m_timer = 0.0f;//타이머
    
    private void Start()
    {
        m_timer = 10.0f;
        m_controller.SetMoveSpeed(6.0f);
 
        m_map = new bool[m_mazeWidth, m_mazeHeight];//미로 크기 9x9
        m_pos = new Vector2Int(1, 1);
 
        for (int x = 0; x < m_mazeWidth; x++)
        {
            for (int y = 0; y < m_mazeHeight; y++)
            {
                m_map[x, y] = true; //모든 타일을 벽으로 채움
            }
        }
 
        BuildMaze();
 
        //벽 배치
        for (int x = 1; x < m_mazeWidth-1; x++)
        {
            for (int y = 1; y < m_mazeHeight-1; y++)
            {
                if (m_map[x, y])
                {
                    GameObject obj = Instantiate(m_wall, transform.GetChild(1));
                    obj.transform.position = new Vector3(0.8f * x-4, 0.8f, 0.8f * y-6.8f);
                }
                
            }
        }
    }
 
    // Update is called once per frame
    private void FixedUpdate()
    {
        m_timer -= Time.deltaTime;
        m_controller.UpdateMove();
    }
 
    private void BuildMaze()
    {
        do
        {
            int index = -1; //-1은 갈 수 있는 길이 없음을 의미
 
            RandDirection(); //방향 무작위로 섞음
 
            for (int i = 0; i < m_direction.Length; i++)
            {
                if (CheckWall(i))
                {
                    index = i; //선택한 방향에 길이 없을 경우 방향 배열의 인덱스 저장
                    break;
                }
            }
 
            if (index != -1) //갈 수 있는 길이 있을 경우
            {
                for (int i = 0; i < 2; i++) //길과 길 사이에 벽을 생성하기 위해 3칸을 길로 바꿈
                {
                    m_stackDir.Push(m_direction[index]); //스택에 방향 저장
                    m_pos += m_direction[index]; //위치 변수 수정
                    m_map[m_pos.x, m_pos.y] = false; //타일 생성
                }
            }
            else //갈 수 있는 길이 없을 경우
            {
                for (int i = 0; i < 2; i++) //길을 만들 때 3칸을 만들었기 때문에 뒤로 돌아갈 때도 3칸 이동
                {
                    m_map[m_pos.x, m_pos.y] = false; //완성된 길 의미
                    m_pos += m_stackDir.Pop() * -1; //방향을 저장하는 스택에서 데이터를 꺼낸뒤 -1을 곱해 방향 반전
                }
            }
        }
        while (m_stackDir.Count != 0); //스택이 0이라는 것은 모든 길을 순회했다는 것을 의미하므로 반복문 종료
 
 
    }
 
    private void RandDirection() //무작위로 방향을 섞는 메소드
    {
        for (int i = 0; i < m_direction.Length; i++)
        {
            int randNum = Random.Range(0, m_direction.Length); //4방향 중 무작위로 방향 선택
            Vector2Int temp = m_direction[randNum]; //현재 인덱스에 해당하는 방향과 랜덤으로 선택한 방향을 서로 바꿈
            m_direction[randNum] = m_direction[i];
            m_direction[i] = temp;
        }
    }
 
    private bool CheckWall(int index)
    {
        if ((m_pos + m_direction[index] * 2).x > m_mazeWidth - 2) return false; //2를 곱하는 이유는 길과 길 사이에 벽이 있기 때문
        if ((m_pos + m_direction[index] * 2).x < 0) return false;
        if ((m_pos + m_direction[index] * 2).y > m_mazeHeight - 2) return false;
        if ((m_pos + m_direction[index] * 2).y < 0) return false;
        if (!m_map[(m_pos + m_direction[index] * 2).x, (m_pos + m_direction[index] * 2).y]) return false;
 
        return true;
    }
 
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
            Debug.LogFormat("GameClear in {0}sec", m_timer);
    }
}
 
cs

bool 배열에 미로를 설계하고 이것에 맞춰서

벽 프리펩을 Instantiate 구현해준다.



미로 시작 위치는 정해놨지만 끝부분을 안뚫어놔서  

가끔 막히는 경우가 있다. 이럴 땐

m_map[x, y] = false; 특정 위치의 벽을 없애면 그만이다.



댓글 없음:

댓글 쓰기

c++ thread.h

 c++에서 쓰레드 돌릴려면 thread.h 헤더를 쓰면 되는데 이 친구는 쓰레드가 아직 실행 중인지, 아니면 강제 종료하거나 하는 함수가 없어서 조금 아쉬운 애다. std::thread 는 로컬 변수로 선언하든 new 동적 할당을 하든 start 함...