Hello! I am relatively new to the game dev/programming scene, and I’m struggling with making further progress. I am using Unity 2021.2.4f1 on Ubuntu Linux.
So to put things simply, I have been learning about how to make object pools work. They are to my knowledge, a much better way to handle multiple gameObjects. They stop the garbage collector from kicking in and doing its thing(perfect for a weapon that shoots bullets). What I needed was a way to generate “bullets” for any player/enemy to use.
I actually succeeded here, and managed to make a script that seems to do an ok job with accomplishing this. Here is the script if you are curious:
using System.Collections.Generic;
using UnityEngine;
public class BulletObjectPool : MonoBehaviour {
public List<GameObject> pooledBullets;
public GameObject bulletToPool;
public int amountToPool;
private void Start() {
pooledBullets = new List<GameObject>();
GameObject tmp;
for (int i = 0; i < amountToPool; i++) {
tmp = Instantiate(bulletToPool);
tmp.SetActive(false);
pooledBullets.Add(tmp);
}
}
public GameObject GetFreeBullet() {
for (int i = 0; i < amountToPool; i++) {
if (!pooledBullets[i].activeInHierarchy) {
return pooledBullets[i];
}
}
return null;
}
}
Great! I figured it out, but I’m still not happy with it. For starters, what if I want/need to make more than just bullets in the future(maybe I want a horde of Zombies to fall in a pit or something? :p)? To my knowledge, I would essentially have to duplicate this script for every kind of new gameObject/prefab I want to work with, which is not ideal.
So I went to the internet and found that Unity recently introduced the new concept of “generic object pools”. This sounded exactly like what I was looking for! As such, I familiarized myself with how they work, and managed to get it working somehow(Really cool functionality btw :)).
Now I thought I could take this a step further, and get it working on a separate script so I could call something like “GetGameObject()”, or “ReleaseGameObject()”(similar to how you you would use “Instantiate()”, or “Destroy()”.). If I could do this, then that could save me a lot of time as I wouldn’t even have to think about writing all the other stuff. Just drop a GenericObjectPool script onto my player gameObject, then call the needed methods from my PlayerController script.
That was the idea anyways, but I just can’t get it to fully work. Here is the code that I currently have for 3 different scripts. Note that a lot of code was removed from the PlayerController script to help make this post shorter(still long though). Also Both “GameObjectPool.cs”/“PlayerController.cs” are on the player gameObject, while “Bullet.cs” is on any Bullet gameObject.
//GameObjectPool.cs
using UnityEngine;
using UnityEngine.Pool;
public interface IGameObjectPool {
GameObject GetGameObject();
GameObject ReleaseGameObject();
GameObject DestroyGameObject();
}
public class GameObjectPool : MonoBehaviour, IGameObjectPool {
[SerializeField] private GameObject _objectInPool;
private ObjectPool<GameObject> _pool;
private void Awake() => _pool = new ObjectPool<GameObject>(InstantiateGameObject, EnableGameObject, DisableGameObject, DisposeGameObject, false, 10, 10);
private GameObject InstantiateGameObject() {
var objectInPool = Instantiate(_objectInPool);
return objectInPool;
}
private void EnableGameObject(GameObject obj) {
obj.SetActive(true);
}
private void DisableGameObject(GameObject obj) {
obj.SetActive(false);
}
private void DisposeGameObject(GameObject obj) {
Destroy(obj);
}
public GameObject GetGameObject() {
_pool.Get();
return _objectInPool;
}
public GameObject ReleaseGameObject() {
Debug.Log("test");
_pool.Release(_objectInPool);
return _objectInPool;
}
public GameObject DestroyGameObject() {
_pool.Dispose();
return _objectInPool;
}
}
//PlayerController.cs
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour {
public short xDirection = 1;
private float getFireWeaponCooldown;
private bool fireWeapon = false;
private Rigidbody rb;
private PlayerInput controls;
[SerializeField] private float fireWeaponCooldown = 0.5f;
private IGameObjectPool iGameObjectPool;
private void OnEnable() {
controls.Player.Enable();
}
private void OnDisable() {
controls.Player.Disable();
}
private void Awake() {
controls = new PlayerInput();
controls.Player.Fire.performed += context => fireWeapon = true;
controls.Player.Fire.canceled += context => fireWeapon = false;
}
private void Start() {
rb = GetComponent<Rigidbody>();
getFireWeaponCooldown = fireWeaponCooldown;
iGameObjectPool = gameObject.GetComponent<IGameObjectPool>();
}
private void FixedUpdate() {
Shoot();
}
private void Shoot() {
bool shoot = false;
if (getFireWeaponCooldown > 0.0f) {
getFireWeaponCooldown -= Time.fixedDeltaTime;
} else if (getFireWeaponCooldown <= 0.0f && fireWeapon) {
shoot = true;
getFireWeaponCooldown = fireWeaponCooldown;
}
if (shoot == true) {
GameObject bullet = iGameObjectPool?.GetGameObject();
if (bullet) {
bullet.transform.position = transform.position;
bullet.GetComponent<Bullet>().speed *= GetComponent<PlayerController>().xDirection;
}
}
}
}
//Bullet.cs
using UnityEngine;
public class Bullet : MonoBehaviour {
public float speed, setBulletTimer = 15.0f;
[SerializeField] private float setSpeed = 25.0f;
[SerializeField] private LayerMask floor;
private float bulletTimer;
private Rigidbody rb;
private Vector3 previousPosition;
IGameObjectPool iGameObjectPool;
private void OnEnable() {
speed = setSpeed;
bulletTimer = setBulletTimer;
}
private void Start() {
iGameObjectPool = gameObject.GetComponent<GameObjectPool>();
rb = GetComponent<Rigidbody>();
previousPosition = gameObject.transform.position;
rb.velocity = new Vector3(speed, rb.velocity.y, rb.velocity.z);
}
private void FixedUpdate() {
GameObject bullet = this.gameObject;
if (bulletTimer > 0) {
bulletTimer -= Time.fixedDeltaTime;
} else {
iGameObjectPool?.ReleaseGameObject();
}
RaycastHit[] detected = Physics.RaycastAll(previousPosition, (transform.position - previousPosition).normalized, (transform.position - previousPosition).magnitude, floor);
for(int i = 0; i < detected.Length; i++) {
Debug.Log(detected[i].collider.gameObject.name);
}
Debug.DrawLine(gameObject.transform.position, previousPosition);
previousPosition = gameObject.transform.position;
}
}
Now, “GameObject bullet = iGameObjectPool?.GetGameObject();” on line 45 in “PlayerController.cs” seems to work just fine(although I would like to pass through a gameObject if possible). What isn’t working is my “ReleaseGameObject()” method on line 29 in “Bullet.cs”, and I have no idea why. I have tried many things, and none of them seem to work. One thing worth pointing out is I am also pretty new to using interfaces, so I’m not 100% sure if I’m using it correctly. Also I have yet to test my DestroyGameObject() method, so that might not even work.
So now my question, what am I doing wrong here? Am I overthinking this? Any help whether it be a super detailed post, or a simple link to push me in the right direction would be much appreciated!
Thank you very much for reading this rather large post of mine!