Building a Cannonball Shooter Game: Part 2 — Launch Cannonballs

Simon Pham
4 min readAug 16, 2024

--

In this second blog post of the series, we’ll focus on launching cannonballs.

Disclaimer: This series is inspired by a tutorial from Tarodev. You can check out his excellent tutorial and download the assets here.

Cannonball prefab

Cannonball model

Our cannonball model is just a sphere with a Sphere Collider and Rigidbody components.

Cannonball Script

First, we need to get a reference to its Rigidbody component and create a public method that can be called in the Cannon script to add a velocity to it.

[SerializeField] private Rigidbody _rb;

public void Init(Vector3 velocity)
{
_rb.AddForce(velocity, ForceMode.Impulse);
}

I chose Impulse as the ForceMode because I want to add an instant force impulse to the Rigidbody while considering its mass, but you can also experiment with the other options. Details on the different ForceModes can be found here.

Next, I want the cannonball to disappear 0.5 seconds after colliding with the ground. To do that, we can use the OnCollisionEnter method.

private void OnCollisionEnter(Collision collision)
{
if (collision.transform.CompareTag("Ground"))
Destroy(gameObject, 0.5f);
}

You need to ensure that you’ve created a corresponding tag for the ground.

Cannon script

Cannonball launching

Let’s declare some variables that we’ll use.

[SerializeField] private Transform _cannonballSpawn;
[SerializeField] private Cannonball _cannonballPrefab;
[SerializeField] private float _force;
[SerializeField] private float _fireRate;
private float _lastTimeFire = 0f;

I’ll create a new function called Fire to instantiate cannonballs at the spawn position. In this function, I’ll also call the Init method and pass in the vector multiplied by the launch force.

void Fire()
{
var ball = Instantiate(_cannonballPrefab, _cannonballSpawn.position, _cannonballSpawn.rotation);
ball.Init(_cannonballSpawn.forward * _force);
}

The spawn position is at the tip of the barrel.

Cannon spawn position

Next, to be able to launch cannonballs, I’ll bind the Fire function to the Space key and add a fire rate feature so that players can only fire a cannonball after a short period of time.

void LaunchCannonball()
{
if (Input.GetKeyDown(KeyCode.Space) && Time.time - _lastTimeFire >= 1 / _fireRate)
{
Fire();
_lastTimeFire = Time.time;
}
}

Now, let’s assign values to the force and fire rate in the Inspector and test the result.

It doesn’t matter how fast I press the Space key; the cannon will only shoot one cannonball every second.

Adding particle effects

I have a particle effect nested inside the Spawn object, which has a one-second duration.

In the Cannon script, let’s get a reference to this particle effect and assign it in the Inspector.

[Header("Particles")]
[SerializeField] private ParticleSystem _launchParticles;

To play this effect, I’ll create a new function called PlayLaunchParticles and call it inside the LaunchCannonball function.


void LaunchCannonball()
{
if (Input.GetKeyDown(KeyCode.Space) && Time.time - _lastTimeFire >= 1 / _fireRate)
{
Fire();
PlayLaunchParticles();
_lastTimeFire = Time.time;
}
}

void PlayLaunchParticles() {_launchParticles.Play();}

Here’s the result:

Adding audio

First, let’s add an Audio Source component to the Cannon in the Inspector.

Next, let’s create two variables: one for the Audio Source component and one for the audio clip that will be played by the audio source.

[Header("Audio")] 
[SerializeField] private AudioSource _audioSource;
[SerializeField] private AudioClip _cannonFireSound;

After that, I’ll assign the Audio Source component and the audio file to their respective slots in the Inspector.

Finally, I’ll create a new function called PlayLaunchSound and call it from the LaunchCannonball function.

void LaunchCannonball()
{
if (Input.GetKeyDown(KeyCode.Space) && Time.time - _lastTimeFire >= 1 / _fireRate)
{
Fire();
PlayLaunchParticles();
PlayLaunchSound();
_lastTimeFire = Time.time;
}
}

void PlayLaunchSound() {_audioSource.PlayOneShot(_cannonFireSound);}

--

--