L'eroe
Fa parte della sezione Architettura dei giochi in Unity del percorso Unity di Coddy. Lezione 50 di 64.
Questo capitolo crea Arena Survivor, l'ultimo gioco del corso. Ondate di nemici si riversano in un'arena circondata da mura e tu le affronti con una spada: grugniti che ti vengono incontro e sfere che aspettano e poi scattano. Un'ondata sconfitta lascia un potenziamento, un HUD mostra la tua salute e l'ondata e, quando la tua salute si esaurisce, la partita termina e viene salvata la tua migliore partita. Il gioco usa gli strumenti di questa sezione dove sono adatti: un Enemy astratto con due tipi, IDamageable per tutto ciò che può subire danni, le ondate come risorsa ScriptableObject, oggetti raccolti tramite un'interfaccia, un HUD che si limita ad ascoltare gli eventi C# e un salvataggio JSON. Tutti e dodici gli script sono nel progetto fin dall'inizio. Per ora funziona solo Hero; gli altri contengono i campi necessari a una lezione successiva, e ogni lezione ne completa altri.
La spada dell'eroe colpisce ciò che si trova davanti, quindi il fendente deve verificare cosa si trova «davanti». Vector3.Dot(a, b) moltiplica le parti corrispondenti di due vettori e le somma: a.x * a.x + a.y * b.y + a.z * b.z. Per due direzioni di lunghezza 1, il risultato è il coseno dell'angolo che le separa: 1 quando puntano nella stessa direzione, 0.866 a 30 gradi, 0.707 a 45, 0 ad angolo retto e -1 quando puntano in direzioni opposte. Quindi «entro 30 gradi rispetto alla direzione in cui sono rivolto» diventa «un prodotto scalare di almeno 0.866», senza dover calcolare alcun angolo. Questo robot sentinella lancia l'allarme quando il giocatore è entro 10 unità e nel suo campo visivo di 30 gradi:
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!");
}
}Entrambe le direzioni devono avere lunghezza 1. transform.forward ce l'ha sempre, ma toPlayer è lungo quanto la distanza, quindi prima viene normalizzato; altrimenti, un giocatore due volte più lontano produrrebbe un prodotto scalare doppio. La sua y viene impostata a 0 prima di questo passaggio, così un giocatore su una passerella più alta viene considerato in base alla sua posizione sulla mappa, non alla sua altezza. Il segno distingue davanti e dietro: un prodotto scalare maggiore di 0 indica un punto qualsiasi nella metà anteriore, mentre uno minore di 0 indica un punto qualsiasi dietro.
Sfida
MedioDai una spada all’eroe.
- In
Update(), quando viene premuto Space o si fa clic con il pulsante sinistro del mouse eTime.time >= nextSwing, impostanextSwing = Time.time + swingCooldown;e chiamaSwing(). - In
Swing(), esegui un ciclo suPhysics.OverlapSphere(transform.position, reach). - Distruggi ogni colpo contrassegnato con
Enemyla cui direzione sul pianotoHit(con y impostato a 0) produceVector3.Dot(transform.forward, toHit.normalized) >= 0.5f.
Premi «Gioca» e sferra un colpo: i manichini davanti a te scompaiono. Poi premi «Verifica».
Provalo tu
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;
private float nextSwing;
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);
}
// Premendo Space o facendo clic con il tasto sinistro si sferra un colpo, al massimo una volta ogni swingCooldown secondi
}
void Swing()
{
StartCoroutine(ShowSlash());
// Distruggi tutto ciò che ha il tag Enemy, si trova entro reach ed è davanti all'eroe
}
IEnumerator ShowSlash()
{
slash.SetActive(true);
yield return new WaitForSeconds(0.1f);
slash.SetActive(false);
}
}
Tutte le lezioni di Architettura dei giochi in Unity
1Componenti, non gerarchie
Componenti piccoliUno script base per i nemiciComponenti astrattiComponenti che collaboranoRipasso: torretta modulareRipasso: cannone doppio4Dati serializzati
Classi serializzabiliElenchi nell'InspectorEnum nell'InspectorRiepilogo: tabella del bottinoRiepilogo: tira il bottino7Macchine a stati
Gli stati come classiLo stato di allertaUna macchina a statiTransizioniIntelligenza dei nemiciIl cavaliere attaccaRiepilogo: stati delle porte10Progetto: Sopravvissuto nell'arena
L'eroeTipi di nemici2Interfacce in Unity
IDamageableInterfacce e TryGetComponentGetComponentsRiepilogo: tasto InteragisciRiepilogo: il più vicino a portata5ScriptableObjects
Asset di datiStatistiche delle armiImpostazioni condiviseSalva il ventoOnde come datiRiepilogo: database degli oggettiRiepilogo: comprare dal negozioEsercitati da solo: Playground Unity