코드네임 :

#7 싱글톤패턴 & 오브젝트 풀 본문

프로그래밍/Unity(C#)

#7 싱글톤패턴 & 오브젝트 풀

비엔 Vien 2023. 7. 9. 23:01

옵젝풀부터 설명좀 다시 들으삼 

 

정적변수가지고 싱그론 패턴 객체 만듦

ScoreManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

// 여기 써있는 Start()과 Update()는 private이므로 ScoreManager에서만 사용가능 
//SetScore(int value)는 외부에서 쓸 수 있음 

public class ScoreManager : MonoBehaviour
{
    public Text currentScoreUI;
    public int currentScore;

    public Text bestScoreUI;
    public int bestScore;

    public static ScoreManager Instance = null; // 공용 정적변수
                                                // static은 이 클래스에서 딱 하나만 쓸 수 있는 변수가 됨
                                                // 자기 자신의 이름으로 변수를 하나 만듦

    public int Score
    {
        get //받음 
        {
            return currentScore;
        }

        set // 보냄 
        {
            currentScore = value;


            currentScoreUI.text = "현재점수 : " + currentScore;


            if (currentScore > bestScore)
            {
                bestScore = currentScore;
                bestScoreUI.text = "최고점수 : " + bestScore;

                PlayerPrefs.SetInt("Best Score", bestScore); // 갱신된 최고점수 저장 
            }
        }
    }


    private void Awake() //싱글톤 객체 
                         // 이 Awake 함수는 Start보다 더 먼저 실행 됨
                         // + Unity가 실행될때 바로 Awake 호출됨 (즉 object가 활성화/비활성화에 관계없이 항상 호출됨 
    {
        if(Instance == null)
        {
            Instance = this; // Find 사용 안했으므로 빠르게 처리 가능

        }

    }



    void Start()
    {
        bestScore = PlayerPrefs.GetInt("Best Score", 0);
        // 여기에 있는 0은 디폴트 값으로 게임을 처음 시작했을때 최고점수에 아무것도 들어있지 않으므로 0값을 넣어주는 역할을 함 

        bestScoreUI.text = "최고점수 : " + bestScore;
        // 자기 자신의 멤버변수이기 때문에 bestScore앞에  sm. 필요없음 !
    }


    void Update()
    {
        
    }


    public void SetScore(int value) // 자기꺼한테 가져왔으니까 앞에 sm 안 써두 됨 
    {

        currentScore++;
        // ScoreManager안에 들어있는 currentScore의 값을 1 증가

        currentScoreUI.text = "현재점수 : " + currentScore;
        // str + int = 결과는 str(문자열)형으로

        if (currentScore > bestScore) // 최고점수 갱신 
        {
            bestScore = currentScore;
            bestScoreUI.text = "최고점수 : " + bestScore;

            PlayerPrefs.SetInt("Best Score", bestScore); // 갱신된 최고점수 저장 
        }

    }

    public int GetScore()
    {
        return currentScore; // 현재 스코어를 되돌려준다 
    }

}

 

 

 

 

Enemy.cs

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

public class Enemy : MonoBehaviour
{
    public float speed = 5;

    Vector3 dir; // 전역변수 dir
                 // Start() 함수에 있던 걸 Update()함수에 적용하기 위해 전역변수로!

    public GameObject explosionFactory; // 폭발을 위한 PreFab

    // GameObject smObject; //느려진다는 그 충돌처리함수의 Find 의 해결책 

    void Start()
    {
        int randValue = Random.Range(0,10); // ()에 들어가 있는 숫자가 실수인 경우 마지막 숫자까지 포함하지만
                                            // 정수인 경우 포함 X
                                            // 즉 위의 경우 0~9 사이의 숫자만 나옴 
                                            // int randValue = UnityEngine.Random.Range(0,10);


        if (randValue < 3) // 30%의 확률로 
        {
            GameObject target = GameObject.Find("Player");
            // Find : Hierarchy창의 모든 이름 중에서 해당하는거("Player") 찾기
            // GameObject.Find("Player") : Player 오브젝트를 반환
            // GameObject target = GameObject.Find("Player"); : 반환된 Player 오브젝트를 target에다가 지역변수로 넣어주기 
                                                             // 즉 target이 Player가 됨

            dir = target.transform.position - transform.position;
            // 적이 현재 주인공이 있는 방향으로 고개(?)를 향하게 됨 !!!
            // target.transform.position : Player의 포지션 (주인공의 위치)
            // transform.position : 적의 위치 
            //  - : " 현재 위치레서 목표 위치쪽 방향으로 틀어라 "라고 하고 싶을 때 '벡터'의 뺄셈 이용
                  //  벡터A - 벡터B = 목표 위치A - 현재 위치B
                  //  (근데 벡터이므로 방향만.. 걍 고개 돌리는거 생각하삼) 


            dir.Normalize();
            // dir.Normalize : 정규화 (단위 벡터)
                            // Player와 적의 거리가 멀 경우 그 거리를 1로 만들어버림 (방향은 동일)
                            // 이걸 해줌으로써 나중에 speed 곱할 때 동일한 방향, 동일한 속도로 움직일 수 있게 해줌 
        }
        else // 30% 확률 안에 못 들었다면 
        {
            dir = Vector3.down; // 적을 자기가 떨어지는 위치 그대로에서 떨어지게 함   (;; 말이 이상한디  
        }

        // smObject = GameObject.Find("ScoreManager"); //느려진다는 그 충돌처리함수의 Find 의 해결책 
    }


    void Update()
    {                       
        transform.position += dir * speed * Time.deltaTime;
    }

    void OnCollisionEnter(Collision other) // 충돌 처리 함수 
    {
        ////GameObject smObject = GameObject.Find("ScoreManager"); // Find는 Hierarchy창 전체를 훑고 지나가니까 느려진다고 많이 쓰지 말래
        //                                                         // 따라서 여기에다가 쓴 이 Find는 충돌이 일어날떄마다 호출되기 떄문에 일케 쓰는 건 좋지 않은 방법

        // ScoreManager sm = smObject.GetComponent<ScoreManager>(); // ScoreManager 안에 있는 Script를 얻어올수 있게 됨
        //sm.SetScore(sm.GetScore() + 1); // 현재 값보다 하나 더 증가한 값을 SetScore에 저장


        //위 3 줄 대신 써주기 (Find는 비효율적이니!)
        //ScoreManager.Instance.SetScore(ScoreManager.Instance.GetScore() + 1);
        // 이렇게 쓰는 것만으로도 Find 안 써도 해당 스크립트에 접근할수 있음 (효율적이고 간단해짐)
        /* 다시 위에 있는 코드한줄을 아래에 있는 한줄로도 바꿔쓰기 가능 */
        ScoreManager.Instance.Score++; // ScoreManager.Instance.Score = ScoreManager.Instance.Score + 1;
        // Score을 불러준 순간 get이 되어서
        // Scoremanager의 Get함수에 있는 currentscore을 가져오게 됨 (바로 위에 주석처리한 코드에서 ScoreManager.Instance.GetScore() 보면 이해 잘 됨)
        // 그럼 ScoreManager.Instance.Score은 currentscore가 됨
        // 그후 거기에 +1을 하고
        // 그럼 ScoreManager.Instance.Score에 대입 하면 set 호출됨 ~~~ 




        GameObject explosion = Instantiate(explosionFactory); //여기서 오류나면 이제 아래있는 코드들이 실행이 안되므로 꼬옥 enemy의 explosionFactory 프리팹에 효과 넣어주기!!
        // Instantiate() : ()안에 있는 원본을 복사
        // 요 코드는 그 복사한거를 explosion에다가 넘겨주는 것

        explosion.transform.position = transform.position;
        // 폭발 이펙트의 위치를 설정  
        // 폭발 위치는 = 이 스크립트가 현재가지고 있는 위치
          // transform의 의미는 : 이 스크립트를 포함하고 있는 옵젝이 갖고 있는 위치값 

        Destroy(other.gameObject); // 다른 게임 오브젝트 파괴
        Destroy(gameObject); // 자기 자신 파괴
    }
}

 

 


 

 

미사일 오브젝트풀

 

- 메모리 아끼기!! (모바일 컨텐츠에서)

 

playerfire.cs

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

// 버튼을 누르는 순간 총알오브젝트(Bullet)를 생성하는 메소드 - 인스턴시에이트 ?
// 총알이 여러발 나가야 함으로 총알을 복사해주는 프리팹??

public class PlayerFire : MonoBehaviour
{
    public GameObject bulletFactory; // 총알 원본 
                                     // bulletFactory : 총알의 프리팹 ..?
                                     // 이걸 복제해서 계속 똑같은 총알을 만들거래
    public GameObject firePosition; // 발사 위치



    public int poolSize = 10; //10개의 배열 
    GameObject[] bulletObjectPool; // 이 배열에다가 총알 집어넣음


    void Start()
    {
        bulletObjectPool = new GameObject[poolSize];

        for (int i=0; i < poolSize; i++)
        {
            GameObject bullet = Instantiate(bulletFactory);

            bulletObjectPool[i] = bullet;
            bullet.SetActive(false); // 안보이게 '비활성화' 해주는 기능 (시작하자마자 총알이 10발 갑자기 발사되면 안되니)

        }
    }

    void Update()
    {
        if (Input.GetButtonDown("Fire1")) // GetButtonDown : 어떤 버튼이 눌러져 있는가?(마우스 버튼) (버튼을 누른 그 순간에 한번만 작동)
        {
            //GameObject bullet = Instantiate(bulletFactory); // Instantiate : 프리탭옵젝()을 복제 (즉 여기서는 bulletFactory를 복제)
            //                                                // 이렇게 복제된 옵젝은 return값으로 들어오게 되는데 그거를 이제 지역변수인 bullet에다가 넣어줌

            //bullet.transform.position = firePosition.transform.position; // bullet의 초기 위치
            //                                                              // bullet옵젝의 위치를 FirePosition옵젝의 위치(0,1,0)로 이동 

            /*위에꺼 대신에 아래처럼 쓰기*/
            for (int i=0; i < poolSize; i++) 
            {
                GameObject bullet = bulletObjectPool[i]; // 비어있는 총알 오브젝트 하나에 
                if (bullet.activeSelf == false) // 현재 총알이 발사중인 상태가 아니라면
                {
                    bullet.SetActive(true); //활성화
                    bullet.transform.position = transform.position; //bullet의 위치 = player의 위치가 됨 
                    break; //총알을 발사했기떄문에 더이상 반복문을 돌 필요 없으므로 break 

                }
            }
        } 
    }
}

 

 

 

enemy.cs

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

public class Enemy : MonoBehaviour
{
    public float speed = 5;

    Vector3 dir; 

    public GameObject explosionFactory; 

    void Start()
    {
        int randValue = Random.Range(0,10);

        if (randValue < 3) // 30%의 확률로 
        {
            GameObject target = GameObject.Find("Player");
 
            dir = target.transform.position - transform.position;

            dir.Normalize();
            // dir.Normalize : 정규화 (단위 벡터)
        }
        else // 30% 확률 안에 못 들었다면 
        {
            dir = Vector3.down; 
        }
    }


    void Update()
    {                       
        transform.position += dir * speed * Time.deltaTime;
    }

    void OnCollisionEnter(Collision other) // 충돌 처리 함수 
    {

        ScoreManager.Instance.Score++; 




        GameObject explosion = Instantiate(explosionFactory);

        explosion.transform.position = transform.position;



        if (other.gameObject.name.Contains("Bullet")) 
            //Bullet이라면 
        {
            other.gameObject.SetActive(false); //비활성화 
        }
        else //Bullet이 아니라면 
        {
            Destroy(other.gameObject); //제거 
        }


        Destroy(gameObject); // 자기 자신 파괴
    }
}

 

destroyzone.cs

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

//충돌 영역 설정

public class DestroyZone : MonoBehaviour
{
    void Start()
    {
        
    }

    void Update()
    {
        
    }

    private void OnTriggerEnter(Collider other)
    {
        // Destroy(other.gameObject);

        if (other.gameObject.name.Contains("Bullet"))
        //Bullet이라면 
        {
            other.gameObject.SetActive(false); //비활성화 
        }
        else //Bullet이 아니라면 
        {
            Destroy(other.gameObject); //제거 
        }
    }
}

 

bullet 프리팹에서 리짓바디에서  Freeze ~~~ 두개 다 켜주기