O Herói
Parte da seção Arquitetura de Jogos no Unity do Journey de Unity da Coddy. Lição 50 de 64.
Este capítulo cria Arena Survivor, o último jogo do curso. Ondas de inimigos invadem uma arena cercada por muros, e você luta contra eles com uma espada: capangas que caminham em sua direção e orbes que esperam e depois avançam rapidamente. Uma onda derrotada deixa um aprimoramento, um HUD mostra sua vida e a onda, e, quando sua vida acaba, a partida termina e sua melhor partida é salva. O jogo usa as ferramentas desta seção onde elas se encaixam: um Enemy abstrato com dois tipos, IDamageable para tudo que pode sofrer dano, as ondas como um recurso ScriptableObject, itens coletáveis por trás de uma interface, um HUD que apenas escuta eventos C# e um salvamento em JSON. Todos os doze scripts já estão no projeto desde o início. Apenas Hero funciona por enquanto; os outros contêm os campos necessários para uma lição posterior, e cada lição acrescenta mais.
A espada do herói acerta o que está à sua frente, então o golpe precisa de um teste para saber o que está “à frente”. Vector3.Dot(a, b) multiplica as partes correspondentes de dois vetores e as soma: a.x * b.x + a.y * b.y + a.z * b.z. Para duas direções de comprimento 1, esse resultado é o cosseno do ângulo entre elas: 1 quando apontam para a mesma direção, 0.866 a 30 graus, 0.707 a 45, 0 em ângulos retos e -1 quando apontam em direções opostas. Portanto, “dentro de 30 graus da direção para a qual estou olhando” se torna “um produto escalar de pelo menos 0.866”, sem precisar calcular nenhum ângulo. Este robô sentinela dá o alarme quando o jogador está a até 10 unidades e dentro de seu campo de visão de 30 graus:
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!");
}
}As duas direções precisam ter comprimento 1. transform.forward sempre tem esse comprimento, mas toPlayer tem o comprimento da distância, então é normalizado primeiro; caso contrário, um jogador duas vezes mais distante resultaria em um produto escalar duas vezes maior. O valor de y é definido como 0 antes disso, então a posição de um jogador em uma passarela mais alta é determinada pelo lugar em que ele está no mapa, não pela altura em que se encontra. O sinal distingue a frente das costas: um produto escalar acima de 0 indica qualquer posição na metade da frente, e abaixo de 0, qualquer posição atrás.
Desafio
MédioDê uma espada ao herói.
- Em
Update(), quando a tecla Espaço ou um clique esquerdo for pressionado eTime.time >= nextSwing, definanextSwing = Time.time + swingCooldown;e chameSwing(). - Em
Swing(), percorraPhysics.OverlapSphere(transform.position, reach). - Destrua cada hit marcado com
Enemycuja direção horizontaltoHit(com y definido como 0) resulte emVector3.Dot(transform.forward, toHit.normalized) >= 0.5f.
Pressione “Jogar” e desfira um golpe: os bonecos de treino à frente desaparecem. Depois, clique em “Verificar”.
Experimente você mesmo
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);
}
// Pressionar a barra de espaço ou clicar com o botão esquerdo executa um ataque, no máximo uma vez a cada swingCooldown segundos
}
void Swing()
{
StartCoroutine(ShowSlash());
// destrua tudo com a tag Enemy que esteja ao alcance e à frente do herói
}
IEnumerator ShowSlash()
{
slash.SetActive(true);
yield return new WaitForSeconds(0.1f);
slash.SetActive(false);
}
}
Todas as lições de Arquitetura de Jogos no Unity
1Componentes, não hierarquias
Componentes pequenosUm script base de inimigoComponentes abstratosComponentes trabalhando juntosRevisão: torre modularRevisão: canhão duplo4Dados Serializados
Classes SerializáveisListas no InspectorEnums no InspectorRevisão: Tabela de LootRevisão: Sorteie o Loot2Interfaces no Unity
IDamageableInterfaces e TryGetComponentGetComponentsRevisão: tecla de interaçãoRevisão: mais próximo ao alcance5ScriptableObjects
Ativos de DadosAtributos da ArmaConfigurações CompartilhadasSalve o VentoOndas como DadosRevisão: Banco de ItensRevisão: Comprar na LojaPratique por conta própria: Playground de Unity