Skip to content

Commit 7adab84

Browse files
implemented object pooling for projectiles and floating damage text
1 parent 4ba8dc3 commit 7adab84

File tree

6 files changed

+185
-13
lines changed

6 files changed

+185
-13
lines changed

Combat/Projectile.cs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Collections;
23
using RPG.Attributes;
4+
using RPG.Util;
35
using UnityEngine;
46
using UnityEngine.Events;
57

@@ -19,14 +21,24 @@ public class Projectile : MonoBehaviour {
1921
private float damage = 0;
2022
private Health target = null;
2123
private GameObject instigator = null;
24+
private ObjectPooler pooler;
25+
private string poolTag;
2226
/// <summary>
2327
/// Can update UI when damaging an enemy (e.g. update enemy health on HUD)
2428
/// </summary>
2529
private Action updateUI = null;
30+
private float originalSpeed;
2631

27-
private void Start() {
28-
transform.LookAt(GetAimLocation());
29-
onLaunch.Invoke();
32+
private void Awake() {
33+
pooler = ObjectPooler.Instace;
34+
originalSpeed = speed;
35+
}
36+
37+
private void LookTowardsTarget() {
38+
if (target != null) {
39+
transform.LookAt(GetAimLocation());
40+
onLaunch.Invoke();
41+
}
3042
}
3143

3244
private void Update() {
@@ -37,6 +49,7 @@ private void Update() {
3749
transform.LookAt(GetAimLocation());
3850
}
3951
transform.Translate(Vector3.forward * speed * Time.deltaTime);
52+
StartCoroutine(ReturnToPoolWithDelay(projectileTTL));
4053
}
4154

4255
private void OnTriggerEnter(Collider other) {
@@ -45,15 +58,19 @@ private void OnTriggerEnter(Collider other) {
4558
return;
4659
}
4760

61+
foreach (GameObject toDestroy in destroyAfterHit) {
62+
toDestroy.SetActive(true);
63+
}
64+
4865
Health targetHealth = other.GetComponent<Health>();
4966

5067
if (targetHealth == null) { // to disallow friendly fire - || targetHealth != target
51-
Destroy(gameObject);
68+
ReturnToPool();
5269
return;
5370
}
5471

5572
if (targetHealth.IsDead) {
56-
Destroy(gameObject, projectileTTL);
73+
StartCoroutine(ReturnToPoolWithDelay(projectileTTL));
5774
return;
5875
}
5976

@@ -73,7 +90,7 @@ private void OnTriggerEnter(Collider other) {
7390
}
7491

7592
foreach (GameObject toDestroy in destroyAfterHit) {
76-
Destroy(toDestroy);
93+
toDestroy.SetActive(false);
7794
}
7895

7996
onHit.Invoke();
@@ -82,7 +99,7 @@ private void OnTriggerEnter(Collider other) {
8299
updateUI();
83100
}
84101

85-
Destroy(gameObject, afterHitTTL);
102+
StartCoroutine(ReturnToPoolWithDelay(afterHitTTL));
86103
}
87104

88105
private Vector3 GetAimLocation() {
@@ -93,11 +110,36 @@ private Vector3 GetAimLocation() {
93110
return target.transform.position + Vector3.up * targetCollider.height / 2;
94111
}
95112

113+
private void ReturnToPool() {
114+
pooler.AddToPool(poolTag, gameObject);
115+
}
116+
117+
private IEnumerator ReturnToPoolWithDelay(float delay) {
118+
yield return new WaitForSeconds(delay);
119+
ReturnToPool();
120+
}
121+
122+
// TODO finish this
123+
/// <summary>
124+
/// Sets the target for the projectile.
125+
/// </summary>
126+
/// <param name="target"></param>
127+
/// <param name="instigator"></param>
128+
/// <param name="damage"></param>
129+
/// <param name="updateUI"></param>
96130
public void SetTarget(Health target, GameObject instigator, float damage, Action updateUI) {
97131
this.target = target;
98132
this.damage = damage;
99133
this.instigator = instigator;
100134
this.updateUI = updateUI;
135+
speed = originalSpeed;
136+
LookTowardsTarget();
137+
}
138+
139+
public void SetPoolTag(string tag) {
140+
if (string.IsNullOrEmpty(poolTag)) {
141+
poolTag = tag;
142+
}
101143
}
102144
}
103145
}

Combat/WeaponConfig.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using RPG.Attributes;
3+
using RPG.Util;
34
using UnityEngine;
45

56
namespace RPG.Combat {
@@ -8,6 +9,7 @@ namespace RPG.Combat {
89
public class WeaponConfig : ScriptableObject {
910
[SerializeField] private AnimatorOverrideController animatorOverride = null;
1011
[SerializeField] private Weapon equippedPrefab = null;
12+
[SerializeField] private string projectileTag = "arrow";
1113
[SerializeField] private Projectile projectile = null;
1214
[SerializeField] private float weaponRange = 3f;
1315
[SerializeField] private float weaponDamage = 25f;
@@ -16,7 +18,7 @@ public class WeaponConfig : ScriptableObject {
1618
private const string WEAPON_NAME = "Weapon";
1719
private const string DESTORYING = "Destroying";
1820
private enum HAND { LEFT, RIGHT }
19-
21+
private ObjectPooler pooler = null;
2022
public float Range => weaponRange;
2123
public float Damage => weaponDamage;
2224
public float PercentageBous => percentageBonus;
@@ -37,6 +39,12 @@ private Transform GetTransform(Transform rightHand, Transform leftHand) {
3739
return hand == HAND.RIGHT ? rightHand : leftHand;
3840
}
3941

42+
private void EstablishPoolerReference() {
43+
if (pooler == null) {
44+
pooler = ObjectPooler.Instace;
45+
}
46+
}
47+
4048
public Weapon Spawn(Transform rightHand, Transform leftHand, Animator animator) {
4149
DestroyOldWeapon(rightHand, leftHand);
4250
Weapon weapon = null;
@@ -69,8 +77,16 @@ public bool HasProjectile() {
6977
/// <param name="calculatedDamage"></param>
7078
/// <param name="updateUI"></param>
7179
public void LaunchProjectile(Transform rightHand, Transform leftHand, Health target, GameObject instigator, float calculatedDamage, Action updateUI) {
72-
Projectile projectileInstance = Instantiate(projectile, GetTransform(rightHand, leftHand).position, Quaternion.identity);
80+
EstablishPoolerReference();
81+
GameObject instance = pooler.SpawnFromPool(projectileTag);
82+
if (!instance) {
83+
return;
84+
}
85+
Projectile projectileInstance = instance.GetComponent<Projectile>();
86+
projectileInstance.transform.position = GetTransform(rightHand, leftHand).position;
87+
projectile.transform.rotation = Quaternion.identity;
7388
projectileInstance.SetTarget(target, instigator, calculatedDamage, updateUI);
89+
projectileInstance.SetPoolTag(projectileTag);
7490
}
7591
}
7692
}

UI/Damage Text/DamageText.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
using System;
2+
using RPG.Util;
23
using UnityEngine;
34
using UnityEngine.UI;
45

56
namespace RPG.UI {
67
public class DamageText : MonoBehaviour {
78
[SerializeField] private Text damageText = null;
9+
private ObjectPooler pooler;
10+
private string poolTag;
11+
12+
private void Awake() {
13+
pooler = ObjectPooler.Instace;
14+
}
815

916
public void DestroyText() {
10-
Destroy(gameObject);
17+
pooler.AddToPool(poolTag, gameObject);
18+
}
19+
20+
public void SetPoolTag(string tag) {
21+
if (string.IsNullOrEmpty(poolTag)) {
22+
poolTag = tag;
23+
}
1124
}
1225

1326
public void SetValue(float amount) {

UI/Damage Text/DamageTextSpawner.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
1-
using UnityEngine;
1+
using RPG.Util;
2+
using UnityEngine;
23

34
namespace RPG.UI {
45
public class DamageTextSpawner : MonoBehaviour {
6+
[SerializeField] private string poolTag = "damageText";
57
[SerializeField] private DamageText damageTextPrefab = null;
8+
private ObjectPooler pooler;
9+
10+
private void Start() {
11+
pooler = ObjectPooler.Instace;
12+
}
613

714
public void Spawn(float damage) {
8-
DamageText instance = Instantiate<DamageText>(damageTextPrefab, transform);
9-
instance.SetValue(damage);
15+
GameObject instance = pooler.SpawnFromPool(poolTag);
16+
if (instance == null) {
17+
return;
18+
}
19+
instance.transform.position = transform.position;
20+
DamageText damageText = instance.GetComponent<DamageText>();
21+
damageText.SetValue(damage);
22+
damageText.SetPoolTag(poolTag);
1023
}
1124
}
1225

Util/ObjectPooler.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System.Collections.Generic;
2+
using UnityEngine;
3+
4+
namespace RPG.Util {
5+
public class ObjectPooler : MonoBehaviour {
6+
7+
[System.Serializable]
8+
private struct Pool {
9+
public string tag;
10+
public GameObject prefab;
11+
public int size;
12+
}
13+
14+
[SerializeField] private List<Pool> pools;
15+
[SerializeField] private Dictionary<string, Queue<GameObject>> poolDict;
16+
17+
public static ObjectPooler Instace;
18+
19+
private void Awake() {
20+
Instace = this;
21+
poolDict = new Dictionary<string, Queue<GameObject>>();
22+
23+
FillPools();
24+
}
25+
26+
private void FillPools() {
27+
for (int i = 0; i < pools.Count; i++) {
28+
Queue<GameObject> objectPool = new Queue<GameObject>();
29+
for (int j = 0; j < pools[i].size; j++) {
30+
GameObject obj = Instantiate(pools[i].prefab);
31+
obj.transform.SetParent(transform);
32+
obj.SetActive(false);
33+
objectPool.Enqueue(obj);
34+
}
35+
poolDict.Add(pools[i].tag, objectPool);
36+
}
37+
}
38+
39+
public void FillPool(string tag) {
40+
if (!poolDict.ContainsKey(tag)) {
41+
return;
42+
}
43+
Pool pool = pools.Find(p => p.tag == tag);
44+
for (int j = 0; j < pool.size; j++) {
45+
GameObject obj = Instantiate(pool.prefab);
46+
obj.transform.SetParent(transform);
47+
obj.SetActive(false);
48+
poolDict[tag].Enqueue(obj);
49+
}
50+
}
51+
52+
public void AddToPool(string tag, GameObject instance) {
53+
if (!poolDict.ContainsKey(tag)) {
54+
return;
55+
}
56+
instance.SetActive(false);
57+
poolDict[tag].Enqueue(instance);
58+
}
59+
60+
public GameObject SpawnFromPool(string tag) {
61+
if (!poolDict.ContainsKey(tag)) {
62+
return null;
63+
}
64+
65+
if (poolDict[tag].Count == 0) {
66+
FillPool(tag);
67+
}
68+
69+
GameObject instance = poolDict[tag].Dequeue();
70+
instance.SetActive(true);
71+
72+
return instance;
73+
74+
}
75+
}
76+
77+
}

Util/ObjectPooler.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)