Unity 입문 팀프로젝트 - 4

이준호·2023년 12월 5일
0
post-custom-banner

📌 2D 입문 팀프로젝트

자식 클래스에서 부모 클래스의 변수에 접근해서 사용하는데 부모 클래스에서 값이 바뀌어도 자식 클래스에서 값이 참조가 다르게 되길래 왜 이런가 했었다. 나는 상속이 부모의 값을 참조하여 같은 메모리를 공유하는 줄 알았는데 착각을 하고 있었다.

붕어빵 틀에 붕어빵을 찍는 것처럼 아예 다른 것이었다. 상속을 받는다고 참조가 아닌 서로 다른 각자의 변수에 값이 할당이 되는 것이었다. 튜터님의 도움 덕에 밑에 부분들에 대해서 다시 한번 인지하고 공부할 수 있는 좋은 기회였다.





  • Static : 클래스의 일반 멤버 변수는 클래스의 객체가 생성될 때, 각 객체마다 따로 생기지만, 정적 변수는 해당 클래스가 처음으로 사용되는 때에 한 번만 초기화되어 계속 동일한 메모리를 사용하게 된다.

출저 : 베르의 프로그래밍 노트

  • Instance

  • 상속

  • 컴포지션

위 개념에 대해 다시한번 인지하고 코드들을 수정해보았다.

➔ 수정된 부분

PlayerAttackSystem.cs

  • PlayerAttackSystem.cs 에서 ItemManager의 상속을 뺏다.
  • 이 스크립트에서는 단일책임원칙을 위해 PlayerItemState.cs에서 Item의 상태만 참조해서 그 Item에 맞는 공격만 하도록 수정하였다.
public class PlayerAttackSystem : MonoBehaviour
{
    #region Example
    //// Coroutine Caching
    //private IEnumerator ItemCoolTime;
    //private WaitForFixedUpdate fixedUpdate = new WaitForFixedUpdate();
    #endregion

    #region Global_Variale
    // Scripts
    TopDownCharacterController _controller;
    PlayerItemState _itemManager;

    // Component
    Rigidbody2D bulletRigid;    // bullet Prefab Clone Rigidbody

    // Bullet Prefab
    GameObject bullet; // ( bullet는 player가 발사하는 총알 Prefab )
    GameObject PenetrateItemBullet;
    GameObject bounceBullet;
    GameObject guidedMissileBullet;

    // Basic Value
    public float Force { get; set; }
    public bool CoolTimeCheck {  get; set; }
    #endregion

    #region Initialization_Settings
    private void Awake()
    {
        // Scripts Search
        _controller = GetComponent<TopDownCharacterController>();
        _itemManager = FindObjectOfType<PlayerItemState>();
        // Resources Load
        bullet = Resources.Load<GameObject>("Prefabs/Bullet");
        PenetrateItemBullet = Resources.Load<GameObject>("Prefabs/PenetrateItemBullet");
        bounceBullet = Resources.Load<GameObject>("Prefabs/BounceBullet");
        guidedMissileBullet = Resources.Load<GameObject>("Prefabs/GuidedMissileBullet");
    }

    private void Start()
    {
        _controller.OnAttackEvent += Attack;
        PlayerBasicValue();
    }
    #endregion

    #region Player_AttackLogic
    private void Attack()
    {
        // CoolTime Check
        if (CoolTimeCheck == true)
        {
            CoolTimeCheck = false;
            RecallBullet();
        }
    }

    private void RecallBullet()
    {
        switch (_itemManager.currentItem)
        {
            case PlayerItemState.ItemType.Normal:
                ItemNormalBullet();
                break;
            case PlayerItemState.ItemType.BulletUPItem:
                ItemBulletCountUp();
                break;
            case PlayerItemState.ItemType.PenetrateItem:
                ItemBulletPenetrateItem();
                break;
            case PlayerItemState.ItemType.BounceItem:
                ItemBulletBounce();
                break;
            case PlayerItemState.ItemType.GuidedMissileItem:
                ItemBulletGuidedMissile();
                break;
            default:
                break;
        }
    }

    private void ApplyAttck(GameObject obj)
    {
        bulletRigid = obj.GetComponent<Rigidbody2D>();
        bulletRigid.AddForce(transform.up * Force, ForceMode2D.Impulse);
    }
    #endregion

    #region BulletState
    private void ItemNormalBullet()
    {
        GameObject playerbullet = Instantiate(bullet);
        playerbullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
        ApplyAttck(playerbullet);
    }

    private void ItemBulletCountUp()
    {
        GameObject playerBullet1 = Instantiate(bullet);
        GameObject playerBullet2 = Instantiate(bullet);
        playerBullet1.transform.position = new Vector3(transform.position.x - 0.2f, transform.position.y + 0.4f, transform.position.z);
        playerBullet2.transform.position = new Vector3(transform.position.x + 0.2f, transform.position.y + 0.4f, transform.position.z);
        ApplyAttck(playerBullet1);
        ApplyAttck(playerBullet2);
    }

    private void ItemBulletPenetrateItem()
    {
        GameObject playerbullet = Instantiate(PenetrateItemBullet);
        playerbullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
        ApplyAttck(playerbullet);
    }

    private void ItemBulletBounce()
    {
        GameObject playerbullet = Instantiate(bounceBullet);
        playerbullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
        ApplyAttck(playerbullet);
    }

    private void ItemBulletGuidedMissile()
    {
        GameObject playerbullet = Instantiate(guidedMissileBullet);
        playerbullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
    }
    #endregion

    #region StateReset

    private void PlayerBasicValue()
    {
        _itemManager.currentItem = PlayerItemState.ItemType.Normal;
        Force = 8f;
        CoolTimeCheck = true;
    }
    #endregion
}










ItemManager -> PlayerItemState.cs

  • Player가 아이템을 먹으면 이 쪽에서 상태를 바꿔주고 아이템의 지속시간 관리를 하도록 하였다.
public class PlayerItemState : MonoBehaviour
{
    #region ItemTypeEnum
    public enum ItemType
    {
        Normal,
        BulletUPItem,       // bullet 갯수 증가
        PenetrateItem,      // 관통
        BounceItem,         // 튕김
        GuidedMissileItem   // 유도탄
    }

    // ItemType Reset Value
    public ItemType currentItem;

    #endregion

    #region Global_Variale

    public PlayerAttackSystem _player;

    // Item Use DurationTime
    public float itemDuration = 0f;

    #endregion

    private void Awake()
    {
        _player = FindObjectOfType<PlayerAttackSystem>();
    }

    private void Update()
    {
        BulletStateCheck();
    }

    #region Continuation_Item

    public void BulletSpeedUp()
    {
        _player.Force += 0.5f;
    }
    #endregion

    #region OneTime_Item
    public void BulletCountUpItem()
    {
        BulletStateReset(_player);
        currentItem = ItemType.BulletUPItem;
    }

    public void BulletPenetrateItem()
    {
        BulletStateReset(_player);
        currentItem = ItemType.PenetrateItem;
    }

    public void BulletBounceItem()
    {
        BulletStateReset(_player);
        currentItem = ItemType.BounceItem;
    }

    public void BulletGuidedMissileItem()
    {
        BulletStateReset(_player);
        currentItem = ItemType.GuidedMissileItem;
    }
    #endregion

    public void BulletCoolTimeReset()
    {
        _player.CoolTimeCheck = true;
    }

    private void BulletStateCheck()
    {
        if (currentItem != ItemType.Normal && itemDuration > 0f)
        {
            itemDuration -= Time.deltaTime;
        }
        if (itemDuration <= 0f && currentItem != ItemType.Normal)
        {
            itemDuration = 0f;
            currentItem = ItemType.Normal;
        }
    }

    private void BulletStateReset(PlayerAttackSystem player)
    {
        itemDuration = 0f;
        itemDuration = 5;
    }
}










Item & Bullet Scripts

  • 각 아이템 오브젝트의 스크립트들 에도 상속을 빼주고 PlayerItemState.cs에게 아이템 상태만 전달하도록 바꾸었다.
  • Bullet의 스크립트들 경우에는 작동방식을 쿨타임이 아닌 벽or공 에 부딪히면 공격을 다시 할수있게 바꾸어서 부딪힐 경우 리셋 메서드를 실행하게 하였다.
public class BulletBounceItem : MonoBehaviour
{
    PlayerItemState _itemManager;

    private void Awake()
    {
        _itemManager = FindObjectOfType<PlayerItemState>();
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Player")
        {
            // Bullet Bounce
            _itemManager.BulletBounceItem();

            Destroy(gameObject);    // 추후 오브젝트 풀링
        }
    }
}
public class GuidedMissileBulletPrefabLogic : MonoBehaviour
{
    PlayerItemState _itemManager;

    List<GameObject> Balls;

    GameObject LockOnTarget;

    float nearDis;

    // LockOn Setting
    private void Awake()
    {
        _itemManager = FindObjectOfType<PlayerItemState>();
        Balls = new List<GameObject>(GameObject.FindGameObjectsWithTag("Ball"));
        nearDis = Vector3.Distance(gameObject.transform.position, Balls[0].transform.position);
    }

    // LockOn Search
    private void Start()
    {
        // Null Value Ready
        LockOnTarget = Balls[0];

        // Search Near Distance Target
        foreach (GameObject index in Balls)
        {
            float distance = Vector3.Distance(gameObject.transform.position, index.transform.position);
            
            if (distance < nearDis)
            {
                LockOnTarget = index;
            }
        }
    }

    // LockOn Target Attack
    private void FixedUpdate()
    {
        transform.position = Vector3.MoveTowards(transform.position, LockOnTarget.transform.position, _itemManager._player.Force * Time.deltaTime);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Ball" || collision.tag == "Wall" || collision.tag == "TopWall")
        {
            _itemManager.BulletCoolTimeReset();
            Destroy(gameObject);    // 추후 오브젝트 풀링
        }
    }
}










➔ 추가된 부분

ItemFall.cs

  • 아이템이 떨어지는 스크립트 부분에 아이템이 바닥에 닿으면 1초 뒤에 10번 깜빡이는 효과를 주고 사라지게 하는 코드를 추가했다.
public class ItemFall : MonoBehaviour
{
    GameObject item;
    Rigidbody2D rigid;
    SpriteRenderer spriteRenderer;

    float timer = 1;
    bool filckerCheck = true;

    private void Awake()
    {
        item = GetComponent<GameObject>();
        rigid = transform.GetComponent<Rigidbody2D>();
        spriteRenderer = transform.GetComponent<SpriteRenderer>();
    }

    private void Start()
    {
        rigid.AddForce(transform.up * -1, ForceMode2D.Impulse);
    }

    
    private void FixedUpdate()
    {
        if (transform.position.y <= -4.3f)
        {
            rigid.constraints = (RigidbodyConstraints2D)RigidbodyConstraints.FreezeAll;
            timer -= Time.deltaTime;

            if (timer <= 0 && filckerCheck == true)
            {
                timer = 0;
                StartCoroutine(Filcker());
                filckerCheck = false;
            }
        }
    }
    

    private IEnumerator Filcker()
    {
        float Counter = 0;

        while ( Counter < 10)
        {
            float alpCol = 0;
            while (alpCol <  1.0f)
            {
                alpCol += 0.1f;
                yield return new WaitForSeconds(0.01f);
                spriteRenderer.color = new Color(1, 1, 1, alpCol);
            }
            while (alpCol > 0f)
            {
                alpCol -= 0.1f;
                yield return new WaitForSeconds(0.01f);
                spriteRenderer.color = new Color(1, 1, 1, alpCol);
            }
            Counter++;
        }

    }
}
profile
No Easy Day
post-custom-banner

0개의 댓글