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 .

Tuesday, 6 May 2014

Lerps

To begin with, i found lerps fiddly to work with, but i have got my head around them now. They are really useful for moving objects in 3D space. Before, if i wanted to move something through code and have it not instantly move there, i would write something like:

if(this.x < 500)

{
 this.x += 10;
}
//This wont actually work but helps to explains my point

Lerps are more efficient and easier to manage.

transform.position = Vector3.Lerp(StartPoint, EndPoint, Time.deltaTime * Speed);

The object moves from the StartPoint to the EndPoint, Time.deltaTime is the time frame and Speed is how quick the object moves.
StartPoint and EndPoint need to be Vector3s (or Vector2s with a Vector2.Lerp). The logical thing to would be to use the objects current position for StartPoint. The benefit of using a variable for the EndPoint it that it can then be edited outside of the current class.

Here is how i have been using lerps:

private var endpoint : Vector3

function Update ()
{
  if(transform.position != endpoint)
  {
   transform.position = Vector3.Lerp(transform.position, endpoint, Time.deltaTime * 10);
  }
}



transform.position != endpoint Means that the lerp is only active when the endpoint var has changed.

Another benefit that i have discovered with lerps over moving an object position by a set amount, the object will always finish at the endpoint even if the var changes before the object reaches it. for example if a player clicks too fast, I have had issues with objects not moving the full amount.

I struggled to get rotation lerps to work. I could not get the example from from Unity3d.com to work for me. Their exemple is:

var from : Transform; 
var to : Transform; 
var speed = 0.1; 
function Update () 

  transform.rotation = Quaternion.Lerp (from.rotation, to.rotation, Time.time * speed); 
}

But after looking at what other people were suggesting on the forums, i managed to get the same effect from the following lines of code.

private var to : Quaternion;

function Start ()

{
  to = Quaternion.Euler(0,-90,0);
}

function Update ()
{
  transform.rotation = Quaternion.Lerp(transform.rotation, to, Time.time * 0.1);
}

Drag and Rotate

In one of the prototypes i want to be able to drag and rotate a game object using the mouse. After some time googling the topic, i could not find anything on this topic or in a simple language that i could understand. The only thing that i could find helpful was rotate to face function and other code that had the same result.

This was semi-useful as this gave me a means to rotate and object. I could not get the rotate to face function to feel right so i complied bits of code from several forums, it looked something like this:

private var MouseDown = false;

function OnMouseDown()
{
  MouseDown = true;
}

function FixedUpdate ()
{
  if(MouseDown)
  {
   //Gets the mouse vectors
   var mousePos = Input.mousePosition;

   //Gets the mouse Position relative to the camera's Position
   var lookPos : Vector3 = Camera.main.ScreenToWorldPoint(mousePos);

   //Creates an angle value
   var angle = Mathf.Atan2(lookPos.y, lookPos.x) * Mathf.Rad2Deg;

   //Rotates Object along Z axis using new value.
  transform.localEulerAngles -= new Vector3(0,0,angle);

  }
}

function OnMouseUp()
{
MouseDown = false;
}

This worked to a degree. When i clicked and dragged the mouse, the object would rotate. However, the object would snap and point to the mouse when the mouse was clicked. This was a problem as i only wanted it to rotate when the mouse was moved. Thus, to achieve what i wanted, i had to figure out a way for it to add rotation rather than rotating to the mouse.

On a side note:
var angle = Mathf.Atan2(lookPos.y, lookPos.x) * Mathf.Rad2Deg;
Only works because the objects were in the center of the screen. To rotate objects that are off center:
var angle = Mathf.Atan2(lookPos.y - transform.position.y, lookPos.x - transform.position.x) * Mathf.Rad2Deg;
This uses the center of the object rather that the center of the screen.

Firstly i tried to add rotation to the object when the mouse moved using the OnMouseDrag function. However the issue was that rotation was added every time the mouse was moved rather that how much the mouse was moved, this meant that the object would rotate too slow or too fast.

The solution to the problem was to use two angles and have the first one check against the second, if there was a difference, rotate by the difference. The final code looked like this:

private var Angle : float;
private var Oldangle : float;
private var Difference: float;
private var MouseDown = false;

function OnMouseDown()
{
 // This creates an angle from where the position of mouse is clicked.
 var mousePos = Input.mousePosition;
 var lookPos : Vector3 = Camera.main.ScreenToWorldPoint(mousePos);

 //This stores the first angle
 Oldangle = Mathf.Atan2(lookPos.y, lookPos.x) * Mathf.Rad2Deg;

 MouseDown = true;
}

function FixedUpdate ()
{
 if(MouseDown)
 {
  //This creates a second angle based on the mouse’s current posistion
  var mousePos = Input.mousePosition;
  var lookPos : Vector3 = Camera.main.ScreenToWorldPoint(mousePos);
  Angle = Mathf.Atan2(lookPos.y, lookPos.x) * Mathf.Rad2Deg;

  //This creates a value that the object will rotate by.
  Difference = Oldangle - Angle;

  //Traces if the mouse has moved
  if(Difference != 0)
  {
   //Rotate the object by the difference
   transform.localEulerAngles -= new Vector3(0,0, Difference);

   //Now the differenace has been added,
   //Change the starting angle to the current angle
   Oldangle = Angle;
  }
 }
}

function OnMouseUp()
{
 MouseDown = false;
}

This works exactly how i wanted it to. In layman's terms, when the mouse is clicked a new angle is created. Then in the update function a second angle is created and constantly updated. The second angle is checked against the first to see if they are the same, if they are not, rotate the object by the difference of the two angles. After the difference has been added, update the first angle using the mouse’s new position. Repeat until the mouse is released.

I could not find any drag and rotate code online so the second part of the code was my own work. The first part was a result of looking at several different ways of calculating angles.

On a side note, i learned that you can find an object’s rotation by printing transform.rotation. But to change an object’s rotation you need to use transform.localEulerAngles = new Vector3(#,#,#).

Game Update

The game is now currently at MVP. It has what i planned MVP to have at the beginning of the year,  a main menu, stats and 3 mini games. The games have basic art on them and the menus have some basic art and animations.

The reason why the game is not further than MVP is not because i have struggled with unity code, but because i have been swamped with my other modules, mostly with the design master class module. However, for the design master class module i was coding prototypes in unity, so while i was not making any progress on the game, i was still learning new code. I can not show any of the work because i signed an NDA (Non-disclosure agreement). However, i will rewrite some code showing what i have learned from this module.