본문 바로가기

Development/[Game] Basic Game

[Game] #5. 게임 메니저

728x90
반응형

게임 메니저 스크립트

게임 메니저의 역할

  1. 게임의 점수를 관리한다.
  2. 게임의 도착지에 도달 시 스테이지를 관리한다.
  3. 스테이지의 인덱스를 관리한다.
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public int totalPoint;
    public int stagePoint;
    public int stageIndex;
    void Start()
    {
        
    }
    void Update()
    {
        
    }
}

게임 메니저를 위 사진과 같이 생성한 후 플레이어에서 매니저 변수를 만들어 점수 변수에 접근한다.

PlayerMove 스크립트에 다음과 같이 게임 메니저 스크립트를 추가한다.

public GameManager gameManager;

게임 포인트

동전에 접촉 시 사용하는 메서드를 수정하여 게임 포인트를 증가하도록 한다.

이때 동전 게임 오브젝트를 금,은,동을 각각 태그를 달아준다.

tag Gold
Silver
Bronze
private void OnCollisionEnter2D(Collision2D collision) {
    if (collision.gameObject.tag == "Enemy") {
        //Attack - 3.22
        Debug.Log("Enemy Connect!");
        if(rigid.velocity.y<0 && transform.position.y > collision.transform.position.y){
            OnAttack(collision.transform);
            Debug.Log("Attack to Enemy!");
        }
        else {
            OnDamaged(collision.transform.position); //Damaged - 3.22
            Debug.Log("Too Sad...");
        }

    }
    //Coin Manager
    else if (collision.gameObject.tag == "Bronze")gameManager.stagePoint += 50;
    else if (collision.gameObject.tag == "Silver")gameManager.stagePoint += 150;
    else if (collision.gameObject.tag == "Gold")gameManager.stagePoint += 300;

    //Finish
    else if (collision.gameObject.tag == "Finish"){
        Destroy(collision.gameObject);
    }
}

이제 플레이어가 데미지를 입으면 체력을 감소시킬 HealthDown 함수를 만든다.

이때 게임 메니저를 참조하여 스테이지 포인트를 관리한다. 이 과정에서 오류가 나면 다음 블로그 포스팅을 참조한다.

https://udangtangtang-cording-oldcast1e.tistory.com/125

 

[GameManager] 게임 메니저 오류

유니티 프로그래밍을 하다보면 NullReferenceException: Object reference not set to an instance of an object 와 같이 뜨는 경우가 있다. NullReferenceException: Object reference not set to an instance of..

udangtangtang-cording-oldcast1e.tistory.com

public void HealthDown(){
    if(healthPoint > 1) healthPoint --;
    else{
        //player Die Effect
        player.OnDie();
        //Result UI
        Debug.Log("죽었습니다!");
        //Retry Button UI
    }
}

해당 함수를 만드면 Healthpoint를 감소시키는 코드인 Healthpoint--;를 가진 코드를 모두 HealthDown();로 변경한다.

게임 스테이지

스테이지가 올라가면 totalPoint에 누적한다. 이때 NextStage 함수는 Finish 오브젝트를 접촉할 때 사용한다. 

Finish 오브젝트를 접촉은 스테이지 클리어의 의미를 가진다.

[GameManager]

이때 NextStage의 범위public으로 정의하는 것을 주의한다!

using UnityEngine;

public class GameManager : MonoBehaviour
{
    public int totalPoint;
    public int stagePoint;
    public int stageIndex;
    public void NextStage()
    {
        stageIndex ++;

        totalPoint += stagePoint;
        stagePoint = 0;
    }
    void Update()
    {
        
    }
}

[PlayerMove]

private void OnCollisionEnter2D(Collision2D collision)
...이후 생략
else if (collision.gameObject.tag == "Finish"){
    //NextStage
    gameManager.NextStage();
}

 

플레이어의 체력 관리

게임 메니저 스크립트에 플레이어의 체력을 관리할 healthPoint 변수를 public으로 선언한다.

public int healthPoint;
public int totalPoint;
public int stagePoint;
public int stageIndex;

public int healthPoint;
void OnTriggerEnter2D(Collision2D collision){
    if (collision.gameObject.tag == "Player"){
        //HealthDown
        healthPoint --;

        collision.attachedRigdbody.velocity = Vector2.zero;
        collision.transform.position = new Vector3(0,0,-1);
    }
}

 

이때 collision.transform.position = new Vector3(0,0,-1); 는 캐릭터의 시작점이 (0,0)일 때만 가능하다.

따라서 캐릭터의 재시작 로직의 Vector3는 각 스테이지의 캐릭터 시작점으로 설정한다.

[PlayerMove]

PlayerMove의 스크립트의 Ongamaged 함수를 변경한다.

public void OnDamaged(Vector2 TargetPos){//-3.22
    //Health Down
    gameManager.healthPoint --;
    ...이하 중략
}

 

플레이어의 낙사/사망 시 원위치

게임 메니저에 BoxCollider2D 컴포넌트를 추가하고, isTrigger를 체크한다.

게임 메니저

void OnTriggerEnter2D(Collider2D collision){
        if (collision.gameObject.tag == "Player"){
            //HealthDown
            healthPoint --;
            Debug.Log("Player Die!");

            collision.attachedRigidbody.velocity = Vector2.zero;
            collision.transform.position = new Vector3(0,0,-1);
            
        }
    }
}

PlayerMove 스크립트 변경

플레이어가 Die 판정을 받을 때의 함수를 생성한다.

  • 플레이어의 컬러를 변경한다.
  • 플레이어의 y 축 Flip를 true로 변경한다.
  • 플레이어의 콜라이더를 비활성화 한다.
  • 플레이어의 리지드 바디에 힘을 부여한다.
  • DeActive를 5초 후에 실행한다.
void OnDie(){
    //Sprite Alpha
    spriteRenderer.color = new Color(1,1,1,0.4f);
    //Sprite Flip Y
    spriteRenderer.flipY = true;
    //Collider Disable
    PlayerColider.enabled = false;
    //Die Effect Jump
    rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
    // //Destroy
    Invoke("DeActive",5);
}

PlayerMove 스크립트 전체 코드

using UnityEngine;
public class PlayerMove : MonoBehaviour
{
    public GameManager gameManager;
    public float maxSpeed; //최대 속력 변수 
    public float jumpPower;
    private Rigidbody2D rigid; //물리이동을 위한 변수 선언 
    private SpriteRenderer spriteRenderer; //방향전환을 위한 변수 
    private Animator animator; //애니메이터 조작을 위한 변수 
    private CircleCollider2D PlayerColider;


    private AudioSource playerAudio; // 사용할 오디오 소스 컴포넌트
    private bool isDead = false; // 사망 상태

    private void Awake() {
        
        rigid = GetComponent<Rigidbody2D>(); //변수 초기화 
        spriteRenderer = GetComponent<SpriteRenderer>(); // 초기화 
        animator = GetComponent<Animator>();
        PlayerColider = GetComponent<CircleCollider2D>();
        // gameManager = GetComponent<GameManager>();
    }


    void Update(){

        // 버튼에서 손을 떄는 등의 단발적인 키보드 입력은 FixedUpdate보다 Update에 쓰는게 키보드 입력이 누락될 확률이 낮아짐


        //Jump
        if(Input.GetButtonDown("Jump") && !animator.GetBool("isJumping")){
            rigid.AddForce(Vector2.up* jumpPower , ForceMode2D.Impulse);
            animator.SetBool("isJumping",true);
        }

        //Stop speed 
        if(Input.GetButtonUp("Horizontal")){ // 버튼에서 손을 때는 경우 
            // normalized : 벡터 크기를 1로 만든 상태 (단위벡터 : 크기가 1인 벡터)
            // 벡터는 방향과 크기를 동시에 가지는데 크기(- : 왼 , + : 오)를 구별하기 위하여 단위벡터(1,-1)로 방향을 알수 있도록 단위벡터를 곱함 
            rigid.velocity = new Vector2( 0.5f * rigid.velocity.normalized.x , rigid.velocity.y);
        }

        //Direction Sprite
        if(Input.GetButtonDown("Horizontal"))
            spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1;

        
        //Animation
        if( Mathf.Abs(rigid.velocity.x) < 0.2) //속도가 0 == 멈춤 
            animator.SetBool("isWalking",false); //isWalking 변수 : false 
        else// 이동중 
            animator.SetBool("isWalking",true);
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        float h = Input.GetAxisRaw("Horizontal");   
        rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);

        if(rigid.velocity.x > maxSpeed)  //오른쪽으로 이동 (+) , 최대 속력을 넘으면 
            rigid.velocity= new Vector2(maxSpeed, rigid.velocity.y); //해당 오브젝트의 속력은 maxSpeed 
        
        else if(rigid.velocity.x < maxSpeed*(-1)) // 왼쪽으로 이동 (-) 
            rigid.velocity =  new Vector2(maxSpeed*(-1), rigid.velocity.y); //y값은 점프의 영향이므로 0으로 제한을 두면 안됨 


        //Landing Paltform
        Debug.DrawRay(rigid.position, Vector3.down, new Color(0,1,0)); //빔을 쏨(디버그는 게임상에서보이지 않음 ) 시작위치, 어디로 쏠지, 빔의 색 

        RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform"));
        //빔의 시작위치, 빔의 방향 , 1:distance , ( 빔에 맞은 오브젝트를 특정 레이어로 한정 지어야할 때 사용 ) // RaycastHit2D : Ray에 닿은 오브젝트 클래스 
    
        //rayHit는 여러개 맞더라도 처음 맞은 오브젝트의 정보만을 저장(?) 
        if(rigid.velocity.y < 0){ // 뛰어올랐다가 아래로 떨어질 때만 빔을 쏨 
            // Debug.Log("Drop!");
            if(rayHit.collider != null){ //빔을 맞은 오브젝트가 있을때  -> 맞지않으면 collider도 생성되지않음 
                // Debug.Log("Down!");
                if(rayHit.distance < 0.5f) 
                    animator.SetBool("isJumping",false); //거리가 0.5보다 작아지면 변경

            }
        }
    }

    private void OnTriggerEnter2D(Collider2D other) {
        if (other.tag == "Dead" && !isDead) Die();
        Debug.Log("Player Dead");
    }

    private void OnCollisionEnter2D(Collision2D collision) {
        if (collision.gameObject.tag == "Enemy") {
            //Attack - 3.22
            Debug.Log("Enemy Connect!");
            if(rigid.velocity.y<0 && transform.position.y > collision.transform.position.y){
                OnAttack(collision.transform);
                Debug.Log("Attack to Enemy!");
            }
            else {
                OnDamaged(collision.transform.position); //Damaged - 3.22
                Debug.Log("Too Sad...");
            }

        }//Coin Manager
        
        else if (collision.gameObject.tag == "Bronze") {
            gameManager.stagePoint += 50; 
            collision.gameObject.SetActive(false);
        }
        else if (collision.gameObject.tag == "Silver") {
            gameManager.stagePoint += 150;
            collision.gameObject.SetActive(false);
        }
        else if (collision.gameObject.tag == "Gold") {
            gameManager.stagePoint += 300;
            collision.gameObject.SetActive(false);
        }
        
        else if (collision.gameObject.tag == "Finish"){//Finish
            // Destroy(collision.gameObject);
            //NextStage
            Debug.Log("스테이지 클리어!");
            gameManager.NextStage();

        }
    }
    void OnAttack(Transform enemy){
        //Point

        //Enemy Die
        EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
        enemyMove.OnDamaged();
    }

    private void Die() {

        animator.SetTrigger("Die");
        // // playerAudio.clip = deathClip;

        // // playerAudio.Play();
        rigid.velocity = Vector2.zero;

        isDead = true;
    }

    public void OnDamaged(Vector2 TargetPos){//-3.22
        //Health Down
        // gameManager.healthPoint --;
        gameManager.HealthDown();
        //Change Layer
        gameObject.layer = 11;
        //Sprite Alpha
        spriteRenderer.color = new Color(1,1,1,0.4f);
        //Readctio Force 3.22
        int dirc = transform.position.x - TargetPos.x > 0 ?1:-1;
        rigid.AddForce(new Vector2(dirc,1)*5,ForceMode2D.Impulse);

        animator.SetTrigger("Die"); //- 3.25
        Invoke("OffDamaged",3);
    }
    void DeActive(){
        gameObject.SetActive(false);
    }
    void OffDamaged(){
        gameObject.layer = 10;
        spriteRenderer.color = new Color(1,1,1,1);
    }
    public void OnDie(){
        //Sprite Alpha
        spriteRenderer.color = new Color(1,1,1,0.4f);
        //Sprite Flip Y
        spriteRenderer.flipY = true;
        //Collider Disable
        PlayerColider.enabled = false;
        //Die Effect Jump
        rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
        // //Destroy
        Invoke("DeActive",5);
    }
    public void VelocityZero(){
        rigid.velocity = Vector2.zero;
    }
}

GameManager 스크립트

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

public class GameManager : MonoBehaviour
{
    public int totalPoint;
    public int stagePoint;
    public int stageIndex;

    public GameObject[] Stages;
    public PlayerMove player;

    public int healthPoint;
    public void NextStage()
    {
        Debug.Log("다음 스테이지 개방!");
        if(stageIndex < Stages.Length -1){
            Stages[stageIndex].SetActive(false);
            Debug.Log("stageIndex:"+stageIndex);
            stageIndex ++;
            PlayerReposition();
            Stages[stageIndex].SetActive(true);
            // SceneManager.LoadScene(stageLevel, LoadSceneMode.Single);
        }
        else{
            Time.timeScale = 0;
            Debug.Log("게임 클리어!");

        }
        

        
        totalPoint += stagePoint;
        stagePoint = 0;
    }
    void OnTriggerEnter2D(Collider2D collision){
        if (collision.gameObject.tag == "Player"){
            //HealthDown
            // healthPoint --;
            if(healthPoint> 1) PlayerReposition();
            HealthDown();
        }
    }

    public void HealthDown(){
        if(healthPoint > 1) healthPoint --;
        else{
            //player Die Effect
            player.OnDie();
            //Result UI
            Debug.Log("죽었습니다!");
            //Retry Button UI
        }
    }

    void PlayerReposition(){
        player.transform.position = new Vector3(0,0,-1);
        player.VelocityZero();
    }
}
728x90
반응형

'Development > [Game] Basic Game' 카테고리의 다른 글

[Game] #7. 게임 UI 생성 및 완성  (0) 2022.03.29
[Game] #4. 충돌 및 사냥 구현  (0) 2022.03.28
[Game] #3. AI 구현  (0) 2022.03.20
[Game] #2. 게임 UI 및 팔레트  (0) 2022.03.20
[Game] #1. 새로운 프로젝트  (0) 2022.03.20
댓글