[Unity 강의] 뱀서라이크 강의 - 스킬 맵(무한맵) 개선

업데이트:

카테고리:

태그: , ,


IsSubclassOf



1. 개선 코드

PlayerController(평타 추가)

✔ Init - StartEgoSword() 추가
✔ StartEgoSword() EgoSword 스폰, 위치 설정, 코루틴 시작
✔ CollectEnv - Grid를 사용해서 체크

PlayerController
public class PlayerController : CreatureController
{
    public override bool Init()
    {
        if (base.Init() == false)
            return false;
        _speed = 5.0f;

        Managers.Game.OnMoveDirChanged += HandleOnMoveDirChanged;

        StartProjectile();
        StartEgoSword();

        return true;
    }

    void CollectEnv() 
    {
        float sqrCollectDis = EnvCollectDist * EnvCollectDist;

        var findGems = GameObject.Find("@Grid").GetComponent<GridController>().GatherObjects(transform.position, EnvCollectDist + 0.4f);

        foreach (var go in findGems)
        {
            GemController gem = go.GetComponent<GemController>();

            Vector3 dir = gem.transform.position - transform.position;
            if (dir.sqrMagnitude <= EnvCollectDist)
            {
                Managers.Game.Gem += 1;
                Managers.Object.Despawn(gem);
            }   
        }
    }

    #region EgoSword

    EgoSwordController _egoSword;
    void StartEgoSword() 
    {
        if (_egoSword.IsValid())
            return;

        _egoSword = Managers.Object.Spawn<EgoSwordController>(_indicator.position, SkillID.EGO_SWORD_ID);
        _egoSword.transform.SetParent(_indicator);

        _egoSword.ActivateSkill();
    }

    #endregion
}

몬스터 스폰 개선

몬스터 스폰 플레이어 기준으로 Min~Max의 랜덤원 범위에서 스폰
✔ SpawningPool - TrySpawn()에서 스폰 Position 설정
✔ Utils.GenerateMonsterSpawnPosition - 랜덤 위치 계산, 반환

PlayerController
public class SpawningPool : MonoBehaviour
{
    private void TrySpawn()
    {
        int monsterCount = Managers.Object.Monster.Count;
        if (monsterCount > _maxMonsterCount)
            return;

        Vector3 randPos = Utils.GenerateMonsterSpawnPosition(Managers.Game.Player.transform.position,10,15);
        MonsterController mc = Managers.Object.Spawn<MonsterController>(randPos,Random.Range(0, 2));
    }
}

public class Utils
{
    public static Vector2 GenerateMonsterSpawnPosition(Vector2 characterPosition, float minDistance = 10.0f, float maxDistance = 20.0f) 
    {
        float angle = Random.Range(0, 360) * Mathf.Deg2Rad;

        float distance = Random.Range(minDistance, maxDistance);

        float xDist = MathF.Cos(angle) * distance;
        float yDist = MathF.Sin(angle) * distance;

        Vector2 spawnPosition = characterPosition + new Vector2(xDist, yDist);

        return spawnPosition;
    }
}





2. 스킬 추가 (EgoSwordController)

✔ EgoSword(평타) 추가
✔ SkillData.xml 스킬 데이터 추가
🔹 EgoSwordController
✅ Init() - 물리 적용, 1 ~ 4 타 Owner, Damage Set
✅ SetParticles() - 평타 각도 체크
✅ TurnOnPhysics - 물리 적용 On
🔹 EgoSwordChild
✅ 1 ~ 4 타 SET
✅ OnTriggerEnter2D 몬스터 충돌 시 체크, 데미지

SkillData.xml
<?xml version="1.0" encoding="utf-8"?>
<SkillDatas>
  <SkillData templateID="1" name="화염구" type="Projectile" prefab="FireProjectile.prefab" damage="1000" speed="2" >
  </SkillData>
  <SkillData templateID="10" name="평타" type="Melee" prefab="EgoSword.prefab" damage="1000">
  </SkillData>
</SkillDatas>
EgoSwordController

public class EgoSwordController : SkillController
{
    [SerializeField]
    ParticleSystem[] _swingParticles;

    protected enum SwingType 
    {
        First,
        Second,
        Third,
        Fourth
    }

    //✅ Init() - 물리 적용, 1~4타 Owner, Damage Set  
    public override bool Init()
    {
        base.Init();

        // Active 되기 전까지 콜라이더 물리 적용 x
        for (int i = 0; i < _swingParticles.Length; i++)
            _swingParticles[i].GetComponent<Rigidbody2D>().simulated = false;

        for (int i = 0; i < _swingParticles.Length; i++)
            _swingParticles[i].gameObject.GetOrAddComponent<EgoSwordChild>().SetInfo(Managers.Object.Player, 100);
        return true;
    }

    public void ActivateSkill() 
    {
        StartCoroutine(CoSwingSword());
    }


    float CoolTime = 2.0f;
    IEnumerator CoSwingSword() 
    {
        while (true)
        {
            yield return new WaitForSeconds(CoolTime);

            SetParticles(SwingType.First);                  // 스킬 각도 세팅
            _swingParticles[(int)SwingType.First].Play();   // 파티클 플레이
            TurnOnPhysics(SwingType.First, true);           // 물리 적용
            yield return new WaitForSeconds(_swingParticles[(int)SwingType.First].main.duration); //파티클 실행 시간
            TurnOnPhysics(SwingType.First, false);

            SetParticles(SwingType.Second);
            _swingParticles[(int)SwingType.Second].Play();
            TurnOnPhysics(SwingType.Second, true);
            yield return new WaitForSeconds(_swingParticles[(int)SwingType.Second].main.duration); 
            TurnOnPhysics(SwingType.Second, false);

            SetParticles(SwingType.Third);
            _swingParticles[(int)SwingType.Third].Play();
            TurnOnPhysics(SwingType.Third, true);
            yield return new WaitForSeconds(_swingParticles[(int)SwingType.Third].main.duration); 
            TurnOnPhysics(SwingType.Third, false);

            SetParticles(SwingType.Fourth);
            _swingParticles[(int)SwingType.Fourth].Play();
            TurnOnPhysics(SwingType.Fourth, true);
            yield return new WaitForSeconds(_swingParticles[(int)SwingType.Fourth].main.duration); 
            TurnOnPhysics(SwingType.Fourth, false);
        }
        
    }

    private void SetParticles(SwingType swingtype) // 각도 틀어주기
    {
        float z = transform.parent.transform.eulerAngles.z;
        float radian = (Mathf.PI / 180) * z * -1;

        var main = _swingParticles[(int)swingtype].main;
        main.startRotation = radian;
    }

    private void TurnOnPhysics(SwingType swingtype, bool simulated) //물리적용
    {
        for (int i = 0; i < _swingParticles.Length; i++)
            _swingParticles[i].GetComponent<Rigidbody2D>().simulated = false;

        _swingParticles[(int)swingtype].GetComponent<Rigidbody2D>().simulated = simulated;
    }
}
EgoSwordChild
public class EgoSwordChild : MonoBehaviour
{
    BaseController _owner;
    int _damage;

    public void SetInfo(BaseController owner, int damage) // 스킬 요소 세팅
    {
        _owner = owner;
        _damage = damage;
    }
    private void OnTriggerEnter2D(Collider2D collision)
    {
        MonsterController mc = collision.transform.GetComponent<MonsterController>();
        if (mc.IsValid() == false)
            return;

        mc.OnDamaged(_owner,_damage);
    }
}



ObjectManager - Spawn (IsSubclassOf)

typeof(T).IsSubclassOf(typeof(SkillController))
T가 SkillController를 상속했는지 체크하는 코드

ObjectManager
public class ObjectManager 
{
    public T Spawn<T>(Vector3 position, int  templateID =0) where T : BaseController 
    {
        System.Type type = typeof(T);

        else if (typeof(T).IsSubclassOf(typeof(SkillController)))
        {
            if (Managers.Data.SkillDic.TryGetValue(templateID,out Data.SkillData skillData)==false)
            {
                Debug.LogError($"ObjectManager Spawn Skill Failed{templateID}");
                return null;
            }
            GameObject go = Managers.Resource.Instantiate(SkillPrefabsName.EgoSword, pooling: true);
            go.transform.position = position;

            T t = go.GetComponent<T>();
            
            t.Init();

            return t;
        }
        return null;
    }
}





3. 맵 개선 - 무한 맵 (MapTileController)

✔ X(4) 개의 map을 플레이어 위치에 따라 위치 변경해서 무한처럼 만든다. (방법은 다양)
✔ Camera에 Collider2D 배치, 현재 위치 체크, OnTriggerExit2D를 사용해 맵 재사용
Image

MapTileController
public class MapTileController : MonoBehaviour
{
    private void OnTriggerExit2D(Collider2D collision)
    {
        Camera camera = collision.gameObject.GetComponent<Camera>();
        if (camera == null)
            return;

        Vector3 dir = camera.transform.position - transform.position;

        float dirX = dir.x < 0 ? -1 : 1;
        float dirY = dir.y < 0 ? -1 : 1;

        if (Mathf.Abs(dir.x) > Mathf.Abs(dir.y))
            transform.Translate(Vector3.right * dirX * 200);
        else
            transform.Translate(Vector3.up * dirY * 200);
    }
}





이것저것 메모

무적구현

상대에게 무적 버프? -> 복잡
상대에게 충돌 범위를 없애면 무적이 될 수 있다.





잡담, 일기?

코드, 어드레서블, 프리팹 등 규칙을 만들고 구조를 만든 후 작업을 하는 게 이해되는 느낌이다.
좀 더 편하고 뭔가를 해야 할 때 어디를 건드리면 되는지 알 거 같다.
다음 강의 UI
UI - Sprite, Prefab 옮겨놓기.
UI 팝업인지 유지되는지, 떠 있는 상태에서 실시간으로 변하는지 확인하고 설계




📔

댓글남기기