인프런 커뮤니티 질문&답변

이승훈님의 프로필 이미지
이승훈

작성한 질문수

[Unity] 함께 만들어가는 방치형 게임 개발

스테이지 반복이 진행될 수록 몬스터 스케일이 작아집니다.

해결된 질문

작성

·

47

0

현재 스테이트패턴 Dead 까지 수강한 상태이구요,

스테이지의 반복이 진행될수록, 몬스터 스케일이 불규칙적으로 작아집니다.

처음에는, 몬스터 스케일이 불규칙적으로 작아지는것에 있어서 몬스터가 스폰되는 중에 캐릭터가 100%까지 처치를 완료하여, SpawnStart 코루틴메서드를 통해 LocalScale이 커지고 있는 도중, Return 메서드가 호출되어 비활성화가 된듯한 느낌을 받아(게임오브젝트가 비활성화가 되면 코루틴메서드가 중단되지않나요?) 확인해보았으나, 정확하게 원인을 파악하지 못했습니다 ㅠ

 

그리고 몬스터가 스폰되지 않았음에도 허공에 어택모션을 계속 취하는 버그도 있습니다.

분명 풀링에는 전부 비활성화인데, 타겟이 있는것처럼 허공에 공격모션을 계속 취하다가, 몬스터가 실제로 스폰되면 추적하여 공격을 실행합니다. 제가 혹시 코드에 잘못된부분이 있다면 리뷰 한번만 부탁드립니다. ㅠ 추가적으로 필요하신 코드나, 직접적인 버그영상이 필요하시면 댓글한번만 남겨주시면 동영상을 개인메일이나, 유튜브 링크로 첨부해서 보여드리겠습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Spawner : MonoBehaviour
{
    public int M_Count; // 몬스터의 수
    public float M_SpawnTime; // 몇 초마다 스폰이 될 것인지 결정.
    // 1. 몬스터는 여러마리가 몇 초 마다 수시로 여러번 스폰 되어야 한다.

    //Spawner 에 손쉽게 접근하기 위해, static으로 설계
    public static List<Monster> m_monsters = new List<Monster>();
    public static List<Player> m_players = new List<Player>();

    private Coroutine coroutine;

    private void Start()
    {
        Base_Manager.Stage.M_PlayEvent += OnPlay;
        Base_Manager.Stage.M_BossEvent += OnBoss;
    }
    public void OnPlay()
    {
        coroutine = StartCoroutine(SpawnCoroutine());
    }
    public void OnBoss()
    {
        if(coroutine != null)
        {
            StopCoroutine(coroutine);
        }
        for(int i = 0; i<m_monsters.Count; i++)
        {
            Base_Manager.Pool.m_pool_Dictionary["Monster"].Return(m_monsters[i].gameObject);
            //Destroy(m_monsters[i].gameObject);
        }
        m_monsters.Clear();

        StartCoroutine(BossSetCoroutine());
      
    }

    IEnumerator BossSetCoroutine()
    {
        yield return new WaitForSeconds(2.0f);
        var monster = Instantiate(Resources.Load<Monster>("Boss"), Vector3.zero, Quaternion.Euler(0, 180, 0)); // 보스 생성
        monster.Init();

        Vector3 Pos = monster.transform.position; // 같은 변수를 사용할 때는, 한 변수로 묶어서 사용하면 메모리 절약이 됨. (중복계산방지)


        // 일정 소환거리 내부에 플레이어가 존재하면, 보스 소환 시, 넉백을 합니다.
        for(int i = 0; i<m_players.Count; i++)
        {
            if(Vector3.Distance(Pos, m_players[i].transform.position) <= 3.0f)
            {
                m_players[i].transform.LookAt(monster.transform.position);
                m_players[i].Knock_Back();
            }
         
        }

        yield return new WaitForSeconds(1.5f);

        m_monsters.Add(monster);

        Base_Manager.Stage.State_Change(Stage_State.BossPlay);
    }
    //Random.insideUnitSphere = Vector3(x,y,z)
    //Random.insideUnitCircle = Vector3(x,y)
    IEnumerator SpawnCoroutine()
    {
        Vector3 pos;

        for(int i = 0; i < M_Count; i++)
        {
            pos = Vector3.zero + Random.insideUnitSphere * 5.0f;
            pos.y = 0.0f;
            Vector3 returnPos = Vector3.zero;

            while (Vector3.Distance(pos, Vector3.zero) <= 3.0f)
            {
                pos = Vector3.zero + Random.insideUnitSphere * 5.0f;
                pos.y = 0.0f;
            }

            //몬스터 스폰
            var go = Base_Manager.Pool.Pooling_OBJ("Monster").Get((value) => 
            {
                // 풀링이 생성될때의 기능을 구현한다.

                value.GetComponent<Monster>().Init();
                value.transform.position = pos;
                value.transform.LookAt(Vector3.zero);
                m_monsters.Add(value.GetComponent<Monster>());

            });

        }

        yield return new WaitForSeconds(M_SpawnTime);

        coroutine = StartCoroutine(SpawnCoroutine());
    }
   
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Monster : Character
{

    /// <summary>
    /// 몬스터가 스폰이 될 때, 스케일의 크기변화를 줍니다.
    /// </summary>
    /// <returns></returns>
    IEnumerator Spawn_Start()
    {
        float current = 0.0f;
        float percent = 0.0f;
        float start = 0.0f;
        float end = transform.localScale.x; // 몬스터의 로컬스케일
        Debug.Log($"몬스터의 로컬스케일 변화 :{transform.localScale.x}");

        while(percent < 1)
        {
            current += Time.deltaTime;
            percent = current / 0.2f;
            float LerpPos = Mathf.Lerp(start,end, percent); // 선형보간 (시작값,끝값,시간)
            transform.localScale = new Vector3(LerpPos, LerpPos, LerpPos);
            yield return null;
        }

        yield return new WaitForSeconds(0.3f);
        isSpawn = true;
    }
   
    private void Dead_Event()
    {
        if (!isBoss)
        {
            Stage_Manager.Count++;
            Main_UI.Instance.Monster_Slider_Count();
        }
        else
        {
            Base_Manager.Stage.State_Change(Stage_State.Clear);
        }

        Spawner.m_monsters.Remove(this);

        Base_Manager.Pool.Pooling_OBJ("Smoke").Get((value) =>
        {
            value.transform.position = new Vector3(transform.position.x, 0.5f, transform.position.z);
            Base_Manager.instance.Return_Pool(value.GetComponent<ParticleSystem>().duration, value, "Smoke");

        });

        Base_Manager.Pool.Pooling_OBJ("COIN_PARENT").Get((value) =>
        {
            value.GetComponent<Coin_Parent>().Init(transform.position);
        });

        for (int i = 0; i < 3; i++)
        {
            Base_Manager.Pool.Pooling_OBJ("Item_OBJ").Get((value) =>
            {
                value.GetComponent<Item_OBJ>().Init(transform.position); // 몬스터 위치 삽입
            });
        }

        if (!isBoss)
        {
            Base_Manager.Pool.m_pool_Dictionary["Monster"].Return(this.gameObject);
        }
        else
        {
            Destroy(this.gameObject); // 보스몬스터는 풀링하지않고 파괴한다.
        }
        
    }

   

}

답변 2

0

Pinous님의 프로필 이미지
Pinous
지식공유자

안녕하세요! 내용이 꽤 길어서 정확한 진단을 위하여 프로젝트를 보내주신다면 상세히 답변 드리겠습니다!

pinous.g@gmail.com으로 프로젝트를 보내주시면 수정 후 말씀드리겠습니다 🙂

이승훈님의 프로필 이미지
이승훈
질문자

네, 메일로 1차로 내용을 보내드렸지만 프로젝트첨부해서 다시한번 보내드리겠습니다! 감사합니다

0

안녕하세요, 인프런 AI 인턴입니다. 이승훈 님.

공유해주신 문제는 Unity에서의 오브젝트 스케일과 비활성화 처리가 관련된 것으로 보입니다. 코드를 기반으로 몇 가지 문제를 추측하고 해결책을 안내드리겠습니다.

발생 문제 분석

  1. 몬스터 스케일 문제:

    • Spawn_Start 코루틴에서 스케일을 변화시키는 동안 Return 메서드가 호출되어 해당 코루틴이 멈출 수 있습니다. Unity에서 오브젝트가 비활성화되면 해당 오브젝트의 코루틴은 중단됩니다.
  2. 허공에 공격하는 문제:

    • 공격 대상이 없는 상태에서 공격 모션을 계속 취한다는 것은 타겟이 올바르게 업데이트되지 않음을 의미할 수 있습니다. 오브젝트가 비활성화되면서 타겟 초기화나 상태 변경이 누락되었을 가능성도 있습니다.

제안하는 해결 방법

  1. 스케일 문제 해결:
    • Spawn_Start 코루틴이 중간에 멈추는 것을 방지하기 위해 오브젝트가 비활성화되기 전에 코루틴이 완전히 종료되게 하거나, 코루틴 진행 중에 비활성화가 되더라도 진행이 보장되도록 코루틴 관리 방법을 수정해야 합니다.
   if (gameObject.activeInHierarchy)
   {
       StartCoroutine(Spawn_Start());
   }
  • Spawn_Start 메서드가 오브젝트 스폰 시마다 정확히 호출되고, 비활성화 시까지 제대로 종료되는지 확인하세요.
  1. 공격 모션 문제 해결:
    • 공격 상태 갱신 부분을 검토하여 타겟이 제대로 지정되지 않거나, 공격 상태가 초기화되지 않는 부분을 점검하세요. 타겟이 올바르게 변경되는지 타겟팅 로직을 확인하고 필요하다면 수정합니다.
  • 몬스터가 비활성화될 때 상태와 타겟 변수를 초기화하는 코드를 추가해보세요.
void ClearAttackState()
{
    currentTarget = null;
    animator.SetBool("isAttacking", false);
}
  • 비활성화 시 호출하는 부분:
private void OnDisable()
{
   ClearAttackState();
}

확인할 사항

  • 오브젝트 풀 시스템 내 Return 메서드가 오브젝트를 리셋하고 초기화하는 부분을 점검하세요.
  • 오브젝트가 풀에 반환될 때 초기화가 정확히 이루어지고 있는지 확인합니다.

저는 질문자님의 이해를 돕고자, 지식을 참고하여 답변을 드리고 있습니다.
유사한 문제를 겪은 수강생들의 질의응답 링크를 첨부하니 참고해 보세요:
- 캐릭터 이동 구현 rotation 값 변경
- 오브젝트 풀링

현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

이승훈님의 프로필 이미지
이승훈

작성한 질문수

질문하기