[Unity3D] 객체 미리 생성 후 재활용 - Memory pool [Part 3]

※ 주의 

이 글은 아마추어가 개인적으로 생각하여 작성하는 것으로, 이곳에 나오는 내용을 맹신하지 않는것을 당부드립니다.

 

 

Menu

0. 미리보기

1. 오브젝트를 미리 생성.

- 프리팹 제작.

- 싱글톤 제작.

- 오브젝트를 생성시킬 함수 제작.

- 오브젝트를 찾을 함수를 제작.

2. 만들어진 오브젝트 활용하기.

 

추가.

3. 적 생성

- 적 생성 후 프리팹으로 만들기.

- 적과 총알이 충돌하면 총알 비활성화.

- 적이 총알에 맞아 죽었을 때 단순한 객체로 이펙트 효과 표현하기.

 

4. 메모리 풀을 이용해서 적 생성하기.

- 적 리스폰

 * Enemy를 생산하는 Area만들기. (랜덤한 위치로 Area생산 및 Area간 간격조절.)

 * Araa범위 안에 Enemy 랜덤한 위치로 리스폰 시키기.

 * Enemy가 죽었을 경우 리스폰 되는 간격 조절.

 

5. FSM을 이용해 적 AI만들기.

 

 

※ 미리보기

 

 

 

 

3. 적 생성.

 

- 적 생성 후 프리팹으로 만들기.

 

 

 

1. 캡슐을 적당히 생성해서 마테리얼을 노란색으로 씌워준다.

2. Enemy 인스펙터 정보

 - 이름은 'Enemy'

 - Rigidbody를 추가하여 isKinematic을 체크해준다.

 - Enemy라는 스크립트를 생성하여 에너미의 컴포넌트로 추가해준다.

 - Tag에 "Enemy"를 추가해 준 후 이 객체의 태그를 Enemy로 설정한다.

 - Layer에 "Enemy"를 추가해 준 후 이 객체의 레이어를 Enemy로 설정한다.

 

※ 

- Enemy태그 지정은 총알과 Enemy가 충돌했을 때 태그로 충돌체크를 하기 위한 것.

- Enemy레이어 지정은 적이 죽었을 때 소환되는 잔해물과 다른 적이 충돌할수 없도록 레이어를 나누어 놨다.

- 충돌체크, 즉 물리적인 충돌체크를 하기 때문에 Rigidbody를 추가하고 총알의 전진하는 물리적인 힘에 영향을 받지 않게 하기 위해 is Kinematic을 체크해 준다.

 

 

"Enemy" Script

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Enemy : MonoBehaviour {
 
    // 공개
    public float HP;         // 현재 체력
 
    // 비공개
    private float MAX_HP;    // 최대 체력
    private bool Life;       // 살아있는지에 대한 여부.
 
    // 정보 초기화 함수.
    void Init()
    {
        MAX_HP = 100;
        HP = MAX_HP;
 
        Life = true;
 
        StartCoroutine("StateCheck");
    }
 
    void Start()
    {
        Init();
    }
 
    // 상태체크.
    IEnumerator StateCheck()
    {
        // 살아있는 동안에만 반복문을 돌림.
        while (Life)
        {
            // 적 AI
            yield return null;
        }
 
        // 만약 죽는 애니메이션이 있는 객체라면,
        // 죽었을 때 다른 객체의 영향을 받지 않게 하기위해
        // 충돌에 관한 콜라이더를 제거하고 죽는 애니메이션을 재생.
        // 그 후 객체를 비활성화 한다.
        gameObject.SetActive(false);
    }
 
    // 정보 갱신.
    public void InfoUpdate(float _Damage)
    {
        HP -= _Damage;
 
        if (HP <= 0)
        {
            // 폭발 하는 잔해물 소환.
            
 
            HP   = 0;       // 체력의 수치가 음의 값으로 갔을 경우를 대비한 초기화.
            Life = false;   // 죽었음을 알림.
        }
    }
}
 
cs

 

 

스크립트 작성 후 

에너미 인스펙터 창에 정보를 수정.

 


HP 를 100으로.

 

 

- 적과 총알이 충돌하면 총알 비활성화.


적과 총알이 충돌하면 총알의 Active을 비활성화 시키는데, 이때 돌아가던 코루틴은 자동으로 중지한다.

 

총알 객체에 붙어있는 스크립트에 새로운 함수를 추가 하도록 하자.

 

"Bullet" Script

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Bullet : MonoBehaviour {
 
    public float BulletPower;
 
    // 총알의 움직임 및 일정 시간뒤 비 활성화.
    IEnumerator MoveBullet()
    {
        float timer = 0;
        while (true)
        {
            timer += Time.deltaTime;    // 시간 축적
            if(timer > 2)               // 2초뒤 반복문을 빠져나간다.
                break;
 
            transform.Translate(Vector3.forward * Time.deltaTime * 40f);    // 총알을 움직인다.
            yield return null;
        }
 
        // 총알 비활성화.
        gameObject.SetActive(false);
    }
    
    // 충돌체크
    void OnTriggerEnter(Collider _Col)
    {
        // 총알이 적과 충돌.
        if (_Col.transform.CompareTag("Enemy"))
        {
            // 총알에 맞은 객체의 Enemy컴포넌트를 가져와 Enemy에게 데미지를 준다.
            _Col.GetComponent<Enemy>().InfoUpdate(BulletPower);
            gameObject.SetActive(false);
        }
    }
}
cs

 

이젠 적이 총알을 맞을때 마다 HP가 감소하면서 HP가 0이하가 되면 사라지게 된다.

그렇게 하기 위해서 총알의 공격력을 설정해줘야 하는데, 고정 값으로 총알의 공격력을 설정해줘도 좋지만

플레이어가 버프를 받았을때 라던가를 고려하여 총알의 공격력을 업데이트 할 수 있게 함수를 하나 만들기로 하자.

 

물론 업데이트 되는 정보는 총알의 공격력 뿐만 아니라, HP, 움직임 스피드, 살아있는지에 대한 여부 등을 업데이트 할 것이며,

이렇게 업데이트를 해야 향후에 여러방면으로 써먹을수 있다.

 

예를들어 플레이어가 죽었을 경우 적들은 플레이어에 대한 공격을 멈춰야 한다.

어떻게 멈춰야 할까? 그건 플레이어의 Life값을 체크하여 생존 여부를 판단할수 있다.

 

이러한 변화가 있을때마다 정보들을 ObjManager에 업데이트 하고,

다른 객체들은 ObjManager에서 플레이어 정보를 가져와 여러가지 판단을 할 수 있게 한다.

 

특히나 플레이어에 대한 정보는 변화가 많으므로 모든 객체들을 관리하는 ObjManager가 플레이어에 대한

실시간 정보를 가지고 있는게 여러방면으로 유리하다고 생각했다. (물론 개인적인 생각이다.)

 

그러기 위한 업데이트 함수를 만들어 보자.

물론 아이템이라던가 플레이어의 능력치에 변화를 주는 요소가 아직은 없기 때문에

Start에서 한 번만 정보를 업데이트 시킬 예정이다.

 

"Player" Script

 

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Player : MonoBehaviour {
 
    // 구조체 정보.
    public struct PINFO
    {
        public float MAX_HP;            // 체력의 최대치.
        public float HP;                // 현재 체력
        public float BulletPower;       // 총알의 힘.
        public float MoveSpeed;         // 움직임 스피드.
        public bool Life;               // 플레이어의 생명.
    }
 
    public float Speed;         // 움직이는 스피드.
    public float AttackGap;     // 총알이 발사되는 간격.
 
    private PINFO pInfo;        // 플레이어 정보.
    private Transform Vec;      // 카메라 벡터.
    private Vector3 MovePos;    // 플레이어 움직임에 대한 변수.
 
    private bool ContinuouFire; // 게속 발사할 것인가? 에 대한 플래그.
 
    void Init()
    {
        // 구조체 정보 초기화.
        pInfo.MAX_HP        = 100;
        pInfo.HP            = pInfo.MAX_HP;
        pInfo.BulletPower   = 20;
        pInfo.MoveSpeed     = 5;
        pInfo.Life          = true;
 
        //공개.
        AttackGap = 0.2f;
 
        // 비공개
        MovePos = Vector3.zero;
        ContinuouFire = true;
 
        // 플레이어 정보갱신.
        ObjManager.Call().PlayerInfoUpdate(pInfo);
    }
 
    void Start()
    {
        Vec = GameObject.Find("CameraVector").transform;
 
        Init();
    }
 
    void Update () 
    {
        Run();
        KeyCheck();
    }
 
    // 플레이어 움직임.
    void Run()
    {
        int ButtonDown = 0;
        if (Input.GetKey(KeyCode.LeftArrow))    ButtonDown = 1;
        if (Input.GetKey(KeyCode.RightArrow))   ButtonDown = 1;
        if (Input.GetKey(KeyCode.UpArrow))      ButtonDown = 1;
        if (Input.GetKey(KeyCode.DownArrow))    ButtonDown = 1;
 
        // 플레이어가 움직임 버튼에서 손을 땠을 때 Horizontal, Vertical이 0으로 돌아감으로써
        // 플레이어의 회전상태가 다시 원상태로 돌아가지 않게 하기 위해서.
        if (ButtonDown != 0)
            Rotation();
        else
            return;
 
        transform.Translate(Vector3.forward * Time.deltaTime * pInfo.MoveSpeed * ButtonDown);
    }
 
    // 플레이어 회전.
    void Rotation()
    {
        MovePos.Set(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));   // 벡터 셋팅.
        Quaternion q = Quaternion.LookRotation(Vec.TransformDirection(MovePos));  // 회전
 
        if (MovePos != Vector3.zero)
            transform.rotation = q;
    }
 
    // 총알 키 체크.
    void KeyCheck()
    {
        if (Input.GetButtonDown("Jump"))
            StartCoroutine("NextFire");
        else if (Input.GetButtonUp("Jump")) 
            ContinuouFire = false;
 
        if (Input.GetKeyDown(KeyCode.Q))
            ObjManager.Call().MemoryDelete();
 
        if (Input.GetKeyDown(KeyCode.E))
            ObjManager.Call().CreateObject("Bullet"20);
 
    }
 
    // 연속발사.
    IEnumerator NextFire()
    {
        ContinuouFire = true;
        while (ContinuouFire)
        {
            // 총알을 리스트에서 가져온다.
            BulletInfoSetting(ObjManager.Call().GetObject("Bullet"));
            yield return new WaitForSeconds(AttackGap);
        }
    }
 
    // 총알정보 셋팅.
    void BulletInfoSetting(GameObject _Bullet)
    {
        if (_Bullet == nullreturn;
 
        _Bullet.transform.position = transform.position;                // 총알의 위치 설정
        _Bullet.transform.rotation = transform.rotation;                // 총알의 회전 설정.
        _Bullet.SetActive(true);                                        // 총알을 활성화 시킨다.
        _Bullet.GetComponent<Bullet>().StartCoroutine("MoveBullet");    // 총알을 움직이게 한다.
    }
}
 
cs

 

 

플레이어의 실시간 정보를 가지고 있는 함수.

"ObjManager" Script

 

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class ObjManager : MonoBehaviour {
 
    // 싱글톤
    static ObjManager st;
    public static ObjManager Call() { return st; }
    void Awake()                    { st = this; }
    // 게임종료 후 메모리 날려버림.
    void OnDestroy()                
    {
        MemoryDelete();
        st = null;
    }
    
    // 공개
    public GameObject[] Origin;         // 프리팹 원본.
    public List<GameObject> Manager;    // 생성된 객체들을 저장할 리스트.
 
    // 비공개.
    private Player.PINFO pInfo;         // 플레이어 정보.
 
    void Start()
    {
        // 총알 생성.
        SetObject(Origin[0], 20"Bullet");
    }
 
    // 오브젝트를 받아 생성.
    public void SetObject( GameObject _Obj, int _Count, string _Name)
    {
        for (int i = 0; i < _Count; i++)
        {
            GameObject obj = Instantiate(_Obj) as GameObject;
            obj.transform.name = _Name;                     // 이름을 정한다.
            obj.transform.localPosition = Vector3.zero;     // 위치를 정한다.
            obj.SetActive(false);                           // 객체를 비활성화.
            obj.transform.parent = transform;               // 매니저 객체의 자식으로.
            
            if(_Name != "Bullet") // 총알이 아니면 색을 랜덤으로 설정.
                obj.GetComponent<Renderer>().material.color = new Color(Random.value, Random.value, Random.value, 1.0f);
 
            Manager.Add(obj);                               // 리스트에 저장.
        }
    }
 
    // 필요한 오브젝트를 찾아 반환.
    public GameObject GetObject(string _Name)
    {
        // 리스트가 비어있으면 종료.
        if (Manager == null)
            return null;
 
        int Count = Manager.Count;
        for (int i = 0; i < Count; i++)
        {
            // 이름이 같지 않으면.
            if (_Name != Manager[i].name)
                continue;
 
            GameObject Obj = Manager[i];
 
            // 활성화가 되어있다면.
            if (Obj.active == true)
            {
                // 리스트의 마지막까지 돌았지만 모든 객체가 사용중이라면.
                if (i == Count - 1)
                {
                    // 총알을 새롭게 생성.
                    SetObject(Obj, 1, _Name);
                    return Manager[i + 1];
                }
                continue;
            }
            return Manager[i]; 
        }
        return null;
    }
 
    // 메모리 삭제.
    public void MemoryDelete()
    {
        if (Manager == null)
            return;
 
        int Count = Manager.Count;
 
        for (int i = 0; i < Count; i++)
        {
            GameObject obj = Manager[i];
            GameObject.Destroy(obj);
        }
        Manager = null;
    }
 
    // 원하는 오브젝트를 만든다.
    public void CreateObject(string _Name, int _Count)
    {
        if (Manager == null)
            Manager = new List<GameObject>();
 
        int Count = Origin.Length;
        for (int i = 0; i < Count; i++)
        {
            GameObject obj = Origin[i];
            if (obj.name == _Name)
            {
                SetObject(obj, _Count, _Name);   // 총알을 생성.
                break;
            }
        }
    }
 
    // 플레이어의 정보갱신.
    public void PlayerInfoUpdate(Player.PINFO _Info)
    {
        // 플레이어 정보 업데이트.
        pInfo = _Info;
 
        int Count = Manager.Count;
 
        for (int i = 0; i < Count; i++)
        {
            GameObject obj = Manager[i];
            if (obj.name == "Bullet") // 총알의 데미지 정보 업데이트.
                obj.GetComponent<Bullet>().BulletPower = _Info.BulletPower;
        }
    }
 
    // 플레이어의 정보를 가져간다.
    public object GetPlayerInfo(string _Type)
    {
        switch (_Type)
        {
            case "Life":        return pInfo.Life;
            case "MAXHP":       return pInfo.MAX_HP;
            case "HP":          return pInfo.HP;
            case "Speed":       return pInfo.MoveSpeed;
            case "BulletPower"return pInfo.BulletPower;
            case "All":         return pInfo;
        }
        return null;
    }
}
cs

 

 

적이 총알을 맞아 HP가 깎이면 사라지는것을 볼 수 있다.

 

 

 

이펙트가 없으니 밋밋하다.

그러니 큐브 조각들로 이펙트를 흉내 내보기로 하자.

 

1). 잔해물 오브젝트를 만든다. (4종류: 큐브, 실린더, 캡슐 등...)

2). 잔해물의 인스페터창 정보를 수정한다.

- 잔해물의 크기(Scale)조정

- 잔해물이 생성 후 퍼져나가는 효과를 내기 위한 Material추가.

 

 

- 렌더러에서 Cast Shadows를 off하고 Receive Shadows 체크를 해제한다. (그림자가 필요 없기 때문)

- Rigidbody 생성.

- "ExplosinHide" 잔해물을 비활성화 해주는 스크립트를 생성

- "Wreckage" 레이어 추가 후 각 객체(잔해물)에 설정.

 

 

 

 

"ExplosinHide" Script

 

더보기

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class ExplosionHide : MonoBehaviour {
 
    // 블럭이 사라진다.
    IEnumerator StartExplosionHide()
    {
        int Rand = Random.Range(37);
        float timer = 0;
        while (true)
        {
            timer += Time.deltaTime;
            if(timer > Rand)
                break;
 
            yield return null;
        }
 
        // 물체가 사라진 후 다시 소환됐을 때 전에 받던 물리력을 없애주기위한 체크.
        transform.GetComponent<Rigidbody>().isKinematic = true;
        // 물리력을 다시 받기 위해 체크 해제.
        transform.GetComponent<Rigidbody>().isKinematic = false;
        gameObject.SetActive(false);
    }
}
cs

 

3). 잔해물과 적이 충돌되지 않게 PhysicsManager를 설정한다.

 

 

3.5) 다 만들어진 오브젝트를 프리팹으로 만든다.

4). 메모리 풀을 이용하여 잔해물을 미리 생성한다.

 

"ObjManager" 스크립트의 Start()함수를 수정.

 

더보기

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class ObjManager : MonoBehaviour {
 
    // 싱글톤
    static ObjManager st;
    public static ObjManager Call() { return st; }
    void Awake()                    { st = this; }
    // 게임종료 후 메모리 날려버림.
    void OnDestroy()                
    {
        MemoryDelete();
        st = null;
    }
    
    // 공개
    public GameObject[] Origin;         // 프리팹 원본.
    public List<GameObject> Manager;    // 생성된 객체들을 저장할 리스트.
 
    // 비공개.
    private Player.PINFO pInfo;         // 플레이어 정보.
 
    void Start()
    {
        int Count = Origin.Length;  // 총알, 잔해물 등의 기본 프리팹을 가지고있는 배열의 크기.
        string Name = "NoData";
 
        for (int i = 0; i < Count; i++)
        {
            switch (i)
            {
                case 0: Name = "Bullet";   break;
                case 1: Name = "Cube";     break;
                case 2: Name = "Cylinder"break;
                case 3: Name = "Capsule";  break;
                case 4: Name = "Sphere";   break;
            }
 
            // 오브젝트 생성.
            SetObject(Origin[i], 20, Name);
        }
    }
 
    // 오브젝트를 받아 생성.
    public void SetObject( GameObject _Obj, int _Count, string _Name)
    {
        for (int i = 0; i < _Count; i++)
        {
            GameObject obj = Instantiate(_Obj) as GameObject;
            obj.transform.name = _Name;                     // 이름을 정한다.
            obj.transform.localPosition = Vector3.zero;     // 위치를 정한다.
            obj.SetActive(false);                           // 객체를 비활성화.
            obj.transform.parent = transform;               // 매니저 객체의 자식으로.
            
            if(_Name != "Bullet")
                obj.GetComponent<Renderer>().material.color = new Color(Random.value, Random.value, Random.value, 1.0f);
 
            Manager.Add(obj);                               // 리스트에 저장.
        }
    }
 
    // 필요한 오브젝트를 찾아 반환.
    public GameObject GetObject(string _Name)
    {
        // 리스트가 비어있으면 종료.
        if (Manager == null)
            return null;
 
        int Count = Manager.Count;
        for (int i = 0; i < Count; i++)
        {
            // 이름이 같지 않으면.
            if (_Name != Manager[i].name)
                continue;
 
            GameObject Obj = Manager[i];
 
            // 활성화가 되어있다면.
            if (Obj.active == true)
            {
                // 리스트의 마지막까지 돌았지만 모든 객체가 사용중이라면.
                if (i == Count - 1)
                {
                    // 총알을 새롭게 생성.
                    SetObject(Obj, 1, _Name);
                    return Manager[i + 1];
                }
                continue;
            }
            return Manager[i]; 
        }
        return null;
    }
 
    // 메모리 삭제.
    public void MemoryDelete()
    {
        if (Manager == null)
            return;
 
        int Count = Manager.Count;
 
        for (int i = 0; i < Count; i++)
        {
            GameObject obj = Manager[i];
            GameObject.Destroy(obj);
        }
        Manager = null;
    }
 
    // 원하는 오브젝트를 만든다.
    public void CreateObject(string _Name, int _Count)
    {
        if (Manager == null)
            Manager = new List<GameObject>();
 
        int Count = Origin.Length;
        for (int i = 0; i < Count; i++)
        {
            GameObject obj = Origin[i];
            if (obj.name == _Name)
            {
                SetObject(obj, _Count, _Name);   // 총알을 생성.
                break;
            }
        }
    }
 
    // 플레이어의 정보갱신.
    public void PlayerInfoUpdate(Player.PINFO _Info)
    {
        // 플레이어 정보 업데이트.
        pInfo = _Info;
 
        int Count = Manager.Count;
 
        for (int i = 0; i < Count; i++)
        {
            GameObject obj = Manager[i];
            if (obj.name == "Bullet")
                obj.GetComponent<Bullet>().BulletPower = _Info.BulletPower;
        }
    }
 
    // 플레이어의 정보를 가져간다.
    public object GetPlayerInfo(string _Type)
    {
        switch (_Type)
        {
            case "Life":        return pInfo.Life;
            case "MAXHP":       return pInfo.MAX_HP;
            case "HP":          return pInfo.HP;
            case "Speed":       return pInfo.MoveSpeed;
            case "BulletPower"return pInfo.BulletPower;
            case "All":         return pInfo;
        }
        return null;
    }
}
cs

 

 

5). 적이 죽은 후 일정 개수의 잔해물을 소환하고 일정 시간이 지나면 잔해물을 사라지게 한다.

 

"Enemy" Script에 explosion() 함수 추가 후 InfoUpdate() 함수에서 호출.

 

더보기

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Enemy : MonoBehaviour {
 
    // 공개
    public float HP;        // 현재 체력
 
    // 비공개
    private float MAX_HP;   // 최대 체력
    private bool Life;      // 살아있는지에 대한 여부.
 
    // 정보 초기화 함수.
    void Init()
    {
        MAX_HP = 100;
        HP = MAX_HP;
 
        Life = true;
 
        StartCoroutine("StateCheck");
    }
 
    void Start()
    {
        Init();
    }
 
    // 상태체크.
    IEnumerator StateCheck()
    {
        // 살아있는 동안에만 반복문을 돌림.
        while (Life)
        {
            // 적 AI
            yield return null;
        }
 
        // 만약 죽는 애니메이션이 있는 객체라면,
        // 죽었을 때 다른 객체의 영향을 받지 않게 하기위해
        // 충돌에 관한 콜라이더를 제거하고 죽는 애니메이션을 재생.
        // 그 후 객체를 비활성화 한다.
        gameObject.SetActive(false);
    }
 
    // 정보 갱신.
    public void InfoUpdate(float _Damage)
    {
        HP -= _Damage;
 
        if (HP <= 0)
        {
            // 폭발한다.
            explosion();
 
            HP   = 0;       // 체력의 수치가 음의 값으로 갔을 경우를 대비한 초기화.
            Life = false;   // 죽었음을 알림.
        }
    }
 
    // 폭발은 예술이다.
    void explosion()
    {
        string Name = "NoData";
        // 잔해물 소환.
        for (int i = 0; i < 4; i++)
        {
            // 잔해물 4종류.
            switch (i)
            {
                case 0: Name = "Cube";      break;
                case 1: Name = "Cylinder";  break;
                case 2: Name = "Capsule";   break;
                case 3: Name = "Sphere";    break;
            }
 
            // 잔해물을 4종류를 5개씩 총 20개를 생성 후 뿌린다.
            for (int j = 0; j < 5; j++)
            {
                GameObject obj = ObjManager.Call().GetObject(Name);
                
                if (obj == null)
                    continue;
 
                obj.transform.position = transform.position;
                obj.SetActive(true);
                obj.GetComponent<ExplosionHide>().StartCoroutine("StartExplosionHide");
            }
        }
    }
}
 
cs

 

 

 

결과.