[expand title=”Variables & Set-up”]
A list of variables in which would be used and changed throughout the player class
In the Start() Function all components are found and set up for runtime, some variables are found through the Resources.Load() function so they are gathered from the asset folder data.
Two functions are also called at Start() being the function for disabling the laser beam for the cannon players shoot and another function to add a particle effect on the fire position of our cannon.
[/expand]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class SpaceCannon_Controller : MonoBehaviour
{
// Shooting
// distance in which we shoot
float range = 200;
// The max ammo the cannon can hold by default
[HideInInspector]
public int maxAmmo = 4;
[Tooltip("What layers is the cannon bullets allowed to hit?")]
public LayerMask whatToHit;
[Tooltip("The current ammo that we have loaded inside the cannon")]
public int currentAmmo;
bool isReloading = false;
public float reloadSpeed = 2f;
private float nextTimeToFire;
public float FireRate = 15f;
private Game_Manager GM;
private AudioSource AS;
private AudioClip shoot;
#region Laser Variables
[Header("Laser Variables")]
public LineRenderer lineRenderer;
// Position where we fire the projectile bullet from
public Transform firePoint;
public GameObject startVFX;
private List<ParticleSystem> LaserParticles = new List<ParticleSystem>();
#endregion
// Effects
public Image reloadImage;
private AudioClip reloadChargeUp, reloadClick;
// Start is called before the first frame update
void Start()
{
if(AS == null)
{
AS = gameObject.AddComponent<AudioSource>();
}
else
{
AS = gameObject.GetComponent<AudioSource>();
}
AS.loop = false;
GM = GameObject.Find("GameManager").GetComponent<Game_Manager>();
// find the first child to get the fire position Transform component
firePoint = gameObject.transform.GetChild(0).GetComponent<Transform>();
// make sure the cannon is loaded with the max amount of bullets
currentAmmo = maxAmmo;
lineRenderer = gameObject.transform.GetChild(1).GetComponentInChildren<LineRenderer>();
startVFX = gameObject.transform.GetChild(1).GetChild(1).gameObject;
reloadImage = GameObject.Find("ReloadImage").GetComponent<Image>();
shoot = Resources.Load<AudioClip>("Audio/Player/Shoot Cannon");
reloadChargeUp = Resources.Load<AudioClip>("Audio/Player/Cannon Reload Charge");
reloadClick = Resources.Load<AudioClip>("Audio/Player/Cannon Reload Click");
FillParticleLost();
DisableLaser();
} [expand title=”Runtime Function”]
Update() Holds logic within a key condition checking if the game has actually started which is balanced by a boolean in the game manager class.
When the game has started Functions like CannonRotation(), Shoot() and reload are monitored to check the state of play.
Reloading the gun prevents the player from shooting it but does not prevent the player from rotating it. Runtime also stops particle effects from rendering.
[/expand]
// Update is called once per frame
void Update()
{
if (GM.startGame != true)
return;
else
{
CannonRotation(5, -90);
if (isReloading)
{
for (int i = 0; i < LaserParticles.Count; i++)
LaserParticles[i].Stop();
return;
}
// shoot when we press left click
if (Input.GetButtonDown("Fire1") && currentAmmo > 0 && Time.time > nextTimeToFire)
{
// Calculate next time to shoot cannon
nextTimeToFire = Time.time + FireRate;
// Shoot the cannon
Shoot();
}
// Dont make distant distort sound on shoot
if(AS.time >= 2 && !isReloading)
{
AS.volume = 0;
}
// we are now reloading
if (currentAmmo <= 0)
StartCoroutine(ReloadCannon(reloadSpeed));
}
} [expand title=”Functions to be called on runtime”]
The CannonRotation() function gathers two parts of important data.
The first part is the direction in which the cannon is going to face. This is important as we need to know what direction we face to shoot the cannon
Secondly, the other data is rotation. getting the angle so we can Slerp() towards the direction and have the correct angle.
So both of these pieces of data work together so when the player presses the screen of their smartphone device, the cannon can detect that they have inputted and then the cannon will rotate towards that position.
The Shoot() function edits certain variables like an audio source to balance the volume of the shooting sound.
Shoot() uses 2D raycasts to detect if the player has hit an object of relevant layer using a layer mask, called “whatToHit”
Then the “hit” data can return true or false and when its true then we enable the laser animation and start our shoot effect, then finding data from the object we hit and then detecting health so the obstacle will explode.
Finally, we have the ReloadCannon() IEnumerator, which allows the cannon to reload to max ammo within a set time during frames. The class has a “reloadTime” variable
The time will be deducted in the function so when the time is zero then we have max again.
We also use an IEnumerator as we are editing the fillamount of a UI image inside unity. So it can provide a smooth annimation.
[/expand]
void CannonRotation(float rotationSpeed, float offset)
{
// OBTAIN DIRECTION \\
// ----------------
// get the mouse position
Vector3 mousePosition = Input.mousePosition;
// get the mouse position on screen
mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);
// get the direction in which we will be facing towards (where the mouse is) // target
Vector2 direction = new Vector2(mousePosition.x - transform.position.x, mousePosition.y - transform.position.y);
// SET ROTATION \\
// -------------
// Get the angle from the current direction to the desired target
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
// set the angle into a quaternion + sprite offset depending on initial sprite facing direction
Quaternion rotation = Quaternion.Euler(new Vector3(0, 0, angle + offset));
rotation.eulerAngles = new Vector3(0, 0, angle + offset);
// Rotate current game object to face the target using a slerp function which adds some smoothing to the movement
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, rotationSpeed * Time.deltaTime);
}
Obsticle_Controller hazzardScript;
void Shoot()
{
AS.volume = 0.5f;
AS.clip = shoot;
// Draw the ray in scene
Debug.DrawRay(firePoint.transform.position, firePoint.transform.TransformDirection(Vector2.up) * range, Color.blue);
// Data to understand what passes through Hit
RaycastHit2D hit = Physics2D.Raycast(firePoint.position, firePoint.transform.TransformDirection(Vector2.up), range, whatToHit);
if(hit)
{
EnableLaser();
startVFX.transform.position = firePoint.position;
StartCoroutine(LaserAnimation(firePoint.position, hit.point));
// Debug message to show we hit something
Debug.Log("Hit Object" + "" + "Name" + "" + hit.collider.name);
currentAmmo--;
hazzardScript = hit.transform.GetComponent<Obsticle_Controller>();
AS.Play();
}
}
IEnumerator ReloadCannon(float reloadTime)
{
AS.volume = 1;
// Start the event
isReloading = true;
// get the current realtime of the reload value time
float currentTime = reloadTime;
// turn on the image that will show when we reload
reloadImage.enabled = true;
// set the value for the fill to be 0 so the effect is about to start
reloadImage.fillAmount = 0;
// show we are reloading in the console
Debug.Log("Reloading . . .");
// play sound
AS.clip = reloadChargeUp;
AS.Play();
// Loop check to see if the time we have is still greater than 0
while (currentTime > 0)
{
// Decrease value of current time
currentTime -= Time.deltaTime;
// fill the image and divide by the duration to make sure it does not increase to fast
reloadImage.fillAmount = currentTime / reloadTime;
// get next frame
yield return null;
}
// AFTER LOOP IS DONE
AS.volume = 0;
// reload the cannon by giving ammo back
currentAmmo = maxAmmo;
// turn off the effect image
reloadImage.enabled = false;
// End function
isReloading = false;
} [expand title=”Line Renderer Animation control”]
EnableLaser() allows for the cannon shoot laser particle effect to be called
The laser effect has an animation which is called LaserAnimation() it uses a LineRenderer component to fire towards the obstacles
the variable “t” increases over time with a for loop so that the LineRenderer.SetPosition(1) which is our target, will animate smoothly towards the target instead of instant travel.
The animation also holds important logic like damaging the obstacles. placing it here makes more sense as we can tell when the animation is over and then we can damage the obstacle and turn off the laser so everything looks normal. If not the obstacle would have destroyed to fast which would have sent the wrong type of feedback to the player.
When the animation is over we call DisableLaser() function which stops all the particle effects from playing and turns off the LineRenderer component.
[/expand]
#region Laser Functions
void EnableLaser()
{
lineRenderer.enabled = true;
for (int i = 0; i < LaserParticles.Count; i++)
LaserParticles[i].Play();
}
Vector3 newPos;
IEnumerator LaserAnimation(Vector2 startPosition, Vector2 endPosition)
{
float t = 0;
float time = 0.3f;
startPosition = firePoint.position;
lineRenderer.SetPosition(0, startPosition);
for (; t < time; t+= Time.deltaTime)
{
newPos = Vector2.Lerp(startPosition, endPosition, t / time);
lineRenderer.SetPosition(1, newPos);
yield return null;
}
if(lineRenderer.GetPosition(1) == newPos)
{
hazzardScript.Damage(1);
yield return new WaitForSeconds(1);
DisableLaser();
}
lineRenderer.SetPosition(1, endPosition);
}
void DisableLaser()
{
lineRenderer.enabled = false;
for (int i = 0; i < LaserParticles.Count; i++)
LaserParticles[i].Stop();
}
void FillParticleLost()
{
for(int i = 0; i < startVFX.transform.childCount; i++)
{
var ps = startVFX.transform.GetChild(i).GetComponent<ParticleSystem>();
if (ps != null)
LaserParticles.Add(ps);
}
}
#endregion 


















