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());
}
}
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;
}
}
The enemy function script controls three main objectives with the enemies
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());
}
}
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());
}
}