게임/개발

|유니티| 5 | GameManager을 통한 전역 처리 & 점수표 UI & 발사체 & 클래스와 상속

no title

Send Message

게임 전역적으로 처리해야하는 값,메소드를 모아놓는다

다음 기능을 각 오브젝트가 아닌 전역적으로 처리하도록 해보자.
1. 게임 재시작
2. 빨간코인 -> 장애물 삭제
3. 코인획득 -> 점수표

1. 게임 매니저

1. 전역 스크립트에 저장하기/스크립팅

  • 절차는 다음과 같다

    1. 전역에서 실행되기를 가정한 WORLD 오브젝트를 생성한다
    2. WORLD 오브젝트에 WORLD 스크립트를 삽입한다
  • 다음은 게임 매니저에서 전역으로 실행될 메소드를 만든코드이다.

    public class GameManager : MonoBehaviour
    {
        bool DEBUG = true;
        private int coinCount = 0;
        void RestartGame(){
            if(DEBUG) {Debug.Log("Total Coins" + coinCount);}
            Application.LoadLevel("BallGame");
            if(DEBUG) {Debug.Log("Restarted");}
        }
        void DestroyObstacle(string _Target){
            GameObject[] ObstaclesArr = GameObject.FindGameObjectsWithTag(_Target);
            foreach(GameObject E in ObstaclesArr){
                Destroy(E);
            }
            if(DEBUG) {Debug.Log("Get RedCoin Remove All");}
        }        
        void CalCoin(){
            coinCount++;
        }
    }
    

2. 전역 스크립트를 불러오기/호출하기

  • 다음은 WORLD 오브젝트에 삽입한 스크립트를 호출하는방법이다
    GameObject.Find("_WORLD 오브젝트_").SendMessage("_WORLD 스크립트 메소드_, 메소드 인자");
    

3. 궁극적으로 SendMessage는 쓰지말자

결과

  • 다음은 게임 매니저를 통해 전달된 함수 시연이다.

클래스

1. 컴포넌트

  • 인스펙터 창에 추가된것을 말한다. 곧 클래스로 구현된것이다.
    • 스크립트에서 다른(내부 혹은 외부 오브젝트에 선언된) 컴포넌트 접근하기/가져오기
      // 1
      GetComponent<_컴포넌트,스크립트 명_>();
      
      혹은, 
      // 2
      _컴포넌트,스크립트명(클래스)_ _instance_ = gameObject.GetComponent<_컴포넌트,스크립트 명_>(); 
          // 컴포넌트 클래스 자료형이다.
          //gameObject는 컴포넌트를 포함하는 자기자신의 오브젝트를 의미함
      
    • 인스턴스의 분류
      1. 메소드
      2. 멤버변수
      • 클래스에는 메소드와 멤버변수를 가져올 수 있음

직접 만들어보기

  • 가져온 컴포넌트/스크립트명 클래스에서 메소드나 멤버변수를 사용하려면 조건이 있다
    • public으로 선언하면 .___으로 사용가능
      //참조할 스크립트 내부
      public class GameManager : MonoBehaviour
      {
          private bool DEBUG = true;
          private int coinCount = 0;
      
          public void CalCoin(){ }
          public void RestartGame(){ }
          public void DestroyObstacle(string _Target){}
      }
      
  • 메소드로 호출하자
  • 다음 컴포넌트 접근 방법은 worldGM을 통해 메소드를 실행할 것이다.
    1. 게임 재시작
      public class RestartGame : MonoBehaviour
      {
          bool DEBUG = false;
          private GameManager worldGM;
          public GameObject G; //인스펙터에서 GM 지정하기
          void OnTriggerEnter(Collider _collider) {
              if(DEBUG) {Debug.Log(_collider.gameObject.name);}
              if(_collider.gameObject.name == "Ball"){
                  worldGM.RestartGame();
                  //Application.LoadLevel("BallGame");
              }
          }
          // Start is called before the first frame update
          void Start(){
              worldGM = G.GetComponent<GameManager>();
          }
      }
      
    2. 빨간코인 -> 장애물삭제
      public class RedCoin : MonoBehaviour
      {
          private GameManager worldGM;
          public GameObject G; //인스펙터에서 GM 지정하기
          void OnTriggerEnter(Collider _col){
              if(_col.gameObject.name == "Ball"){
                  worldGM.DestroyObstacleWithTarget("_Obstacle");
                  Destroy(gameObject);
              }
          }
          // Start is called before the first frame update
          void Start()
          {
              worldGM = G.GetComponent<GameManager>();
          }
      }
      
    3. 코인획득 -> 점수표
      public class GetCoin : MonoBehaviour {
          private GameManager worldGM;
          public GameObject G; //인스펙터에서 GM 지정하기
          void OnTriggerEnter(Collider _col){
              if(_col.gameObject.name == "Ball"){
                  worldGM.CalCoin();
                  //Debug.Log(gameObject.name +" " + gameObject.tag);
                  Destroy(gameObject);
              }
          }
          void Start(){
              worldGM = G.GetComponent<GameManager>();
          }
      }
      

2. 인스펙터와 Public

  • public 으로 선언한 멤버변수가 인스펙터에서 오염될 수도 있지 않을까?
    • 음.. 그래서 일반적으로 다음과 같이 캡슐화 하라고 한다..
      • 멤버 변수는 private
      • 메소드는 public
    • 혹시 모르니 아예 게임 Start()에서 멤버변수를 초기화 해주자.

using UnityEngine.UI; (UI 보여주기)

응집성을 낮추기 위해 따로 DrawText() 메소드를 만들자

보간문자열 String Formating

  • "문자열" 이라고 있는 이 큰 따옴표(") 앞에 달라($)표기를 해주면 중괄호 안에 변수를 입력할 수 있습니다.
    • 예를들어 $"문자열 테스트 {num}" 이런식으로 만들 수 있다는 이야기 입니다. 직접 예제를 보시죠.
      int a = 10; int b = 20;
      Console.WriteLine($"string example1 : {a} + {b} = {a+b}")
      

코인획득 UI 결과

  • 다음은 코인을 얻을때 좌측상단의 초록 텍스트의 숫자가 올라가는것을 볼 수있다

스크립트 클래스 컴포넌트의 차이

같은 클래스라도 따로 생성된 인스턴스는 각각 메모리 공간이 다르다.

오브젝트 슈터 & Time.Delta

1. 오브젝트 슈터

오브젝트의 발사는 어떻게 하는건가?

  • 날아간다는것은 어떻게 표현되는거지?
    1. 목표지점/타겟 설정
    2. Vector3.MoveTowards
      transform.position = Vector3.MoveTowards(vector3 _현재위치_ ,vector3 _타겟_,_스피드f_);
      
    3. 애니메이션 설정 (Rotate)
      transform.Rotate(new Vector3(x,y,z));
      
  • 소비된후 파괴
    1. OnCollisionEnter & 트리거를 쓰든...
      void OnCollisionEnter(Collider _col){
          if(_col.gameObject.name == "Ball"){
              worldGM.Restart();
          }
      }
      

결과_1 : 직선 탄

  • 타겟 위치는 Start에서만 초기화
    public GameObject GM;
    public GameObject target;
    private GameManager worldGM;
    private Vector3 targetPos;
    private void OnCollisionEnter(Collision _col) {
        if(_col.gameObject.name == target.name){
            Destroy(gameObject);
            worldGM.RestartGame();
        }
    }
    // Start is called before the first frame update
    void Start()
    {
        worldGM = GM.GetComponent<GameManager>();
        targetPos = target.transform.position;
    }
    
    // Update is called once per frame
    void Update()
    {
        transform.Rotate(new Vector3(0,0,5));
        transform.position = Vector3.MoveTowards(transform.position, targetPos, 0.1f);
    }
    

결과_2 : 유도 탄

  • Update 마다 타겟위치 초기화
    void Update()
    {
        targetPos = target.transform.position;
        transform.Rotate(new Vector3(0,0,5));
        transform.position = Vector3.MoveTowards(transform.position, targetPos, 0.1f);
    }
    

2. 오브젝트 위치 세팅 Global & Local

1.Local은 Scene 기준 기즈모
2.Global은 대상 오브젝트 기준 기즈모

Local Global

3. Time.delta

  1. 델타 타임 카운트
    timeCount += Time.deltaTime;
    
    if(timeCount > _N초_){...}
    
  2. 델타 타임 초기화
    timeCount = 0;
    

4. 오브젝트 생성/출산(?)

Instantiate

  • Instantiate를 통해 오브젝트를 런타임동안 생성할 수 있다
    Instantiate(_만들 오브젝트_, _자신의 위치_, _돌아간 정도_);
    
  • 적용 코드
    public GameObject GenOb;
    float timeCount = 0f;
    void Update()
    {
        timeCount += Time.deltaTime;
        if(timeCount > 3){
            Instantiate(GenOb, transform.position, Quaternion.identity);
            timeCount = 0;
        }
    }
    

결과물

  • 3개의 장애물이 탄막을 발사한다

4 Prefeb

Hierarchy에서 Project_Asset으로 드래그

  • 나중에도 사용할 수 있게끔 오브젝트를 저장한다
    1. 다음은 드래그하는 모습
    2. 프리펩의 인스펙터 컴포넌트도 저장되었다.

상속

1. 중복한 코드를 넣는것 비효율적이고 수정하기도 어렵다.

부모와 자식 & 설정

  • public class Shoting : MonoBehaviour //상속전
    public class Shoting : _상속할 스크립트_ //상속후
    

부모클래스의 메소드 사용하기

  • 일단 부모 클래스의 메소드는 다음과 같아야한다
    • protected 작성
    • 오직 자식 클래스만 호출할 수 있음
      protected void _METHOD_() {};
      
  • 자식 클래스에서 부모 메소드 호출하기
    • base.부모메소드
      base.Update();
      

같은 클래스라도 따로 생성된 인스턴스는 각각 메모리 공간이 다르다.


⭐완강 경축⭐

❓❓❓ 의문점 & 해야할 일 ❓❓❓

1.스크립트는 사실 클래스 아닐까? ❓(다음에 찾아보기)

2.디자인 패턴 ❓(다음에 찾아보기)

3.MoveToward의 단점 ❓(다음에 찾아보기)

4. 타임 개념을 잘 알아보기 ❓(다음에 찾아보기)

5.C# 과 OOP를 확실하게 배우자 ❓(다음에 찾아보기)

6. 등등 여러강의를 더 보면서 공부하자 ❓(다음에 찾아보기)

1. 볼트