Friday, 23 May 2014

Current Build

This is the current build of what i have. It is not as well presented as other game i have made in the past, but my main focus on this game was to try out different code and find a way of integrating that into the game. As a result, more effort when into working on the code rather than the art and sounds. Currently, if you complete the 3rd level, it takes you back to the main menu, but without adding a fail to the stats.


The Current Build


The game takes a little longer to load after the preloader has finished.

Custom Preloader

I've had a look on Unity Docs and Unity Answers on how to achieve this. However ' Please note that modifying the loader images is only possible with Unity Pro.' - Unity Docs. I dont own a pro license or know any one who does. I will have to have a look at a preloader at a later date.


Customizing the Unity Web Player loading screen

Adding Sound

To begin with, i looked at Unity Answers to find a way of adding sound into the game. I found out how to start and stop sound, ways to loop sound from certain points and trace whether or not the sound was playing. However, i could not figure out how to get the sound file with the project window into the AudioSource variable. After a little digging on the Unity Docs site i found out that you needed to add an Audio Source Component. Unity Docs suggested to create an empty game object for this. This component can then be dragged into the AudioSource variable space in the inspector.
I wanted to have music on the main menu, but i wanted the music to continue if you changed between the main menu and the stats screen, but not when the games are being played. So i created an empty game object on the first scene (the one that never gets returned to) and wrote the following bit of code:

public var PlayMusic : AudioSource;
private var LoopReady : boolean = false;

function Start ()
{
 DontDestroyOnLoad(gameObject);
}

function Update ()
{
 if(Application.loadedLevel == 1 || Application.loadedLevel == 2)
 {
  if(!PlayMusic.isPlaying)
  {
    if(!LoopReady)
   {
     PlayMusic.Play();
     LoopReady = true;
   }else{

    PlayMusic.time = 16.55;
    PlayMusic.Play();
   }
  }
 }else{

  if(PlayMusic.isPlaying)
  {
   PlayMusic.Stop(); 
   LoopReady = false;
  }
 }
}


When the scene is loaded the game object is created but doesnt get destroyed when the scene changes. Next, when the game loads up the main menu (LoadedLevel 1), if the music is not already playing and if it is the first time it is being played, start playing the music from the beginning. When the music stops playing (track ends), the music is played again but from 16.5 seconds in which skips the intro part. If the game changes to a scene that is not the main menu or the stats screen and the music is still playing, it stops and waits for the game to change back to the main menu again.

I wanted to experiment with a different way of coding the music for the games. This is the code i ended up writing:

var PlayMusic : AudioSource;
var currentObject: GameEngine = null;

function Start ()
{
  if(currentObject == null)
  {
   currentObject = GameObject.Find("GameEngine").GetComponent(GameEngine);
  }
  if(currentObject.GamesCompleted == 0)
  {
   DontDestroyOnLoad(gameObject);
   PlayMusic.time = 62.00;
   PlayMusic.Play();
  }
}

function Update ()
{
  if(Application.loadedLevel == 1)
  {
   Destroy(gameObject);
  }

  if(!PlayMusic.isPlaying)
  {
   PlayMusic.time = 62.00;
   PlayMusic.Play();
  }
 
}

This gameobject gets added in on the game loader screen. However, the issue is that the game returns frequently to the game loader screen. So to stop the game from having several music tracks being played over the top of each over, i have the file trace to see if any games have been played yet, if no games have been played, it becomes active otherwise it stays inactive until the scene changes and it is removed. If the game changes back to the main menu, the game object is removed.

On the first pass of the code, because the music is one big file, i tried changing the music based on what game was playing. However, because the games are only 10 seconds or less, it sounded like a mess, so i changed it to this code.

The sound file was created in MilkyTracker by a fellow course mate Scott Taylor.

Tuesday, 20 May 2014

Camera Controls

I came across some simple camera control code on Unity Answers and wanted to have a go and expand upon it.

To begin with, i wanted to rotate the camera around a point and be able to zoom in and out.

The easiest way that i have figured out to rotate an object around a point, is to have the object you want to rotate inside another gameobject and rotate that gameobject.

To start with:

private var to : Quaternion;

private var ToX:float;
private var ToY:float;

public var TheCamera:GameObject;

function Start ()
{
  to = Quaternion.Euler(0,0,0);
  ToX = 0;
  ToY = 0;
}

function Update ()
{
  //this turns a vector3 into a Quaternion.

  to = Quaternion.Euler(ToY,ToX,0);
  //this uses the Quaternion and lerps its current position to it.
  transform.rotation = Quaternion.Lerp(transform.rotation, to, Time.time * 0.05);
}

 

From Unity Answers, i found out how to trace if the mouse had moved. But only when the left mouse button was pressed.

if(Input.GetAxis("Mouse X")<-0)
{
  ToX -= 2;
}

if(Input.GetAxis("Mouse X")>0)
{
  ToX += 2;
}

if(Input.GetAxis("Mouse Y")<-0)
{
  ToY += 1;
}
if(Input.GetAxis("Mouse Y")>0)
{
  ToY -= 1;
}


This changes what rotation X and rotation Y the camera needs to lerp to.

Tracing the mouse wheel is the same as tracing X and Y movement, but i wanted to limit how far the camera could zoom in and out.

if(Input.GetAxis("Mouse ScrollWheel")<0)
{
  if(TheCamera.camera.fieldOfView < 100)
  {
   TheCamera.camera.fieldOfView += 4;
  }
}

if(Input.GetAxis("Mouse ScrollWheel")>0)
{
  if(TheCamera.camera.fieldOfView > 10)
  {
  TheCamera.camera.fieldOfView -= 4;
  }
}


I then placed this all inside a if(Input.GetMouseButton(1)) so it was only being traced when the left button was being pressed.

Lastly i looked up a way of locking the cursor to the screen and hiding it. This stops you from accidentally clicking outside of the screen. It is as simple as:
Screen.showCursor = true/false;
Screen.lockCursor = true/false;


Next i placed one of my models in the center to test the code out. Initially i used a lerp to animate the ship hovering, but it was too sharp on the start points. So i wrote a little code that works like a pendulum:

private var MoveUp : float = 0.001;

function Update ()
{
  transform.position += Vector3(0,MoveUp,0);

  if(transform.position.y < -0.002)
  {
   if(MoveUp < 0.001)
   {
   MoveUp += 0.000005;
   }
  }else if(transform.position.y > 0.002){
   if(MoveUp > -0.001)
   {
    MoveUp -= 0.000005;
   }
  }
}


End Result:
Camera Test

Friday, 16 May 2014

Texture Scale and Offset

In Unity3d, when an object has a material applied to it, if you change the scale of that object, the material keeps the 1:1 scale on the object.

One of the prototypes that i was working need to have objects of different sizes showing the same texture, but having the texture showing the same resolution. So to save having to make 10+ different sizes of the same image, i had a look to see if i could make a little code to solve this.


I had a look on Unity answers (http://answers.unity3d.com) to see if any one had come up with a solution to the problem. i found one post on the subject, they suggested:

renderer.material.mainTextureScale = Vector2 (#,#);

This put me on the right track. However, unity works from the bottom left, so when i scaled the texture, it scaled it from this point rather than the center. 




After a little more digging on Unity Answers, i found another post about a different problem and they suggested:

renderer.material.mainTextureOffset = Vector2 (#,#);


Now i just need to work out a algorithm for how much the texture need to be off set depending on the objects scale and the material scale.


Ill skip all the trial and error part and explain the end result:

private var TexScale:float;
private var TexOffSetPrem:float;
private var TexOffSet:float;

function Start ()
{
  TexScale = transform.localScale.x;
  TexOffSetPrem = (transform.localScale.x - 1) * -10;
  TexOffSet = (Mathf.Round(TexOffSetPrem * 5)) /100;

  renderer.material.mainTextureScale = Vector2 (TexScale,TexScale);
  renderer.material.mainTextureOffset = Vector2 (TexOffSet,TexOffSet);
}


To begin with, 'TexScale' looks at the object's scale and adjusts the scale of the texture accordingly. Beacuse X scale and Y scale are the same, there was no need to look at both. 

Next, to make things easy, i stated that when the object had a scale of 1, the material also had a scale of one. After that, i then worked out that for every +/- 0.1 that the scale differed, the material's offset needed to be adjusted by +/- 0.5 respectively.

There was some issues with multiplying decimals, so i converted it into whole numbers then back to decimals to be used in the off set function.

This little bit of code gave me the desired result.
 

Friday, 9 May 2014

Global Scripts

Im not sure what to really call them, but they are scripts that are loaded into the first scene and stay throughout the whole game. I am using them to store the player's progress through the whole game, games played, difficulty level etc.

They are created by adding 'DontDestroyOnLoad(gameObject);' in a script file that is created on the first scene. This means that when the scene changes, this object does not get removed from the game. One thing to note is that they are best created on a scene that the game never returns to. Otherwise, the game will create another copy each time that scene is reloaded.

The code that i am using to get other scripts to edit or read from the Global is:

private var VarName: ScriptName= null;

function Update ()
{

 if(VarName == null)
 {
   VarName= GameObject.Find("ObjectName").GetComponent(ScriptName);
 }


From my understanding,
'VarName' can be named anything so it is different.
'ScriptName' is the name of the script you are trying to access.
'ObjectName' is the name of the gameobject that the script is attached to.

Then to read or edit the scripts vars it is as simple as VarName.VarYourInterstedIn.


Because this script is loaded on the first scene and not in the current scene, i have to use this method of doings things as opposed to just dragging the gameobject into the inspector.



Ping Pong

Ping pong is similar to a lerp function, except they bounce between value A and value B until they are told to stop.

From unity3d.com, their example is:

function Update ()
{
transform.position = Vector3(Mathf.PingPong(Time.time, 3), transform.position.y, transform.position.z);
}


The gameobject's position is being constatly updated and 'Mathf.PingPong(Time.time, 3)' is constatly changing between 0 and 3. As a result, the game object will be moving back and forth.

For use in my game, i wanted to animate my menu title a little.

function Update ()
{
transform.localScale = Vector3(11 - (Mathf.PingPong(Time.time*2, 1)),2 + (Mathf.PingPong(Time.time, 0.5)),0.1);
}


In Layman's terms the objects scale is constantly being changed to (ScaleX, ScaleY, ScaleZ).

Where ScaleX is 11 and being subtracted by 0.0-1.0, ScaleY is 2 and being added by 0.0-0.5 and ScaleZ is 0.1.

This gives the effect of the title being squashed in and out .