본문 바로가기

Unity 공부/Unity_2D

[Day 13] 플레이 캐릭터 선택

[ 플레이 캐릭터 선택 ] --> 캐릭터를 선택하는 로직과 UI 구현

  • Canvas\GameStart안에서 Create Empty(Charactor Group)
        - Pos Y = -40
        - Width = 101 | Height = 101
  • Charactor Group에 Add Component
        - Grid Layout Group
  • Cell Size X = 50 | Y = 50
  • Spacing X = 1 | Y = 1
  • Button Start를 Charactor Group에 자식 오브젝트로 넣기(드래그&드랍)
  • Button Start 이름을 Charactor 0로 변경
  • Charactor 0에 UI\Image 추가
        - Source Image를 Player 0의 Stand 0 sprite로 변경
        - Set Native Size 클릭
        - Pos Y = 8
        - 이름을 Icon으로 변경
  • Text(Legacy) 이름을 Text Name으로 변경
        - Text = 벼농부
        - Font Size = 8
        - 앵커 선택 후 Shift + Alt 누른 후 양옆 꽉 채운 가운데 앵커로 설정
        - Left = 2 | Right = 2
        - Pos Y = -8
  • Text Name을 Ctrl + D로 복사(Text Adv)
        - Pos Y = -16
        - Foont Size = 5
        - Text = 이동속도 10% 증가
  • Charactor 0의 Color를 캐릭터 색상과 비슷하게 변경(하늘~파랑)
  • Text Name, Adv의 Outline 색상을 Charactor 0 의 색상보다 살짝 어둡게 변경
  • Charactor 0를 Ctrl + D로 복사(Charactor 1)
        - Source Image를 Player 1의 Stand 0 sprite로 변경
        - Charactor 0의 Color를 캐릭터 색상과 비슷하게 변경(분홍)
        - Text Name, Adv의 Outline 색상을 Charactor 1의 색상보다 살짝 어둡게 변경
        - Text Name의 Text = 보리농부
  • 기존의 GameManager 스크립트에 코드 추가
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    public static GameManager instance;
    [Header("# Game Conrol")]
    public bool isLive;
    public float gameTime;
    public float maxGameTime = 2 * 10f;
    [Header("# Player Info")]
    public int playerId;
    public float hp;
    public float maxHp = 100;
    public int level;
    public int kill;
    public int exp;
    public int[] nextExp = { 3, 5, 10, 100, 150, 210, 280, 360, 450, 600 };
    [Header("# Game Object")]
    public PoolManager pool;
    public Player player;
    public LevelUp uiLevelUp;
    public Result uiResult;
    public GameObject enemyCleaner;

    private void Awake()
    {
        instance = this; 
    }

    public void GameStart(int id)
    {
        playerId = id; // 플레이어 id 값 적용
        hp = maxHp; 

        player.gameObject.SetActive(true); // 버튼을 눌러 게임을 시작했을 때, 활성화
        uiLevelUp.Select(playerId % 2); // 플레이어 id 별로 다른 기본무기 지금
        Resume();
    }

    public void GameOver()
    {
        StartCoroutine(GameOverRoutine());
    }

    IEnumerator GameOverRoutine()
    {
        isLive = false;
        yield return new WaitForSeconds(0.5f);
        uiResult.gameObject.SetActive(true);
        uiResult.Lose();
        Stop();
    }

    public void GameRetry()
    {
        SceneManager.LoadScene(0);
    }

    void Update()
    {
        if(!isLive)
        {
            return;
        }
        gameTime += Time.deltaTime;

        if (gameTime > maxGameTime){
            gameTime = maxGameTime;
            GameVictory();
        }
    }


    public void GetExp()
    {
        if (!isLive)
        {
            return;
        }

        exp++;
        if (exp == nextExp[Mathf.Min(level, nextExp.Length-1)])
        {
            level++;
            exp = 0;
            uiLevelUp.Show();
        }
    }

    public void Stop()
    {
        isLive = false;
        Time.timeScale = 0;
    }

    public void Resume()
    {
        isLive = true;
        Time.timeScale = 1;
    }

    public void GameVictory()
    {
        StartCoroutine(GameVictoryRoutine());
    }

    IEnumerator GameVictoryRoutine()
    {
        isLive = false;
        enemyCleaner.SetActive(true);
        yield return new WaitForSeconds(0.5f);
        uiResult.gameObject.SetActive(true);
        uiResult.Win();
        Stop();
    }

}
  • Player 오브젝트 비활성화
  • Undead Survivor\Animations에 farmer 2,3의 애니메이션도 추가로 만들기(2D 셀 애니메이션 제작 참고)
  • 기존의 Player 스크립트에 코드 추가
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public Vector2 inputVec;
    public float speed;
    public Scanner scanner;
    public Hand[] hands;
    public RuntimeAnimatorController[] animCon; // 애니메이션 컨트롤러 배열 변수

    Rigidbody2D rigid;
    SpriteRenderer spriter;
    Animator anim;
    
    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        spriter = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
        scanner = GetComponent<Scanner>();
        hands = GetComponentsInChildren<Hand>(true);
    }

    private void OnEnable()
    {
        speed *= Character.Speed;
        anim.runtimeAnimatorController = animCon[GameManager.instance.playerId]; // 애니메이션 변경
    }

    void Update()
    {
        if (!GameManager.instance.isLive)
            return;

        inputVec.x = Input.GetAxisRaw("Horizontal");
        inputVec.y = Input.GetAxisRaw("Vertical");
    }

    private void FixedUpdate()
    {
        if (!GameManager.instance.isLive)
            return;

        Vector2 nextVec = inputVec.normalized*speed* Time.fixedDeltaTime;
        rigid.MovePosition(rigid.position + nextVec);
    }

    private void LateUpdate()
    {
        if (!GameManager.instance.isLive)
            return;

        anim.SetFloat("Speed", inputVec.magnitude); 

        if (inputVec.x != 0) 
        {
            spriter.flipX = inputVec.x < 0;
        }
    }

    private void OnCollisionStay2D(Collision2D collision)
    {
        if (!GameManager.instance.isLive)
            return;

        GameManager.instance.hp -= Time.deltaTime * 10;

        if (GameManager.instance.hp < 0)
        {
            for(int index=2; index<transform.childCount; index++)
            {
                transform.GetChild(index).gameObject.SetActive(false);
            }

            anim.SetTrigger("Dead");
            GameManager.instance.GameOver();
        }
    }
}
  • Player의 Anim Con에 ACPlayer 0,1,2,3 오브젝트 초기화
  • Charactor 0의 On Click ()에 GameManager.GameStart 함수 재연결
        - Charactor 번호에 맞게 0-->0 | 1-->1 
  • Charactor 1\Text Adv의 Text = 연사속도 10% 증가
  • C# 스크립트 생성(Character)
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Character : MonoBehaviour
{
	// 함수가 아닌 속성을 작성
    public static float Speed 
    {
        get { return GameManager.instance.playerId == 0 ? 1.1f : 1f; } // 캐릭터 0
    }

    public static float WeaponSpeed
    {
        get { return GameManager.instance.playerId == 1 ? 1.1f : 1f; } // 캐릭터 1
    }

    public static float WeaponRate
    {
        get { return GameManager.instance.playerId == 1 ? 0.9f : 1f; } // 캐릭터 1
    }

    public static float Damage
    {
        get { return GameManager.instance.playerId == 2 ? 1.2f : 1f; } // 캐릭터 2
    }

    public static int Count
    {
        get { return GameManager.instance.playerId == 3 ? 1 : 0; } // 캐릭터 3
    }
}
  • 기존의 Gear 스크립트에 코드 추가
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Gear : MonoBehaviour
{
    public ItemData.ItemType type;
    public float rate;

    public void Init(ItemData data)
    {
        // Basic set
        name = "Gear " + data.itemId;
        transform.parent = GameManager.instance.player.transform;
        transform.localPosition = Vector3.zero;

        // Property set
        type = data.itemType;
        rate = data.damages[0];
        ApplyGear();
    }

    public void LevelUp(float rate)
    {
        this.rate = rate;
        ApplyGear();
    }

    void ApplyGear()
    {
        switch (type)
        {
            case ItemData.ItemType.Glove:
                RateUp();
                break;
            case ItemData.ItemType.Shoe:
                SpeedUp(); 
                break;

        }
    }

    private void RateUp()
    {
        Weapon[] weapons = transform.parent.GetComponentsInChildren<Weapon>();

        foreach(Weapon weapon in weapons)
        {
            switch (weapon.id)
            {
                case 0: // 캐릭터마다 다르게 적용
                    float speed = 150 * Character.WeaponSpeed;
                    weapon.speed = speed + (speed * rate);
                    break;
                default: // 캐릭터마다 다르게 적용
                    speed = 0.5f * Character.WeaponRate;
                    weapon.speed = speed * (1f - rate);
                    break;
            }
        }
    }

    void SpeedUp()
    {
        float speed = 3 * Character.Speed; // 캐릭터에 따라 다른 스피드
        GameManager.instance.player.speed = speed + speed * rate;
    }
}
  • 기존의 Weapon 스크립트에 코드 추가
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using UnityEngine;

public class Weapon : MonoBehaviour
{
    public int id;
    public int prefabId;
    public float damage;
    public int count;
    public float speed;

    float timer;
    Player player;

    void Awake(){
        player = GameManager.instance.player;
    }

    void Update(){
        if (!GameManager.instance.isLive)
            return;

        switch (id){
            case 0:
                transform.Rotate(Vector3.back * speed * Time.deltaTime);
                break;

            default:
                timer += Time.deltaTime;

                if(timer > speed){
                    timer = 0f;
                    Fire();
                }
                break;

        }

        // .. Test Code ..
        if (Input.GetButtonDown("Jump")){
            LevelUp(10, 1);
        }
    }

    public void LevelUp(float damage, int count)
    {
        this.damage += damage * Character.Damage; // 레벨업 수치 조정
        this.count += count; // 레벨업 수치 조정

        if(id == 0){
            Batch();
        }

        player.BroadcastMessage("ApplyGear",SendMessageOptions.DontRequireReceiver);
    }

    public void Init(ItemData data)
    {
        // Basic Set
        name = "Weapon " + data.itemId;
        transform.parent = player.transform;
        transform.localPosition = Vector3.zero;

        // Property Set
        id = data.itemId;
        damage = data.baseDamage * Character.Damage;
        count = data.baseCount + Character.Count;

        for(int index=0; index<GameManager.instance.pool.prefabs.Length; index++){
            if (data.projectile == GameManager.instance.pool.prefabs[index]){
                prefabId = index;
                break;
            }    
        }

        switch(id){
            case 0:
                speed = 150 * Character.WeaponSpeed; // 캐릭터마다 다른 무기 회전 속도
                Batch();
                break;

            default:
                speed = 0.5f * Character.WeaponRate; // 캐릭터마다 다른 무기 발사 속도
                break;
        }

        // Hand Set
        Hand hand = player.hands[(int)data.itemType];
        hand.spriter.sprite = data.hand;
        hand.gameObject.SetActive(true);

        player.BroadcastMessage("ApplyGear", SendMessageOptions.DontRequireReceiver);
    }

    void Batch(){
        for(int index=0; index < count; index++) {
            Transform bullet;
           
            if(index < transform.childCount){
                bullet = transform.GetChild(index);
            }
            else{
                bullet = GameManager.instance.pool.Get(prefabId).transform;
                bullet.parent = transform;

            }

            bullet.localPosition = Vector3.zero;
            bullet.localRotation = Quaternion.identity;

            Vector3 rotVec = Vector3.forward * 350 * index / count;
            bullet.Rotate(rotVec);
            bullet.Translate(bullet.up * 1.5f, Space.World);
            bullet.GetComponent<Bullet>().Init(damage, -1, Vector3.zero); // -1 is Infinity Per.

        }
    }

    void Fire()
    {
        if (!player.scanner.nearestTarget){
            return;
        }

        Vector3 targetPos = player.scanner.nearestTarget.position;
        Vector3 dir = targetPos - transform.position;
        dir = dir.normalized;

        Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
        bullet.position = transform.position;
        bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir);
        bullet.GetComponent<Bullet>().Init(damage, count, dir);

    }
}

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

★Slow and Steady★

'Unity 공부 > Unity_2D' 카테고리의 다른 글

[Day 15] 오디오 시스템 구축  (0) 2023.07.25
[Day 14] 캐릭터 해금 시스템  (0) 2023.07.24
[Day 12] 게임 시작과 종료 구현  (0) 2023.07.18
[Day 11] 레벨업 시스템  (0) 2023.07.17
[Day 10] 무기 장착 표현  (0) 2023.07.16