본문 바로가기

유니티(리스트, 스택 실습 _ 하노이의 탑) _ 멋쟁이사자처럼 유니티 부트캠프 후기 37회차

@salmu2025. 7. 10. 16:06
반응형

[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 모양 만들기 

 

 

 

 

Torus 만들기

 

 

 

 

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)hanoiLevelC#에서 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의 활용법을 이용할 수 있었다. 도넛끼리 조금 떠서 박스콜라이더 조정하면 좋을 것 같고, 도넛 크기나 두께도 더 예쁘게 조정해볼 수 있을 것 같다.

반응형
salmu
@salmu :: SMU 각종 기록

목차