Desenvolvendo um Mundo em Movimento

*Este conteúdo é traduzido por IA (Beta) e pode conter erros. Para ver a página em inglês, clique aqui.

Criar movimento em qualquer ambiente dentro de uma experiência ajuda-a a se sentir mais imersiva e realista para o nosso mundo, seja isso de movimento de árvore ambiente, reações de portas do jogador ou até mesmo caixas que se movem quando elas colidem com elas. O Studio tem muit

Criando a Tempestade

A tempestade passou por muitas iterações antes de nós termos nos deitado no que é vivo no Mistério do Duvall Drive. No início, pensamos na tempestade como um grande pilão de obsidiana, e em iterações posteriores, consideramos-a um portal gigante para o espaço corrupto. Depois de experimentar com muitas diferentes tempestades com olhos e sensações únicos, chegamos a uma tempestade com um olho central menor porque:

  • A tempestade deve dar aos jogadores uma sensação de impacto deste evento no mundo , incluindo árvores soprando e dejetos voando ao redor.
  • O vortex giratório da própria nuvem deve dar aos jogadores uma olhada no portal central sem revelar tudo . Isso encorajaria os jogadores a investigar mais de perto para ver o que está acontecendo.
  • O ponto de luz mais apertado nos permitiria concentrar-nos na composição da casa , que é tanto o personagem principal quanto onde a maioria do gameplay está localizada.

Para fazer com que a tempestade se sinta dinâmica, agressiva e sempre mudando dentro de seu ambiente, usamos os seguintes sistemas e recursos:

  1. TweenService > para movimento de nuvem.
  2. Mudanças de iluminação - Para criar a nuvem para relâmpago nuvem.
  3. Raios - Para a "iluminação volumétrica" e os relâmpagos.
  4. Emissores de Partículas - Para o lixo voar até o portal e voar ao redor devido ao vento soprando.
  5. Animações > - Para as árvores que estavam soprando no vento.

Adicionando Nuvens com Texturas

Enquanto nuves dinâmicas são ótimas para nuvens de alta altitude, realistas, nós precisávamos de algo que se sentisse dramát

Um único malha de nuvem.
Malhas de nuvem em camadas sem suas texturas!

Como cada malha de nuvem necessária para ser massiva para cercar a casa e transmitir o quão enorme a tempestade estava, sabíamos que precisávamos azulejar a textura que queríamos usar nas malhas individuais para que fosse repetida repetidamente em toda a superfície da malha. Nós testamos os materiais que fazemos para a nuvem nessas peças simples, e então aplicamos-os ao vortex!

Ao contrário de emissores de partículas ou feixes, malhas nos permitem bater luz de cada malha, o que foi importante quando queríamos implementar raios de choque entre nuvens. Nós também modelamos no torcimento para que o bounce de luz de cada malha fosse como se tivesse profundidade! Isso foi importante especialmente em situações onde os requisitos de desempenho da experiência derrubaram os níveis de qualidade de nossos objetos de aparência da superfície.

Quando começamos a adicionar iluminação a ele, precisávamos adicionar detalhes aos meshes para fazê-los reagir melhor à iluminação!

Meshes de Nuvem Rotativos

Depois de termos ficado satisfeitos com a aparência geral das nuvens, precisávamos fazer com que elas se movimento! Tínhamos as formas gerais de cada camada de nuvem em local, mas foi preciso algumas tentativas e erros para garantir que o efeito de rotação fosse bom na prática. Nós inicialmente tentamos usar 约束 para introduzir velocidade que

Queríamos um método fácil de usar para girar instâncias que estavam muito longe para serem interativas, como nuvem, ou muito pequenas ou decorativas para serem important

Como em muitos casos na demonstração, usamos uma etiqueta LocalSpaceRotation para que pudéssemos gerenciar instâncias afetadas no Studio usando um plugin de etiqueta de instância. Nós utilizamos apenas um único LocalScript que gerenciou todas as instâncias afetadas usando o plugin de etiqueta de instância CollectionService para que não tivéssemos um monte de


local function Init()
for _, obj in CollectionService:GetTagged("LocalSpaceRotation") do
if obj:IsDescendantOf(workspace) then
SetupObj(obj)
end
end
end
CollectionService:GetInstanceAddedSignal("LocalSpaceRotation"):Connect(function(obj)
objInfoQueue[obj] = true
end)
CollectionService:GetInstanceRemovedSignal("LocalSpaceRotation"):Connect(function(obj)
if objInfo[obj] then
objInfo[obj] = nil
if objInfoQueue[obj] then
objInfoQueue[obj] = nil
end
end
end)
Init()

objInfo é um mapa que tem informações para todos os objetos relevantes, como sua velocidade de rotação e eixo. Observe que não chamamos

Nós giramos as instâncias na função Update conectada ao bate-p


local parentTransform
if parentObj:IsA("Model") then
if not parentObj.PrimaryPart then
-- a parte primária pode não estar disponível ainda
continue -- espere pela parte primária para se replicar
end
parentTransform = parentObj.PrimaryPart.CFrame
else
parentTransform = parentObj.CFrame
end
curObjInfo.curAngle += dT * curObjInfo.timeToAngle
local rotatedLocalCFrame = curObjInfo.origLocalCFrame * CFrame.Angles( curObjInfo.axisMask.X * curObjInfo.curAngle, curObjInfo.axisMask.Y * curObjInfo.curAngle, curObjInfo.axisMask.Z * curObjInfo.curAngle )
if obj:IsA("Model") then
obj.PrimaryPart.CFrame = parentTransform * rotatedLocalCFrame
else
obj.CFrame = parentTransform * rotatedLocalCFrame
end

Verificamos um válido Model.PrimaryPart para ser definido para lidar com streaming. Se uma atualização for chamada em nosso objeto enquanto um Model.PrimaryPart (que pode apontar para uma malha filha) ainda não estiver em streaming, nós simplesmente pularíamos a atualização. O sistema at

Projetando Golspes de Relâmpago

Como o Studio não oferece um gerador de raios do caixote, e o sistema de partículas tinha algumas limitações que não funcionariam para os ataques de raios do herói, nós tivemos que ser criativos com uma solução para os ataques de raios do herói. Nós decidimos em dois sistemas principais para compor os raios de textura para os heróis que estão vindo do olho da tempestade

Texturando Raios

Normalmente, usamos uma ferramenta de sequenciador ou de linha do tempo para impulsionar o tempo de um efeito de choque de iluminação como este, mas, como o Studio ainda não oferece essa funcionalidade, decidimos escrever scripts que controlariam o tempo de um choque de iluminação. O scripting desse efeito é muito simples, mas alcança os seguintes objetivos importantes:

  1. Os elementos dos golpes de relâmpago, como suas texturas, brilho e atrasos, são aleatórios com cada golpe.
  2. As mudanças de áudio e pós-FX estão sincronizadas com as mudanças de strike FX.
  3. Jogadores que estão dentro ou na área corrompida não poderiam vê-los ou ouvi-los.

Temos um Script do lado do servidor que calcula vários parâmetros e tempos, envia-os para todos os clientes e aguarda uma quantidade aleatória de tempo:


local function LightningUpdate()
while true do
task.wait(rand:NextNumber(3.0, 10.0))
local info = CreateFXData()
lightningEvent:FireAllClients(info)
end
end

Dentro de CreateFXData, nós preenchemos a estrutura de informação, para que todos os clientes obtenham os mesmos parâmetros.

No lado do cliente ( LightningVFXClient ) verificamos se este cliente deve executar o FX:


local function LightningFunc(info)
-- Sem FX ao ar livre
if inVolumesCheckerFunc:Invoke() then
return
end
-- Sem FX quando não estiver no mundo "normal"
if not gameStateInfoFunc:Invoke("IsInNormal") then
return
end

Além disso, executamos a sequência para definir texturas, posições e brilho, executamos adolescentes e usamos task.wait(number) . Os parâmetros aleatórios são da estrutura de informação que recebemos do servidor e alguns números são fixos.


beam.Texture = textures[info.textIdx]
beamPart.Position = Vector3.new(info.center.X + og_center.X, og_center.Y, info.center.Y + og_center.Z)
-- Limpar
beam.Brightness = 10
ppCC.Brightness = maxPPBrightness
ppBloom.Intensity = 1.1
bottom.Position = top.Position
tweenBrightness:Play()
tweenPPBrightness:Play()
tweenPPBrightness:Play()
tweenBottomPos:Play()
tweenBrightness.Completed:Wait()
-- áudio
if audioFolder and audioPart then
if audioFolder.Value and audioPart.Value then
audioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)
end
end
task.wait(info.waitTillFlashes)
-- and so on

Para verificar se um jogador está dentro de casa, usamos uma função de ajuda inVolumesCheckerFunc, que vai por cima de volume pré-posicionado aproximando áreas dentro de áreas, e verifica se a posição do jogador está dentro de qualquer deles (PointInABox). Nós poderíamos ter detectado detecção baseada em toque, mas encontramos que quando um jogador se

Para verificar se um jogador está em áreas corrompidas, invocamos uma função de ajuda gameStateInfoFunc, que verifica o estado de jogo atual. Para jogar um som aleatório de um pasta, usamos também uma função de ajuda PlayOneShot. Para os relâmpagos em si, esses foram super fáceis de criar no Photoshop; nós desenhamos uma linha curva, então adicionamos

Utilizando sistemas de emissão de partículas

Os heróis relâmpagos são apoiados por um sistema de partículas que suga uma nuvem distante criando a impressão de uma camada de nuvem no fundo capturando luz de distantes ataques, ou iluminação de nuvem para nuvem. Nós alcançamos esse efeito através de um sistema de partículas muito simples que acende uma nuvem em um bilhete de nuvem no lado periférico da nuvem principal. O sistema emite uma nuvem de partículas periodically com uma curva de transparência

Fazendo Árvores Soprar no Vento

Depois de termos as nuvens e o relâmpago funcionando da maneira que queríamos, nós então precisávamos adicionar dois outros componentes principais de uma tempestade: o vento e a chuva! Esses elementos apresentaram alguns desafios, incluindo precisar trabalhar dentro dos limites atuais do nosso sistema de física e efeitos especiais. Por exemplo, fazer as árvores se mover

Sabíamos que realmente vendíamos o efeito do vento e chuva, precisávamos de árvores em si mesmas para se movimento. Existem algumas maneiras de fazer isso dentro do motor, incluindo mover peças usando plugins que estão disponíveis publicamente, usando TweenService ou animando modelos diretamente. Para nossos propósitos,

Começamos por derrotar várias árvores do Pacote de Modelos de Endorse - Floresta Assets. Como essas árvores já existiam, e nossa experiência teve lugar no noroeste do Pacífico, isso nos salvou um tempo cedo de ter que criar cada modelo de árvore.

O pacote Floresta contém vários tipos de árvores, que podem salvar seu tempo em suas próprias experiências.

Depois de escolhermos nossas árvores, sabíamos que precisávamos delas. Skinning um malha é o ato de adicionar articulações (ou ossos) à uma malha em outro aplicativo de modelagem 3D, como Blender ou Maya, então aplicar a influência às articulações

Sabíamos que queríamos economizar tempo e reutilizar a mesma animações, então construímos nossa primeira rede de árvores e certificamos-nos de que os nomes das juntas fossem genéricos, pois queríamos usar esses nomes nas juntas para os outros árvores. Nós também sabí

As árvores têm juntas principais, secundárias e terciárias para que pudéssemos ter um movimento crível de ser soprados pela ventilação.

Uma vez que tivéssemos criado nossas articulações/ossos, era hora de criar uma animação de teste para mover ao redor de todas as articulações e ossos no Studio para ver se ela se movia da maneira que queríamos. Para fazer isso, tínhamos que importar a árvore no Studio atrav

A mesma hierarquia dentro do Studio.

Depois de estarmos satisfeitos com os resultados naquele árvore, era hora de testar a mesma animação em uma árvore diferente! Nós já sabíamos que seria a mesma animação entre as diferentes ricas para cada digitarde árvore, então nós apenas certificamos que nossa animação parecia que era geral o suficiente para trabalhar entre uma alta Redwood e um robusto Beechwood árvore!

A animação que importamos na árvore de Redwood.

Para fazer isso, nós pegamos a árvore de Beechwood daquele Forest Pack e construímos um rig / plataforma / equipamentosimilar, usando o mesmo nome exato para as juntas. Isso foi para que a animação que importamos anteriormente pudesse ser aplicada a essa árvore também. Como as animações eram todas baseadas em articulações giratórias, não importava o quão grande, pequeno, alto ou largo a árvore fosse!

A árvore Beechwood tem o mesmo nome exato para suas articulações, apenas não a mesma quantidade. Isso é bom, já que o sistema de animação só aplicará animação às articulações específicas que correspondem ao nome nelas! Por esta razão, podemos aplicar as mesmas animações a qualquer coisa que corresponda aos nomes das articulações!

Depois de termos rigado e pintado a árvore de Beechwood, podemos então importá-la e aplicar a mesma animação exata. Isso significou iteração e edição apenas necessárias para serem feitas em um arquivo, e também salva na performance com menos animações quando a experiência é executada.

Usando o Editor de Animação, podemos aplicar a mesma animação de árvore de Redwood à árvore de Beechwood!

Uma vez que tínhamos todos os tipos de árvores que queríamos animações, nós fazemos cada um em pacotes para que pudéssemos continuar editando e atualizando enquanto estamos jogando várias das animações ao redor da área principal da experiência. Como sabíamos que eles tinham um custo de desempenho, nós os usamos de forma ponderada ao redor da casa onde o efeito foi mais valioso! No futuro, à medida que isso se

Usamos árvores animadas imediatamente ao redor da casa onde o vortex estava mais forte e o efeito visual seria o mais impactante para os jogadores.

Fazendo Números da Tempestade

Queríamos que a chuva parecesse pesada, e para que a névoa e os débris pudessem soprar através das árvores. Para fazer isso, configuramos algumas peças invisíveis para atuar como volume de partículas com filho emissor de partículas imediatamente abaixo das grandes nuvens de tempestade. Devido ao limite de

Usamos vários volumes para obter tanto a quantidade de chuva quanto a cobertura específica de chuva que queríamos.

As partículas de chuva usaram uma nova propriedade de emissor de partículas ParticleEmitter.Squash que permite que você faça uma partícula mais longa ou coloque. É particularmente

Um valor de Squash de 3 começa a esticar a textura mais longamente.
Um valor Squash de 20 estica as partículas muito mais tempo, mas também precisávamos aumentar o valor Size também.

Para o pó, névoa e folhas voando, era muito mais simples adicionar um único volume maior cobrindo menos áreas porque não precisávamos de uma tonelada de partículas em uma vez. Começamos por configurar um volume e obtive a frequência das partículas onde queríamos elas.

Acabou sendo um volume de algumas peças de partículas, então não tivemos partículas entrando na casa, e porque não sentimos que eles precisavam se mover através das árvores como a neblina fez.
O volume da partícula de neblina foi muito maior, já que as partículas eram grandes e não era necessário ser tão preciso com a localização.

Depois disso, nós fizemos nossas texturas de folha soprando e vento, e configuramos as partículas para que todas girassem/se movêssem em diferentes taxas e começássem em diferentes velocidades. Isso significou que as maiores partículas de névoa interagiriam mais naturalmente e não parecessem tão repetitivas, especialmente dado o tamanho delas.

Particula de Fumaça
Particula de Folha

O resultado foi alguma ação grande entre as árvores se movendo, a janela soprando e o relâmpago para criar o efeito da tempestade ao redor do olho central da tempestade.

Configurando o Olho da Tempestade

O olho de pedra quebrado com um núcleo brilhante está destinado a dar às jogadores a primeira dica de que algo sinistro e arcano está ocorrendo na casa que eles devem explorar mais a frente. Como nossa cena é escura e o olho está muito longe do céu, foi importante criar uma silhueta de pedra quebrada de

Configurar a iluminação final no início da sua cena pode economizar muito trabalho. Você não poderia ver detalhes de superfície nos anéis com a iluminação final da nossa cena, então não havia necessidade de gastar o tempo colocando-os lá!

A distância do jogador também significou que podíamos confiar inteiramente em um mapa normal para os detalhes da superfície do olho, para que o malha fosse apenas uma esfera plana! Nós esculpimos os detalhes em uma malha de polímero de alta performance e assamos seu mapa normal em uma esfera muito menor para que pudéssemos obter todo o detalhe sem o custo de desempenho massivo.

Alto polímero escultura
Malha de baixa densidade
O low poly mesh com as informações normais da escultura de alta polia assada em

Para adicionar um senso sobrenatural aos olhos e para enfatizar sua presença, decidimos criar uma magma brilhante e neon que se espalharia por seus rachaduras. Embora não haja um canal emissivo para a aparência

A pintura de vértice na esfera interna. Criamos um gradiente que fosse mais leve ao redor do olho para dar uma sensação de profundidade e interesse visual maior.

Outro desafio que enfrentamos ao criar o olho foi imposto pelo nosso uso de streaming combinado com a distância do malhado jogador. Dado a centralidade desta est

Pudemos adicionar movimento ao olho e seus anéis graças ao mesmo script que usamos para girar as malhas de mundo. Para um toque final, decidimos adicionar uma dica à presença de outro mundo além

A imagem que usamos para criar uma ilusão de um mundo além das nuvens. Quando os jogadores estão longe de algo, uma imagem simples pode ser suficiente para criar a ilusão de mais profundidade e complexidade em sua cena!

Fazendo a Expansão da Pantry

Uma das coisas mais divertidas para produzir foram os espaços corrompidos, onde podíamos subverter as expectativas dos jogadores de realidade alterando-as literalmente ao seu redor. Por exemplo, no quebra-cabeça do pai, queríamos emular um momento semelhante a um pesadelo, onde não importa o quão rápido você executar, o salão parece ficar cada vez mais longo. Decidimos fazer uma pantry expandida que ficaria longe dos jogadores enqu

Configuramos isso com um simples movimento das paredes, e um layout inteligente de nossas salas que apareceria em ambos os lados da pantry. No estado normal da sala, a pantry era um corredor simples, mas no espaço corrupto, era realmente muito maior com várias alas e uma parede falsa!

O estado corrupto da panela da cozinha.
A parede falsa se movendo para longe dos jogadores.

A parede falsa era um grupo de modelos que nós movíamos de volta quando os jogadores entravam em um volume de gatilho, que era uma parte transparente antes na pantry que eles caminhariam através. Aquele gatilho também foi usado em um script semelhante aos usados em todas as nossas portas, que chamou o TweenService para mover de uma posição de início e término para a parede. N

O volume da peça ativa uma parede falsa atrás dele para se mover para seu ponto de destino. É feito visível nesta imagem com um tom amarelo.
Target_Closed era uma parte de metas genérica que usamos em todas as nossas portas para onde elas devem girar. Aqui, foi repurposado para dizer à parede do corredor onde ir.

Como TweenService é um sistema tão geral, todo o nosso modelo de dados de parede tinha que conter os mesmos componentes. Por exemplo, a seguinte imagem é um exemplo de um script de porta geral que chama um som definido por um "valor" abaixo do modelo "Grow_Wall".

Aquele mesmo script, com algumas modificações no seguinte código de exemplo, também gerou áudio para o movimento da panela. Isso adicionou muito ao movimento!


local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local model = script.Parent
local sound = model.Sound.Value
local trigger = model.Trigger
local left = model.TargetL_Closed
local right = model.TargetR_Closed
local tweenInfo = TweenInfo.new(
model.Speed.Value, --Velocidade/Tempo de Ajuste da Porta
Enum.EasingStyle.Quart, --Estilo de Aplainar
Enum.EasingDirection.InOut, --Direção de fácil
0, --Repetir Contagem
false, --Reverter verdade
0 --Atraso
)
local DoorState = {
["Closed"] = 1,
["Opening"] = 2,
["Open"] = 3,
["Closing"] = 4,
}
local doorState = DoorState.Closed
local playersNear = {}
local tweenL = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Open.CFrame})
local tweenR = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Open.CFrame})
local tweenLClose = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Closed.CFrame})
local tweenRClose = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Closed.CFrame})
local function StartOpening()
doorState = DoorState.Opening
sound:Play()
tweenL:Play()
tweenR:Play()
end
local function StartClosing()
doorState = DoorState.Closing
--modelo["Porta"]:Jogar()
tweenLClose:Play()
tweenRClose:Play()
end
local function tweenOpenCompleted(playbackState)
if next(playersNear) == nil then
StartClosing()
else
doorState = DoorState.Open
end
end
local function tweenCloseCompleted(playbackState)
if next(playersNear) ~= nil then
StartOpening()
else
doorState = DoorState.Closed
end
end
tweenL.Completed:Connect(tweenOpenCompleted)
tweenLClose.Completed:Connect(tweenCloseCompleted)
local function touched(otherPart)
if otherPart.Name == "HumanoidRootPart" then
local player = Players:GetPlayerFromCharacter(otherPart.Parent)
if player then
--print("toque")
playersNear[player] = 1
if doorState == DoorState.Closed then
StartOpening()
end
end
end
end

Uma vez que tínhamos a parede falsa se movendo para trás da sala, precisávamos do resto do conteúdo para se mover com ele. Para fazer isso, precisávamos de todos os itens soltos na pantry para serem soldados na parede enquanto se movia. Para fazer isso, precisávamos usar Con約束 Con限 , para que pêssemos em todos os itens na pantry para se mover como um ú

Fazendo a Corrupta Treehouse

O Studio é um motor baseado em física fantástico que você pode usar para criar tudo, desde um portão balançando até uma plataforma girando. Com nossa demonstração, queríamos usar a física para criar um senso de realismo em um conjunto de ambientes de outra forma não realista. Usando apenas alguns constrangimentos , você pode criar alguns cursos de obstáculos divertidos e desafiadores dentro de suas próprias experiências!

Constraints > são um grupo de motores baseados em pisos que alinham objetos e limita comportamentos. Por exemplo, você pode usar uma restrrição de pano para conectar-se a objetos para manter-os em

O quebra-cabeça do filho começou com os jogadores na mesma sala, mas tudo estava ao lado.

Quando os jogadores chegaram à área principal do quebra-cabeça, eles foram recebidos com uma visão familiar no Roblox: uma pista de obstáculos. Essa pista de obstáculos particular consistia em várias plataformas giratórias e paredes giratórias, juntamente com "áreas seguras" que progrediam a história. Nós nos concentraremos nos elementos giratórios/giratórios.

A aparência mente-bending escondeu o fato de que o gameplay aqui era muito simples.

Por que usamos restrições aqui? Porque TweenService ou outros métodos não moveriam o jogador enquanto eles estavam em cima deles. Sem o objeto se movendo o jogador, alguém poderia pular em uma plataforma e ela giraria a partir deles. Em vez disso, queríamos que os jogadores navegassem atrav

Você pode assistir seus amigos girando enquanto tenta navegar pelo obstacle course também.

Para fazer isso, precisávamos usar primeiro recursos de nosso kit atual e adicionar qualquer novo conteúdo para um efeito visual. Nós fizemos algumas paredes e plataformas incompletas com buracos nelas para contar a história da avó que construiu a casa na árvore. Como não queríamos criar um monte de plataformas e peças de base diferentes, nós fizemos 4 peças de base diferentes e peças de carvalho separ

Sabíamos que, uma vez que estávamos usando restrições, não poderíamos ancorar essas malhas, pois elas não se moveriam mesmo com a presença de uma restrição/mot

Agora era hora de configurar o comportamento real da restrição de dobradiça, e adicionar os acessórios que agiriam como a orientação da peça e a restrição juntos. Colocamos o acessório de giro na Motor_Turn, que as peças de caminhada estavam soldadas, e out

Para manter as plataformas girando em uma velocidade constante, então configuramos as propriedades HingeConstraint.AngularVelocity, HingeConstraint.MotorMaxAcceleration e HingeConstraint.MotorMaxTorque para valores que permitiriam movimento e impediriam interrupção se um jogador pular sobre ela.

Anex0 era basicamente o ancor para a articulação e Anex1 representava a articulação em si. Tínhamos a articulação girando constantemente, mas você também pode usar uma restrição de articulação para portas.

Agora, precisávamos fazer as paredes giratórias. As paredes precisavam girar em seu centro aparente, e sabíamos que queríamos que elas pudessem lidar com qualquer orientação em relação ao resto do nível. Como as plataformas, nós construímos essas para que todas as paredes fossem ancoradas e soldadas ao Motor_Turn.

Queríamos reutilizar o máximo de malhas de arvore reais para salvar no performance, então seguimos um caminho semelhante ao das plataformas. Vários tipos de parede foram feitos que podiam se agarrar em diferentes combinações para alguma variação.

Usamos objetos Texture em cima de objetos SurfaceAppearance para adicionar alguma variação aos nossos materiais de base. Texturas</

Você pode ver tanto o comportamento semelhante quanto a configuração para a restrição de hinge e também como usamos objetos Class.Texture.

Depois de testarmos algumas plataformas e paredes giratórias, nós fizemos várias variações e jogamos com seu posicionamento para garantir que o obstacle course fosse desafiador, mind-bending e também limpo onde o jogador precisava ir! Levou algumas ajustes para ambos os seus valores e posições para garantir que eles fossem bem. Tivemos vários pontos onde as plataformas e paredes estavam bater umas

Se você não tiver certeza de que seus objetos físicos estão aterrissando, você pode ativar Colisão fiel a partir do widget Opções de Visão no canto superior direito da janela de visualização 3D.

A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.
Quando a visualização de colisão é desativada, você pode ver a representação de geometria normal que é exibida no jogo.
Quando a visualização de colisão é ativada, você pode ver as folhas de árvore não tendo colisões, então elas não irão interferir com as plataformas giratórias ou paredes.

Como você pode ver abaixo das portas/janela, os buracos são visíveis, mas os detalhes menores, como sub-panela, não são. Isso é porque a propriedade CollisionFidelity para as paredes foi definida como Caixa. Não precisávamos da precisão para esses painéis, então, para economizar o custo de desem