Criar movimento em qualquer ambiente dentro de uma experiência ajuda-a a se sentir instantaneamente mais imersiva e realista para o nosso mundo, seja isso do movimento de árvores ambientais, portas reativas da interação do jogador ou até mesmo caixas que se movem quando baterem nelas.O Studio tem muitos métodos únicos para criar movimento para ajudar os mundos a se sentirem mais vivos, incluindo um sistema de física, TweenService , e animações, e analisar as necessidades específicas de sua experiência pode ajudá-lo a determinar qual usar.Nesta seção, demonstraremos como determinamos que tipo de movimento queríamos criar no Studio e quais ferramentas usamos para alcançar esses objetivos distintos.

Crie a tempestade
A tempestade passou por muitas iterações antes de nos decidirmos pelo que está ao vivo na Mystery of Duvall Drive.No início, pensamos na tempestade como um pilar de obsidiana gigante, e em iterações posteriores consideramos que fosse um portal gigante para o espaço corrupto.Depois de experimentar com muitas tempestades diferentes com aparências e sensações únicas para elas, acordamos em uma tempestade com um "olho" central menor porque:
- A tempestade deve dar aos jogadores uma sensação do impacto desse evento no mundo , incluindo árvores voando e detritos voando ao redor.
- O vórtice giratório da própria nuvem deve dar aos jogadores uma olhada no portal central sem revelar tudo .Isso incentivaria os jogadores a investigarem mais de perto para ver o que está acontecendo.
- O ponto de luz mais apertado nos permitiria focar na composição da casa , que é tanto o personagem principal e onde a maior parte do jogo está localizado.




Para fazer com que a tempestade se sinta dinâmica, agressiva e sempre em mudança dentro de seu ambiente, usamos os seguintes sistemas e recursos:
- TweenService - Para movimento na nuvem.
- Mudanças de iluminação - Para criar a iluminação da nuvem para a nuvem.
- Raios - Para a "iluminação volumétrica" e os raios de trovão.
- Emissores de Partículas - Para detritos voando até o portal e voando ao redor devido ao vento soprando.
- Animações - Para as árvores que estavam soprando no vento.
Adicione nuvens com texturas
Enquanto nuvems dinâmicas são ótimas para nuvens realistas de alta altitude normais, precisávamos de algo que se sentisse dramático e que pudéssemos direcionar e personalizar mais fortemente.Para fazer isso, aplicamos aparências de superfície objetos com semi-transparência a uma série de malhas de nuvem empilhadas e sobrepostas para falsificar a cobertura da nuvem.Por que os empilhamos e os sobrepusemos tão fortemente? Porque quando cada malha de nuvem se move a diferentes velocidades, elas se intersectam e criam formas de nuvem que vão dentro e fora umas das outras.Esse processo fez com que as nuvens se sentissem um pouco mais dinâmicas e naturais, apesar de apenas serem discos giratórios.Também foi importante que as nuvens fossem semi-transparentes, porque queríamos que os jogadores pudessem olhar através delas para ver algo brilhante no centro antes de chegar à casa!


Como cada malha de nuvem precisava ser massiva para envolver completamente a casa e transmitir quão enorme a tempestade era, sabíamos que precisávamos azulejar a textura que queríamos usar nas malhas de nuvem individuais para que ela se repetisse fortemente em toda a superfície da malha.Testamos os materiais que fizemos para a nuvem nessas peças simples, e depois os aplicamos ao vórtice!

Ao contrário de emissores de partículas ou raios, malhas nos permitiram ser capazes de refletir luz de cada malha, o que era importante quando queríamos implementar raios na nuvem para a nuvem.Também modelamos no torcimento para que a iluminação rebatendo dele parecesse ter profundidade! Isso foi importante especialmente em situações em que as exigências de desempenho da experiência reduziu os níveis de qualidade da aparência da nossa superfície.

Girar malhas de nuvem
Depois de estarmos satisfeitos com a aparência visual geral das nuvens, precisávamos fazê-la mover! Tínhamos as formas gerais de cada camada de nuvem em local, mas levou algumas tentativas e erros para garantir que o efeito giratório parecesse bom na prática.Inicialmente, tentamos usar constrangimentos para introduzir velocidade que levaria fisicamente as nuvens a se mover.Isso foi mais difícil do que queríamos que fosse para iterar mais tarde, e o jogador nunca interagiria com ele, então não precisávamos que fosse tão preciso em seu movimento.
Queríamos um método fácil de usar para girar instâncias que fossem muito distantes para serem interativas, como nuvens, ou muito pequenas ou decorativas para serem importantes para o jogo/física, como móveis internos como pequenas lâmpadas.Decidimos usar um LocalScript para reduzir a largura de banda cliente-servidor, permitir um movimento mais suave e fazer com que cada malha de nuvem seja capaz de ter uma taxa de rotação e atraso diferente.Para torná-lo mais genérico, também tornamos possível especificar o eixo de rotação.É possível usar 3 atributos, mas para o nosso caso usamos 3 valores: Axis , Delay e Speed .

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 marcação de instâncias.Utilizamos apenas um único LocalScript que lidou com todas as instâncias marcadas usando o CollectionService para que não tivéssemos uma tonelada de scripts para manter ao longo do processo de desenvolvimento.
Na nossa demonstração, partes do mundo são clonadas do ServerStorage no espaço de trabalho conforme necessário, então precisávamos lidar com casos em que objetos marcados foram criados e destruídos.Com LocalScripts, também temos que estar cientes do streaming, onde malhas e seus valores filhos podem ser transmitidos para dentro e para fora.Nós processamos objetos inicialmente colocados na função Init() e conectamos a CollectionService.GetInstanceAddedSignal e CollectionService.GetInstanceRemovedSignal para objetos marcados para lidar com objetos recém-criados/destruídos.A mesma função SetupObj foi usada para inicializar novos objetos em Init() e em CollectionService.GetInstanceAddedSignal .
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 SetupObj de CollectionService.GetInstanceAddedSignal imediatamente, mas adicionamos um objeto em objInfoQueue.Com objetos de streaming e clonagem no servidor, quando CollectionService.GetInstanceAddedSignal é chamado, pode ser que ainda não tenhamos tido nossos Axis , Delay e Speed valores, então adicionamos o objeto em uma fila e chamamos SetupObj na próxima frame da função Update até que os valores estejam lá e pudéssemos lê-los na estrutura "info" por objeto.
Nós giramos as instâncias na função Update conectada ao batimento cardíaco.Nós temos a transformação pai (parentTransform), acumulamos um novo ângulo de rotação ( curObjInfo.curAngle ) com base na velocidade de rotação deste Objeto, calculamos a transformação local ( rotatedLocalCFrame) , e finalmente definimos para o CFrame .Observe que tanto o pai quanto o objeto podem ser um Model ou MeshPart, então tivemos que verificar IsA("Model") e usar um PrimaryPart.CFrame ou CFrame.
local parentTransformif parentObj:IsA("Model") thenif not parentObj.PrimaryPart then-- a parte principal pode não estar transmitida aindacontinue -- espere que a parte primária se repliqueendparentTransform = parentObj.PrimaryPart.CFrameelseparentTransform = parentObj.CFrameendcurObjInfo.curAngle += dT * curObjInfo.timeToAnglelocal rotatedLocalCFrame = curObjInfo.origLocalCFrame * CFrame.Angles( curObjInfo.axisMask.X * curObjInfo.curAngle, curObjInfo.axisMask.Y * curObjInfo.curAngle, curObjInfo.axisMask.Z * curObjInfo.curAngle )if obj:IsA("Model") thenobj.PrimaryPart.CFrame = parentTransform * rotatedLocalCFrameelseobj.CFrame = parentTransform * rotatedLocalCFrameend
Verificamos se havia um válido Model.PrimaryPart para ser definido para lidar com streaming.Se uma atualização foi chamada em nosso objeto enquanto um Model.PrimaryPart (que pode apontar para uma malha filha) ainda não foi transmitido, nós apenas pularíamos a atualização.O sistema atual é uma segunda iteração da rotação de objetos e o sistema anterior funcionou de forma diferente: os valores eram 12 vezes diferentes! Para manter os mesmos dados, convertemos-los em nosso script, como "12 * obj.Speed.Value".
Projete quedas de relâmpago
Como o Studio não oferece um gerador de relâmpago fora da caixa e o sistema de partículas tinha algumas limitações que não funcionariam para os ataques de relâmpago do herói, tivemos que ser criativos com uma solução para os ataques de relâmpago do herói.Decidimos por dois sistemas principais para compor o relâmpago: raios texturizados para os ataques de relâmpago do herói que vêm do olho da tempestade são raios texturizados que revelam e sincronizam com efeitos de áudio e pós-processamento, e um efeito de partículas simples para o relâmpago distante da nuvem para a nuvem.
Raios de textura
Normalmente, usaríamos um sequenciador ou uma ferramenta de cronograma para controlar o tempo de um efeito de ataque de raio de iluminação como este, mas como o Studio ainda não oferece essa funcionalidade, decidimos escrever scripts que controlariam o tempo do raio de iluminação.A programação desse efeito é bastante simples, mas consegue alcançar os seguintes objetivos importantes:
- Os elementos dos golpes do raio, como suas texturas, brilho e atrasos, são randomizados com cada golpe.
- As alterações de áudio e pós-FX estão sincronizadas com as alterações de FX de greve.
- Jogadores que estão dentro ou na área corrompida não seriam capazes de vê-los ou ouvi-los.
Temos um lado do servidor Script que calcula vários parâmetros e prazos, envia-os a 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, preenchemos a estrutura de informação para que todos os clientes recebam os mesmos parâmetros.
No lado do cliente ( LightningVFXClient ), verificamos se este cliente deve executar o FX:
local function LightningFunc(info)
…
-- sem FX quando estiver dentro de casa
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 pré-adolescentes e usamos task.wait(number).Os parâmetros randomizados 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)-- Limparbeam.Brightness = 10ppCC.Brightness = maxPPBrightnessppBloom.Intensity = 1.1bottom.Position = top.PositiontweenBrightness:Play()tweenPPBrightness:Play()tweenPPBrightness:Play()tweenBottomPos:Play()tweenBrightness.Completed:Wait()-- áudioif audioFolder and audioPart thenif audioFolder.Value and audioPart.Value thenaudioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)endendtask.wait(info.waitTillFlashes)-- and so on
Para verificar se um jogador está dentro de casa, usamos uma função de auxílio inVolumesCheckerFunc, que passa por volumes pré-posicionados que aproximam áreas internas e verifica se a posição do jogador está dentro de qualquer um deles (PointInABox).Poderíamos ter usado detecção baseada em toque, mas descobrimos que quando um jogador toma um assento dentro do volume, eles não estão mais "tocando" o volume.Testar um ponto em algumas caixas é mais simples, e fazemos isso somente quando um jogador se move o suficiente da posição previamente testada.
Para verificar se um jogador está em áreas corrompidas, invocamos uma função auxiliar gameStateInfoFunc, que verifica o estado do jogo atual.Para tocar um som aleatório de uma pasta, também usamos uma função de auxílio PlayOneShot.Para os próprios raios, isso foi super fácil de criar no Photoshop; desenhamos uma linha ondulada e adicionamos um efeito de camada "Brilho Exterior".


Utilize sistemas de emissão de partículas
Os ataques de relâmpago de herói são suportados por um sistema de partículas que sugere um relâmpago distante criando a impressão de uma camada de nuvens no plano que captura luz de ataques distantes ou iluminação de nuvem para nuvem.Conseguimos esse efeito através de um sistema de partículas muito simples que lampeja um letreiro de nuvem na periferia da nuvem de tempestade principal.O sistema emite um período de partícula de nuvem periodicamente com uma curva de transparência aleatória:


Faça as árvores soprarem no vento
Depois de termos as nuvens e os raios funcionando do jeito que queríamos, precisávamos adicionar dois outros componentes principais de uma tempestade: o vento e a chuva! Esses elementos apresentaram alguns desafios, incluindo precisar trabalhar dentro das limitações atuais do Studio de nossos sistemas de física e efeitos especiais.Por exemplo, fazer com que as árvores se movam com vento real não é possível no motor de hoje, então utilizamos os efeitos de emissor de partículas e animações de personagens personalizados para as árvores.
Sabíamos para realmente vender o efeito do vento e da chuva, precisávamos que as próprias árvores se movessem.Existem algumas maneiras que você pode fazer isso dentro do motor, incluindo mover peças usando plugins que estão disponíveis publicamente, usando TweenService ou animar modelos diretamente.Para nossos propósitos, as animações nos deram a capacidade de controlar o movimento que queríamos fora de nossas árvores e nos permitiu usar uma única animação que pudéssemos compartilhar entre todas as árvores dentro da experiência.
Começamos por esfolar várias árvores do Endorse Model Pack - Forest Assets.Como essas árvores já existiam e nossa experiência ocorreu no Noroeste do Pacífico, isso nos poupou algum tempo no início de ter que criar cada modelo de árvore.

Depois de escolhermos nossas árvores, sabíamos que precisávamos retirar a casca delas. Skinar uma malha é o ato de adicionar articulações (ou ossos) a uma malha em outra aplicação de modelagem 3D, como Blender ou Maya, então aplicar influência a essas articulações/ossos para mover a malha.Isso é mais comumente usado em personagens humanóides, mas com personagens personalizados, você pode skinar praticamente qualquer coisa.
Sabíamos que queríamos economizar tempo e reutilizar a mesma animações, então construímos nosso primeiro modelo de árvore e garantimos que os nomes das articulações fossem genéricos porque queríamos usar esses mesmos nomes nos modelos das outras árvores.Sabíamos também que precisávamos incluir juntas/ossos primárias, secundárias e terciárias para que os troncos se dobrassem com o vento, os ramos balançassem e as folhas parecessem que estavam tremendo em resposta.Para esse processo, precisávamos criar um movimento secundário , que é um conceito de animação onde qualquer ação causa outras partes do objeto a reagirem a essa ação e parecerem alcançar o movimento inicial.

Uma vez que criamos nossas articulações/ossos, era hora de criar uma animação de teste para se mover por todas as articulações e ossos no Studio para ver se se moveu do jeito que queríamos.Para fazer isso, tivemos que importar a árvore no Studio através da configuração Rig Personalizado no Importador 3D , e então mover/animar a malha usando o Editor de Animação.Configuramos os materiais e texturas após esses testes, mas você pode ver o resultado abaixo.

Depois de termos ficado satisfeitos com os resultados naquela á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 os diferentes modelos para cada digitarde árvore, então apenas nos certificamos de que nossa animação parecesse suficientemente geral para funcionar entre uma Redwood alta e um Beechwood forte!

Para fazer isso, pegamos a árvore Beechwood daquele Pacote de Floresta e construímos um rig / plataforma / equipamentosimilar, usando o mesmo nome exato para as articulações.Isso foi para que a animação que tínhamos importado 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, pequena, alta ou larga a árvore fosse!

Depois de termos rigado e skinado a árvore Beechwood, poderíamos então importá-la e aplicar a mesma animação exata.Isso significava que a iteração e a edição só precisavam ser feitas em um arquivo e também foram salvas na performance com menos animações ao executar a experiência.

Uma vez que tivemos todos os tipos de árvore que queríamos animar, transformamos cada um em pacotes para que pudéssemos continuar a editar e atualizar enquanto jogávamos várias das animações ao redor da área principal da experiência.Como sabíamos que eles tinham um custo de desempenho, usamos-los com parcimônia ao redor da casa onde o efeito era mais valioso! No futuro, à medida que isso se torna mais performático, você poderá adicionar mais e mais instâncias de malha esfoladas!

Faça resíduos de tempestade
Queríamos que a chuva parecesse pesada, e para que a nebluma e os detritos soprassem através das árvores.Para fazer isso, configuramos algumas peças invisíveis para atuar como volumes de partículas com emissores de partículas filhos imediatamente abaixo das grandes nuvens de tempestade.Devido ao limite de contagem de partículas no Studio, não pudemos usar um emissor de partículas para todo o espaço.Em vez disso, adicionamos vários que tinham o mesmo tamanho um do outro em um padrão de grade sobre o espaço da área tocável, porque a presença das árvores significa que os jogadores não seriam capazes de ver muito longe.

As partículas de chuva aproveitaram uma nova propriedade de emissor de partículas ParticleEmitter.Squash que permite fazer uma partícula mais longa ou mais espalhada.É particularmente útil para a chuva porque significava que não precisávamos de uma grande textura de chuva, apenas esticar a que estava lá.Apenas saiba que se você aumentar o valor de ParticleEmitter.Squash , pode precisar aumentar também a propriedade geral ParticleEmitter.Size para que não seja muito magra! No geral, foi apenas uma questão de brincar com os valores até que conseguimos a chuva suficientemente pesada, mas não tanto que bloqueou a visibilidade da experiência!


Para a névoa, neve e folhas soprando através, era muito mais simples adicionar um único volume de parte maior que cobria menos áreas porque não precisávamos de uma tonelada de partículas correndo ao mesmo tempo.Começamos configurando um volume e obtivemos a frequência dos participantes onde os queríamos.


Depois disso, fizemos nossas folhas soprando e texturas de vento, e definimos as partículas para girar/mover em diferentes taxas e começar em diferentes velocidades.Isso significava que as partículas de neve maiores interagiriam de forma mais natural e não pareceriam tão semelhantes a uma textura repetitiva, especialmente dada a sua tamanho.



O resultado foi alguma grande ação 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.
Configure o olho da tempestade
O olho de pedra fraturado com um núcleo brilhante é destinado a dar aos jogadores a primeira dica de que há algo sinistro e arcano ocorrendo na casa que eles devem explorar mais a fundo.Como nossa cena é escura e o olho está muito alto no céu, foi importante criar uma silhueta de pedra fracturada credível, mas não foi tão importante criar detalhes de superfície de pedra credíveis porque os jogadores não seriam capazes de ver isso.Saber o que é realista para seus jogadores verem dentro da iluminação da sua cena antes de colocar muito tempo em detalhes desnecessários pode economizar muitos recursos no processo de desenvolvimento.


A distância do jogador também significava que poderíamos confiar inteiramente em um mapa normal para os detalhes da superfície do olho, então a malha é apenas uma esfera normal! Esculpimos os detalhes em uma malha de alta qualidade e assamos seu mapa normal em uma esfera de baixa qualidade para que pudéssemos obter todos esses detalhes bonitos sem o enorme custo de desempenho.



A fim de adicionar um sentimento sobrenatural ao olho e enfatizar sua presença, decidimos criar um magma brilhante e neon que iria se filtrar através de suas rachaduras.Embora não haja canal emissivo para a aparência da superfície, superamos esse obstáculo criando o olho a partir de 2 esferas: uma para a superfície rochosa do exterior e uma segunda, ligeiramente menor, para a magma brilhante.Em Pintor de Substância, criamos uma textura de cor base para a esfera externa com transparência nas áreas onde queríamos que o núcleo interno passasse.No Blender, nós "vertexamos pintado" a esfera interna de uma maneira barata e fácil de obter alguma variação de cores nela.

Outro desafio que enfrentamos ao criar o olho foi imposto pelo nosso uso de streaming combinado com a distância do olho do jogador.Dada a centralidade dessa estrutura, queríamos que ela sempre fosse visível apesar de sua distância, mas, sem nenhum hack em sua malha, os jogadores não conseguiram ver o olho a menos que estivessem no solário.Conseguimos forçar a presença constante do olho na cena adicionando alguma geometria ao olho e seus anéis.Essa geometria fica bem abaixo da superfície do terreno, e isso é suficiente para enganar o motor a pensar que a esfera está mais perto do jogador do que realmente é e sempre a transmitir.Isso deve ser feito com bastante parcimônia, porém, pois forçar que muitos objetos grandes sejam transmitidos pode negar os benefícios da transmissão ativada e impactar negativamente o performancedo jogo.
Conseguimos adicionar movimento ao olho e seus anéis graças ao mesmo script que usávamos para rotacionar as malhas da nuvem.Para um toque final, decidimos adicionar uma dica à presença de outro mundo além das nuvens, mas tivemos que adotar uma abordagem criativa para evitar adicionar mais geometria à cena e, além disso, ter que lidar com os obstáculos mencionados anteriormente causados pela ativação do streaming.Criamos uma cena que tinha muita profundidade devido ao tamanho e distância relativa dos objetos, renderizamos uma imagem dessa cena, então usamos a imagem dita como um adesivo em uma peça colocada logo atrás do olho da tempestade.Usamos o mesmo método para girar esta peça como usamos para o olho e seus anéis.

Faça a panela expandida
Uma das coisas mais divertidas de produzir eram os espaços corrompidos, onde poderíamos subverter as expectativas dos jogadores de realidade, literalmente alterando-a ao redor deles.Por exemplo, no quebra-cabeça do pai queríamos emular um momento semelhante a um pesadelo onde, não importa quão rápido você executar, a sala parece ficar cada vez mais longa.Decidimos fazer uma despensa expandida que fugiria dos jogadores à medida que eles buscavam ingredientes para transformar a sala de volta ao seu estado normal.
Configuramos isso com um simples movimento das paredes e um layout inteligente de nossos quartos que apareceria em ambos os lados da despensa.No estado normal da sala, o depósito era um simples corredor, mas no espaço corrompido, na verdade era muito mais longo com várias asas e uma parede falsa!


A parede falsa era um grupo de modelos que moveríamos de volta no momento em que os jogadores entrassem em um volume de gatilho, que era uma parte transparente mais cedo na despensa que eles passariam.Esse gatilho também foi usado em um script semelhante aos usados em todas as nossas portas, que chamou o TweenService para se mover de um objetivo para outro.Usamos volumes de parte para dizer à operação de transição onde as posições de início e fim eram para a parede.


Como TweenService é um sistema tão geral, todos os nossos modelos de dados de parede tinham que conter os mesmos componentes.Por exemplo, um script geral "Door_Script" toca um som definido por um "valor" abaixo do modelo "Grow_Wall".Esse mesmo script, com algumas modificações no seguinte exemplo de código, também acionou o áudio para o movimento da despensa.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, --Tempo/Velocidade da Porta Tween
Enum.EasingStyle.Quart, --Estilo de facilitação
Enum.EasingDirection.InOut, --Direção de Facilitação
0, --Repetir Contagem
false, --Inversão verdadeira
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"]:Play()
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("touch")
playersNear[player] = 1
if doorState == DoorState.Closed then
StartOpening()
end
end
end
end
Uma vez que tivemos a parede falsa se movendo para trás da sala, precisávamos do resto do conteúdo se mover com ela.Para fazer isso, precisávamos que todos os itens soltos na despensa fossem soldados à parede à medida que se movia.Usando Restrições de solda, rapidamente pudemos soldar todos os objetos à parede da despensa para mover como um único Objeto.Fazer isso significava que tínhamos a opção de des soldar esses itens para que os jogadores pudessem bater neles e os derrubar!
Faça a casa na árvore corrompida
O Studio é um motor fisicamente baseado fantástico que você pode usar para criar tudo, de um portão oscilante a uma plataforma girante.Com a nossa demonstração, queríamos usar a física para criar um senso de realismo em um conjunto de ambientes de outro modo irrealista.Usando apenas algumas restrições , você pode criar algumas pistas de obstáculos divertidas e desafiadoras dentro de suas próprias experiências!

Restrições são um grupo de motores fisicamente baseados que alinham objetos e restringem comportamentos.Por exemplo, você pode usar uma restrição de vara para se conectar a objetos para mantê-los a uma distância fixa uns dos outros ou a restrição de corda para ter uma lâmpada pendurada no fim da linha.Para o quebra-cabeça do filho em que os jogadores são transportados para o estado corrupto do estudo, queríamos literalmente virar o mundo do lado dele.Fazer isso subverteria as expectativas dos jogadores da realidade e das regras lá, enquanto ainda usaria o sistema de física como pretendido!

Uma vez que os jogadores chegaram à área principal do quebra-cabeça, eles foram recebidos com uma vista familiar no Roblox: uma pista de obstáculos.Este percurso de obstáculos específico consistiu em várias plataformas giratórias e paredes giratórias, além de "áreas seguras" que progrediram a história.Vamos nos concentrar nos elementos giratórios/giratórios.

Por que usamos restrições aqui? Porque ou outros métodos não moveriam o jogador enquanto ele estivesse sobre eles.Sem o objeto mover o jogador, alguém poderia pular em uma plataforma e ela giraria debaixo deles.Em vez disso, queríamos que os jogadores navegassem por uma plataforma girante enquanto tentavam fazer seu pulo para a próxima.Devido a essa abordagem, os jogadores se sentiram enraizados onde estavam enquanto tomavam uma decisão com como proceder através do percurso, e não precisávamos fazer nada de especial para garantir que se movessem com uma superfície rotativa!

Para fazer isso, precisávamos primeiro usar recursos do nosso kit atual e adicionar qualquer conteúdo novo para um efeito visual.Fizemos algumas paredes e plataformas incompletas com buracos nelas para contar a história da avó construir a casa na árvore.Como não queríamos criar um monte de plataformas únicas, fizemos 4 peças de base e peças de guarda-corpo separadamente.Isso nos permitiu misturar e combinar peças de base e guarda-corpo individuais para ter muita variedade.

Sabíamos que, uma vez que estávamos usando restrições, não seríamos capazes de ancorar essas malhas porque elas não se moveriam mesmo com a presença de uma restrição/motor que as dirigisse.A restrição necessária para ser filha de algo que foi ancorado para que a plataforma não apenas caia do mundo.Resolvemos isso através de uma parte que chamamos de Motor_Ancorar que tinha uma restrição de dobradiça para impulsionar o movimento geral da plataforma.Depois disso, precisávamos das duas malhas para se moverem como uma, então criamos uma peça que chamamos de Motor_Turn , e então soldamos as duas malhas a ela.Dessa forma, a restrição seria capaz de trabalhar em uma única peça, em oposição a várias dobradiças trabalhando com várias peças.

Agora era hora de configurar o comportamento real da própria restrição de dobradiça e adicionar os anexos que atuariam como a orientação da peça e da restrição juntos.Colocamos o acessório de giro no Motor_Turn, ao qual as peças de caminhada foram soldadas, e outro acessório para o comportamento do ancorador no próprio Motor_Anchor, ao lado da restrição de dobradiça.Como isso precisava girar por conta possuir, em oposição a ser influenciado pelo jogador (como uma dobradiça de porta), definimos o HingeConstraint.ActuatorType para Motor , tratando a restrição como um motor que se move sozinho.
Para manter as plataformas girando a uma velocidade constante, então configuramos as propriedades HingeConstraint.AngularVelocity, HingeConstraint.MotorMaxAcceleration e HingeConstraint.MotorMaxTorque para valores que permitiriam o movimento e impediriam a interrupção se um jogador saltasse sobre ela.

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, construímos essas para que todas as paredes fossem desancoradas e soldadas ao Motor_Turn.

Usamos Texture objetos sobre SurfaceAppearance objetos para adicionar alguma variação aos nossos materiais de base.Texturas, semelhantes a Imagens, permitem que você coloque uma imagem em um plano de uma malha.Isso pode ser útil se você quiser adicionar sujeira a uma parede de tijolos ou fazer com que a madeira pareça envelhecida ao usar o mesmo material de madeira base. Texture objetos têm um comportamento ligeiramente diferente do que Decal objetos na medida em que você pode azulejar e deslocar a imagem da maneira que quiser, o que é muito útil se você quiser ser capaz de escalar sua textura sobreposta e não se importa que ela se repita!

Depois de testarmos algumas plataformas e paredes giratórias, fizemos várias variações e brincamos com sua colocação para garantir que a pista de obstáculos fosse desafiadora, intrigante e também clara onde o jogador precisava ir! Levou algum ajuste para ambos os valores e posições para fazê-los funcionar bem.Tivemos vários pontos em que as plataformas e paredes estavam batendo umas nas outras ou nos arredores, mas com algumas movimentações e testes frequentes, conseguimos pousar nas configurações que temos na demonstração!
Se você não tem certeza de quais objetos físicos estão atingindo, você pode ativar fidelidade de colisão a partir do widget Opções de visualização no canto superior direito do janela3D.



Como você pode ver abaixo, os buracos da porta/janela são visíveis, mas os detalhes menores, como o sub-painel, não são.Isso porque a propriedade CollisionFidelity para as paredes foi definida como Caixa .Não precisávamos da precisão desses painéis, então, para economizar no custo de desempenho, isso foi detalhado o suficiente para que os jogadores pudessem saltar.Com as plataformas e paredes giratórias concluídas, só precisávamos adicionar recursos de detalhe, como caixas e lâmpadas, então estava pronto para ser jogado!
