florisvdaa

Floris van der Aa

Floris transparent logo

Night Racer

What is Night Racer?

For this project, we needed to create a racing game. Together with another developer and three artists, we created this race game. For this project we got 5/6 weeks, we decided to make a cyberpunk like race game. Where you drive a hover craft over a high speed sci-fi track. Our goal was to polish the game even more, but unfortunately we did not have enough time to do so.

Hovercraft movement


using UnityEngine;

public class CarMovement : MonoBehaviour
{
    [Header("Ship Handeling")]
    [SerializeField] private float accelaration = 50f;
    [SerializeField] private float maxSpeed = 200f;
    private float oldMaxSpeed;
    [SerializeField] private float maxBoost = 300f;
    [SerializeField] private float brakeSpeed = 70f;
    [SerializeField] private float turnSpeed = 10f;
    [SerializeField] private float airTurnSpeed = 3f;
    private float input = 0f; // horizontal axis
    [SerializeField] private float currentSpeed;
    private bool isDead;

    private bool canBoost;

    [Header("Hovering")]
    [SerializeField] private LayerMask groundLayer; // layer we can hover on
    private Rigidbody rb;
    private Vector3 prevUpDir; // stores transform.up
    [SerializeField] private float hoverHeight = 3f; // distance from the ground
    [SerializeField] private float heightSmoothing = 10f; // how fast the ship will readjust to "Hoverheight"
    [SerializeField] private float normalRotSmoothing = 50f; // how fast the ship will adjust its rotatioin to match ground normal
    private float smoothY = 1f;
    [SerializeField] private float startDelay = 0.2f;
    [SerializeField] private bool isGrounded;
    [Header("dropping")]
    [SerializeField] private float dropOffTime = 0.2f;
    private bool isDroppingOff;
    private float oldDropOffTime;
    [SerializeField] private float rotationLerp = 7f; // how fast we will rotate towards the ground if the ship is in the air
    [SerializeField] private float positionLerp = 5f; // how fast ship will sink down if is in the air
    private float oldPositionLerp;
    private bool dropOffForceAdded;

    private void Start()
    {
        // ship handeling
        currentSpeed = 0f;
        oldMaxSpeed = maxSpeed;
        canBoost = true;
        isDead = false;
        //hovering
        rb = GetComponent<Rigidbody>();
        //rb.useGravity = false;
        isGrounded = true;

        //dropping
        isDroppingOff = false;
        oldDropOffTime = dropOffTime;
        oldPositionLerp = positionLerp;
        dropOffForceAdded = false;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            DropOff();
        }

        if (startDelay >= 0f)
        {
            startDelay -= Time.deltaTime;
        }

        input = Input.GetAxis("Horizontal");

        if (startDelay <= 0)
        {
            DrivingBehaviour();
        }
        
    }

    private void DrivingBehaviour()
    {
        if (isDead)
        {
            //stops movement if dead
            currentSpeed = 0;
            rb.velocity = Vector3.zero;
            GetComponent<CarMovement>().enabled = false;
            transform.gameObject.isStatic = true;
            return;
        }

        if (isDroppingOff)
        {
            //can boost while on ground
            canBoost = true;

            //input
            if (HasInput())
            {
                // moving
                if (!IsBoosting())
                {
                    maxSpeed = oldMaxSpeed;
                    Accelerate(accelaration);
                }
                else
                {
                    maxSpeed = maxBoost;
                    Accelerate(accelaration * 2);
                }

                if (currentSpeed >= maxSpeed)
                {
                    Brake(brakeSpeed);
                }
            }
            else
            {
                // no input? brake
                Brake(brakeSpeed);
            }

            // set velocity to zero if not moving and if not dropping off
            if (currentSpeed <=0f && isGrounded)
            {
                rb.velocity = Vector3.zero;
            }
            // get current up dir for fixedupdate calculations
            prevUpDir = transform.up;
        }
        else
        {
            // if dropping off stop boosting
            canBoost = false;

            //add little force to velocity once we take off
            if (!dropOffForceAdded)
            {
                rb.velocity += new Vector3(0, 250, 0);
                dropOffForceAdded = true;
            }
            //start brake, so we cant fly in the air
            // also decrease brakesped a little amount so we can glide a little longer
            Brake(brakeSpeed / 1.5f);

            // measure time we are in the air
            dropOffTime -= Time.deltaTime;
        }
    }

    private void FixedUpdate()
    {
        if (isDead) 
        {
            return;
        }

        // turning
        if (IsTurning())
        {
            if (isDroppingOff)
            {
                TurnShip(airTurnSpeed);
            }
            else
            {
                TurnShip(turnSpeed);
            }
        }
        else
        {
            // if we dont turn, then stop the angular rotation
            rb.angularVelocity = Vector3.Lerp(rb.angularVelocity, Vector3.zero, 0.05f * Time.deltaTime);
        }

        // if we are not in the air
        if (!isDroppingOff)
        {
            //normal alignment
            RaycastHit hit;
            if (Physics.Raycast(transform.position, /*-prevUpDir*/ Vector3.down * 10, out hit, 10f, groundLayer))
            {
                // we hit the groudn
                isGrounded = true;
                //smooth new up vector
                Vector3 desiredUp = Vector3.Lerp(prevUpDir, hit.normal, Time.deltaTime * normalRotSmoothing); // fixedDeltaTime??\
                //get the angle
                Quaternion tilt = Quaternion.FromToRotation(transform.up, desiredUp) * transform.rotation;
                // apply rotation
                transform.localRotation = tilt;
                // smoothly adjust height
                smoothY = Mathf.Lerp(smoothY, hoverHeight - hit.distance, Time.deltaTime * heightSmoothing); // fixedDeltaTime??

                smoothY = smoothY <= 0.01f ? 0f : smoothY;
                transform.localPosition += prevUpDir * smoothY;
            }
            else
            {
                // if we dont hit anything that means we are in the arir
                DropOff();
            }
        }
        else
        {
            //if we are in the air
            RaycastHit rotationHit;
            // rotate towards ground normal
            if (Physics.Raycast(transform.position, Vector3.down * 10, out rotationHit, groundLayer))
            {
                Vector3 desiredUp = Vector3.Lerp(transform.up, Vector3.up, Time.deltaTime * normalRotSmoothing); // fixedDeltatime?
                // get the angel
                Quaternion tilt = Quaternion.FromToRotation(transform.up, desiredUp) * transform.rotation;
                //apply rotation
                transform.localRotation = Quaternion.Lerp(transform.localRotation, tilt, rotationLerp * Time.deltaTime); // fixedDeltaTime?
                Debug.DrawRay(transform.position, Vector3.down * 10, Color.red);
                Debug.Log(groundLayer);
            }


            // only sink for a given amount ot time so we dont get extremely high values
            if (dropOffTime <= 0)
            {
                // increase lerp value over time so we sink faster and faster
                positionLerp += 10 * Time.deltaTime; // fixed delta time?
                //start sinking
                transform.localPosition += Vector3.down * 9.81f * positionLerp * Time.deltaTime; // fixed?
                RaycastHit hit;
                // check distance ground
                if (Physics.Raycast(transform.position, Vector3.down * 10, out hit, groundLayer))
                {
                    // if we are at our hoverheight againt
                    if (hit.distance <= hoverHeight)
                    {
                        dropOffTime = oldDropOffTime;
                        // reset bool for next dropoff
                        dropOffForceAdded = false;
                        positionLerp = oldPositionLerp;
                        //get back to the ground

                        Debug.Log("raycast" + hit);
                        DropOn();
                    }
                }
            }
        }

        // now actually move the ship
        MoveShip(currentSpeed * 80);
    }

    private void TurnShip(float speed)
    {
        rb.AddTorque(prevUpDir * speed * input * Time.fixedDeltaTime, ForceMode.Impulse);
    }

    private bool IsTurning()
    {
        return (Input.GetAxis("Horizontal") > 0 || Input.GetAxis("Horizontal") < 0);
    }


    private void Accelerate(float speed)
    {
        // if current speed is greater than max speed, only add 0f so we dont get faster and faster
        // else apply our acceleration
        currentSpeed += (currentSpeed >= maxSpeed) ? 0f : speed * Time.deltaTime;
    }

    private void MoveShip(float speed)
    {
        // move ship forard with current speed
        rb.velocity = transform.forward * speed * Time.fixedDeltaTime;
    }

    private void Brake(float brakeSpeed)
    {
        if (currentSpeed > 0)
        {
            //decrease only if it's greater than 0, so we dont start flying backwards
            currentSpeed -= brakeSpeed * Time.deltaTime;
        }
        else
        {
            //set speed to 0 if we cant brake anymore
            currentSpeed = 0;
        }
    }

    private bool HasInput()
    {
        return (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow));
    }

    private bool IsBoosting()
    {
        //only boost if we get correct input and we can boost
        return ((Input.GetKey(KeyCode.B)) && canBoost);
    }
    
    private void DropOff()
    {
        if (!isDroppingOff)
        {
            //not on the ground
            isGrounded = false;
            isDroppingOff = true;
        }
    }

    private void DropOn()
    {
        // on the ground again
        isGrounded = true;
        isDroppingOff = false;
    }


}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
public class LapHandler : MonoBehaviour
{
    [SerializeField] private int cpAmmount;

    public GhostHolder holder;
    public float lapTime;

    private void Awake()
    {
        PlayerPrefs.SetFloat("LastLap", 0);
    }
    private void Start()
    {
        lapTime = -3f;
    }
    private void FixedUpdate()
    {
        lapTime += Time.deltaTime;
        if (Input.GetKeyDown(KeyCode.O))
        {
            SaveToJSON();
        }
    }
    private void OnTriggerEnter(Collider other)
    {
        OnFinish(other);
    }
    private void OnFinish(Collider other)
    {
        if (other.GetComponent<KartLap>())
        {
            KartLap kart = other.GetComponent<KartLap>();

            if (kart.CheckpointIndex == cpAmmount)
            {
                kart.CheckpointIndex = 0;
                UIManager.Instance.UpdateLapCountUI();
                UIManager.Instance.UpdateCheckpointUI();
                SaveLastLapTime();
                SaveRecordTime();
                ResetLapTime();
            }
        }
    }
    private void ResetLapTime()
    {
        lapTime = 0;
    }
    private void SaveLastLapTime()
    {
        PlayerPrefs.SetFloat("LastLap", lapTime);
        UIManager.Instance.UpdateLastLapUI();
    }
    private void SaveRecordTime()
    {
        float recordTime = PlayerPrefs.GetFloat("RecordTime");

        if (recordTime == 0 || lapTime <= PlayerPrefs.GetFloat("RecordTime"))
        {
            PlayerPrefs.SetFloat("RecordTime", lapTime);
            UIManager.Instance.UpdateRecordLapUI();
            Debug.Log(PlayerPrefs.GetFloat("RecordTime"));
        }
    }
    public void SaveToJSON()
    {
        string ghostPos = JsonUtility.ToJson(holder.ghostPos);
        string filePath = Application.persistentDataPath + "/GhostPos.data";
        Debug.Log(filePath);
        System.IO.File.WriteAllText(filePath, ghostPos);
        Debug.Log("Ghost Saved");
    }
}
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;

public class UIManager : MonoBehaviour
{
    #region singleton
    public static UIManager Instance;
    #endregion

    [SerializeField] private TextMeshProUGUI lapCount;
    [SerializeField] private TextMeshProUGUI checkpoint;
    [SerializeField] private TextMeshProUGUI lapTime;
    [SerializeField] private TextMeshProUGUI lastLap;
    [SerializeField] private TextMeshProUGUI recordLap;
    [SerializeField] private TextMeshProUGUI speedText;
    [SerializeField] private TextMeshProUGUI countDown;
    [SerializeField] private GameObject[] countDownOverlay;
    [SerializeField] private GameObject[] UI;
    [SerializeField] private Slider slider;
    [SerializeField] private GameObject speedOutline;
    [SerializeField] private GameObject mainButton;
    [SerializeField] private GameObject resumeButton;
    private float speedMeter;
    [SerializeField] public int countDownTime = 3;
    private bool isPause = false;

    [SerializeField] private AudioSource startAudio;

    KartLap kartLap;
    LapHandler lapHandler;
    Movement movementScript;

    [SerializeField] private GameObject player;
    private void Awake()
    {
        Instance = this;
    }

    public void Start()
    {
        StartCoroutine(StartCountDown());

        player = GameObject.FindGameObjectWithTag("Player");
        movementScript = player.GetComponent<Movement>();
        //kartLap = player.GetComponent<KartLap>();
        //movementScript = GameObject.Find("Player").GetComponent<Movement>();

        kartLap = GameObject.FindGameObjectWithTag("Player").GetComponent<KartLap>();
        //_lapHandler = GameObject.FindGameObjectWithTag("Finish");
        lapHandler = GameObject.FindGameObjectWithTag("Finish").GetComponent<LapHandler>();
        UpdateCheckpointUI();
        UpdateLapCountUI();
        UpdateLastLapUI();
        UpdateRecordLapUI();
        UpdateSpeedMeter();
    }

    private IEnumerator StartCountDown()
    {
        DeactivateUI();


        while(countDownTime > 0)
        {
            countDown.text = countDownTime.ToString();
            //Overlay();
            if (countDownTime == 3)
            {
                startAudio.Play();
                countDownOverlay[0].SetActive(true);
                countDownOverlay[1].SetActive(false);
                countDownOverlay[2].SetActive(false);
            }
            else if (countDownTime == 2)
            {
                countDownOverlay[1].SetActive(true);
                countDownOverlay[2].SetActive(false);
                countDownOverlay[0].SetActive(false);
            }
            else if (countDownTime == 1)
            {
                countDownOverlay[2].SetActive(true);
                countDownOverlay[1].SetActive(false);
                countDownOverlay[0].SetActive(false);
            }
            yield return new WaitForSeconds(1f);
            countDownTime--;
        }

        countDown.text = "GO!";

        yield return new WaitForSeconds(1f);
        ActivateUI();

        countDown.gameObject.SetActive(false);
        for (int i = 0; i < countDownOverlay.Length; i++)
        {
            countDownOverlay[i].SetActive(false);
        }

    }

    private void ActivateUI()
    {
        for (int i = 0; i < UI.Length; i++) // activeert alle elementen die in de ui array zitten
        {
            UI[i].gameObject.SetActive(true);
        }
    }

    private void DeactivateUI() 
    {
        for (int i = 0; i < UI.Length; i++) // deactiveert alle elementen die in de ui array zitten
        {
            UI[i].gameObject.SetActive(false);
        }
    }

    public void UpdateSpeedMeter()
    {
        speedMeter = movementScript.currentSpeed;
        slider.value = speedMeter;
    }

    public void UpdateLapCountUI()
    {
        //lapCount.text = "Lap: " + kartLap.lapNumber.ToString();
    }

    public void UpdateCheckpointUI()
    {
        checkpoint.text = "Checkpoint: " + kartLap.CheckpointIndex.ToString();
    }

    public void UpdateLastLapUI()
    {
        lastLap.text = "Last Lap: " + PlayerPrefs.GetFloat("LastLap").ToString("F2");
    }

    public void UpdateRecordLapUI()
    {
        recordLap.text = "Record Lap: " + PlayerPrefs.GetFloat("RecordTime").ToString("F2");
    }

    public void PauseGame()
    {
        if (isPause)
        {
            Time.timeScale = 0f;
            mainButton.gameObject.SetActive(true);
            resumeButton.gameObject.SetActive(true);
            DeactivateUI();
        }
        else
        {
            Time.timeScale = 1;
            mainButton.gameObject.SetActive(false);
            resumeButton.gameObject.SetActive(false);
            ActivateUI();
        }
    }

    public void ResumeButton()
    {
        isPause = !isPause;
        PauseGame();
    }

    void Update()
    {
        lapTime.text = "Laptime: " + lapHandler.lapTime.ToString("F2");

        UpdateSpeedMeter();

        if (Input.GetKeyDown(KeyCode.Escape))
        {
            isPause = !isPause;
            PauseGame();
        }
    }
}