Neste artigo, examinarei os recursos de trabalhar em três motores de jogo, usando o exemplo de escrever código para um veículo aracnídeo.
A estrutura geral do "carro-aranha" foi assumida da seguinte forma - são 6 patas, cada cópia do script de controle, que rastreia o movimento da estrutura principal e move a pata para uma nova posição quando ela se afasta a uma certa distância.
Para um pouso mais preciso das patas, planejou-se adicionar raycasts, mas, por exemplo, no mesmo Godot eu queria fazer uma aranha para um jogo com vista de cima, em que tal precisão não é particularmente necessária.
Assim, a tarefa era formar a estrutura desejada dentro do motor e escrever o código para um pé separado. Aqui está o que aconteceu em diferentes ambientes de desenvolvimento:
Godot
Aqui eu já tinha um pequeno projeto com carros e uma aranha prontos, resolvi adicionar a uma das cenas (pré-fabricadas), que contém uma subclasse de carros que não têm rodas.
A própria cena specific_base é organizada de tal forma que na base está um nó fictício, que simplesmente fica pendurado em algum lugar do mundo, sem movimento, e o corpo cinemático dentro dele se move ao redor do mundo. A câmera está dentro da cena, mas fora do corpo, apenas acompanhando.
Para criar a aranha, adicionei um nó separado dentro do corpo, contendo pontos fictícios para a localização das patas (onde são colocadas no chão, não onde estão presas ao corpo).
Por conveniência, coloquei as próprias patas aqui, nesta cena, mas fora do corpo. O script que controla cada um deles, além de mover a pata, o desdobrará constantemente para o centro da "aranha".
Código dentro do editor Godot
Escrevendo o código. Eu uso GDScript porque não vejo muito sentido em escrever em C # em Godot (não sou muito fã de colchetes):
extends Spatial
export var distance = 2.5# ,
export var step = 1# ( )
# ,
export (NodePath) var spidercenter = null
var trg_center
export (NodePath) var spiderleg = null
var trg_leg
# x z
var x_dis = 0.0
var z_dis = 0.0
#-,
var time_lag = -1.0
#
func _ready():
self.hide()#
trg_center = get_node(spidercenter)#
trg_leg = get_node(spiderleg)
LegPlace()#
#
func _process(delta):
# . ,
self.look_at(trg_center.global_transform.origin, Vector3(0,1,0))
# , . , , ( , ). LegPlace
if self.visible == false: self.show()
if time_lag>=0:# - ,
time_lag +=1*delta
if time_lag>0.06:#
time_lag = -1.0
LegPlace()
else:#
x_dis = abs(trg_leg.global_transform.origin.x - self.global_transform.origin.x)
z_dis = abs(trg_leg.global_transform.origin.z - self.global_transform.origin.z)
if (x_dis + z_dis) > distance:# ,
time_lag = 0.0
pass
func LegPlace():#,
self.hide()
step = step*(-1)
self.global_transform.origin = trg_leg.global_transform.origin+Vector3(0,0,0.5*step)
Godot Spider Test Cutscene. Como um bônus, também incluiu momentos de um jogo separado com terraformação do mundo voxel, onde uma "aranha" também é usada, mas puramente animada, bem como uma demo com carros em playcanvas, antes da forma de aranha ser adicionada lá.
Unigine
Após a implementação do Godot estar pronta, decidi transferir esta solução para o motor Unigine. Lá eu também tive um projeto com carros , porém para não sobrecarregá-lo fiz um garfo "aranha" separado, para que depois, provavelmente, retirasse completamente as rodas dele e o desenvolvesse de alguma forma separadamente.
Há toda a cena do mundo do jogo em que o corpo do carro do jogador está localizado. No início do jogo, rodas localizadas separadamente são encaixadas neste corpo.
Estou procurando um manequim dentro do corpo, dentro do qual haverá pontos que definirão as posições das patas.
As patas são simplesmente colocadas no mundo do jogo. O movimento em si é deixado realizado através das rodas, mas sua representação visual é desativada.
Unigine lança ambiente externo para edição de código
Código:
using System;// ""
using System.Collections;
using System.Collections.Generic;
using Unigine;
// ,
[Component(PropertyGuid = "5a8dd6f85781adf7567432eae578c5414581ddac")]
public class theLegBehavior : Component
{
[ShowInEditor][Parameter(Tooltip = "CenterSpider")]//
private Node spiderCenter = null;
[ShowInEditor][Parameter(Tooltip = "Target Leg Point")]//
private Node legPoint = null;
//
private float x_dis= 0.0f;
private float z_dis= 0.0f;
private float ifps;//
private float time_lag = -1.0f;//-
private void Init()//
{
node.Enabled = false;//
LegPlace();//
}
private void Update()//
{
ifps = Game.IFps;//
if (time_lag>=0.0f){//
time_lag += 1.0f*ifps;
if (time_lag>=0.6f) {
time_lag = -1.0f;
LegPlace();
}
}else{
x_dis = MathLib.Abs(legPoint.WorldPosition.x - node.WorldPosition.x);
z_dis = MathLib.Abs(legPoint.WorldPosition.z - node.WorldPosition.z);
if (x_dis + z_dis > 0.8f){
time_lag = 0.0f;
}
}
}
// . . . , , , - . , Update.
private void LegPlace()
{
node.Enabled = false;
vec3 targetDirection = vec3.ZERO;
targetDirection = (legPoint.WorldPosition - node.WorldPosition);
quat targetRot = new quat(MathLib.LookAt(vec3.ZERO, targetDirection, vec3.UP, MathLib.AXIS.Y));
quat delta = MathLib.Inverse(targetRot);
delta.z = 0;
delta.Normalize();
node.WorldPosition = legPoint.WorldPosition;
targetDirection = (spiderCenter.WorldPosition - node.WorldPosition);
node.SetWorldDirection(targetDirection, vec3.UP, MathLib.AXIS.Y);
node.Enabled = true;
}
}
Unigine Spider Dough Cutscene
PlayCanvas
PlayCanvas é um motor de jogo webGL que usa javascript. Recentemente comecei a entender isso. Parece um cruzamento entre Unity e Godot, mas com o desenvolvimento online - o editor abre no navegador.
Neste caso, refiz um dos exemplos oferecidos por esta plataforma, adicionando alguns dos meus modelos de carro, uma pequena funcionalidade adicional como pular, adicionar uma câmera e brincar com materiais / configurações.
A representação visual do carro base no exemplo original foi dada por um modelo sólido, que desativei parcialmente, deixando apenas as rodas, que são puxadas de dentro do modelo por seu script de controle (ou seja, não são enroladas por nós separados na hierarquia da cena). Esta representação visual durante o jogo se apega a um modelo de roda física em movimento.
Para implementar a "aranha" neste projeto, criei um "centro aranha" dentro da representação visual do carro. Por conveniência, o "corpo da aranha" está aninhado dentro dele, pois eu queria mudar imediatamente para a "forma de aranha" e voltar.
Assim, a "aranha" local também é colocada no topo da tração das rodas, e as próprias rodas ficam escondidas. Talvez fosse mais correto anexar o frame da aranha ao nó físico, mas a alternância entre as formas já está localizada no script do nó com o visual do carro, e outros modelos estão dentro deste nó, então escolhi esta opção por simplicidade. Na verdade, verifica-se que o visual em seu roteiro se move para trás da física, e os roteiros das patas já estão olhando para a aranha se movendo junto com o visual. Em teoria pode haver alguma dessincronização aqui, pelo menos notei que em aparelhos fracos algumas patas não têm tempo para calcular posições e apenas algumas se movem.
E assim, em geral, a decisão, é claro, não está atrelada à física ou ao corpo rígido, as pernas, na verdade, não ligam como você move o objeto principal - por forças ou simplesmente mudando a posição.
As patas estão localizadas na própria cena, mas para a conveniência de habilitá-las / desabilitá-las, elas são coletadas dentro de um nó separado, de modo que você pode simplesmente desabilitá-lo, e não cada uma por um link separado.
No playcanvas, o editor de código é iniciado em uma nova guia do navegador
Código:
var TheLegBehavior = pc.createScript('theLegBehavior');
//
TheLegBehavior.attributes.add('N_spiderCenter', { type: 'entity' });
//
TheLegBehavior.attributes.add('N_legPoint', { type: 'entity' });
//
this.x_dis = 0.0;
this.z_dis = 0.0;
this.time_lag = -1.0;//-
// , , -
TheLegBehavior.prototype.initialize = function() {
};
//
TheLegBehavior.prototype.update = function(dt) {
if (this.N_spiderCenter) {// -
this.entity.lookAt(this.N_spiderCenter.getPosition());// ,
}
};
//. , , , , .
TheLegBehavior.prototype.postUpdate = function(dt) {
//,
if (time_lag>=0.0){
time_lag+=1.0*dt;
if (time_lag>=0.06){
time_lag=-1.0;
this.LegUpdate();
}
} else {
x_dis = Math.abs(this.entity.getPosition().x-this.N_legPoint.getPosition().x);
z_dis = Math.abs(this.entity.getPosition().z-this.N_legPoint.getPosition().z);
if ((x_dis+z_dis)>3.0){
time_lag=0.0;
}
}
};
// , ", , ",
TheLegBehavior.prototype.LegUpdate = function() {
if (this.N_legPoint) {// , ,
this.entity.setPosition(this.N_legPoint.getPosition());
}
};
Em geral, até agora, obtivemos uma aranha em branco com quatro patas e cálculos não muito bons.
Você pode testar o quadro resultante
aqui .
Eu pessoalmente tentei rodar em um smartphone não muito potente através do Chrome e Dolphin, os gráficos ficam parecidos com o PsOne e os cálculos para algumas patas não funcionam, enquanto o nível fica visível na tela, as patas aparecem na borda do mapa. Em um laptop fraco, havia freios muito fortes no Chrome, mas tudo funciona bem no Firefox. Em um laptop com uma placa de vídeo discreta e em um estacionário, ele voa em ambos os navegadores.
Em um PC, ao contrário dos smartphones, este demo dá um salto (pressionando a barra de espaço), preparando uma metralhadora (Q e E) e recarregando uma fase (em R).
Resultado
Como você pode ver, as linguagens e os mecanismos são diferentes, mas, apesar disso, muitas coisas na família das linguagens de script são feitas de maneira bastante semelhante. Com que frequência você dominou novos mecanismos e frameworks de jogos, quais foram as principais dificuldades que enfrentou?