The Hero
Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 41 of 50.
This chapter builds Arena Survivor, the last game of the journey. Waves of enemies pour into a walled arena and you fight them off with a sword: grunts that walk at you, and orbs that wait and then dash. A defeated wave leaves a power-up, a HUD shows your health and the wave, and when your health runs out the run ends and your best run is saved. The game uses this section's tools where they fit: an abstract Enemy with two kinds, IDamageable for everything that can be hurt, the waves as a ScriptableObject asset, pickups behind an interface, a HUD that only listens to C# events, and a JSON save. All twelve scripts are in the project from the start. Only Hero has code yet, and each lesson fills in more.
The hero's sword hits what is in front of it, so the swing needs a test for "in front". Vector3.Dot(a, b) multiplies the matching parts of two vectors and adds them up: a.x * b.x + a.y * b.y + a.z * b.z. For two directions of length 1, that is the cosine of the angle between them: 1 when they point the same way, 0.866 at 30 degrees, 0.707 at 45, 0 at right angles and -1 when they point opposite ways. So "within 30 degrees of where I face" becomes "a dot of at least 0.866", with no angle to work out. This sentry robot raises the alarm when the player is within 10 units and inside its 30-degree field of view:
public Transform player;
public float range = 10f;
void Update()
{
Vector3 toPlayer = player.position - transform.position;
toPlayer.y = 0f;
bool near = toPlayer.magnitude <= range;
bool inView = Vector3.Dot(transform.forward, toPlayer.normalized) >= 0.866f;
if (near && inView)
{
Debug.Log("Alarm!");
}
}Both directions need length 1. transform.forward always has it, but toPlayer is as long as the distance, so it is normalized first; otherwise a player twice as far away would give twice the dot. Its y is set to 0 before that, so a player on a higher walkway counts by where it stands on the map, not by how high it is. The sign tells front from back: a dot above 0 is anywhere in the front half, below 0 anywhere behind.
Challenge
MediumStart Arena Survivor with the hero's sword. Walking is written: the Move action walks the hero with SimpleMove and turns it to face the way it walks. Five training dummies tagged Enemy stand around it. The Inspector gives Hero reach (2) and swingCooldown (0.5), and the slash it shows during a swing.
- Space or a left click swings, at most once every
swingCooldownseconds: a press before the cooldown is over does nothing. Read the keys withKeyboard.currentandMouse.current, and callSwing(). - In
Swing(), after the slash is shown, destroy every object taggedEnemythat is withinreachand in front of the hero. Within reach is any collider thatPhysics.OverlapSpherefinds around the hero with a radius ofreach. In front is a dot of at least 0.5 between the hero's forward and the direction to the object along the ground: up to 60 degrees to either side.
Leave the other scripts as they are. Press Play, walk up to the dummies and swing, then Check.
Try it yourself
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
public class Hero : MonoBehaviour
{
[SerializeField] private float speed = 5f;
[SerializeField] private float reach = 2f;
[SerializeField] private float swingCooldown = 0.5f;
[SerializeField] private GameObject slash;
private CharacterController controller;
private InputAction moveAction;
void Start()
{
controller = GetComponent<CharacterController>();
moveAction = InputSystem.actions.FindAction("Move");
}
void Update()
{
Vector2 input = moveAction.ReadValue<Vector2>();
Vector3 move = new Vector3(input.x, 0f, input.y);
controller.SimpleMove(move * speed);
if (move != Vector3.zero)
{
transform.rotation = Quaternion.LookRotation(move);
}
// Space or a left click swings, at most once every swingCooldown seconds
}
void Swing()
{
StartCoroutine(ShowSlash());
// destroy everything tagged Enemy within reach and in front of the hero
}
IEnumerator ShowSlash()
{
slash.SetActive(true);
yield return new WaitForSeconds(0.1f);
slash.SetActive(false);
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Game Architecture in Unity
1Components, Not Hierarchies
Small ComponentsA Base Enemy ScriptAbstract ComponentsComponents Working TogetherRecap: Modular Turret10Project: Arena Survivor
The HeroEnemy Types