Physics Projectiles, Coroutines and Object Pooling (Unity)
Using Corotuines and Object Pools to instantiate projectiles, and Ribidbodies for simple physics.
References
Table of Content
- References
- Table of Content
- Projectile: Bullet
- ObjectPool: Gun
- Player
- All the Pieces Together
- Correcting Projectile Trajectory
Projectile: Bullet
- For now, a simple component that has a
Collider
and aRigidbody
. - The
Rigidbody
will not be kinematic, to be able to useVelocity
. And will ignore gravity, to follow a straight line.
1public float speed 2{ 3 get { return rigidBody.velocity.z; } 4 set { rigidBody.AddForce(transform.forward * value, ForceMode.VelocityChange); } 5} 6private Rigidbody rigidBody; 7 8private void Awake() 9{ 10 rigidBody = GetComponent<Rigidbody>(); 11}
ObjectPool: Gun
- This component will use the
ObjectPool
to store theBullet
game objects. - It will also define a
Coroutine
to periodically fire the gun, and shoot projectiles.
Properties
1public Bullet bulletPrefab; 2public float bulletsPerSecond; 3public float bulletSpeed; 4public int defaultBulletsPoolCapacity = 10; 5public int maxBulletsPoolSize = 20; 6private ObjectPool<Bullet> bulletsPool; 7private Coroutine shootCoroutine;
Initialize Object Pool
1void Awake() 2{ 3 bulletsPool = new ObjectPool<Bullet>( 4 createFunc: CreatePooledBullet, 5 actionOnGet: OnGetBulletFromPool, 6 actionOnRelease: OnReleaseBulletToPool, 7 actionOnDestroy: OnDestroyBulletFromPool, 8 collectionCheck: true, 9 defaultCapacity: defaultBulletsPoolCapacity, 10 maxSize: maxBulletsPoolSize 11 ); 12}
Object Pool Event Handlers
1private Bullet CreatePooledBullet() => Object.Instantiate(bulletPrefab, transform.position, transform.rotation); 2private void OnGetBulletFromPool(Bullet bullet) => bullet.gameObject.SetActive(true); 3private void OnReleaseBulletToPool(Bullet bullet) => bullet.gameObject.SetActive(false); 4void OnDestroyBulletFromPool(Bullet bullet) => Destroy(bullet.gameObject);
Shoot Projectiles
1public void StartShooting() 2{ 3 shootCoroutine = StartCoroutine(Shoot()); 4} 5 6public void StopShooting() => StopCoroutine(shootCoroutine); 7 8private IEnumerator Shoot() 9{ 10 while (true) 11 { 12 var bullet = bulletsPool.Get(); 13 14 if (bullet != null) 15 { 16 bullet.transform.position = transform.position; 17 bullet.transform.rotation = transform.rotation; 18 bullet.speed = bulletSpeed; 19 } 20 21 yield return new WaitForSeconds(1.0f / bulletsPerSecond); 22 } 23}
Player
- The
Player
game object will react to player input, and shoot projectiles.
1public List<Gun> guns = new List<Gun>(); 2 3public void OnShootAction(InputAction.CallbackContext context) 4{ 5 if (context.performed) 6 StartShooting(); 7 else if (context.canceled) 8 StopShooting(); 9} 10 11private void StartShooting() 12{ 13 foreach (var gun in guns) 14 gun.StartShooting(); 15} 16 17private void StopShooting() 18{ 19 foreach (var gun in guns) 20 gun.StopShooting(); 21}
All the Pieces Together
Correcting Projectile Trajectory
- Given the perspective of the camera, projectiles that are aligned to the direction of the player do not follow a straight line in the viewport.
- This is a problem since the player won't be able to aim for enemies in the top left and right corners.
- Using a
Raycast
again, to get the corresponding point at the top edge, we can make thePlayer
toLookAt
it. - This effectively makes the aiming dynamic, and keeps a straight like of projectiles in the viewport.
1public void OnPointerPosition(InputAction.CallbackContext context) 2{ 3 ... 4 5 // look at 6 point = new Vector2(point.x, Screen.height); 7 ray = mainCamera.ScreenPointToRay(point); 8 if (boundaries.plane.Raycast(ray, out distance)) 9 { 10 transform.LookAt(ray.GetPoint(distance), Vector3.up); 11 } 12}