Esta página ainda não foi traduzida: mostrando a versão em inglês.
Kits médicos no Tanks
Esta é a segunda lição do Tanks. São três passos e nenhum módulo novo: uma declaração para o kit médico (health crate), uma declaração para onde os kits aparecem e um hook para o que acontece na coleta (pickup). Faça depois de Primeiros passos, antes ou depois da lição do ranking.
Declare o kit médico
O kit é uma entidade com o preset de objetos do mundo aplicado e um corpo que informa o contato sem bloquear ninguém. É o Response.Pass na camada pickups que faz dele um item de coleta, e não um obstáculo: o contato é informado e o movimento passa direto.
pickups — contato informado, movimento livre[Entity("health-crate", Persistence = Persistence.Runtime, Presets = new[] { Preset.WorldObjects })]
public class HealthCrate
{
[Sync] public Vector3 Position;
[Body(Shape.Sphere, Radius = 0.5f, Layer = "pickups")]
[CollidesWith("vehicles", Response.Pass)] // reported, motion passes through
public Body Body;
}@Entity('health-crate', { persistence: Persistence.Runtime, presets: [Preset.WorldObjects] })
export class HealthCrate {
@Sync position: Vector3;
@Body({ shape: 'sphere', radius: 0.5, layer: 'pickups' })
@CollidesWith('vehicles', Response.Pass) // reported, motion passes through
body: Body;
}@entity("health-crate", persistence=Persistence.RUNTIME, presets=[Preset.WORLD_OBJECTS])
class HealthCrate:
position: Vector3 = sync()
body: Body = body(shape="sphere", radius=0.5, layer="pickups",
collides_with=[("vehicles", Response.PASS)])Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
UCLASS(PSEntity = (Name = "health-crate", Persistence = "Runtime", Presets = "world-objects"))
class UHealthCrate : public UObject
{
GENERATED_BODY()
UPROPERTY(PSSync) FVector3f Position;
UPROPERTY(PSBody = (Shape = "Sphere", Radius = "0.5", Layer = "pickups"),
PSCollidesWith = "vehicles:Pass") // reported, motion passes through
FPSBody Body;
};
// declarations compile into the same pushed model — playserv push from the UE project or CIAs declarações ficam no projeto do servidor e são publicadas com playserv push; o binding de Unity usa a API tipada gerada (HealthCrate) no lado do cliente.
Duas coisas que você não precisou escrever: onde o kit é desenhado (o cliente já renderiza os objetos do mundo declarados) e como a posição dele chega aos clientes — o próprio [Sync] é a chamada de rede.
Módulos responsáveis: Entity Presets e Collision.
Declare onde os kits aparecem
A alocação também é uma declaração, e é este passo que decide se o recurso parece justo. O espaçamento impede que os kits se amontoem, a distância dos jogadores impede que surjam no meio de um duelo, e a regra de não repetir evita que o mesmo lugar seja sempre a resposta.
[DropTable("health-crates", Layer = "ground", MinSpacing = 8, AwayFromPlayers = 10, NoRepeat = 3)]
public static partial class HealthCrates
{
public static readonly Drop Crate = Drop.Of<HealthCrate>(weight: 1);
}@DropTable('health-crates', { layer: 'ground', minSpacing: 8, awayFromPlayers: 10, noRepeat: 3 })
export class HealthCrates {
static crate = Drop.of(HealthCrate, { weight: 1 });
}@drop_table("health-crates", layer="ground", min_spacing=8, away_from_players=10, no_repeat=3)
class HealthCrates:
crate = drop_of(HealthCrate, weight=1)Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
USTRUCT(PSDropTable = (Name = "health-crates", Layer = "ground", MinSpacing = 8,
AwayFromPlayers = 10, NoRepeat = 3))
struct FHealthCrates
{
GENERATED_BODY()
UPROPERTY(PSEntry = (Entity = "health-crate", Weight = 1)) FPSDrop Crate;
};
// declarations compile into the same pushed model — playserv push from the UE project or CIAs declarações ficam no projeto do servidor e são publicadas com playserv push; o cliente Unity vê o resultado como itens do mundo que deram spawn e eventos de coleta.
As posições válidas vêm do Map: a tabela pede um lugar na camada ground, e o mapa responde com um que dá mesmo para alcançar — assim um kit nunca vai parar dentro de uma parede.
Módulos responsáveis: Entity Presets e Map.
Cure na coleta
Um hook, e é o único código da lição. Ele roda na plataforma como cloud function, por isso não aparece em nenhuma das abas de engine.
[Before(Drops.Pickup)]
public static Verdict HealOnPickup(PickupIntent p)
{
if (p.WorldItem.Kind != "health-crate") return Hook.Continue(p);
if (p.Player.Tank.Hp.IsFull) return Hook.Reject("already at full health");
p.Player.Tank.Hp.Adjust(+40, by: p.Player);
return Hook.Continue(p);
}export const healOnPickup = before(Drops.pickup, (p: PickupIntent) => {
if (p.worldItem.kind !== 'health-crate') return Hook.continue(p);
if (p.player.tank.hp.isFull) return Hook.reject('already at full health');
p.player.tank.hp.adjust(+40, { by: p.player });
return Hook.continue(p);
});@before(drops.pickup)
def heal_on_pickup(p: PickupIntent) -> Verdict:
if p.world_item.kind != "health-crate":
return Hook.continue_(p)
if p.player.tank.hp.is_full:
return Hook.reject("already at full health")
p.player.tank.hp.adjust(+40, by=p.player)
return Hook.continue_(p)Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
Um hook é uma cloud function: ele roda na plataforma, não na engine. Escreva em C#, TypeScript ou Python — o Unreal assina os eventos de mudança de stat e de coleta que resultam dele. Funções escritas na engine estão planejadas: grafos Blueprint e C++ (traduzido para um formato de script validado pela plataforma). Os dois serão analisados e publicados como qualquer declaração, e a execução continua na plataforma.
Um hook é uma cloud function: ele roda na plataforma, não na engine. Escreva em C#, TypeScript ou Python — o Unity assina os eventos de mudança de stat e de coleta que resultam dele.
O que o hook não precisa fazer:
- A recusa é tipada. Um tanque com a vida cheia recebe
already at full health, com um motivo que o cliente pode mostrar, e o kit continua lá para quem precisar. - Ajustar um stat é autoritativo no servidor. O cliente não tem como pedir isso, então não há exceção de anti-cheat para escrever para as coletas.
- O HUD se atualiza sem ninguém mandar.
Adjustemitechanged, e o cliente já assina os stats declarados do tanque. Você não escreveu nenhuma mensagem de rede.
Módulos responsáveis: Extensibility e Entity Presets.
Próximos passos
- Entity Presets — o gerador de drops, os objetos do mundo e o modelo de stats em que esta lição se apoiou; os três são presets de
entity, não módulos. - Collision — camadas, respostas e a diferença entre um contato que só é informado e um que bloqueia.
- Map — como uma posição válida é escolhida e o que significa “alcançável”.
- Extensibility — todos os pontos de hook, em ordem, com o contrato de veto.
- Um ranking no Tanks — o outro exemplo do Tanks.