Player

Controller

The controller I took from a previous University Assignment and fitted into this game. Mainly changing the input keys from just the standard wasd keycodes, to be set up with a different script then read here.

public class Controller : MonoBehaviour
{
    public float speed = 5;
    public bool jump;
    public float jumpSpeed = 6;
    public float jumpHeight = 3;
    public bool health;
    public int maxHealth = 10;
    public int attackDamage = 1;
    public int damageRange = 2;
    public bool verticalMovement;

    public KeyCode upMove;
    public KeyCode downMove;
    public KeyCode leftMove;
    public KeyCode rightMove;

    public Vector3 directionX = new Vector3 (1, 0, 0);
    public Vector3 directionY = new Vector3(0, 1, 0);
    public int playerHealth;
    Controller controller;

    bool jumpStart = false;
    bool jumpUp = false;
    bool jumpDown = false;
    bool jumpReset = false;
    float jumpMax;
    float currentYPosition;
    public int damage;

    private GameObject gameOverObj;

    private GameObject hpObj;
    private HP hp;

    private Animator anim;
    private GameObject player;
    private bool controlChange = false;
    private SpriteRenderer renderDir;

    public PauseMenu pMenu;
    private int sfxrandom;
    private AudioSource gameOversfx;

    private void Awake()
    {
        player = this.gameObject;
        if (GameObject.FindWithTag("Respawn") != null)
        {
            gameOverObj = GameObject.FindWithTag("Respawn");
            gameOverObj.SetActive(false);
        }
        hpObj = GameObject.Find("HP");
        if (hpObj != null)
        {
            hp = hpObj.GetComponent<HP>();
        }
        anim = player.GetComponent<Animator>();
        renderDir = player.GetComponent<SpriteRenderer>();
        if(GameObject.Find("ShutDown") != null )
        {
            gameOversfx = GameObject.Find("ShutDown").GetComponent<AudioSource>();
        }

    }
    void Start()
    {
        jumpMax = currentYPosition + jumpHeight;
        currentYPosition = transform.position.y;

        controller = this;
    }

    void Update()
    {
        // Setting Players Position
        if (currentYPosition != transform.position.y && jumpStart == false)
        {
            currentYPosition = transform.position.y;
            jumpMax = currentYPosition + 5;
        }

        // Horiziontal Movement
        if (jumpStart == false && PauseMenu.Instance.paused == false)
        {
            if(Input.GetKey((rightMove)))
            {
                this.transform.position += directionX * speed * Time.deltaTime;
                if ( controlChange == false)
                {
                    renderDir.flipX = false;
                    anim.SetInteger("AnimSwitch", 1);
                }
                
            }
            if (Input.GetKey((leftMove)))
            {
                this.transform.position -= directionX * speed * Time.deltaTime;
                if (controlChange == false)
                {
                    renderDir.flipX = true;
                    anim.SetInteger("AnimSwitch", 1);
                }
            }

            if (controlChange == false)
            {
                anim.SetInteger("AnimSwitch", 0);
            }

        }

        // Vertical Movement (Not jump)
        if (verticalMovement == true && PauseMenu.Instance.paused == false)
        {
            if (Input.GetKey((upMove)))
            {
                this.transform.position += directionY * speed * Time.deltaTime;
                if (controlChange == false)
                {
                    anim.SetInteger("AnimSwitch", 1);
                }
            }
            if (Input.GetKey((downMove)))
            {
                this.transform.position -= directionY * speed * Time.deltaTime;
                if (controlChange == false)
                {
                    anim.SetInteger("AnimSwitch", 1);
                }
            }
        }

        // Jumping
        if (jump == true && PauseMenu.Instance.paused == false)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                jumpStart = true;
                jumpUp = true;
            }

            if (jumpStart == true)
            {
                if (jumpUp == true)
                {
                    if (transform.position.y < jumpMax)
                    {
                        this.transform.position += directionY * jumpSpeed * Time.deltaTime;
                    }
                    else
                    {
                        jumpDown = true;
                        jumpUp = false;
                    }
                }

                if (jumpDown == true)
                {
                    if (transform.position.y > currentYPosition)
                    {
                        this.transform.position -= directionY * jumpSpeed * Time.deltaTime;
                    }
                    else
                    {
                        jumpReset = true;
                        jumpDown = false;
                    }
                }
            }

            if (jumpReset == true)
            {
                currentYPosition = transform.position.y;
                jumpStart = false;
                jumpReset = false;
            }
        }

        //Health
        if (health == true && PauseMenu.Instance.paused == false)
        {
            playerHealth = maxHealth - damage;
            if (hpObj != null)
            {
                hp.HPTextUpdate(playerHealth, maxHealth);
            }

            if (playerHealth < 0.01f && gameOverObj.activeInHierarchy == false)
            {
               playerHealth = 0;
               gameOverObj.SetActive(true);
               gameOversfx.Play();
            }
        }

        // SFX
        sfxrandom = Random.Range(0, 1000000);
        if(sfxrandom == 99828)
        {
            ZippySoundEffects.Instance.PlayZippyNoise();
        }
        sfxrandom = 0;
   
    }

    public void AnimControlChangeSetUp()
    {
        StartCoroutine(AnimControlChange());
    }

    private IEnumerator AnimControlChange()
    {
        controlChange = true;
        anim.SetInteger("AnimSwitch", 2);
        yield return new WaitForSeconds(1);
        controlChange = false;
        StopCoroutine(AnimControlChange());
    }

}

Movement Input

The Input letters are selected through a random number generator, translating to Keycodes of each letter, these keycodes are then read by the player controller, and the “KeyUiUpdate” function updates the UI with the new letters

public class RandomiseInputs : MonoBehaviour
{
    public Controller controller;
    public Interacting attack;
    public int changeNum;
    private KeyCode[] keys;
    public float duration;
    public float counter;
    public List<int> letterNumbers;
    public KeyUi UI;
    private GameObject uiObj;

    public PauseMenu pMenu;
    private void Awake()
    {
        pMenu = PauseMenu.Instance;
        uiObj = GameObject.Find("Controlls");
        UI = uiObj.GetComponent<KeyUi>();

        keys = new KeyCode[26];
        keys[0] = KeyCode.A;
        keys[1] = KeyCode.B;
        keys[2] = KeyCode.C;
        keys[3] = KeyCode.D;
        keys[4] = KeyCode.E;
        keys[5] = KeyCode.F;
        keys[6] = KeyCode.G;
        keys[7] = KeyCode.H;
        keys[8] = KeyCode.I;
        keys[9] = KeyCode.J;
        keys[10] = KeyCode.K;
        keys[11] = KeyCode.L;
        keys[12] = KeyCode.M;
        keys[13] = KeyCode.N;
        keys[14] = KeyCode.O;
        keys[15] = KeyCode.P;
        keys[16] = KeyCode.Q;
        keys[17] = KeyCode.R;
        keys[18] = KeyCode.S;
        keys[19] = KeyCode.T;
        keys[20] = KeyCode.U;
        keys[21] = KeyCode.V;
        keys[22] = KeyCode.W;
        keys[23] = KeyCode.X;
        keys[24] = KeyCode.Y;
        keys[25] = KeyCode.Z;

        letterNumbers.Add(22);
        letterNumbers.Add(18);
        letterNumbers.Add(0);
        letterNumbers.Add(3);
        
    }

    public void Start()
    {
        UpdateControlls();
    }
    public void UpdateControlls()
    {
        if (letterNumbers.Count > 4)
        {
            letterNumbers.RemoveRange(0, 4);
        }

        do
        {
            changeNum = Random.Range(0,26);
            if(!letterNumbers.Contains(changeNum))
            {
                letterNumbers.Add(changeNum);
                counter++;
            }

        } while (counter < 4);

        controller.upMove = keys[letterNumbers[4]];
        UI.KeyUiUpdate("Up", letterNumbers[4]);

        controller.downMove = keys[letterNumbers[5]];
        UI.KeyUiUpdate("Down", letterNumbers[5]);

        controller.leftMove = keys[letterNumbers[6]];
        UI.KeyUiUpdate("Left", letterNumbers[6]);

        controller.rightMove = keys[letterNumbers[7]];
        UI.KeyUiUpdate("Right", letterNumbers[7]);

        counter = 0; 
    }

}

Enemy

Enemy Function

The enemy function script controls three main objectives with the enemies

  1. Moving towards the player by reading the player location and the Navmesh (AI movement through mazes)
  2. Attacking the player when close enough
  3. “Hacking” the triggers to move past them towards the player

Similar to the player, the stats of the enemy is controlled through a scriptable object

public class EnemyFunction : MonoBehaviour
{
    [SerializeField] public EnemyData stats;
    private GameObject Player;
    private GameObject Enemy;
    private Controller playerStats;
    public List<GameObject> electric;
    private GameObject hackedElectric;
    private Animator animator;
    private bool cooldown= false;
    public int damage;
    public int currentHealth;
    private bool hacking = false;
    private float hackedEleDistance;
    private bool movethroughCheck = false;

    NavMeshAgent agent;
    public Transform target;

    private AudioSource hackSFX;

    private void Start()
    {
        
        agent = GetComponent<NavMeshAgent>();
        agent.updateRotation = false;
        agent.updateUpAxis = false;
        
        Player = GameObject.Find("Player");
        playerStats = Player.GetComponent<Controller>();
        Enemy = this.gameObject;

        electric = new List<GameObject>();
        var arrayele = GameObject.FindGameObjectsWithTag("Triggers");
        foreach(var ele in arrayele)
        {
            electric.Add(ele);
        }

        animator = Enemy.GetComponent<Animator>();
        if (SceneManager.GetActiveScene().buildIndex != 5)
        {
            hackSFX = GameObject.Find("Hacking").GetComponent<AudioSource>();
        }
    }

    public void Update()
    {
        // Moving towards Player
        if (PauseMenu.Instance.paused == false)
        {
            if (hacking == false)
            {
                agent.SetDestination(target.position);
                animator.SetInteger("Switch", 1);
            }
        }
        else if (hacking == false && PauseMenu.Instance.paused == false)
        {
            animator.SetInteger("Switch", 0);
        }

        // Attacking
        if (Vector3.Distance(transform.position, Player.transform.position) < stats.damageRange && PauseMenu.Instance.paused == false)
        {
            EnemyAttack();
        }

        // Health
        currentHealth = stats.maxhealth - damage;
        if (currentHealth < 0.1)
        {
            Enemy.SetActive(false);
            damage = 0;
        }

        // Checking triggers
        if (hacking == false)
        {
            foreach (var ele in electric)
            {
                float eledist = Vector3.Distance(ele.transform.position, transform.position);
                if (eledist < 2)
                {
                    hackedElectric = ele;
                    hackedEleDistance = eledist;
                    if (hackedElectric.GetComponent<ControlTrigger>() != null)
                    {
                        if(hackedElectric.GetComponent<ControlTrigger>().perminateOff == false)
                        {
                            if(hacking == false)
                            {
                                StartCoroutine(ElectricBarrier());
                            }
                        }
                        else
                        {
                            return;
                        }
                    }else if (hackedElectric.GetComponent<EnemyTrigger>() != null)
                    {
                        if (hackedElectric.GetComponent<EnemyTrigger>().perminateOff == false)
                        {
                            if(hacking == false)
                            {
                                StartCoroutine(ElectricBarrier());
                            }
                            
                        }
                        else
                        {
                            return;
                        }
                    }
                    else
                    {
                        return;
                    }
                    
                }
            }
        }
    }

    IEnumerator ElectricBarrier()
    {
        hacking = true;
        var curPos = this.gameObject.transform.position;
        agent.SetDestination(curPos);
        animator.SetInteger("Switch", 2);
        hackSFX.Play();
        yield return new WaitForSeconds(2);
        if (hackedElectric.GetComponent<ControlTrigger>() != null)
        {
            hackedElectric.GetComponent<ControlTrigger>().HackedSetUp();
        }
        else
        {
            hackedElectric.GetComponent<EnemyTrigger>().HackSetUp();
        }

        agent.SetDestination(target.position);
        do
        {
            hackedEleDistance = Vector3.Distance(hackedElectric.transform.position, transform.position);
            if (hackedEleDistance > 2)
            {
                movethroughCheck = true;
            }
            else
            {
                yield return new WaitForSeconds(0.1f);
            }

        } while (movethroughCheck == false);

        var curPos2 = this.gameObject.transform.position;
        agent.SetDestination(curPos2);
        Debug.Log(curPos2);
        //hackSFX.Play();
        if (hackedElectric.GetComponent<ControlTrigger>() != null)
        {
            hackedElectric.GetComponent<ControlTrigger>().hackFinished = true;
        }
        else
        {
            hackedElectric.GetComponent<EnemyTrigger>().hackFinished = true;
        }
        animator.SetInteger("Switch", 2);
        yield return new WaitForSeconds(2);
        
        hacking = false;
        movethroughCheck = false;
        animator.SetInteger("Switch", 0);
        agent.SetDestination(target.position);
        StopCoroutine(EnemyAttackCalc());
    }

    private void EnemyAttack()
    {
        if(cooldown == false)
        {
            StartCoroutine(EnemyAttackCalc());
            cooldown = true;
        }
    }

    IEnumerator EnemyAttackCalc()
    {
        animator.SetInteger("Switch", 2);
        playerStats.damage += stats.attackDamage; 
        yield return new WaitForSeconds(stats.attackCooldown);
        cooldown = false;
        animator.SetInteger("Switch", 0);
        StopCoroutine(EnemyAttackCalc());
    }
}

Enemy Spawning

The enemy spawning is done through two scripts, the first uses Object pooling to spawn the enemies. Yes 4/5 of the levels its only spawning 1 but is really useful for the final level when it is spawning many. The second script is placing the enemies. For 4/5 of the levels the placement is random so long as it detects the NavMesh (aka its not in a wall), the final level they all spawn in the same spot spaced apart

    public static EnemyObjectPool instance;
    public List<GameObject> pooledObjects = new List<GameObject>();
    private int amountToPool = 1;
    public GameObject Enemy;
    private Animator animator;
    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }

        animator = this.gameObject.GetComponent<Animator>();

        if (SceneManager.GetActiveScene().buildIndex == 5 )
        {
            amountToPool = 20;
        }
        
    }

    public void CreatePooledObjects()
    {
        for (int i = 0; i < amountToPool; i++)
        {
            GameObject obj = Instantiate(Enemy);
            obj.SetActive(false);
            pooledObjects.Add(obj);
        }
    }

    public GameObject GetPooledObject()
    {
        for (int i = 0; i < pooledObjects.Count; i++)
        {
            if (!pooledObjects[i].gameObject.activeInHierarchy)
            {
                return pooledObjects[i];
            }
        }

        return null;
    }
}
public class PlacingEnemies : MonoBehaviour
{
    private Vector3 spawnLocation = Vector3.zero;
    public EnemyFunction nav;
    private bool finalboosspawn = false;
    private void Update()
    {
        GameObject enemy = EnemyObjectPool.instance.GetPooledObject();
        

        if (enemy != null && enemy.activeInHierarchy == false )
        {
            if (SceneManager.GetActiveScene().buildIndex != 5)
            {
                spawnLocation = new Vector3(Random.Range(-15, 20), Random.Range(-8, 10), 0);
                NavMeshHit hit;
                if (NavMesh.SamplePosition(spawnLocation, out hit, Mathf.Infinity, NavMesh.AllAreas) == true)
                {
                    var myRandomPositionInsideNavMesh = hit.position;

                    enemy.transform.position = myRandomPositionInsideNavMesh;
                    enemy.GetComponent<EnemyFunction>().target = GameObject.FindWithTag("Player").transform;
                    enemy.SetActive(true);
                }
            }
            else if (SceneManager.GetActiveScene().buildIndex == 5)
            {
                if (finalboosspawn == false)
                {
                    StartCoroutine(FinalBoss());
                }
            }
        }
        else
        {
            return;
        }
    }

    IEnumerator FinalBoss()
    {
        finalboosspawn = true;
        GameObject enemy = EnemyObjectPool.instance.GetPooledObject();

        spawnLocation = new Vector3(-14, -7f, 0);
        NavMeshHit hit;
        if (NavMesh.SamplePosition(spawnLocation, out hit, Mathf.Infinity, NavMesh.AllAreas) == true)
        {
            var myRandomPositionInsideNavMesh = hit.position;

            enemy.transform.position = myRandomPositionInsideNavMesh;
            enemy.GetComponent<EnemyFunction>().target = GameObject.FindWithTag("Player").transform;
            enemy.SetActive(true);
        }

        yield return new WaitForSeconds(1);
        finalboosspawn = false;
        StopCoroutine(FinalBoss());
    }
}

Settings