Menu
CoddyTech

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.

challenge icon

Sfida

Medio

Dai una spada all’eroe.

  1. In Update(), quando viene premuto Space o si fa clic con il pulsante sinistro del mouse e Time.time >= nextSwing, imposta nextSwing = Time.time + swingCooldown; e chiama Swing().
  2. In Swing(), esegui un ciclo su Physics.OverlapSphere(transform.position, reach).
  3. Distruggi ogni colpo contrassegnato con Enemy la cui direzione sul piano toHit (con y impostato a 0) produce Vector3.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

Esercitati da solo: Playground Unity