How to Create an AR Shoot ’Em Up Gallery Game – Part 4

Intro

Check out the previous parts for our How to Create an AR Shoot’em Up Gallery Game here:

In the previous tutorials, we learned how to:

  • Set up Unity Project to create an iOS AR game
  • Import a 3D bottle model
  • Design and assign materials for the above
  • Implement physics for the 3D bottle
  • Create a placement indicator
  • Add a script to the above
  • Update said placement indicator
  • Lay out the shooting interface

After all that, we still have a few final things to do to our AR carnival shooting gallery. But as I said before, there is no need to fear our dive into the ARverse, as it is all about the journey. Again, this is just one small step for your education… and one giant leap into your indie game development career.

The complete Unity project for this tutorial is available . All images and models used in this tutorial have been created by yours truly.

Did you come across any errors in this tutorial? Please let us know by completing this form and we’ll look into it!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.

This tutorial was implemented in:

  • Xcode 10.1
  • Unity 2018.3.8f1

Tutorial Outline

  1. Adding Effects
  2. Shooting Script
  3. Updating Placement Indicator Script

Adding Effects

If we were to just make the bottles disappear when the player shot one of them, that wouldn’t be very exciting. Let’s add a bit of excitement by incorporating a small explosion and glass breaking sound for the player.    

You can use a particle system to create a convincing explosion, but the dynamics are perhaps a little more complicated than they seem at first. At its core, an explosion is just an outward burst of particles, but there are a few simple modifications you can apply to make it look much more realistic.

So what exactly is an explosion? An explosion occurs when a large amount of energy is released into a small volume of area in a very short time. Meaning the start of it is has a lot of energy and is there for very bright, then it moves very rapidly which causes it to expand, dissipating as it goes. In our design, the explosion particle will start off moving very fast but then its speed will reduce greatly as it moves away from the center of the explosion. At the end of the particle cycle, we’ll reduce the particle’s size to give the effect of the flames dispersing as the accelerant is used up.

With that being said for our explosion we will use Jean Moreno’s free War FX asset package to simulate it. To import said asset package enter the Asset Store tab and search “War FX.” Select the desired package and click Import. A dialogue box itemizing all asset contents will appear. On the bottom right of said box, click import.

After a few minutes, a folder titled JMO Assets will be placed in the projects Assets folder. The explosion that we will be using is located in the JMO Assets > WFX > _Effects > Explosions. Drag the WFX_Explosion Small prefab into the Scene Hierarchy. Rename it Explosion. If you were to play the effect as you we see that is a bit large for simple gunfire. Let’s go ahead and resize it by going into the Inspector Panel and in the Scale properties change the x y z-axis to 0.1.

Every time an explosion take place the glass is breaking. That means we can apply a glass breaking sound effect to the explosion. To do so, make sure your new Explosion prefab is selected and in the Inspector Panel click New Component. In the search field type Audio Source and select it.

Download the glass breaking sound effect found here. From your download folder drag sound effect file into your project’s Assets folder. From the Asset folder drag sound effect into the Audio Clip field under the Audio Source component in the Inspector Panel.

Your Explosion prefab is complete. Drag it into your Assets folder to save it then delete it from the Scene Hierarchy.

Shooting Mechanics

Now that we have the effects taken care of let’s create the script needed to make them come to life. Navigate to your script folder, right click within it, select Create > C# Script and name your new script file Shooting. Open it up by double-clicking on it. First, we’ll need to import the Event Systems that handles input, raycasting, and sending events. It is responsible for processing and handling events in a Unity Scene. A Scene should only contain one Event System. The Event System works in conjunction with a number of modules and mostly just holds state and delegates functionality to specific, overrideable components.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//import Event Systems library for implementation
using UnityEngine.EventSystems;

There is no need to manipulate the Update and Start method because we only need to create a public method that will be applied to the shoot button. We’ll name said method Shoot.

 public void Shoot() {
 }

The Shoot method will be implementing the same Raycast methodology we used when developing the placement indicator script but it is slightly different from the AR Foundation Physics. The RaycastHit is a boolean that returns true if the ray intersects with a collider, otherwise false.

So in the Shoot method let’s create a RaycastHit variable named hit. After will need to create an if statement that checks if the ray cast originates from the center of the camera, in the forward direction of it and hits a collider. Within said if statement we’ll need another if statement to check if the ray hit a bottle collider.

public void Shoot() {
      //boolean true if the ray intersects with a collider, otherwise false
    	RaycastHit hit;
      
      //if the ray cast originates from the center of the camera, in the forward direction and hits a collider
    	if(Physics.Raycast(gameCamera.transform.position, gameCamera.transform.forward, out hit)) {
            //if the ray intersects with a bottle collider
    		if(hit.transform.name == "bottle") {
    		}
        }
}

If this is true we’ll destroy the bottle game object hit by implementing the Destroy method. In this method, you’ll throw the transform game object property of your hit variable to vaporize the object. The instant you destroy the bottle you will also want to activate the explosion effect at the point that the ray intersected the bottle collider.

public void Shoot() {
    	RaycastHit hit;
        
    	if(Physics.Raycast(gameCamera.transform.position, gameCamera.transform.forward, out hit)) {
    		if(hit.transform.name == "bottle") {
                   //destroy the bottle that the ray hit 
    			 Destroy(hit.transform.gameObject);
                   //activate the explosion effect at the point that the ray intersected the bottle
    			 GameObject go = Instantiate(explosion, hit.point, Quaternion.LookRotation(hit.normal));
    		}
        }
}

Your Shoot method is complete. Save your Shoot script and navigate back to Unity. Right now the Shoot script that you just created is just an abstract piece of code floating in the ARverse with no defined purpose. Let’s go ahead give it one. In your, Scene Hierarchy select the shoot button in your Canvas. In the Inspector Panel navigate to the On Click () field and select the shooting > Shoot () function from the dropdown.

Updating Placement Indicator Script

Right now if we were to test our game we would find that when we touch the shoot button the bottles are placed within the gamespace and if we were to touch it again while aiming at our bottle it does not explode. This occurs because the ray cast system is passing through the shoot button on the canvas. We must make the distinction of whether the user is touching the button or not.

To do so will need to create a new method in the ARTapToPlaceObject.js script that makes that distinction for us. Open said script by navigating to your script folder and double-clicking on it. Once, you have the script open in your editor we’ll need to first import the Event Systems library. When the EventSystem is started it searches for any BaseInputModules attached to the same GameObject and adds them to an internal list. On update, each attached module receives an UpdateModules call, where the module can modify internal state. After each module has been Updated the active module has the Process call executed. This is where custom module processing can take place.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//import the libraries below
using UnityEngine.XR.ARFoundation;
using UnityEngine.Experimental.XR;
using System;
using UnityEngine.EventSystems;

Let’s go ahead create a private boolean method with the name IsPointerOverUIObject. The said method will return true if the ray that cast originates from the user touches a UI object i.e the shoot button.

In our new method lets create a new PointerEventData object with the name eventDataCurrentPosition and throw the current EventSystem. The PointerEventData is the event payload associated with the pointer (mouse/touch) events. Now we’ll need to assign the position of our new object to that of the 2D position of the user’s touch. Will do this by using the Vector2 constructor with properties Input.mousePosition.x for the x component and Input.mousePosition.y for the y component. After we do this will need to list of ray cast hits with the name results. Using our new hit list will cast a ray from our Event system from eventDataCurrentPosition and store its hits in results. Return true if the number of hits is greater than zero.

private bool IsPointerOverUIObject() {
        //The PointerEventData is the event payload associated with the pointer (mouse/touch) events
      	PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);

        //assign the position of our new object to that of the 2D position of the user’s touch
      	eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);

        //list of ray cast hits
      	List<RaycastResult> results = new List<RaycastResult>();

        //cast a ray from the event data current position and store it in the results hit list
      	EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
      	return results.Count > 0;
}

Now let’s use this nifty new method. If you remember from in our Update method we check if our bottles have been placed in the gamespace, if they have not been we update the placement pose position while simultaneously updating the graphical interface of the placement indicator. We also checked if the placement pose is intersecting with a plane and the screen has been touched all to see if we can place the bottles. Here will need to add a new condition to our if statement which is if the user has not touched a UI object i.e our shoot button will go ahead and place the object. To do so just add !IsPointerOverUIObject(). This will run our new method and return turn if the user touches the shoot button.

The ! operator computes logical negation of its operand. That is, it produces true, if our Is Pointer Over UI Object method evaluates to false, and false, if our Is Pointer Over UI Object method evaluates to true.

    void Update() {
        //if object has not been placed in game space
    	if (objectPlaced == false) {
	          UpdatePlacementPose();
	          UpdatePlacementIndicator();

                //if placement pose is a flat plane, if the user has any fingers currently on the screen, the phase of the finger to see if the touch just
                //began and if the touch is not over a UI object 
	        if (placementPoseIsValid && Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began && !IsPointerOverUIObject()) {
	             PlaceObject();
	             objectPlaced = true;
                 Destroy(placementIndicator);
	         }
	    }     
    }

Boom Bam Thank you, Ma’am! It’s done. Now if you run your game on your iOS, device you will find that when you touch the shoot button before placing the bottles in gamespace they do not get placed. But if you touch anywhere else on the screen the bottles are placed where your indicator is. Then once you take aim at the bottles and fire – your bottles explode.

Conclusion

And there you have it: a simple shooting gallery AR game that you can show friends and blow their minds with your splendor. You could take this a step further by throwing in an instruction screen, scorekeeping, and a timer to give your game a little bit more of a challenge.

In this tutorial series, we:

  • Updated the placement indicator to face the direction the camera is pointing
  • Placed the bottle pyramid in our game space when the user taps the screen
  • Stopped the spawning of other bottle pyramids if the user taps the screen repeatedly
  • Destroyed the placement indicator once the bottle pyramid has been placed in the game space
  • Laid out the graphical interface by adding a shooting button and crosshair for aiming
  • Added explosion and sound effect prefabs
  • Created a shooting script
  • Updated the placement indicator to only be responsive when the player doesn’t touch the shoot button

For now, go forth into the ARverse! Like the courageous pioneers Neil Armstrong and Buzz Aldrin, and create more!

“Just remember: there is only one corner of the ARverse you can be certain of improving, and that’s your own code.” – Morgan H. McKie