Unity 입문 팀프로젝트 - 2

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

📌 2D 입문 팀프로젝트


📌 TASKS

  • 공격 시스템 -
  1. Player가 "Space"키를 누르면 "Bullet" 발사 - 완료
    ( Player의 transform.position을 기준으로 머리 쪽에서 Bullet프리팹은 인스턴스 하여
    y 축 방향으로 AddForce 하여 밀어줘 발사 현상 구현)
  1. 공(Enemy)은 Bullet에 맞으면 분열. 일정 크기가 될 때까지 계속 각각의 오브젝트가 2개로 분열.
    ( 제일 작은 크기의 공(분열 x 사라지는 최하위 공) Scale = 0.1 , 그 위 단계로 Scale += 0.2씩 홀수 단위 )

2.1 공이 분열할 때 공의 크기별로 프리팹으로 만들어두기

  1. Bullet이 공에 닿으면? 기본 : 바로 분열 , 추후 시간이 되면 ( 난이도or 스테이지증가시 데미지(예 파랑풍선2방 컷 빨강풍선1대 컷) )

  2. Bullet은 Collider 벽(맵 밖의 한계선 설정 벽)에 맞거나 공(Enemy) 맞으면 Bullet은 사라져야 함. (추후 오브젝트 풀링 최적화)
    ( "Collision"Enter2D를 이용, Bullet은 닿으면 사라지는 로직만, 공 스크립트에서 Bullet에 닿으면 분열하는 로직을 써야 함 ) ( Tag이용)


  • 아이템 -
  1. Player의 공격은 1초의 CoolTime , Bullet을 밀어주는 힘(스피드) Force는 5
    추후 아이템 or 장비로? 강화로 증가 : 드롭 아이템(시간제한) 증가

  2. 아이템의 경우 공 제외 다른 오브젝트( 동물 or 물체 ) 랜덤으로 날아다니는 걸 맞추면 랜덤 아이템 드롭

  3. 아이템을 먹으면 지속시간을 적용 ( ex: n초 )

  4. 아이템 드롭이 되어 바닥에 떨어진 아이템은 - n초뒤에 사라짐 ( x, z축 프리징 고정 )

아이템 : Bullet의 증가, Bullet 변경, 공속 증가, Bullet Speed 증가, 시간 정지, 관통, 튕김, 유도

캐릭터 : Collision
불릿 : Trigger
공 : ?
벽 : ?

  • 지속성 아이템 - ( 스테이지를 넘어가면 초기화 필요 ) or ( 죽으면 초기화 )
  1. 공속 증가 - CoolTime 초를 줄인다.

  2. Bullet Speed 증가 - bullet.foce

###########################

  • 일회성 아이템 (시간 제한) -
  1. Bullet의 증가 - case 문으로 아이템을 먹으면 증가시켜 그 케이스에 맞는 인스턴스 찍어내기.

  2. 관통 - 장애물 오브젝트 및 공을 뚫고 지나감 ( 관통으로 인한 효과가 이 게임에서 클지는 모르겠음 )

  3. 튕김 - Bullet이 벽에 닿으면 랜덤방향으로 튕겨 n번까지 지속

  4. 유도 - 가장 가까운 공을 따라가서 맞춤

  5. 무적 - 맞아도 데미지를 입지 않는다.

  6. Bullet의 변경 - Prefab 추가, 메서드 경우의 수 추가

  7. 시간 정지 - 공의 움직임이 멈춤, 플레이어는 이동 및 공격 가능 ( 플레이어는 이동을 해야해서 Time.timeScale = 0 은 불가능 )

##########################

또는 상점을 만들어서 게임 플레이에서 골드를 휙 득하여 상점에서 아이템 or 장비를 구매하는 방식은? 마감일 맞출수 있을지.

만일 상점을 넣는다면 ( UI, 스테이지 (Map), 상점, 캐릭터, 공, 총알, 골드, 아이템 ) 로 역할 분할

##########################


  • 난이도 ( 과제 선택 기능 ) -
  1. 난이도는 StartScene에서 게임 시작을 누르면 팝업창에서 예) 쉬움, 중간, 어려움으로 해서 버튼 컴포넌트 안에
    Enum 타입으로 각각 받도록 하고 MainScene에 넘어가면 그 받은 Enum 타입으로 적 오브젝트를 생성하는 방식으로.
    ( StartScene에서 난이도 Enum정보를 GameManger에게 넘겨서 가져가는 방식도 좋을것 같음 ( 다만 싱글톤 필요 ))

  • 캐릭터 -
  1. 캐릭터는 피격시 하트(체력) 감소 기본값 3개

  • Manager -
  1. GameManager는 DontDestoryOnLoad를 사용할 것인가? 사용.

  2. GameManager에서 무엇을 관리할 것인지? 예) 점수, 목숨, 난이도, 스테이지 정보 등 ( 랭킹 ) ( 캐릭터 정보들 보관용 )

  3. MainSceneManager, SoundManager, UIManager는 운용할 것인가?


Player의 공격 기능은 만들었기에 이번엔 아이템에 따른 Player의 공격 변화를 구현해보았다.

ItemManager를 통해 Item을 Enum타입으로 관리하고 아이템을 먹으면 상태를 변경하여 공격을 적용하게 하도록 짜보았다.

ItemManager.cs

public class ItemManager : MonoBehaviour
{
    #region ItemType
    public enum ItemType
    {
        Normal,
        BulletUPItem,       // bullet 갯수 증가
        PenetrateItem,      // 관통
        BounceItem,         // 튕김
        GuidedMissileItem   // 유도탄
    }
    // ItemType Reset Value
    public ItemType currentItem;
    #endregion

    public static ItemManager instance;

    private void Awake()
    {
        instance = this;
    }

    // Basic Value
    [SerializeField] protected float force = 5;
    [SerializeField] protected float coolTime = 1.0f;

    public IEnumerator ItemTime(float time, ItemType current)
    {
        float timeRemained = time;
        while (timeRemained > 0.0f)
        {
            //if (current != this.currentItem) yield break;
            timeRemained -= Time.deltaTime;
            Debug.Log(timeRemained);
            yield return null;
            Debug.Log(timeRemained);
        }
        Debug.Log("끝");
        currentItem = ItemType.Normal;
    }
}

PlayerAttackSystem.cs

public class PlayerAttackSystem : ItemManager
{
    TopDownCharacterController _controller;

    Rigidbody2D bulletRigid;    // bullet Prefab Clone Rigidbody

    GameObject bullet; // ( bullet는 player가 발사하는 총알 Prefab )

    //// Coroutine Caching
    //private IEnumerator ItemCoolTime;
    //private WaitForFixedUpdate fixedUpdate = new WaitForFixedUpdate();

    private bool coolTimeCheck = true;


    private void Awake()
    {
        _controller = GetComponent<TopDownCharacterController>();
        bullet = Resources.Load<GameObject>("Prefabs/Bullet");
    }

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

    private void Attack()
    {
        // CoolTime Check
        if (coolTimeCheck == true) StartCoroutine(CollTime(coolTime));
    }

    private void RecallBullet()
    {
        Debug.Log(currentItem);
        switch(currentItem)
        {
            case ItemType.Normal:
                GameObject playerBullet = Instantiate(bullet);
                playerBullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
                ApplyAttck(playerBullet);
                break;
            case ItemType.BulletUPItem:
                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);
                break;
            case ItemType.PenetrateItem:
                break;
            case ItemType.BounceItem:
                break;
            case ItemType.GuidedMissileItem:
                break;
            default:
                break;
        }
    }

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

    private IEnumerator CollTime(float time)
    {
        // CoolTime Setting
        coolTimeCheck = false;

        RecallBullet();

        while (time > 0.0f)
        {
            time -= Time.deltaTime;
            yield return new WaitForFixedUpdate();
        }

        // CoolTime Reset
        coolTimeCheck = true;
    }
}

BulletIncrease.cs

public class BulletIncrease : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Player")
        {
            if (ItemManager.instance.currentItem != ItemManager.ItemType.BulletUPItem)
            {
                ItemManager.instance.currentItem = ItemManager.ItemType.BulletUPItem;
                Debug.Log(ItemManager.instance.currentItem);
                StartCoroutine(ItemManager.instance.ItemTime(5.0f, ItemManager.instance.currentItem));
            }
            //else //if (currentItem == ItemType.BulletUPItem)
            //{
            //    currentItem = ItemType.Normal;
            //    currentItem = ItemType.BulletUPItem;
            //    StartCoroutine(ItemTime(5f, currentItem));
            //    currentItem = ItemType.Normal;
            //}

            //Destroy(gameObject);    // 추후 오브젝트 풀링
        }
    }


}

ItemFall.cs

public class ItemFall : MonoBehaviour
{
    GameObject item;
    Rigidbody2D rigid;

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

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

Player가 아이템을 먹으면 ItemType Enum currentItem의 상태를 바꿔주어 공격 불렛을 바꾸려 했지만 아이템이 부딪혀 Destory가 되면 코루틴이 바로 종료가 되버리는 버그가 있었다.
그리고 먹어서 ItemManager쪽에선 Enum상태가 변해도 그걸 상속받는 PlayerAttackSystem에서는 Enum상태가 변하지 않는 버그도 있어서 주말동안 그 버그를 잡아보려고 한다...

profile
No Easy Day
post-custom-banner

0개의 댓글