Video Explanation Of Game

A quick video that shows some game play and explains the main game mode of the game.


 

 

Advertisements

Feedback and Subsequent Changes

This post shows the feedback received by the studio, the results of which are available here Studio Feedback.

Feedback

Question 1. Did you enjoy playing the game?

chart1One hundred percent of people took the survey said they did indeed enjoy the game.

Question 2. Did you understand the game?

chart1

One hundred percent of people who took the survey said they did indeed understand that game.

Question 3. How do the physics feel?

Apart from being super sedative the game felt thrilling and really intense, with a decent upbeat track this would an amazing experience.

very difficult first, but very immersive.

The physics feel floaty, but at the same time the car feels like it has some weight to it when attempting to rotate or move the car mid-jump.

I felt the games physics felt exactly how it should.

Floaty & Loose, during air time in the game I feel that the game was pulling me in a certain direction.

The physics were a little too floaty and uncontrollable during airtime. A more sudden response to air alteration would be better in my opinion.

Overall the game played really well, and the physics were what I would’ve expected. It has a real ‘arcade’ type of feel and I think that’s great in those types of games.

Spectacular! The car felt like it had weight to it, and moved as one would expect such a vehicle to move.

Fluent, realistic, the front moves then the back follows, how a car should turn rather than the orient axis on the middle car on basic driving games.

Very fast and floaty which makes the game overall feel very arcade like.

The main comment that the team received about the physics was that they felt tho floaty in the air, for this reason a slight change will be made the car physics in the air. Explained in a later section.

Question 4. How are the visuals?

Beautiful on some parts but a lot of the environment felt static and dull. The car was superb and maintained the focus as it should.

Very good.

I think the visuals are pretty appealing and eye catching and only add to the experience, giving the game a sort of neon-esque aesthetic.

The games visuals were impressive and too a high quality.

Was hard to notice the visuals with the lighting conditions, and the speed of the vehicle.

The car looks great.

Amazing. Definitely no problem there, added lighting effects adds to the overall visuals creating a very decent looking environment.

Visually it was very good, the car model itself looked well done and clearly had a lot of time put into it. The other textures looked fine, although it’s hard to judge when travelling at high speed.

Stunning, is all I can really say.

Looks good man, textures on the car are industry level.

Very good, although more scf if elements in the game such as Neon/Future buildings but game itself looks very good.

The team received good feedback about the visuals of the game, however comment mentioned that certain areas are dark and hard to see the other 3D art, for this reason I will add some more lighting to the scene.

Question 5. Suggest ways the game can be improved.

Responding the player to the track and making the car a lot less sensitive.

Alternative to WASD.

Personally I feel the issue is the learning curve of the game and would be better if the player is given an easier time as opposed to a tricky jump as the first obstacle.

The first jump needs to be improved so that the player can land in the area a large sum easier.

Auto Restart on Crash or prompt, waited a while before realising that I needed to force the restart.

– More variety of tracks – More car varieties.

Play-test more, get an idea of where things are going wrong for players and change those aspects accordingly.

I feel the view port can be a little small at times, when it comes to making perfect maneuvers it can become difficult to see.

More car variations.

Ghost mode (after you completed the best time).

The studio received a number of comments about the difficulty of the game spefically the first jump. For this reason the start location will be changed, so that the hardest jump in the game it not first. More over the studio received feedback that an alternative control system would help improve the play ability of the game.

Question 6. Did you find any bugs?

I was able to fly off track and drive through terrain.

No

If you’re unlucky, you can get the car caught along the rim of the hoop you’re required to jump into. The game processes it as a crash if you recover from the collision.

Not in my game experience.

Car Flipping on Respawn.

– Collisions on the walls and especially in tunnel areas are buggy causing random crashes (car crashes not game crash) – Can go outside the map – UI text on loading screen overlaps in certain resolutions.

Not too many game-breaking, no. When travelling round a bend the car did mount the wall which seemed to slow me down, but the car stayed in the same up-right position, it didn’t tilt with the wall.

When going through a ramp further into the game, upon boosting out of the end I immediately hit a large boulder that I couldn’t have avoided.

Nope!

Yeah, when you re-spawn in bonnet view, it re spawns upside down.

You can reset more than once by mashing the R button.

After receiving a number of bug reports, the studio has implemented a number of fixes to  correct the bugs. These fixes are explained in further detail below.

Changes per Feedback.

Question 3.

Feedback about the games physics where overall positive, however a common theme emerged. Many people reported the car felt ‘floaty’ in the air. To combat this I created a script that changes various physics values such as drag on the car, whenever it it not grounded.

To further decrease this ‘floaty’ feeling in the air, I now allow the player to have some control of the car when it is flying. Whenever the car is  not flying normal controls are applied, however whenever the car is flying a secondary set of controls are used which allow the player to rotate the car but not accelerate.

Question 4.

The visuals of the game overall also received good feedback. However again common themes can be extruded from the data. The most common of which was that it was hard to see the environmental art because of the speed of the game, although slowing the game down is not an option in my opinion I could make the environmental assets larger in the future thus making them more obvious at high speed.

As well as making the environmental assets larger, I also need to add more lights into the scene. Although many parts of the track are lit up, many parts of the back ground enviroment are in complete darkness.

Question 5.

The most common feed back as to how the game could be improved the difficulty of the game. Although people commented on the difficulty of the game I do not think that it had anything to do with mechanics of the game but instead the level design. Especially the first jump that the player comes to.

To fix this I could take multiple paths, such as removing the ramp all together. However I think a smarter solution would be to simply move the start finish line so that this particular difficult jump is not the first one the player has to negotiate.

Another common theme reported in the feed back was the request for an alternative control scheme because using the WASD buttons was very ‘jerky’. For this reason I have programmed into the game the ability to use a controller instead of mouse and keyboard.

Question 6.

The studio received a number of bug reports in question 6, this section explains how the studio went about fixing those bugs.

Many players reported that they could leave the track by simply driving off it, for this reason many areas have had fences and other collision objects to stop the player leaving the track.

Resetting the car also had a bug whereby the player could reset the car multiple times by repeatedly pressing the reset button. For this reason a simple check was put in place whereby the player cannot reset the car, if the car is already going through the reset cycle.

Also reported was the crash system seemed to sensitive, player reported that they crashed when they felt that it should not have been a crash. For this reason the velocity difference required to initiate a crash was increased. The crash script also now checks the direction of the crash, so that if it is directly below the car a crash is not registered.

Programming the Main Menu

This post explains how the main menu of Veclicous racing works. The main menu scene is just a few props and a UI canvas, but I think the visual effect it quite powerful. Below is a video showing it functioning.

A screen shot of the main menu in the editor is shown below,

The only programming that is contained within the main menu scene is that programming which makes the main menu move and of course the logic to make the buttons function. I will first explain the movement of the main menu when a player ever the player clicks certain button.

The main menu is contained on a single canvas, so if this canvas is moved all of the buttons on the canvas will be subsequently  moved. This is essentially how the main menu of Velicous racing function, when the player presses a certain button, the canvas is moved directly up or down to reveal new buttons

The picture below attempts to illustrate the programming of the main menu.

mainmenu4

A screen shot of the in engine Editor also shows the setup.

mainmenu5

As usual I provide a flow char to help explain the later code.

New-Mind-Map (14)

Pasted Raw Code,

public class Main_Menu_Manager_Script : MonoBehaviour {

 public GameObject MenuHolder;
 public GameObject LoadingScreen;
 public Slider music;
 public Slider effects;
 private GameObject Target;

 void Start()
 {
  music.value = PlayerPrefs.GetFloat("MusicVolume");
  effects.value = PlayerPrefs.GetFloat("SoundEffectVolume");
 }

 void Update()
 {
  if (Target != null)
  {
   float distance = Vector3.Distance(MenuHolder.transform.position, Target.transform.position);
   MenuHolder.transform.position = Vector3.Lerp(MenuHolder.transform.position, Target.transform.position, 10 * Time.fixedDeltaTime);
   if (distance < 10)
   {
    Target = null;
   }
  }
 }

 public void ChangeSoundEffectVolume(float _value)
 {
  PlayerPrefs.SetFloat("SoundEffectVolume", _value);
 }

 public void ChangeSoundMusicVolume(float _value)
 {
  PlayerPrefs.SetFloat("MusicVolume", _value);
 }

 public void Quit()
 {
  Application.Quit();
 }

 public void MoveMenu(GameObject _target)
 {
  Target = _target;
 }

 public void ChangeLevel(int _level)
 {
  LoadingScreen.SetActive(true);
  SceneManager.LoadScene(_level);
 }

 void OnEnable()
 {
  LoadingScreen.SetActive(false);
 }

 public void ClearSave()
 {
  PlayerPrefs.DeleteAll();
 }
}

Programming Custom Car Effects

This post covers the custom effects that take place on the car such as particle effects, emission changes etc…

All custom effects are contained within one script placed on the player’s car object.

Fake Body Rotation

When the player turns the car, the cars body ‘rotates’ from side to side much like a aircraft banking around a corner. In Velicous racing this body roll is completely  cosmetic and only in place for visual feedback.

The body roll method uses the players input and a constant roll value to create a scalar value that ranges from zero to maximum roll. This value is then converted to a quaternion rotation to avoid gimbal lock problems, finally the quaternion value is apply to the transform by multiplying it with the cars current rotation quaternion.

Video showing the bank angle working, notice how the car rolls as it turns(the effect have been amplified for this video to make it more obvious).

Code Snippet,

//'Bank' the body
private void FakeRotation(float _horizontalInput)
{
 float _rotation = MaxBankAngle * _horizontalInput;
 Quaternion _QRotation = Quaternion.Euler(new Vector3(0, 0, -_rotation));
 VehicleGraphics.transform.rotation = transform.rotation * _QRotation;
}

Engine Exhaust

The engine exhaust method is responsible for controlling the direction of the exhaust particle system and controlling its rate of emission.

The method itself works much like the body roll method in that it takes in the players input and outputs quaternion rotation, which is eventually used to control the direction of the exhaust.

The method does however carry out some other checks, the first of which is to check if the player is grounded. This is because the player cannot use the engine when they are grounded, thus the emission rate should be set back to default whenever the car is not grounded no matter the players input.

The other check that this method carries out is to check whether the player is boosting or not, this is because a secondary particle system is fired whenever the player is boosting to give some extra visual feedback.

Code Snippet with comments to explain,

private void EngineExhaust(float _horizontalInput, float _verticalInput, bool _grounded, bool _boosting)
{
 //Calculate a rotation based on players input
 float _rotation = -MaxExhaustAngle * _horizontalInput;
 //Loop through all of the engine particle system, if there are multiple engines
 for (int i = 0; i < EngineParticleSystems.Length; i++)
 {
  //Check if the player is boosting
  if(_boosting == true)
  {
   //Activate Boost Particle Effect
   BoostParticleSystems[i].Play();
  } else
  { //De-activate Boost Particle Effect
   BoostParticleSystems[i].Stop();
  }
  //Create a quaternion
  Quaternion _QRotation = Quaternion.Euler(new Vector3(0, _rotation, 0));
  //Apply the rotation to all particles in the loop
  EngineParticleSystems[i].transform.rotation = transform.rotation * _QRotation;
  //Create a temporory copy so we can adjust the private value emission
  ParticleSystem temp = EngineParticleSystems[i];
  //Adjust the value
  var tempEm = temp.emission;
  //Check if we are grounded
  if (_grounded == true)
  {
   //We are grounded set a emission rate based on input
   tempEm.rateOverTime = storedEmission + (MaxExhaustEmission * _verticalInput);
  } else
  {
   //Not grounded default emission
   tempEm.rateOverTime = storedEmission;
  }
  //Apply the new emissions
  EngineParticleSystems[i] = temp;
 }
}

Brake Lights

The brake light method is responsible for changing the emission map whenever the player braks,  because some parts car model share emission maps simply increasing the value of emissions would cause other parts of the car to glow and give undesired results. For this reason instead I instead swap the entire material for certain parts of the car, this secondary material has the higher emission value which is required. The swap is contained within a loop just so that multiple break lights can be added.

private void BrakingLights(float _verticalInput)
{
 if(_verticalInput < 0)
 {
  foreach(GameObject light in Lights)
  {
   light.GetComponent<MeshRenderer>().material.SetTexture("_EmissionMap", brakingEmssionMap);
  }
 } else
 if (_verticalInput >= 0)
 {
  foreach (GameObject light in Lights)
  {
   light.GetComponent<MeshRenderer>().material.SetTexture("_EmissionMap", defaultEmssionMap);
  }
 }
}

Sparks on Collide Method

Finally the sparks on collide method is a simply method that is activated each time the players object collides with another object, a flow chart explains the logic.

New-Mind-Map (15)

And the code,

private void OnCollisionEnter(Collision collision)
{
 foreach(ContactPoint contact in collision)
 {
  SparksOnCollide(contact.point, contact.normal);
 }
}
private void SparksOnCollide(Vector3 _position, Vector3 _rotation)
{
 bottomOutSparks.transform.position = _position;
 bottomOutSparks.transform.rotation = Quaternion.Euler(_rotation);
 bottomOutSparks.time = 0;
 bottomOutSparks.Play();
}

Unity 5.6 Post-processing

Unity 5.6 has a bran new post-processing system, for this reason I have decided to dedicate a post to it.

Before Unity 5.6, post processing required the developer to attach a number of scripts to the camera, these scripts had to be placed in the correct order for the post processing to work correctly. This often caused confusion and undesirable results, for this reason Unity’s new post processing on one component, which takes a profile as its input. Adding the new post processing component to the camera is illustrated below.

Once the post profile is created and connected to the camera changing the post profile before run time is as easy editing the profile within Unity by double clicking it.

One thing to note is that many of the new post-process effects are designed to work in a differed lighting pipeline. The post process profile contains effects ranging from bloom to screen space real-time reflections. More information can be found about the new post processing effects in Unity’s official quick start guide.

Unity’s Post Processing Manual

Below are two images, one with post process enabled and the other without.

Programming Checkpoints

Explained in the Time manager post was the use of checkpoints in the game, as players pass through checks points in time trail mode they are given an amount of extra time and given a reset point in case they crash and need to reset the car.

Before giving the player extra time however, it is vital that the check point ensures the player is travelling in the correct direction. If for example the player travels through the checkpoint backwards they should not be awarded the check point. This check is done through a dot product check.

Once the checkpoint has ensured that the player is traveling in the correct direction, it can access the player’s time manager script and award extra time. As well as accessing the time manager script, the checkpoint script also access the player’s  reset script (used to reset the car after a crash) to save the check point as a reset point.

New-Mind-Map (13)

The raw script is pasted below,

public class CheckPoint_Script : MonoBehaviour
{
 private Vector3 checkPointDirection;

 void Start()
 {
  checkPointDirection = transform.forward;
 }

 void OnTriggerEnter(Collider other)
 {
  //Check the obkects tag
  if(other.transform.tag == "Player")
  {
   //Look for the reset script
   ResetCar resetCar = other.GetComponent&amp;lt;ResetCar&amp;gt;();
   if (resetCar != null)
   {
    //Check the player has not passed through the checkpoint twice
    if (resetCar.LastPassedCheckPoint != this.gameObject)
    {
     //Pass this check point as the last past checkpoint
     resetCar.LastPassedCheckPoint = this.gameObject;
     //Look for a rigid body component
     Rigidbody rigid = other.GetComponent&amp;lt;Rigidbody&amp;gt;();
     if (rigid != null)
     {
      //Check the players velocity against the direction of this checkpoint
      //If the value is above 0, the player is traveling in the correct direction
      if (Vector3.Dot(rigid.velocity, checkPointDirection) &amp;gt; 0)
      {
       //Look for the players time manager script
       Player_TimeManager_Script timemanager = other.GetComponent&amp;lt;Player_TimeManager_Script&amp;gt;();
       if (timemanager != null)
       {
        //Activate the checkpointpassed method
        timemanager.CheckPointPassed();
       }
      }
     }
    }
   }
  }
 }
}

Programming the Time Manager

Velicous racing has a time trail mode(endurance) inside of it, for this game made to work a manger is needed that records the players lap information and time remaining. The lap information will be stored so it can be viewed later, while if the time remaining variable reaches zero the game is over.

The time manager script itself is completely self contained for safety and relatively simple. It contains a timer which is used to record both the players lap time and the time remaining, it also contains some other variables such as how far the player has driven, the number of laps they have done and a check to see if they have passed a checkpoint.

The script is illustrated below,

New-Mind-Map (12)

As shown the script is essentially a timer, all other variables contained within it are arbitrary variables that are used to control whether the game is over or record the players performance.

The timer script also contains two methods, one is activated every time the player passes a checkpoint, this ‘checkpoint’ method adds some time to the players total remaining time and checks to see if the player has passed all of the checkpoints, if he has it is presumed he has completed a lap. Finally the checkpoint method talks to the player’s UI to give some visual feedback when they pass through a checkpoint.

The other method contained on this script is activated when all of the checkpoints in the level have been passed and thus a lap completed. This ‘lap’ completed method checks the players lap time to see if it is a personal best and updates the player preferences accordingly. It also resets the lap timer and adds to the lap count.

The entire script is pasted below for reference.

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

[RequireComponent(typeof(Player_UI))]
[RequireComponent(typeof(Player_Motor))]
public class Player_TimeManager_Script : MonoBehaviour {

 public static bool GameOver = false;
 private float distanceDriven = 0;
 public float DistanceDriven { get { return distanceDriven; } }
 private float bestDistance = 0;
 public float BestDistanceDriven { get { return bestDistance; } }

 private float timeLeft = 80;
 public float TimeLeft { get { return timeLeft; } }

 private int lapCount = 1;
 public float LapCount { get { return lapCount; } }

 private float lapTimeSeconds = 0;
 private string formattedLapTime = "00 : 00";
 public string FormattedLapTime { get { return formattedLapTime; } }

 private float bestLapSeconds = 0;
 private string formattedBestLapTime = "00 : 00";
 public string FormattedBestLapTime { get { return formattedBestLapTime; } }

 private int numberOfCheckPointsPassed = 0;
 private int numberOfCheckPointsInLevel;

 private Player_UI playerUI;
 private Player_Motor playerMotor;

 void Start()
 {
  GameOver = false;
  playerUI = GetComponent<Player_UI>();
  playerMotor = GetComponent<Player_Motor>();
  numberOfCheckPointsInLevel =
  GameObject.FindGameObjectsWithTag("Checkpoint").Length;

  bestDistance = PlayerPrefs.GetFloat("BestDistanceDriven");
  bestLapSeconds = PlayerPrefs.GetFloat("BestLapSeconds");
 }

 void Update()
 {
  if(numberOfCheckPointsPassed != 0)
  {
    timeLeft -= Time.fixedDeltaTime;
    lapTimeSeconds += Time.fixedDeltaTime;
    FormatLapTime(lapTimeSeconds);
    if (GameOver == false)
    {
      int speed = Mathf.RoundToInt((playerMotor.ForwardSpeed() / 2));
      distanceDriven += speed * Time.fixedDeltaTime;
    }

    if(distanceDriven > bestDistance)
    {
      bestDistance = distanceDriven;
    }
  }

  if(timeLeft <= 0)
  {
    GameOver = true;
    if (bestLapSeconds != 0 && bestLapSeconds < PlayerPrefs.GetFloat("BestLapSeconds"))     {       PlayerPrefs.SetFloat("BestLapSeconds", bestLapSeconds);     }     if(bestDistance != 0 && bestDistance >
    PlayerPrefs.GetFloat("BestDistanceDriven"))
    {
      PlayerPrefs.SetFloat("BestDistanceDriven", bestDistance);
    }
  }
}

private void FormatLapTime(float _seconds)
{
  string minutes = Mathf.Floor(_seconds / 60).ToString("00");
  string seconds = Mathf.Floor(_seconds % 60).ToString("00");
  formattedLapTime = minutes + " : " + seconds;
}

public void CheckPointPassed()
{
  timeLeft += 10;
  numberOfCheckPointsPassed += 1;
  if(numberOfCheckPointsPassed > numberOfCheckPointsInLevel)
  {
    numberOfCheckPointsPassed = 1;
    LapComplete();
  }
  playerUI.StartCoroutine("FlashCheckPoint");
}

private void LapComplete()
{
  if(lapTimeSeconds < bestLapSeconds || bestLapSeconds == 0)
  {
    bestLapSeconds = lapTimeSeconds;
    string minutes = Mathf.Floor(lapTimeSeconds / 60).ToString("00");
    string seconds = Mathf.Floor(lapTimeSeconds % 60).ToString("00");
    formattedBestLapTime = minutes + " : " + seconds;
  }
  lapTimeSeconds = 0;
  lapCount += 1;
}
}

Unreal Engine Experimentaions

Intro

This post shows some experimentation I undertook using the Unreal Engine. I decided to experiment with the Unreal engine because I  wanted to ensure using Unity was the best choice for me in terms of creating this game.

Because I am focusing on programming, I would create a racing AI system within Unreal. This would not only familiarize me with the Unreal engine but will also give me some more programming experience.

Production Log

A full production log of the experimentation can be found in this forum post.
Unreal Engine Experimentation Log
The final results of this experimentation can be found at the bottom of this post.

Summary

The primary I encountered with the Unreal engine was the programming languages it accepted. Unreal accepts C++ as its programming language and as I currently work in C# or Java. Learning C++ while trying to create the game at the same time would not be the most optimal path. Although learning C++ is something I plan on doing in the near future.

Unreal does have a secondary visual programming system called blueprint and this is what I used for my experimentation to create the AI. Blueprint programming is very intuitive for small operations and the visual system is very easy to debug. However, for me blueprints have more draw backs that positives. For large systems they quickly become cluttered as shown below, moreover because some background operations are undertaken to decode blueprints they generally use more overhead than scripts created code.

picture

The out of the box visuals including in the Unreal Engine are very rewarding and generally look better than Unity, however with Unity’s new 5.6 post-processing and with some tweaks to the engine similar results can be acheived.

Final Results

 

Modeling and Texturing the Checkpoint

This post follows the process I take to model and texture objects within Velicous Racing.

The process begins in Maya, I first block out the basic shape using the existing track block out I have saved in Maya.

mayh1

Once the basic block out is complete I move the checkpoint into its own file so I can finalize the shape and add fine details. At this point I also UV map the blockout, this is so that if I decide to bevel an edge or champfer a corner the UV map is update automatically. I also UV map the sign portion of the checkpoint separately so a tile-able texture can be used.

Now the UV mapping is done i begin to add basic low poly details, such as edge bevels.

maya14

With the basic low poly finished I move on to creating the high poly model so it can be baked for normal maps, so that the edges of the low poly model display correctly. More details will be added to the normal map later using the Quixel Suite.

maya15.jpg

With both the low and high poly models created I go ahead and create a cage for both parts of the model, because only one part of the model(not the flat screen) needs normal maps I will be baking that part of the model only

With the basic normal and ambient occlusion maps baked, I can bring the model into the Quixel suite, specifically so that I can use NDo to create a more detailed normal map.

ndo#

Above model loaded into Ndo with no adjustments made to normal map.The video below shows me created a more detailed map for the prop using NDo.

Once the more detailed normal map is created it is time to move the prop into DDo within the suite so that the other texture maps can be created. Displayed below all the maps that DDo requires in order to operate properly.

The video below show my process of creating the other texture maps within Quixel.

With the prop textured I can import the asset into Unity and assign the various materials. Because I keep the emissive map completely back and white I can now adjust this value in the editor which is how i am able to change the color of the checkpoint to suite its surroundings.

The final step in creating the checkpoint is to animate the lettering, because I have created the object in two parts with two UV maps I can simply offset the texture through script which will give it the appearance of moving. The video below illustrates this,

 

Create a free website or blog at WordPress.com.

Up ↑