Physics Projectiles, Coroutines and Object Pooling (Unity)

z4gon
z4gon

Using Corotuines and Object Pools to instantiate projectiles, and Ribidbodies for simple physics.

References

Table of Content


Projectile: Bullet

  • For now, a simple component that has a Collider and a Rigidbody.
  • The Rigidbody will not be kinematic, to be able to use Velocity. 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 the Bullet 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

Picture


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.

Picture

  • Using a Raycast again, to get the corresponding point at the top edge, we can make the Player to LookAt 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}

Picture