FingerSpell Scene2 제작과정

소련·2024년 5월 21일

졸프

목록 보기
2/2
post-thumbnail

캡스톤디자인과창업프로젝트에서 진행한 프로젝트의 결과물인 FingerSpell의 Scene2의 전체적인 제작을 맡게 되어 그 제작과정을 기록하려고 한다.

기록할 Scene2의 주요한 기능으로는 화면 중심에 위치하는 우주선과 랜덤한 방향에서 스폰되어 그 우주선을 향해 이동하는 운석 오브젝트, 그리고 현재 플레이어가 입력한 지화와 운석 오브젝트의 자모가 일치하는지 확인한 후 오브젝트를 파괴하는 기능이 있다.

운석 오브젝트 스폰

운석 오브젝트의 스폰을 담당하는 MeteorController 코드를 살펴 보겠다.
운석 오브젝트는 프리팹화 하여 저장해둔 후 GetRandomKoreanCharacter() 메서드에서 랜덤한 자모를 선택하고 Instantiate() 메서드를 이용하여 포인트 16개중 랜덤한 위치에서 운석을 초기화하여 소환한다.

using System.Collections;
using UnityEngine;
using TMPro; // TMP_Text를 사용하기 위한 네임스페이스

public class MeteorController : MonoBehaviour
{
    public GameObject meteorPrefab; // 운석 프리팹
    public GameObject[] spawnPoints; // 운석 생성 위치 오브젝트 배열
    public float spawnInterval = 5f; // 운석 생성 간격 (초)

    private void Start()
    {
        StartCoroutine(SpawnMeteorRoutine());
    }

    IEnumerator SpawnMeteorRoutine()
    {
        while (true)
        {
            yield return new WaitForSeconds(spawnInterval);

            // 랜덤한 생성 위치 오브젝트 선택
            GameObject randomSpawnPoint = GetRandomSpawnPoint();

            if (randomSpawnPoint != null)
            {
                // 선택된 위치에서 운석 생성
                Vector3 spawnPosition = randomSpawnPoint.transform.position;
                GameObject newMeteor = Instantiate(meteorPrefab, spawnPosition, Quaternion.identity);

                // MovingMeteor 컴포넌트 확인
                MovingMeteor movingMeteor = newMeteor.GetComponent<MovingMeteor>();

                if (movingMeteor != null)
                {
                    // 랜덤 obsalpha 선택
                    string randomAlpha = GetRandomKoreanCharacter();

                    // MovingMeteor 컴포넌트의 obsalpha 설정
                    movingMeteor.obsalpha = randomAlpha;
                    Debug.Log($"운석이 obsalpha 값 {randomAlpha}으로 초기화되었습니다.");

                    // 운석의 자식 오브젝트에서 TMP_Text 컴포넌트 찾기
                    TMP_Text tmpText = newMeteor.GetComponentInChildren<TMP_Text>();

                    if (tmpText != null)
                    {
                        // TMP_Text 컴포넌트의 텍스트 값을 randomAlpha로 설정
                        tmpText.text = randomAlpha;
                    }
                    else
                    {
                        Debug.LogWarning("운석 자식 오브젝트에서 TMP_Text 컴포넌트를 찾을 수 없습니다.");
                    }
                }
                else
                {
                    Debug.LogWarning("운석 프리팹에 필요한 MovingMeteor 컴포넌트가 없습니다.");
                }
            }
            else
            {
                Debug.LogWarning("운석 생성 위치를 찾을 수 없습니다.");
            }
        }
    }

    GameObject GetRandomSpawnPoint()
    {
        if (spawnPoints != null && spawnPoints.Length > 0)
        {
            // 랜덤하게 생성 위치 오브젝트 선택
            return spawnPoints[Random.Range(0, spawnPoints.Length)];
        }
        else
        {
            Debug.LogWarning("운석 생성 위치 오브젝트가 설정되지 않았습니다.");
            return null;
        }
    }

    string GetRandomKoreanCharacter()
    {
        // 한글 자음/모음 배열
        string[] possibleAlpha = {
            "ㄱ", "ㄴ", "ㄷ", "ㄹ", "ㅁ", "ㅂ", "ㅅ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ",
            "ㅍ", "ㅎ", "ㅏ", "ㅑ", "ㅓ", "ㅕ", "ㅗ", "ㅛ", "ㅜ", "ㅠ", "ㅡ", "ㅣ",
            "ㅐ", "ㅒ", "ㅔ", "ㅖ", "ㅚ", "ㅟ", "ㅢ"
        };

        // 랜덤하게 선택된 한글 자음/모음 반환
        return possibleAlpha[Random.Range(0, possibleAlpha.Length)];
    }
}

운석 오브젝트 이동

운석 오브젝트 프리팹에 컴포넌트로 부착되어 이동을 담당하는 MovingMeteor 코드를 살펴보자.
이 코드의 주요기능은 targetObject로 설정된 우주선 오브젝트로 향하는 것과 향하는 방향으로 스프라이트를 회전시키면서 텍스트는 회전을 주지 않음으로써 운석이 나타내는 자모는 정상적으로 읽히도록 하는 것이다.

using UnityEngine;

public class MovingMeteor : MonoBehaviour
{
    public float moveSpeed = 2.0f;
    public GameObject targetObject;
    public string obsalpha;

    public Transform spriteTransform; // 스프라이트의 Transform
    public Transform textTransform; // 텍스트의 Transform

    void Update()
    {
        if (targetObject != null)
        {
            // 운석을 목표 지점으로 이동
            Vector3 targetPosition = targetObject.transform.position;
            transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);

            // 운석의 방향 벡터 계산
            Vector3 direction = (targetPosition - spriteTransform.position).normalized;

            // 스프라이트의 각도 계산 (방향 벡터를 이용하여)
            float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg + 135f;
            spriteTransform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));

            textTransform.rotation = Quaternion.identity;
        }
        else
        {
            Debug.LogWarning("Target object is not assigned.");
        }
    }
}

운석 오브젝트의 파괴

운석 오브젝트가 파괴되는 경우는 플레이어가 오브젝트와 일치하는 지화를 행하였을 때와 운석 오브젝트가 우주선 오브젝트에 닿을 때 까지 플레이어가 해당하는 지화를 행하지 못하여 우주선과 충돌하게 되어 플레이어의 hp가 깎인 후 파괴되는 경우가 있다.

1. 오브젝트가 우주선과 충돌하여 파괴되는 경우

DestroyAndDamage 코드는 우주선 오브젝트에 부착되어 "suwhaObject"라는 태그를 가진 오브젝트(운석 오브젝트) 가 충돌하였을 경우 플레이어의 체력을 damageAmount만큼 깎고 해당 운석 오브젝트를 파괴하는 코드이다. 이 코드에서 주의할 부분은 OnTriggerEnter2D() 메서드는 두 오브젝트 모두 Collider2D 컴포넌트를 가지고 있어야 하며, 둘 중 한 오브젝트의 Collider2D 컴포넌트의 is Trigger 변수가 TRUE로 설정되어 있어야 하고, 둘 중 최소 하나의 오브젝트가 Rigidbody2D를 컴포넌트로 가지고 있어야 두 오브젝트의 충돌이 시작되었을 때 작동하게 된다. 이 세가지 조건중 하나라도 충족하지 못하면 이 메서드가 실행 되지 않기 때문에 주의해서 확인하도록 한다.

using UnityEngine;

public class DestroyAndDamage : MonoBehaviour
{
    public string targetTag = "suwhaObject"; // 대상 태그
    public int damageAmount = 10; // 입힐 피해량
    public PlayerHealth playerHealth; // PlayerHealth 컴포넌트

    // 트리거 충돌시 호출되는 이벤트
    private void OnTriggerEnter2D(Collider2D other)
    {
        Debug.Log("충돌감지");
                  
        playerHealth.TakeDamage(damageAmount);
        
        
        // 충돌한 오브젝트 파괴         
        Destroy(other.gameObject);        
    }
}

2. 플레이어의 지화가 오브젝트와 일치하여 파괴되는 경우

DestroyMeteorController 코드는 플레이어의 지화가 현재 화면 안에 있는 운석 오브젝트들 중 일치하는 오브젝트가 있다면 그 오브젝트를 파괴하는 코드이다.
화면 안이 기본 범위이므로 카메라와 일치하는 크기의 square 오브젝트를 만들어 이 코드를 컴포넌트로 부착하기로 하였다. 그래서 squareCollider는 현재 부착 되어있는 square 오브젝트의 Collider2D를 의미하는 것이고, Update() 메서드에서는 Mediapipe plugin을 이용하여 얻은 Handlandmark 값을 KNN 알고리즘으로 어떤 지화인지 예측하는 코드인 KNNPrediction 컴포넌트의 Gesture 값을 가져오게 된다. DestroyObjectsInSquareCollider() 메서드는 현재 씬에 존재하는 우주선 오브젝트의 KNNPrediction모든 운석 오브젝트를 찾은 뒤 그 중 squareCollider 안에 위치하는 오브젝트들 중 플레이어의 지화와 오브젝트의 obsalpha값이 일치하는 오브젝트를 파괴하는 역할을 맡았다.

using UnityEngine;

public class DestroyMeteorController : MonoBehaviour
{
    public GameObject square; // 기준 오브젝트 설정
    public GameObject spaceship; // 우주선 오브젝트 설정
    public string targetTag = "suwhaObject"; // 파괴할 대상 태그 설정

    public Collider2D squareCollider; // square 오브젝트의 2D 콜라이더

    void Update()
    {
        // square 오브젝트가 null이거나 spaceship 오브젝트가 null인 경우 처리
        if (square == null || spaceship == null)
        {
            Debug.LogWarning("기준 오브젝트 또는 우주선 오브젝트를 찾을 수 없습니다.");
            return;
        }

        // 우주선 오브젝트의 KNNPrediction 컴포넌트에서 Gesture 변수값 읽기
        string gestureValue = spaceship.GetComponent<KNNPrediction>().gesture;

        // squareCollider가 설정되어 있는지 확인
        if (squareCollider == null)
        {
            Debug.LogError("squareCollider가 할당되지 않았습니다. 인스펙터에서 square 오브젝트의 Collider2D를 할당해주세요.");
            return;
        }

        // squareCollider 범위 안에 있는 특정 태그를 가진 오브젝트 파괴
        DestroyObjectsInSquareCollider(gestureValue);
    }

    void DestroyObjectsInSquareCollider(string gestureValue)
    {
        // 태그가 targetTag인 모든 오브젝트들 찾기
        GameObject[] taggedObjects = GameObject.FindGameObjectsWithTag(targetTag);

        // 찾은 오브젝트들 중 squareCollider 범위 안에 있는 오브젝트 파괴
        foreach (GameObject obj in taggedObjects)
        {
            // obj의 위치가 squareCollider의 바운딩 박스 내에 있는지 확인
            Vector3 objectPosition = obj.transform.position;
            if (squareCollider.bounds.Contains(objectPosition))
            {
                // MovingMeteor 컴포넌트 가져오기
                MovingMeteor meteor = obj.GetComponent<MovingMeteor>();
                if (meteor != null && meteor.obsalpha == gestureValue)
                {
                    Debug.Log(meteor.obsalpha + "파괴됨");
                    Destroy(obj); // obsalpha 값이 gestureValue와 일치하는 오브젝트 파괴
                }
            }
        }
    }
}

마무리

이 외에도 여러 코드와 오브젝트들이 Scene2의 제작에 사용되었지만 가장 주요한 코드들만 설명하였다. 설명한 과정들 중 OnTriggerEnter2D() 메서드가 작동하지 않아 그 이유를 알아보던 때가 가장 힘들고 지루했다. OnTriggerEnter2D() 메서드를 사용할 때에는 이 글을 참고하여 오브젝트들의 컴포넌트를 잘 확인하기 바란다.

profile
힘든 대학생

0개의 댓글