반응형
[37회차 수업내용] 25 0709
1. 리스트
2. 하노이의 탑 (스택)
* 형상관리 _ 의존성 관리
- 프리팹이 참조하는 머티리얼, 텍스처, 사운드 등 리소스가 누락되면 작동에 문제가 발생함. 리소스를 다 옮기면 너무 무거워질때 필요한것만 커밋하기 위해서 Select Dependencies를 활용할 수 있는데 이때 Packages 폴더 내 항목은 제외해야 함.
1. 리스트 실습_ 카드 섞기, 뽑기
public class CardDeck : MonoBehaviour
{
// 카드 덱을 나타내는 문자열 리스트 초기화 (A, B, C, D, E)
private List<string> deck = new List<string> { "A", "B", "C", "D", "E" };
// 카드 섞기 횟수 지정 (100번 섞음)
public int shakeCount = 100;
// 게임 시작 시 실행되는 코루틴
IEnumerator Start()
{
PrintDeck(); // 초기 덱 상태 출력
yield return new WaitForSeconds(1f); // 1초 대기
ShuffleDeck(); // 덱 섞기
PrintDeck(); // 섞은 후 덱 상태 출력
yield return new WaitForSeconds(1f); // 1초 대기
string card = DrawCard(); // 카드 한 장 뽑기
Debug.Log("Drawn: " + card); // 뽑은 카드 출력
yield return new WaitForSeconds(1f); // 1초 대기
// 덱의 상위 4장 출력
Debug.Log($"{deck[0]}, {deck[1]}, {deck[2]}, {deck[3]}");
yield return new WaitForSeconds(1f); // 1초 대기
}
// 덱을 랜덤으로 섞는 함수
void ShuffleDeck()
{
for (int i = 0; i < shakeCount; i++)
{
// 두 개의 랜덤 인덱스 선택
var ranInt1 = Random.Range(0, deck.Count);
var ranInt2 = Random.Range(0, deck.Count);
// 선택한 두 카드 위치 교환
var temp = deck[ranInt1];
deck[ranInt1] = deck[ranInt2];
deck[ranInt2] = temp;
}
}
// 덱에서 카드 한 장 뽑는 함수
string DrawCard()
{
if (deck.Count == 0)
{
Debug.LogWarning("Deck is Empty"); // 덱이 비었을 경우 경고 출력
return null;
}
string drawn = deck[0]; // 덱의 가장 앞 카드 선택 _ 그 값을 변수 drawn에 저장
deck.RemoveAt(0); // 리스트에서 인덱스 0 위치에 있는 요소를 제거하는 메서드_ 뽑은 카드는 덱에서 제거
return drawn; // 뽑은 카드 반환 _ 뽑은 카드(문자열)를 호출한 곳에 돌려줌.
}
// 현재 덱 상태를 로그로 출력하는 함수
void PrintDeck()
{
Debug.Log($"{deck[0]}, {deck[1]}, {deck[2]}, {deck[3]}, {deck[4]}");
}
}
2. Stack을 이용한 하노이타워
2.1. Probuilder
| 도형 생성 | Box, Sphere, Plane 등 기본 3D 도형 생성 가능 |
| 모델 편집 | Vertex(점), Edge(선), Face(면) 단위로 직접 수정 가능 |
| Extrude(돌출) | 면을 선택해 입체적으로 늘릴 수 있음 |
| UV 편집 | 텍스처가 입혀지는 좌표(UV) 직접 수정 가능 |
| Mesh Collider 설정 | 만든 모델에 물리 충돌 기능 설정 가능 |
| 그리드 스냅 | 정밀한 배치를 위한 격자 맞춤 지원 |
| 간단한 머티리얼 적용 | 머티리얼/컬러를 바로 적용 가능 |
Probuilder - 점으로 도형 만들기, or 특정 3D 모양 만들기




2.2 박스 콜라이더 만들기

2.3 코드
2.3.1 Number 부여
using UnityEngine;
public class Donut : MonoBehaviour
{
public int donutNumber;
}
2.3.2 하노이 탑 세팅
using System.Collections;
using UnityEngine;
public class HanoiTower : MonoBehaviour
{
// 하노이 난이도
// Lv1 = 3이라고 명시하면, 이후 값들은 자동으로 4, 5로 증가
public enum HanoiLevel { Lv1 = 3, Lv2, Lv3 }
public HanoiLevel hanoiLevel; // 현재 선택된 난이도 _ (인스펙터) 창에서 드롭다운 메뉴로 자동 표
public GameObject[] donutPrefabs; // 크기별 도넛 프리팹 배열 (작은 것부터 큰 것 순)
public BoardBar[] bars; // 세 개의 막대 (왼쪽, 가운데, 오른쪽)
public static GameObject selectedDonut;
public static bool isSelected;
IEnumerator Start()
{
// 선택한 난이도(hanoiLevel)만큼 반복 (큰 도넛부터 작은 도넛 순으로 생성)
//(int)hanoiLevel는 C#에서 enum을 정수로 변환하는 형변환(casting)
for (int i = (int)hanoiLevel - 1; i >= 0; i--)
{
// 도넛 프리팹을 생성
GameObject donut = Instantiate(donutPrefabs[i]);
// 도넛 생성 위치를 왼쪽 막대기 위쪽으로 지정
donut.transform.position = new Vector3(-5f, 5f, 0);
// 왼쪽 막대(bars[0])의 Stack에 도넛을 쌓음 (아래에서부터 위로)
bars[0].PushDonut(donut);
// 도넛 생성 간격을 1초로 설정 (순차적으로 등장하게 하기 위함)
yield return new WaitForSeconds(1f);
}
}
}
**리마인드
(int)hanoiLevel는 C#에서 enum을 정수로 변환하는 형변환(casting) 문법
using System.Collections.Generic;
using UnityEngine;
// 하노이의 탑에서 하나의 막대(Bar)를 제어하는 클래스
public class BoardBar : MonoBehaviour
{
// 막대의 종류 (왼쪽, 가운데, 오른쪽)
public enum BarType { Left, Center, Right }
public BarType barType;
// 현재 막대 위에 쌓여 있는 도넛들을 관리하는 Stack
public Stack<GameObject> barStack = new Stack<GameObject>();
// 사용자가 막대를 클릭했을 때 호출됨 (Collider가 있어야 동작)
void OnMouseDown()
{
if (!HanoiTower.isSelected) // 현재 선택된 도넛이 없을 경우
{
// 도넛 선택 처리
HanoiTower.isSelected = true;
// 현재 막대에서 가장 위에 있는 도넛을 꺼냄
HanoiTower.selectedDonut = PopDonut();
}
else // 이미 도넛이 선택된 상태일 경우
{
// 선택된 도넛을 현재 막대에 올림
PushDonut(HanoiTower.selectedDonut);
}
}
// 해당 도넛을 현재 막대에 올릴 수 있는지 확인 (작은 도넛만 큰 도넛 위에 가능)
public bool CheckDonut(GameObject donut)
{
if (barStack.Count > 0) // 막대 위에 도넛이 하나라도 있을 경우
{
// 새로 올리려는 도넛의 크기 번호
int pushNumber = donut.GetComponent<Donut>().donutNumber;
// 가장 위에 있는 도넛 가져오기 (제거하지 않음)
GameObject peekDonut = barStack.Peek();
int peekNumber = peekDonut.GetComponent<Donut>().donutNumber;
// 작은 도넛만 큰 도넛 위에 올릴 수 있음
if (pushNumber < peekNumber)
{
return true; // 올릴 수 있음
}
else
{
Debug.Log($"현재 넣으려는 도넛은 {pushNumber}이고, 해당 기둥의 제일 위의 도넛은 {peekNumber}입니다.");
return false; // 규칙 위반
}
}
return true; // 막대 위에 아무것도 없으면 올릴 수 있음
}
// 도넛을 현재 막대에 올리는 함수
public void PushDonut(GameObject donut)
{
// 규칙 위반이면 아무것도 하지 않음
if (!CheckDonut(donut))
return;
// 도넛 선택 상태 초기화
HanoiTower.isSelected = false;
HanoiTower.selectedDonut = null;
// 도넛을 막대 위 위치로 이동 (시각적 효과용)
donut.transform.position = transform.position + Vector3.up * 5f;
// 도넛의 물리 속도 초기화 (정확히 멈추도록)
donut.GetComponent<Rigidbody>().linearVelocity = Vector3.zero;
donut.GetComponent<Rigidbody>().angularVelocity = Vector3.zero;
// 도넛을 Stack에 쌓음
barStack.Push(donut);
}
// 막대에서 도넛을 꺼내는 함수 (맨 위에 있는 도넛을 제거 및 반환)
public GameObject PopDonut()
{
GameObject donut = barStack.Pop(); // Stack에서 꺼냄 (stack의 pop이니까 맨위)
return donut; // 꺼낸 도넛 반환
}
}

카메라 위치도 조정해보고 material의 색도 조정했다. 바닥과 막대의 콜라이더와 터치구역 트리거콜라이더도 넣었다. 스택이 활용되기 정석적인 예시인 것 같았고 Stack의 활용법을 이용할 수 있었다. 도넛끼리 조금 떠서 박스콜라이더 조정하면 좋을 것 같고, 도넛 크기나 두께도 더 예쁘게 조정해볼 수 있을 것 같다.
반응형
'프로그래밍 > 유니티 부트캠프' 카테고리의 다른 글
| 유니티(탐색 알고리즘) _ 멋쟁이사자처럼 유니티 부트캠프 후기 39회차 (0) | 2025.07.12 |
|---|---|
| 유니티(하노이의 탑 2,UI Stack, Queue 오브젝트 풀, 알고리즘1) _ 멋쟁이사자처럼 유니티 부트캠프 후기 38회차 (6) | 2025.07.10 |
| 유니티(자료구조2, 형상관리 깃브랜치, 배열 예제_3D_폭탄) _ 멋쟁이사자처럼 유니티 부트캠프 후기 36회차 (0) | 2025.07.08 |
| 유니티(자료구조1) _ 멋쟁이사자처럼 유니티 부트캠프 후기 35회차 (2) | 2025.07.07 |
| 유니티(몬스터 행동패턴 (인벤토리 기능/ 플랫포머게임 마무리 / 2D Rigging) _ 멋쟁이사자처럼 유니티 부트캠프 후기 34회차 (3) | 2025.07.05 |