[Unity] Stack

2025. 2. 17. 22:20·Game Engine/Unity
using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TheStack : MonoBehaviour
{
    private const float BoundSize = 3.5f;
    private const float MovingBoundsSize = 3f;
    private const float StackMovingSpeed = 5.0f;
    private const float BlockMovingSpeed = 3.5f;
    private const float ErrorMargin = 0.1f;

    public GameObject originBlock = null;
    private Vector3 prevBlockPosition;
    private Vector3 desiredPosition;
    private Vector3 stackBounds = new Vector2(BoundSize, BoundSize);
    Transform lastBlock = null;
    float blockTransition = 0f;
    float secondaryPosition = 0f;

    int stackCount = -1;
    public int Score { get { return stackCount; } }
    int comboCount = 0;
    public int Combo { get { return comboCount; } }
    private int maxCombo = 0;
    public int MaxCombo { get => maxCombo; }
  
    public Color prevColor;
    public Color nextColor;
    bool isMovingX = true;

    int bestScore = 0;
    public int BestScore { get => bestScore; }
    public int bestCombo = 0;
    public int BestCombo { get => bestCombo; }
    private const string BestScoreKey = "BestScore";
    private const string BestComboKey = "BestCombo";

    private bool isGameOver = true;

    // Start is called before the first frame update
    void Start()
    {
        if (originBlock == null)
        {
            Debug.Log("OriginBlock is Null");
            return;
        }

        bestScore = PlayerPrefs.GetInt(BestScoreKey, 0);
        bestCombo = PlayerPrefs.GetInt(BestComboKey, 0);
        prevColor = GetRandomColor();
        nextColor = GetRandomColor();
        prevBlockPosition = Vector3.down;

        Spawn_Block();
        Spawn_Block();

    }

    // Update is called once per frame
    void Update()
    {
        if (isGameOver == true) return;
        if (Input.GetMouseButtonDown(0))
        {
            if (PlaceBlock())
            {
                Spawn_Block();

            }
            else
            {
                Debug.Log("Game Over");
                UpdateScore();
                isGameOver = true;
                GameOverEffect();
                UIManager.Instance.SetScoreUI();
            }

        }
        MoveBlock();
        transform.position = Vector3.Lerp(transform.position, desiredPosition, StackMovingSpeed * Time.deltaTime);
    }

    bool Spawn_Block()
    {
        if (lastBlock != null)
            prevBlockPosition = lastBlock.localPosition;
        GameObject newBlock = null;
        Transform newTrans = null;

        newBlock = Instantiate(originBlock);

        if (newBlock == null)
        {
            Debug.Log("NewBlock Instantiate Failed");
            return false;
        }
        ColorChange(newBlock);

        newTrans = newBlock.transform;
        newTrans.parent = this.transform;
        newTrans.localPosition = prevBlockPosition + Vector3.up;
        newTrans.localRotation = Quaternion.identity;
        newTrans.localScale = new Vector3(stackBounds.x, 1, stackBounds.y);
        stackCount++;

        desiredPosition = Vector3.down * stackCount;
        blockTransition = 0f;
        lastBlock = newTrans;

        isMovingX = !isMovingX;

        UIManager.Instance.UpdateScore();
        return true;


    }
    Color GetRandomColor()
    {
        float r = Random.Range(100f, 250f) / 255f;
        float g = Random.Range(100f, 250f) / 255f;
        float b = Random.Range(100f, 250f) / 255f;

        return new Color(r, g, b);
    }

    void ColorChange(GameObject go)
    {
        Color applyColor = Color.Lerp(prevColor, nextColor, (stackCount % 11) / 10f);
        Renderer rn = go.GetComponent<Renderer>();

        if ((rn == null))
        {
            Debug.Log("Renderer is Null");
            return;

        }

        rn.material.color = applyColor;
        Camera.main.backgroundColor = applyColor - new Color(0.1f, 0.1f, 0.1f);
        if (applyColor.Equals(nextColor) == true)
        {
            prevColor = nextColor;
            nextColor = GetRandomColor();
        }
    }

    void MoveBlock()
    {
        blockTransition += Time.deltaTime * BlockMovingSpeed;
        float movePosition = Mathf.PingPong(blockTransition, BoundSize) - BoundSize / 2;

        if (isMovingX)
        {
            lastBlock.localPosition = new Vector3(movePosition * MovingBoundsSize, stackCount, secondaryPosition);

        }
        else
        {
            lastBlock.localPosition = new Vector3(secondaryPosition, stackCount, -movePosition * MovingBoundsSize);
        }
    }

    bool PlaceBlock()
    {
        Vector3 lastPosition = lastBlock.localPosition;
        
        if ((isMovingX))
        {

            float deltaX = prevBlockPosition.x - lastPosition.x;
            bool isNegativeNum = (deltaX < 0) ? true : false;
            deltaX = Mathf.Abs(deltaX);

            if (deltaX > ErrorMargin)
            {
                stackBounds.x -= deltaX;
                if (stackBounds.x <= 0)
                {
                    return false;
                }
                float middle = (prevBlockPosition.x +lastPosition.x) / 2f;
                lastBlock.localScale = new Vector3(stackBounds.x, 1, stackBounds.y);

                Vector3 tempPosition = lastBlock.localPosition;
                tempPosition.x = middle;
                lastBlock.localPosition = lastPosition = tempPosition;

                float rubbleHalfScale = deltaX / 2f;
                CreateRubble(new Vector3(isNegativeNum ?
                    lastPosition.x + stackBounds.x / 2 + rubbleHalfScale
                    : lastPosition.x - stackBounds.x / 2 - rubbleHalfScale
                    , lastPosition.y
                    , lastPosition.z)
                    , new Vector3(deltaX, 1, stackBounds.y)
                    );
                comboCount = 0;
            }
            else
            {
                ComboCheck();
                lastPosition = prevBlockPosition + Vector3.up;

            }
        }
        else
        {
            float deltaZ = prevBlockPosition.z - lastPosition.z;
            bool isNegativeNum = (deltaZ < 0) ? true : false;


            deltaZ = Mathf.Abs(deltaZ);
            if (deltaZ > ErrorMargin)
            {
                stackBounds.y -= deltaZ;
                if (stackBounds.y <= 0)
                {
                    return false;
                }

                float middle = (prevBlockPosition.z + lastPosition.z) / 2f;
                lastBlock.localScale = new Vector3(stackBounds.x, 1, stackBounds.y);

                Vector3 tempPosition = lastBlock.localPosition;
                tempPosition.z = middle;
                lastBlock.localPosition = lastPosition = tempPosition;

                float rubbleHalfScale = deltaZ / 2f;
                CreateRubble(
                    new Vector3(
                        lastPosition.x,
                        lastPosition.y,
                        isNegativeNum ?
                        lastPosition.z + stackBounds.y / 2 + rubbleHalfScale
                        : lastPosition.z - stackBounds.y / 2 - rubbleHalfScale),
                    new Vector3(stackBounds.x, 1, deltaZ));
                comboCount = 0;
            }
            else
            {
                ComboCheck();
                lastBlock.localPosition = prevBlockPosition + Vector3.up;
            }


        }
        secondaryPosition = (isMovingX) ? lastBlock.localPosition.x : lastBlock.localPosition.z;
        return true;
    }
    void CreateRubble(Vector3 pos, Vector3 scale)
    {
        GameObject go = Instantiate(lastBlock.gameObject);
        go.transform.parent = this.transform;

        go.transform.localPosition = pos;
        go.transform.localScale = scale;
        go.transform.localRotation = Quaternion.identity; 

        go.AddComponent<Rigidbody>();
        go.name = "Rubble";

    }

    void ComboCheck()
    {
        comboCount++;
        if (comboCount > maxCombo)
            maxCombo = comboCount;

        if((comboCount % 5) ==0)
        {
            Debug.Log("5 Combo Success!");
            stackBounds += new Vector3(0.5f, 0.5f);
            stackBounds.x = (stackBounds.x >BoundSize) ? BoundSize : stackBounds.x;
            stackBounds.y = (stackBounds.y > BoundSize) ? BoundSize : stackBounds.y;
        }
    }

    void UpdateScore()
    {
        if (bestScore < stackCount)
        {
            Debug.Log("최고 점수 갱신");
            bestScore = stackCount;
            bestCombo = maxCombo;

            PlayerPrefs.SetInt(BestScoreKey, bestScore);
            PlayerPrefs.SetInt(BestComboKey, bestCombo);    
        }
    }

    void GameOverEffect()
    {
        int childCount = this.transform.childCount;
        for(int i =1; i<20; i++)
        {
            if (childCount <i) break;
            GameObject go = transform.GetChild(childCount - i).gameObject;

            if (go.name.Equals("Rubble")) continue;

            Rigidbody rigid = go.AddComponent<Rigidbody>();

            rigid.AddForce((Vector3.up * Random.Range(0, 10f) + Vector3.right * (Random.Range(0, 10f) - 5f)) * 100f);
        }
    }

    public void Restart()
    {
        int childCount = transform.childCount;

        for(int i =0; i<childCount; i++)
        {
            Destroy(transform.GetChild(i).gameObject);

        }
        isGameOver = false;

        lastBlock = null;
        desiredPosition =  Vector3.zero;
        stackBounds = new Vector3(BoundSize, BoundSize);

        stackCount = -1;
        isMovingX = true;
        blockTransition = 0f;
        secondaryPosition = 0f;

        comboCount = 0;
        maxCombo = 0;

        prevBlockPosition = Vector3.down;
        prevColor = GetRandomColor();
        nextColor = GetRandomColor();

        Spawn_Block();
        Spawn_Block();

    }
}

'Game Engine > Unity' 카테고리의 다른 글

[Unity] 트러블슈팅  (0) 2025.02.20
[Unity] 플레이어를 따라오는 카메라 구현  (0) 2025.02.19
[Unity] 이동구현  (0) 2025.02.18
[Unity] Rigidbody  (0) 2025.02.14
[Unity] Unity 기본  (0) 2025.02.14
'Game Engine/Unity' 카테고리의 다른 글
  • [Unity] 플레이어를 따라오는 카메라 구현
  • [Unity] 이동구현
  • [Unity] Rigidbody
  • [Unity] Unity 기본
Xenawn
Xenawn
제넌 게임개발 블로그
  • Xenawn
    Xenawn
    Xenawn
  • 전체
    오늘
    어제
    • 분류 전체보기 (77)
      • Language (24)
        • C++ (4)
        • C# (20)
      • Game Engine (32)
        • Unity (19)
        • Unity API (1)
        • Game Project (12)
      • Git (2)
      • Algorithm (9)
        • BOJ [C++] (8)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    fps cam
    프레임
    headbob
    걸음fps
    FizzBuzz
    내일배움캠프
    BOJ
    string format
    객체
    c#
    POTION
    클래스
    C++
    FPS
    Unity
    1181
    유니티
    카메라 움직임
    포션
    블랙잭
    문자열 보간
    스파르타내일배움캠프 #스파르타내일배움캠프til
    리스트
    배열
    CPP
    프로퍼티
    백준
    알고리즘
    게임개발
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Xenawn
[Unity] Stack
상단으로

티스토리툴바