카테고리 없음

2024.02.22 내일배움캠프 41일차 TIL_Unity (심화, 알고리즘)

mongle_0l 2024. 2. 22. 19:44

의외로 시간이 많이 남아 만들고 싶었던 게임을 만들어 보았다.

한번 쯤은 만들어 보고 싶었던 하이퍼 캐주얼 게임을 만들어 보았다.

 

오류

게임을 계속 하다보면 큐브가 높게 쌓이게 되는데 카메라가 못따라와서 젤 위에있는 큐브가 안보인다..

또한 큐브을 쌓으면 쌓을 수록 블록이 이동하는 방향에서 범위?가 일정하지 않다.


알고리즘 코드카타 26일차

수박수박수박수박수박수?

문제 설명
길이가 n이고, "수박수박수박수...."와 같은 패턴을 유지하는 문자열을 리턴하는 함수, solution을 완성하세요. 예를들어 n이 4이면 "수박수박"을 리턴하고 3이라면 "수박수"를 리턴하면 됩니다.
public class Solution {
    public string solution(int n) {
        string answer = "";
        for(int i = 0; i < n; i++)
        {
        answer += i % 2 == 0 ? "수" : "박";
        }
        return answer;
    }
}


Stack 게임 만들어 보기

큐브을 생성하는 코드이다
더보기
using UnityEngine;

public enum MoveAxis { x = 0, z }

public class CubeSpawner : MonoBehaviour
{
    [SerializeField]
    private Transform[] cubeSpawnPoints;
    [SerializeField]
    private Transform movingCubePrefab;
    [SerializeField]
    private PerfectController perfectController;

    [field: SerializeField]
    public Transform LastCube { set; get; }
    public MovingCube CurrentCube { set; get; } = null;

    [SerializeField]
    private float colorWeight = 15.0f;

    private int currentColorNumberOfTime = 5;
    private int maxColorNumberOfTime = 5;

    private MoveAxis moveAxis = MoveAxis.x;

    public void SpawnCube()
    {
        // 이동 큐브 생성
        Transform clone = Instantiate(movingCubePrefab);

        // 방금 생성한 이동 큐브의 위치
        if (LastCube == null || LastCube.name.Equals("StartCubeTop"))
        {
            clone.position = cubeSpawnPoints[(int)moveAxis].position;
        }
        else
        {
            float x = moveAxis == MoveAxis.x ? cubeSpawnPoints[(int)moveAxis].position.x : LastCube.position.x;
            float z = moveAxis == MoveAxis.z ? cubeSpawnPoints[(int)moveAxis].position.z : LastCube.position.z;

            float y = LastCube.position.y + movingCubePrefab.localScale.y;

            clone.position = new Vector3(x, y, z);
        }

        // 방금 생성한 이동 큐브의 크기
        clone.localScale = new Vector3(LastCube.localScale.x, movingCubePrefab.localScale.y, LastCube.localScale.z);

        // 방금 생성한 이동 큐브의 색상
        clone.GetComponent<MeshRenderer>().material.color = GetRandomColor();

        // 방금 생성한 이동 큐브의 Setup() 메소드 호출 (이동방향 전달)
        clone.GetComponent<MovingCube>().Setup(this, perfectController, moveAxis);

        moveAxis = (MoveAxis)(((int)moveAxis + 1) % cubeSpawnPoints.Length);

        // 방금 생성한 이동 큐브의 정보를 CurrentCube 프로퍼티에 저장
        CurrentCube = clone.GetComponent<MovingCube>();
    }

    private void OnDrawGizmos()
    {
        for (int i = 0; i < cubeSpawnPoints.Length; ++i)
        {
            Gizmos.color = Color.green;
            Gizmos.DrawWireCube(cubeSpawnPoints[i].transform.position, movingCubePrefab.localScale);
        }
    }

    private Color GetRandomColor()
    {
        Color color = Color.white;

        if (currentColorNumberOfTime > 0)
        {
            float colorAmount = (1.0f / 255.0f) * colorWeight;
            color = LastCube.GetComponent<MeshRenderer>().material.color;
            color = new Color(color.r - colorAmount, color.g - colorAmount, color.b - colorAmount);

            currentColorNumberOfTime--;
        }
        else
        {
            color = new Color(Random.value, Random.value, Random.value);

            currentColorNumberOfTime = maxColorNumberOfTime;
        }

        return color;
    }
}
큐브을 이동하고 위치와 크기 연산 코드이다
더보기
using UnityEngine;

public class MovingCube : MonoBehaviour
{
    [SerializeField]
    private float moveSpeed = 1.5f;
    private Vector3 moveDirection;

    private CubeSpawner cubeSpawner;
    private PerfectController perfectController;

    private MoveAxis moveAxis;

    public void Setup(CubeSpawner cubeSpawner, PerfectController perfectController, MoveAxis moveAxis)
    {
        this.cubeSpawner = cubeSpawner;
        this.perfectController = perfectController;
        this.moveAxis = moveAxis;

        if (moveAxis == MoveAxis.x) moveDirection = Vector3.left;
        else if (moveAxis == MoveAxis.z) moveDirection = Vector3.back;
    }

    private void Update()
    {
        transform.position += moveDirection * moveSpeed * Time.deltaTime;

        if (moveAxis == MoveAxis.x)
        {
            if (transform.position.x <= -1.5f) moveDirection = Vector3.right;
            else if (transform.position.x >= 1.5f) moveDirection = Vector3.left;
        }
        else if (moveAxis == MoveAxis.z)
        {
            if (transform.position.z <= -1.5f) moveDirection = Vector3.forward;
            else if (transform.position.z >= 1.5f) moveDirection = Vector3.back;
        }
    }

    public bool Arrangement()
    {
        moveSpeed = 0;

        float hangOver = GetHangOver();

        if (IsGameOver(hangOver))
        {
            return true;
        }

        // 퍼펙트 여부 검사
        bool isPerfect = perfectController.IsPerfect(hangOver);

        // isPerfect가 false 일 때만 큐브 조각 생성
        if (isPerfect == false)
        {
            float direction = hangOver >= 0 ? 1 : -1;

            if (moveAxis == MoveAxis.x)
            {
                SplitCubeOnX(hangOver, direction);
            }
            else if (moveAxis == MoveAxis.z)
            {
                SplitCubeOnZ(hangOver, direction);
            }
        }

        cubeSpawner.LastCube = this.transform;

        return false;
    }

    private float GetHangOver()
    {
        float amount = 0;

        if (moveAxis == MoveAxis.x)
        {
            amount = transform.position.x - cubeSpawner.LastCube.transform.position.x;
        }
        else if (moveAxis == MoveAxis.z)
        {
            amount = transform.position.z - cubeSpawner.LastCube.transform.position.z;
        }

        return amount;
    }

    private void SplitCubeOnX(float hangOver, float direction)
    {
        // 이동 큐브의 새로운 위치, 크기 연산
        float newXPosition = transform.position.x - (hangOver / 2);
        float newXSize = transform.localScale.x - Mathf.Abs(hangOver);
        // 이동 큐브의 위치, 크기 설정
        transform.position = new Vector3(newXPosition, transform.position.y, transform.position.z);
        transform.localScale = new Vector3(newXSize, transform.localScale.y, transform.localScale.z);

        // 조각 큐브의 위치, 크기 연산
        float cubeEdge = transform.position.x + (transform.localScale.x / 2 * direction);
        float fallingBlockSize = Mathf.Abs(hangOver);
        float fallingBlockPosition = cubeEdge + fallingBlockSize / 2 * direction;
        // 조각 큐브 생성
        SpawnDropCube(fallingBlockPosition, fallingBlockSize);
    }

    private void SplitCubeOnZ(float hangOver, float direction)
    {
        // 이동 큐브의 새로운 위치, 크기 연산
        float newZPosition = transform.position.z - (hangOver / 2);
        float newZSize = transform.localScale.z - Mathf.Abs(hangOver);
        // 이동 큐브의 위치, 크기 설정
        transform.position = new Vector3(transform.position.x, transform.position.y, newZPosition);
        transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, newZSize);

        // 조각 큐브의 위치, 크기 연산
        float cubeEdge = transform.position.z + (transform.localScale.z / 2 * direction);
        float fallingBlockSize = Mathf.Abs(hangOver);
        float fallingBlockPosition = cubeEdge + fallingBlockSize / 2 * direction;
        // 조각 큐브 생성
        SpawnDropCube(fallingBlockPosition, fallingBlockSize);
    }

    private void SpawnDropCube(float fallingBlockPosition, float fallingBlockSize)
    {
        GameObject clone = GameObject.CreatePrimitive(PrimitiveType.Cube);

        // 방금 생성한 조각 큐브의 위치, 크기 설정
        if (moveAxis == MoveAxis.x)
        {
            clone.transform.position = new Vector3(fallingBlockPosition, transform.position.y, transform.position.z);
            clone.transform.localScale = new Vector3(fallingBlockSize, transform.localScale.y, transform.localScale.z);
        }
        else if (moveAxis == MoveAxis.z)
        {
            clone.transform.position = new Vector3(transform.position.x, transform.position.y, fallingBlockPosition);
            clone.transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, fallingBlockSize);
        }

        clone.GetComponent<MeshRenderer>().material.color = GetComponent<MeshRenderer>().material.color;
        clone.AddComponent<Rigidbody>();

        Destroy(clone, 2);
    }

    private bool IsGameOver(float hangOver)
    {

        float max = moveAxis == MoveAxis.x ? cubeSpawner.LastCube.transform.localScale.x : cubeSpawner.LastCube.transform.localScale.z;

        if (Mathf.Abs(hangOver) > max)
        {
            return true;
        }

        return false;
    }

    public void RecoveryCube()
    {
        float recoverySize = 0.1f;

        if (moveAxis == MoveAxis.x)
        {
            float newXSize = transform.localScale.x + recoverySize;
            float newXPosition = transform.position.x + recoverySize * 0.5f;

            transform.position = new Vector3(newXPosition, transform.position.y, transform.position.z);
            transform.localScale = new Vector3(newXSize, transform.localScale.y, transform.localScale.z);
        }
        else
        {
            float newZSize = transform.localScale.z + recoverySize;
            float newZPosition = transform.position.z + recoverySize * 0.5f;

            transform.position = new Vector3(transform.position.x, transform.position.y, newZPosition);
            transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, newZSize);
        }
    }
}
콤보와 이팩트을 생성 하는 코드이다
더보기
using System.Collections;
using UnityEngine;

public class PerfectController : MonoBehaviour
{
    [SerializeField]
    private CubeSpawner cubeSpawner;
    [SerializeField]
    private Transform perfectEffect;
    [SerializeField]
    private Transform perfectComboEffect;
    [SerializeField]
    private Transform perfectRecoveryEffect;

    private AudioSource audioSource;

    [SerializeField]
    private int recoveryCombo = 5;
    private float perfectCorrection = 0.01f;
    private float addedSize = 0.1f;
    private int perfectCombo = 0;

    private void Awake()
    {
        audioSource = GetComponent<AudioSource>();
    }

    public bool IsPerfect(float hangOver)
    {
        // Perfect로 콤보가 중첩될 때
        if (Mathf.Abs(hangOver) <= perfectCorrection)
        {
            EffectProcess();    // 콤보에 따라 이펙트 재생
            SFXProcess();       // 콤보에 따라 사운드 재생

            perfectCombo++;

            return true;
        }
        // 콤보 초기화
        else
        {
            perfectCombo = 0;

            return false;
        }
    }

    private void EffectProcess()
    {
        // 이펙트 생성 위치
        Vector3 position = cubeSpawner.LastCube.position;
        position.y = cubeSpawner.CurrentCube.transform.position.y - cubeSpawner.CurrentCube.transform.localScale.y * 0.5f;

        // 이펙트 크기
        Vector3 scale = cubeSpawner.CurrentCube.transform.localScale;
        scale = new Vector3(scale.x + addedSize, perfectEffect.localScale.y, scale.z + addedSize);

        // 기본 퍼펙트 이펙트 생성
        OnPerfectEffect(position, scale);

        if (perfectCombo > 0 && perfectCombo < recoveryCombo)
        {
            // 콤보 이펙트 생성
            StartCoroutine(OnPerfectComboEffect(position, scale));
        }
        else if (perfectCombo >= recoveryCombo)
        {
            OnPerfectRecoveryEffect();
        }
    }

    private void OnPerfectEffect(Vector3 position, Vector3 scale)
    {
        // 이펙트 생성
        Transform effect = Instantiate(perfectEffect);
        effect.position = position;
        effect.localScale = scale;
    }

    private void SFXProcess()
    {
        int maxCombo = 5;
        float volumeMin = 0.3f;
        float volumeAdditive = 0.15f;
        float pitchMin = 0.7f;
        float pitchAdditive = 0.15f;

        if (perfectCombo < maxCombo)
        {
            audioSource.volume = volumeMin + perfectCombo * volumeAdditive;
            audioSource.pitch = pitchMin + perfectCombo * pitchAdditive;
        }

        audioSource.Play();
    }

    private IEnumerator OnPerfectComboEffect(Vector3 position, Vector3 scale)
    {
        // 콤보가 중첩될 때마다 개수 추가
        int currentCombo = 0;
        float beginTime = Time.time;
        float duration = 0.15f;

        while (currentCombo < perfectCombo)
        {
            float t = (Time.time - beginTime) / duration;

            if (t >= 1)
            {
                // 이펙트 생성
                Transform effect = Instantiate(perfectComboEffect);
                effect.position = position;
                effect.localScale = scale;

                beginTime = Time.time;

                currentCombo++;
            }

            yield return null;
        }
    }

    public void OnPerfectRecoveryEffect()
    {
        // 이펙트 생성
        Transform effect = Instantiate(perfectRecoveryEffect);
        // 이펙트 생성 위치
        effect.position = cubeSpawner.CurrentCube.transform.position;

        // 이펙트의 생성 반경 설정
        var shape = effect.GetComponent<ParticleSystem>().shape;
        float radius = cubeSpawner.CurrentCube.transform.localScale.x > cubeSpawner.CurrentCube.transform.localScale.z ?
                       cubeSpawner.CurrentCube.transform.localScale.x : cubeSpawner.CurrentCube.transform.localScale.z;
        shape.radius = radius;
        shape.radiusThickness = radius * 0.5f;

        // 이동 큐브의 일부분이 재생
        cubeSpawner.CurrentCube.RecoveryCube();
    }
}