Advanced Space Shooter in Unity3D





3.00/5 (3 votes)
Advanced space shooter in Unity3D
Followed this official tutorial, my own version on GitHub, and you can see it in action here (WSAD to move, mouse click to shoot).
- Create a new 2D project, download and import the assets from https://www.assetstore.unity3d.com/en/#!/content/13866
- Save a scene in _Scenes folder and name it
Main
- File -> Build settings -> Web player -> Switch platform
- Player Settings… – define the size
- Reorder the layout the way you want but remember to save it by clicking the Layout button in the top right corner
- Drag
Vehicle_play
from the Assets->Model folder to the Hierarchy- Rename to
player
, reset transform origin - Add component -> Physics -> Rigidbody and deselect Use Gravity
- Add component -> Physics -> Capsule collider, change direction to Z-Axis and adjust position of X, and check Is Trigger
- Click on the y gizmo of the camera and adjust again
- In this example, the Capsule collider would suffice, but we can add Mesh collider and this is very detailed but we can also drag a predefined Mesh collider from Models folder to Mesh collider -> Mesh variable which is less detailed but sufficient and better than the Capsule collider
- To see the mesh collider, turn off the Mesh renderer
- Add Prefabs -> VFX -> Engines ->
engines_player
to theplayer
object
- Rename to
- Reset camera transform origin
- Set transform Y to 10, Z to 5 (so that the ship starts at the bottom)
- Set rotation X to 90
- Set Projection to ortographic
- Set Size to 10
- Set Clear flags to Solid Color
- Background black
- Edit -> Render settings – property Ambient light set to Black (0,0,0,255) to effectively turn it off
- Lights
- Main light
- Hierarchy -> Create -> Directional light (based on rotation!, not position) and rename accordingly
- Reset origin position
- Set Rotation X = 20, Y = – 115
- Intensity = 0.75
- Fill light
- Duplicate the Main light and rename accordingly
- Rename to Fill light
- Reset its rotation only
- Set Rotation Y = 115
- Set Color to some shade of blue
- Rim light
- Duplicate the Fill light and rename accordingly
- Reset origin position
- Color White
- Rotation Y = 65, X = -15
- Intensity = 0.25
- Main light
- Object organization
- Create new empty object (ctrl + shift + n)
- Rename to
Lights
- Rest transform origins
- Move the whole
Lights
object out of the way by setting the position Y = 100
- Background
- Hierarchy -> Create -> 3D object -> Quad; rename accordingly
- Reset transform origin
- Set rotation X = 90
- Remove the Mesh Collider component
- Drag Assets -> Textures ->
tile_nebula_green
to theQuad
object - Scale X = 15, Y = 30
- Shaders; difuse – mat, specular – glossy, unlit -> texture is the one we will use since this makes it independent of the lighting system (it displays the image originally as it looks)
- Position Y = -10 because it otherwise overlaps with 0,0,0 player object position
- Moving the player ship
- In the Assets folder, create folder Scripts
- Click on the player and Add Component -> Script -> name it
PlayerController
- Drag the created script file to the Scripts folder
- You have
Update
andFixedUpdate
we will use since we are dealing with physics.#pragma strict public var speed = 12; function FixedUpdate () { var moveHorizontal = Input.GetAxis("Horizontal") ; var moveVertical = Input.GetAxis("Vertical"); rigidbody.velocity = Vector3(moveHorizontal, 0, moveVertical) * speed; }
Input.GetAxis
returns a number from 0 to 1 that’s why you have to multiply- At this point, the player ship can go out of the play area, to fix this, use the
Mathf.Clamp
function:#pragma strict public class Boundary extends System.Object { public var zMin : float; public var zMax : float; public var xMin : float; public var xMax : float; } public var speed = 10; public var boundary : Boundary; function FixedUpdate () { var moveHorizontal = Input.GetAxis("Horizontal") ; var moveVertical = Input.GetAxis("Vertical"); rigidbody.velocity = Vector3(moveHorizontal, 0, moveVertical) * speed; rigidbody.position = Vector3( Mathf.Clamp(rigidbody.position.x, boundary.xMin, boundary.xMax), 0, Mathf.Clamp(rigidbody.position.x, boundary.zMin, boundary.zMax) ); }
extends System.Object
is used to Serialize the object to be able to be used within other object and visible in the inspector- To add tilting when moving:
#pragma strict public class Boundary extends System.Object { public var zMin : float; public var zMax : float; public var xMin : float; public var xMax : float; } public var speed = 10; public var boundary : Boundary; public var tilt = 4; function FixedUpdate () { var moveHorizontal = Input.GetAxis("Horizontal") ; var moveVertical = Input.GetAxis("Vertical"); rigidbody.velocity = Vector3(moveHorizontal, 0, moveVertical) * speed; rigidbody.position = Vector3( Mathf.Clamp(rigidbody.position.x, boundary.xMin, boundary.xMax), 0, Mathf.Clamp(rigidbody.position.z, boundary.zMin, boundary.zMax) ); rigidbody.rotation = Quaternion.Euler(0, 0, rigidbody.velocity.x * - tilt); }
- Bullets
- Create a new empty object, rename to
Bolt
- Reset transform
- Create Quad, reset transform, rename VFX
- Add VFX as Child of the Bolt
- Rotate X = 90
- Drag Textures ->
fx_lazer_orange_dff
to it - Shader -> Particles -> Additive (you can also use Mobile version)
- Add component -> Physics -> Rigidbody, deselect Use gravity
- Remove the VFX Mesh collider
- Select Bolt and add Capsule colider
- Change radius and Direction to Z-axis
- Check the
Is Trigger
checkbox - Add script to
Bolt
object, name itMover
- Make it a Prefab
- Delete it from Hierarchy
- Create a new empty object, rename to
- Firing Bullets
- PlayerController.js:
#pragma strict public class Boundary extends System.Object { public var zMin : float; public var zMax : float; public var xMin : float; public var xMax : float; } public var bullet : GameObject; public var speed = 10; public var boundary : Boundary; public var tilt = 4; public var waitPeriod = 0.05; private var nextFire : float; function FixedUpdate () { var moveHorizontal = Input.GetAxis("Horizontal") ; var moveVertical = Input.GetAxis("Vertical"); rigidbody.velocity = Vector3(moveHorizontal, 0, moveVertical) * speed; rigidbody.position = Vector3( Mathf.Clamp(rigidbody.position.x, boundary.xMin, boundary.xMax), 0, Mathf.Clamp(rigidbody.position.z, boundary.zMin, boundary.zMax) ); rigidbody.rotation = Quaternion.Euler(0, 0, rigidbody.velocity.x * - tilt); } function Update(){ if (Input.GetButton("Fire1") && Time.time > nextFire){ nextFire = Time.time + waitPeriod; Instantiate(bullet, Vector3(rigidbody.position.x, 0, rigidbody.position.z), Quaternion.identity); } }
- Drag the bullet prefab to the bullet variable on the player script
- PlayerController.js:
- Cleaning up
- Create Cube, rename Boundary
- Reset transform origin and place it around the whole game
- Turn off Mesh Renderer
- Is Trigger on Box collider
- Remove the renderer
- Add script
Destroy
:function OnTriggerExit(other : Collider) { Destroy(other.gameObject); }
- Enemies
- Create an empty game object –
Asteroids
- Reset transform origin, move a bit away along the Z axis
- Drag the
Asteroid
model from the Models folder and place it as a child of theAsteroids
empty game object – RTO (reset transform origin from now on) - Click on the EGO (empty game object) and add Physics ->
Rigidbody
- Deselect Use Gravity
- Add -> Physics -> Capsule collider
- Add Script:
public var tumble : float; function Start () { rigidbody.angularVelocity = Random.insideUnitSphere * tumble; }
AngularVelocity
– how fast the object is rotating- Remove the
AngularDrag
which eventually slows the asteroid to a halt (drag – trenje for all you Croatian readers) - Add script
DestroyByContact
:#pragma strict function OnTriggerEnter (other : Collider) { if (other.tag == "Boundary"){ return; } Destroy(other.gameObject); Destroy(gameObject); }
- Add Tag Boundary to
Boundary
- Create an empty game object –
- Explosions
- Adjust the
DestroyByContact
script:#pragma strict public var asteroidExplosion : GameObject; public var playerExplosion : GameObject; function OnTriggerEnter (other : Collider) { if (other.tag == "Boundary"){ return; } //it will create an explosion on the same position as our asteroid Instantiate(asteroidExplosion, transform.position, transform.rotation); if (other.tag == "Player"){ Instantiate(playerExplosion, other.transform.position, other.transform.rotation); } Destroy(other.gameObject); Destroy(gameObject); }
- Drag the needed explosions from the VFX folder
- Drag the Mover script to the Asteroid and set speed to
-5
- Drag Asteroid to a Prefab folder
- Delete it from Hierarchy
- Adjust the
- Game Controller
- Create EGO and rename accordingly
- Set tag to
GameController
- Add script:
#pragma strict public var enemy : GameObject; public var spawnValues : Vector3; function Start () { SpawnWaves(); } function SpawnWaves(){ var spawnPosition = Vector3(Random.Range (-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z); var spawnRotation = Quaternion.identity; Instantiate(enemy, spawnPosition, spawnRotation); }
- Outside set values for
spawnValues
to6, 0, 18
- Add wave of enemies:
#pragma strict public var enemy : GameObject; public var spawnValues : Vector3; public var enemyCount : int; public var waitForEnemy : float; public var waitForPlayer : float; public var waveWait : float; function Start () { SpawnWaves(); } function SpawnWaves(){ yield WaitForSeconds(waitForPlayer); while (true){ for (var i=0; i<enemyCount; i++){ var spawnPosition = Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z); var spawnRotation = Quaternion.identity; Instantiate(enemy, spawnPosition, spawnRotation); yield WaitForSeconds(waitForEnemy); } } yield WaitForSeconds(waveWait); }
- Destroying explosion animations
- Add new script
DestroyByTime
#pragma strict public var aliveTime : float; function Start () { Destroy(gameObject, aliveTime); }
- Set this script on all of the explosion prefabs
- Set
AliveTime
to2
- Add new script
- Audio
- Drag the Audio files to appropriate Explosion Prefabs
- For a
weapon_player
– drag it to aPlayer
object in Hierarchy and remove the Play on Awake - Update the
PlayerController
script to:function Update(){ if (Input.GetButton("Fire1") && Time.time > nextFire){ nextFire = Time.time + waitPeriod; Instantiate(bullet, Vector3(rigidbody.position.x, 0, rigidbody.position.z), Quaternion.identity); audio.Play(); } }
- Drag
music_background
toGameController
- Select Loop and Play on Awake
- Adjust the volumes:
- Player 0.5
- Game Controller 0.5
- Displaying score
- Create -> UI -> Text, rename to
ScoreText
- Set correct Rect Transform
- In the
GameController
script, add:#pragma strict public var enemy : GameObject; public var spawnValues : Vector3; public var enemyCount : int; public var waitForEnemy : float; public var waitForPlayer : float; public var waveWait : float; public var scoreText : UnityEngine.UI.Text; private var score : int; function Start () { score = 0; updateScore(); SpawnWaves(); } function SpawnWaves(){ yield WaitForSeconds(waitForPlayer); while (true){ for (var i=0; i<enemyCount; i++){ var spawnPosition = Vector3(Random.Range (-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z); var spawnRotation = Quaternion.identity; Instantiate(enemy, spawnPosition, spawnRotation); yield WaitForSeconds(waitForEnemy); } } yield WaitForSeconds(waveWait); } function updateScore(){ scoreText.text = "Score: " + score; } function IncreaseCount(){ score ++; updateScore(); }
- In the
DestroyByContact
:#pragma strict public var asteroidExplosion : GameObject; public var playerExplosion : GameObject; private var gameController : GameController; function Start(){ var gameControllerObject : GameObject = GameObject.FindWithTag("GameController"); if (gameControllerObject != null){ gameController = gameControllerObject.GetComponent(GameController); } else{ Debug.Log("oops"); } } function OnTriggerEnter (other : Collider) { if (other.tag == "Boundary"){ return; } //it will create an explosion on the same position as our asteroid Instantiate(asteroidExplosion, transform.position, transform.rotation); if (other.tag == "Player"){ Instantiate(playerExplosion, other.transform.position, other.transform.rotation); } gameController.IncreaseCount(); Destroy(other.gameObject); Destroy(gameObject); }
- Drag the
ScoreText
object to form the reference to it in theGameController
.
- Create -> UI -> Text, rename to
- Ending the game
- Create an empty game object and rename it to
DisplayText
- Rest transform origin
- Add
ScoreText
to it (drag theCanvas
containing it) - Create new UI -> Text (rename to
RestartText
), and place it in the upper right corner - Create new UI -> Text (rename to
GameOverText
) - Update the script to:
#pragma strict public var enemy : GameObject; public var spawnValues : Vector3; public var enemyCount : int; public var waitForEnemy : float; public var waitForPlayer : float; public var waveWait : float; public var scoreText : UnityEngine.UI.Text; public var restartText : UnityEngine.UI.Text; public var gameOverText : UnityEngine.UI.Text; private var restart : boolean; private var gameOver : boolean; private var score : int; function Start () { restart = false; gameOver = false; restartText.text = ""; gameOverText.text = ""; score = 0; updateScore(); SpawnWaves(); } function Update(){ if (restart){ if (Input.GetKeyDown(KeyCode.R)){ Application.LoadLevel(Application.loadedLevel); } } } function SpawnWaves(){ yield WaitForSeconds(waitForPlayer); while (true){ for (var i=0; i<enemyCount; i++){ var spawnPosition = Vector3(Random.Range (-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z); var spawnRotation = Quaternion.identity; Instantiate(enemy, spawnPosition, spawnRotation); yield WaitForSeconds(waitForEnemy); } if (gameOver){ restartText.text = "R to restart"; restart = true; break; } } yield WaitForSeconds(waveWait); } function updateScore(){ scoreText.text = "Score: " + score; } function IncreaseCount(){ score ++; updateScore(); } function GameOver(){ gameOver = true; gameOverText.text = "Game over!"; }
- Drag the text object to form a reference in
GameController
object. - Update the
DestroyByContact
with:if (other.tag == "Player"){ Instantiate(playerExplosion, other.transform.position, other.transform.rotation); gameController.GameOver(); }
- Create an empty game object and rename it to
- Deploy the game:
- File -> Build settings
- Web Player
- Build