Here at ZuluOneZero we love guns in games and other fantasy settings for their ability to embody drama, tension and action. They are a super power that takes us beyond the normal abilities to exert force. Sadly in real life guns suck and if you like shooting guns for real stay the heck away from me and my friends.
Anywhoo… this is how we use Guns in Endless Elevator to dispatch the Bad Guys and generally wreak havoc in an otherwise quietly innocent building.
This is our hero The LawMan! See he comes with that super powered lawmaster pistol, standard issue bullet proof vest, sherrif’s hat, and a make no mistakes we mean business moustache (can you tell he’s smiling under that?).
This is how he looks in the Unity Scene Editor. See those two Objects he has as Children “Gun” and “smoke”? They sit invisibly just where the 3D curser is in the image below…just at the end of the gun barrel. The Gun object is used as the spawn point for bullets and smoke is one of two particle systems that go off when the gun fires (the other particle system does sparks and is attached as a component directly to the Game Object).
There are four scripts that handle the basic Gun actions in our game. There are of course plenty of ways to do this – but this is the way we do it for this game. One script handles the aiming of the gun as part of the Character Controller. Another script attached to the Character handles the firing of the Gun and the spawning of the bullets. The Bullets have their own script that handles Gravity, Acceleration and Animations while alive and during Collisions. The last script attached to the Bad Guy handles the impact effects of the Bullets and the “blood”.
We wanted to keep the cartoon elements of gun violence in this game and get away from realism as much as possible. That said a bullet strike has a pretty huge impact and when we had a red coloured particle system for the blood it looked really gruesome. We changed the impact force to be over the top and super exaggerated so that it’s funnier reaction and moved the particle system color to yellow (might change it to stars later on). The Bullets are supposed to look like expanding rubber dum dum bullets so they grow out of the gun and enlarge a little bit in-flight. After a collision they start to shrink again and get very bouncy.
Here is a sample of game play where our hero blasts away at some chump.
So the Hero Character has a couple of scripts that handle aiming and firing.
The snippet below is the aiming component of the Character Controller. The Player has a wide Trigger Collider out the front that picks up when it hit’s a Bad Guy. If there are no _inputs from the Controller (ie. the Player stops moving) then he will automagically rotate towards the Bad Guy and thus aim the gun at him.
void OnTriggerStay(Collider otherObj)
{
if (otherObj.name == "bad_spy_pnt" || otherObj.name == "Knife_spy")
{
if (_inputs == Vector3.zero)
{
Vector3 lookatposi = new Vector3(otherObj.transform.position.x, transform.position.y, otherObj.transform.position.z);
transform.LookAt(lookatposi);
}
}
}
Now once we are aiming we can fire (you can shoot at anytime – and straffing is pretty fun – but it’s far easier to hit if you stop and let the auto-aim work for you). For firing this is what the FireBullet script exposes in the Editor:
There is a “bullet” prefab which we will talk about below, the Gun transform (ie. bullet spawn point), the force at which the bullet is spawned with, the audio for the shot, and the particle system for the smoke (called PartyOn …. I know).
The script itself is pretty straightforward: The bullet is spawned at the Gun Transform with a direction and force, the gun noise goes off, and the particle system does smoke and sparks. After two seconds the bullet is destroyed. This is what it looks like:
using UnityEngine;
public class FireBullet : MonoBehaviour
{
public GameObject bulletPrefab;
public Transform bulletSpawn;
public float force;
public AudioClip fireNoise;
private AudioSource MyAudio;
public ParticleSystem partyOn;
public bool includeChildren = true;
void Start ()
{
MyAudio = GetComponent();
partyOn = GetComponent();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Fire();
}
}
public void Fire()
{
partyOn.Play(includeChildren);
var bullet = (GameObject)Instantiate(bulletPrefab, bulletSpawn.transform.position, bulletSpawn.transform.rotation);
MyAudio.Play();
bullet.GetComponent().AddForce(transform.forward * force);
Destroy(bullet, 2.0f);
}
}
One of the really important lessons we learned from this script is to get the Transforms and X/Y/Z directions of your models imported into Unity in the right direction first up. We had a few different models for bullets over the last few weeks ranging from simple cylinders, to pillows and bean bags, and real bullet shapes. It makes it so much easier to direct objects if their rotations are correct to start with. For example we did one quick model of a cylinder but had it sitting on the Z axis instead of X so when we did the “forward” force the bullet would travel sideways.
This is how our bullet looks now:
This is how the script to handle it’s behaviours and the settings of it’s Rigidbody and Collider:
It’s got a Rubber material on the Collider so that it bounces around when Gravity is enabled in the Rigidbody on Collision. We disabled Gravity so that we could slow down the Bullet firing path and not have to use so much Force. Having a slow bullet adds to the cartoon drama, reinforces the rubber bullet idea, and looks less like killing force. Here is the script:
using UnityEngine;
public class BulletGravity : MonoBehaviour {
private Rigidbody rb;
public float force;
public float accelleration;
public bool collided;
public float scaleFactorUp;
public float scaleFactorDown;
public float maxScale;
public float bounceForce;
void Start () {
rb = GetComponent();
}
void Update()
{
if (transform.localScale.x scaleFactorDown)
{
transform.localScale -= new Vector3(scaleFactorDown, scaleFactorDown, scaleFactorDown);
}
}
}
void FixedUpdate ()
{
if (!collided)
{
rb.AddForce(transform.forward * force * accelleration);
}
}
void OnCollisionEnter(Collision col) {
collided = true;
rb.useGravity = true;
rb.velocity = transform.up * bounceForce;
}
}
Below is the Collision part of the script that handles the BadGuy flying up into the air and bleeding all over the place.
void OnCollisionEnter(Collision col)
{
colName = col.gameObject.name;
if (colName == "bullet(Clone)")
{
hitpoint = col.transform.position;
GameObject BloodObject = Instantiate(BloodPrefab, hitpoint, new Quaternion(0, 0, 0, 0), TargetObject.transform) as GameObject;
Destroy(BloodObject, 5.0f);
// We disable his AI script so he doesn't try and walk around after being shot :) It's not a zombie game.
var AI_script = GetComponent();
if (AI_script)
{
AI_script.enabled = false;
}
Vector3 explosionPos = transform.position;
explosionPos += new Vector3(1, 0, 0);
rb.AddExplosionForce(power, explosionPos, radius, offset, ForceMode.Impulse);
yesDead = true;
Destroy(transform.parent.gameObject, 2f);
}
}
Let’s break down the whole process visually. Here is our Hero in a standoff with a Baddy. He hasn’t aimed yet but you can see his big aiming collider in yellow underneath him extending out front and of course the character box colliders are visible too in green. (The Bad Guy has a yellow sphere collider on his head cause we make him get squashed by lifts!).
Here we are just after firing. His aiming script has put him on target and his bullet is there travelling with force and bloating in size.
And this is the impact with the force applied to the Bad Guy and his yellow blood impact stuff spewing out dramatically for effect.
All we got to do now is to clean up the Bad Guy’s object and destroy the used bullets. We don’t do any object pooling for the bullets yet…the overhead for the current system has warranted it but maybe later on.
That’s about it. Hope you’ve enjoyed this gun for fun expo-say.
Trixie out.