Crear movimiento en cualquier entorno dentro de una experiencia ayuda a que se sienta instantáneamente más inmersiva y realista para nuestro mundo, ya sea que provenga del movimiento del árbol ambiental, puertas reactivas de la interacción del jugador o incluso cajas que se mueven cuando se tropiezan con ellas.Studio tiene muchos métodos únicos para crear movimiento con el fin de ayudar a los mundos a sentirse más vivos, incluyendo un sistema de físicas, TweenService , y animaciones, y analizar las necesidades específicas de tu experiencia puede ayudarte a determinar cuál usar.En esta sección, demostraremos cómo determinamos qué tipo de movimiento queríamos crear en Studio y qué herramientas usamos para lograr estos objetivos distintos.

Crear la tormenta
La tormenta pasó por muchas iteraciones antes de que nos decidimos por lo que está en vivo en The Mystery of Duvall Drive.Al principio, pensamos en la tormenta como un pilar de obsidiana gigante, y en las iteraciones posteriores la consideramos como un portal gigante al espacio corrupto.Después de experimentar con muchas tormentas con apariencias y sensaciones únicas para ellas, nos decidimos por una tormenta con un "ojo" central más pequeño porque:
- La tormenta debería dar a los jugadores una sensación del impacto de este evento en el mundo , incluyendo árboles que soplaban y escombros volando alrededor.
- El vórtice giratorio de la nube misma debería darle a los jugadores un vistazo al portal central sin revelar todo .Esto animaría a los jugadores a investigar más cerca para ver qué está pasando.
- El punto de luz más estricto nos permitiría centrarnos en la composición de la casa , que es tanto el personaje principal como donde se encuentra la mayor parte del juego.




Para hacer que la tormenta se sienta dinámica, agresiva y siempre cambiante dentro de su entorno, ambiente, usamos los siguientes sistemas y características:
- TweenService - Para el movimiento de la nube.
- Cambios de iluminación - Para crear la iluminación de nube a nube.
- Rayos - Para la "iluminación volumétrica" y los rayos.
- Emisores de partículas - Para escombros que vuelan hasta el portal y vuelan alrededor debido a la corriente de aire.
- Animaciones - Para los árboles que soplaban con el viento.
Añadir nubes con texturas
Si bien las nubes dinámicas son geniales para nubes realistas de alta altitud normales, necesitábamos algo que se sintiera dramático y que pudiéramos dirigir y personalizar más fuertemente.Para hacer esto, aplicamos apariencia de superficie objetos con semi-transparencia a una serie de mallas de nubes apiladas y superpuestas para falsificar la cobertura de nubes.¿Por qué las apilamos y las superponemos tan fuertemente? Porque cuando cada malla de nube se mueve a diferentes velocidades, se intersectan y crean formas de nube que van dentro y fuera el uno del otro.Este proceso hizo que las nubes se sientan un poco más dinámicas y naturales, a pesar de ser discos giratorios.También era importante que las nubes fueran semi transparentes, ¡ya que queríamos que los jugadores pudieran mirarlas para ver algo brillante en el centro antes de llegar a la casa!


Dado que cada malla de nube necesitaba ser masiva para rodear completamente la casa y transmitir cuán enorme era la tormenta, sabíamos que necesitábamos azulejar la textura que queríamos usar en las mallas individuales de nube para que se repitiera fuertemente en toda la superficie de la malla.Probamos los materiales que hicimos para la nube en estas piezas simples, ¡y luego los aplicamos al vórtice!

A diferencia de los emisores de partículas o los rayos, las mallas nos permitieron ser capaces de rebotar la luz de cada malla, lo que era importante cuando queríamos implementar relámpagos de nube a nube.También modelamos en el torcido para que la iluminación que rebota de él parezca que tiene profundidad! Esto fue importante especialmente en situaciones en las que las demandas de rendimiento de la experiencia redujeron los niveles de calidad de la apariencia de nuestra superficie.

Rotar mallas de nube
Después de que estábamos satisfechos con la apariencia visual general de las nubes, ¡necesitábamos moverla! Teníamos las formas generales de cada capa de nube en su lugar, pero tuvo que pasar un poco de prueba y error para asegurarnos de que el efecto giratorio se viera bien en la práctica.Inicialmente probamos el uso de restricciones para introducir velocidad que impulsaría físicamente a las nubes a herramienta de movimiento.Esto fue más difícil de lo que queríamos que fuera para iterar más tarde, y el jugador nunca interactuaría con él, por lo que no necesitábamos que fuera tan preciso en su movimiento.
Queríamos un método fácil de usar para rotar instancias que fueran demasiado lejos para ser interactuables, como las nubes, o demasiado pequeñas o decorativas para ser importantes para el juego/física, como los muebles interiores como pequeñas lámparas.Decidimos usar un LocalScript para reducir la banda de cliente-servidor, permitir un movimiento más suave y que cada malla de nube sea capaz de tener una velocidad de rotación y retraso diferente.Para hacerlo más general, también hicimos posible especificar el eje de rotación.Es posible usar 3 atributos, pero para nuestro caso usamos 3 valores: Axis , Delay y Speed .

Como en muchos casos en la demostración, usamos una etiqueta LocalSpaceRotation para que pudiéramos administrar instancias afectadas en Studio usando un complementode etiquetado de instancias.Utilizamos solo una sola LocalScript que manejó todas las instancias etiquetadas usando el CollectionService para que no tuviéramos una tonelada de scripts para mantener durante todo el proceso de desarrollo.
En nuestra demostración, se clonan partes del mundo desde el ServerStorage en el espacio de trabajo según sea necesario, por lo que necesitábamos manejar casos en los que se crearon y destruyeron objetos etiquetados.Con LocalScripts , también tenemos que estar conscientes de la transmisión, donde las mallas y sus valores hijos pueden transmitirse dentro y fuera.Procesamos inicialmente objetos colocados en la función Init() y conectamos a CollectionService.GetInstanceAddedSignal y CollectionService.GetInstanceRemovedSignal para objetos etiquetados para manejar objetos recién creados/destruidos.La misma función SetupObj se utilizó para inicializar nuevos objetos en Init() y en 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 es un mapa que tiene información para todos los objetos relevantes, como su velocidad de rotación y eje.Tenga en cuenta que no llamamos SetupObj de CollectionService.GetInstanceAddedSignal inmediatamente, pero agregamos un objeto en objInfoQueue .Con objetos de transmisión y clonación en el servidor, cuando se llama CollectionService.GetInstanceAddedSignal , es posible que aún no tengamos nuestros Axis , Delay y Speed valores, por lo que agregamos el objeto a una cola y llamamos SetupObj en los marcos siguientes de la función Update hasta que los valores estuvieran allí y pudimos leerlos en la estructura "info" por objeto.
Rotamos las instancias en la función Update conectada al latido del corazón.Obtuvimos la transformación padre (parentTransform), acumulamos un nuevo ángulo de rotación ( ) basado en la velocidad de rotación de este objeto, calculamos la transformación local ( ) y finalmente lo establecimos en el >.Tenga en cuenta que tanto el padre como el objeto pueden ser un Model o MeshPart , por lo que tuvimos que verificar IsA("Model") y usar cualquiera de PrimaryPart.CFrame o CFrame .
local parentTransformif parentObj:IsA("Model") thenif not parentObj.PrimaryPart then-- la parte principal puede que aún no se transmitacontinue -- espera a que la parte principal se re複iqueendparentTransform = 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
Comprobamos que se estableció un válido Model.PrimaryPart para manejar el streaming.Si se llamó una actualización a nuestro objeto mientras un Model.PrimaryPart (que puede apuntar a una malla hija) aún no se transmitió, simplemente omitiríamos la actualización.El sistema actual es una segunda iteración de la rotación de objetos, y el sistema anterior funcionó de manera diferente: los valores eran 12 veces diferentes! Para mantener los mismos datos, lo convertimos en nuestro script, como "12 * obj.Speed.Value".
Diseñar golpes de rayo
Debido a que Studio no ofrece un generador de rayos fuera de la caja, y el sistema de partículas tenía algunas limitaciones que no funcionarían para los rayos de relámpago del héroe, tuvimos que ser creativos con una solución para los rayos de relámpago del héroe.Decidimos sobre dos sistemas principales para conformar el rayo: rayos texturados para los golpes de rayo del héroe que provienen del ojo de la tormenta son rayos texturados que se sincronizan con efectos de audio y post-procesamiento, y un efecto de partículas simple para el rayo de nube a nube distante.
Rayos de textura
Normalmente usaríamos una herramienta secuenciadora o cronológica para impulsar el tiempo de un efecto de golpe de rayo iluminador como este, pero dado que Studio aún no ofrece esta funcionalidad, decidimos escribir scripts que controlarían el tiempo de un golpe de rayo iluminador.La programación de este efecto es bastante sencilla, pero logra los siguientes objetivos importantes:
- Los elementos de los golpes de rayo, como sus texturas, brillo y retrasos, se aleatorizan con cada golpe.
- Los cambios de audio y post FX están sincronizados con los efectos de ataque FX.
- Los jugadores que estén en el interior o en la zona corrupta no podrían verlos o escucharlos.
Tenemos un lado del servidor Script que calcula varios parámetros y tiempos, los envía a todos los clientes y espera una cantidad aleatoria de tiempo:
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, llenamos la estructura de información para que todos los clientes obtengan los mismos parámetros.
En el lado del cliente ( LightningVFXClient ), comprobamos si este cliente debe ejecutar el FX:
local function LightningFunc(info)
…
-- sin FX cuando estás en interiores
if inVolumesCheckerFunc:Invoke() then
return
end
-- sin FX cuando no estás en el mundo "normal"
if not gameStateInfoFunc:Invoke("IsInNormal") then
return
end
…
Además, ejecutamos la secuencia para establecer texturas, posiciones y brillo, ejecutamos a los adolescentes y usamos task.wait(number).Los parámetros aleatorios provienen de la estructura de información que recibimos del servidor, y algunos números están fijados.
beam.Texture = textures[info.textIdx]beamPart.Position = Vector3.new(info.center.X + og_center.X, og_center.Y, info.center.Y + og_center.Z)-- Borrarbeam.Brightness = 10ppCC.Brightness = maxPPBrightnessppBloom.Intensity = 1.1bottom.Position = top.PositiontweenBrightness:Play()tweenPPBrightness:Play()tweenPPBrightness:Play()tweenBottomPos:Play()tweenBrightness.Completed:Wait()-- sonido, audioif audioFolder and audioPart thenif audioFolder.Value and audioPart.Value thenaudioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)endendtask.wait(info.waitTillFlashes)-- and so on
Para comprobar si un jugador está en el interior usamos una función ayudante inVolumesCheckerFunc, que revisa los volúmenes precocinados que aproximan áreas interiores y comprueba si la posición del jugador está dentro de cualquiera de ellos (PointInABox).Podríamos haber usado detección basada en el tacto, pero descubrimos que cuando un jugador se sienta dentro del volumen, ya no está "tocando" el volumen.Probar un punto en unas pocas cajas es más sencillo, y lo hacemos solo cuando un jugador se mueve lo suficientemente lejos de la posición previamente probada.
Para verificar si un jugador está en áreas corruptas, invocamos una función ayudante gameStateInfoFunc que verifica el estado del juego actual.Para reproducir un sonido aleatorio de una carpeta, también usamos una función ayudante PlayOneShot.Para los propios relámpagos, estos fueron muy fáciles de crear en Photoshop; dibujamos una línea ondulada, luego agregamos un efecto de capa "Brillo exterior".


Utilizar sistemas emisores de partículas
Los rayos de relámpago heroicos son apoyados por un sistema de partículas que sugiere un rayo distante al crear la impresión de una capa de nubes en el fondo que captura la luz de los golpes distantes o la iluminación de nube a nube.Logramos este efecto a través de un sistema de partículas muy simple que destella un cartel de nube en la periferia de la nube de tormenta principal.El sistema emite una partícula de nube periódicamente con una curva de transparencia aleatorizada:


Hacer que los árboles se muevan con el viento
Después de que tuvimos las nubes y la iluminación trabajando de la manera que queríamos, luego necesitábamos agregar dos otros componentes principales de una tormenta: el viento y la lluvia! Estos elementos presentaron algunos desafíos, incluido el de tener que trabajar dentro de las limitaciones actuales de nuestro sistema de física y efectos especiales de Studio.Por ejemplo, hacer que los árboles se muevan con viento real no es posible en el motor de hoy, por lo que utilizamos efectos de emisor de partículas y animaciones de personajes personalizados para los árboles.
Sabíamos que realmente queríamos vender el efecto del viento y la lluvia, necesitábamos que los árboles mismos se herramienta de movimiento.Hay algunas formas en que puedes hacer esto dentro del motor, incluyendo mover piezas usando plugins que están disponibles públicamente, usando TweenService o animando modelos directamente.Para nuestros propósitos, las animaciones nos dieron la capacidad de controlar el movimiento que queríamos de nuestros árboles, y nos permitieron usar una sola animación que podíamos compartir entre todos los árboles dentro de la experiencia.
Empezamos por desgajar varios árboles del Paquete de modelos de aprobación - Recursos forestales.Dado que estos árboles ya existían, y nuestra experiencia tuvo lugar en el noroeste del Pacífico, nos ahorró tiempo al principio al tener que crear cada aplicación de modeladoárbol.

Después de elegir nuestros árboles, sabíamos que teníamos que pelarlos. Cortar una malla es el acto de añadir articulaciones (o huesos) a una malla en otra aplicación de modelado 3D, como Blender o Maya, luego aplicar influencia a esas articulaciones/huesos para mover la malla.Esto es más comúnmente utilizado en personajes humanoides , pero con personajes personalizados , puedes pielar casi cualquier cosa.
Sabíamos que queríamos ahorrar tiempo y reutilizar la misma animaciones, así que construimos nuestra primera grúa de árbol y nos aseguramos de que los nombres de las articulaciones fueran genéricos porque queríamos usar estos mismos nombres en las grúas de los otros árboles.También sabíamos que necesitábamos incluir articulaciones/huesos primarios, secundarios y terciarios para que los troncos se doblaran con el viento, las ramas se balancearan y las hojas parecieran temblar en respuesta.Para este proceso, necesitábamos crear un movimiento secundario , que es un concepto de animación en el que cualquier acción hace que otras partes del objeto reaccionen a esa acción y parezcan alcanzar el movimiento inicial.

Una vez que hubiéramos creado nuestras articulaciones/huesos, era hora de crear una animación de prueba para moverse por todas las articulaciones y huesos en Studio para ver si se movía de la manera que queríamos.Para hacer esto, tuvimos que importar el árbol en Studio a través de la configuración Rig personalizado en el importador de 3D , luego mover/animar la malla usando el editor de animaciones .Configuramos los materiales y las texturas después de estas pruebas, pero puedes ver el resultado a continuación.

Después de que estábamos contentos con los resultados en ese árbol, ¡era hora de probar la misma animación en un árbol diferente! Ya sabíamos que iba a ser la misma animación entre los diferentes rigs para cada introducirde árbol, ¡así que solo nos aseguramos de que nuestra animación se pareciera lo suficiente como para funcionar entre un Redwood alto y un Beechwood fuerte!

Para hacer esto, tomamos el árbol de Beechwood de ese paquete de bosque y construimos un rig similar, usando el mismo nombre exacto para las juntas.Esto fue para que la animación que previamente importamos pudiera aplicarse a este árbol también.Dado que las animaciones estaban todas basadas en articulaciones giratorias, ¡no importaba cuán grandes, pequeñas, altas o anchas fuera el árbol!

Después de que hayamos rigado y pintado el árbol de Beechwood, podremos importarlo y aplicar exactamente la misma animación.Esto significó que solo se necesitaba iterar y editar en un archivo, y también se guardó en el rendimiento con menos animaciones al ejecutar la experiencia.

Una vez que tuvimos todos los tipos de árbol que queríamos animar, los convertimos en paquetes para que pudiéramos continuar editando y actualizando mientras jugábamos varias de las animaciones alrededor de la zona principal de la experiencia.Como sabíamos que tenían un costo de rendimiento, los usamos con moderación alrededor de la casa donde el efecto era más valioso! En el futuro a medida que esto se vuelva más eficiente, ¡podrás agregar más y más instancias de malla descarnecidas!

Hacer escombros de debris
Queríamos que la lluvia pareciera pesada, y que la niebla y los escombros se vieran a través de los árboles.Para hacer esto, configuramos algunas partes invisibles para actuar como volúmenes de partículas con emisores de partículas hijos inmediatamente debajo de las grandes nubes de tormenta.Debido al límite de cantidad de partículas en Studio, no pudimos usar un emisor de partículas para todo el espacio.En cambio, agregamos varios que tenían el mismo tamaño el uno al otro en un patrón de cuadrícula sobre el espacio de la zona jugable, porque la presencia de los árboles significa que los jugadores no podrían ver muy lejos.

Las partículas de lluvia aprovecharon una nueva propiedad de emisor de partículas ParticleEmitter.Squash que te permite hacer una partícula más larga o aterrizar.Es particularmente útil para la lluvia porque significaba que no necesitábamos una gran textura de lluvia, simplemente estiramos la que estaba allí.Solo sé que si aumentas el valor de ParticleEmitter.Squash , es posible que tengas que aumentar la propiedad global ParticleEmitter.Size también para que no sea demasiado delgada! En general, fue solo cuestión de jugar con los valores hasta que obtuvimos la lluvia lo suficientemente pesada, pero no tanto que bloqueó la visibilidad de la experiencia!


Para que el viento, la niebla y las hojas volaran a través de, fue mucho más simple agregar un solo volumen de parte más grande que cubra menos áreas porque no necesitábamos una tonelada de partículas corriendo a la vez.Empezamos por configurar un volumen y obtuvimos la frecuencia de las partículas donde las querían.


Después de eso, hicimos nuestras texturas de soplado de hojas y viento, y configuramos las partículas para que todas giren/se muevan a diferentes velocidades y comiencen a diferentes velocidades.Esto significó que las partículas de niebla más grandes interactuarían de manera más natural y no se parecerían tanto a una textura repetitiva, especialmente dada su tamaño.



El resultado fue una gran acción entre los árboles en movimiento, la ventana soplando y el rayo para crear el efecto de la tormenta que rodea el ojo central de la tormenta.
Configura el ojo de la tormenta
El ojo de piedra fracturado con un núcleo brillante está destinado a dar a los jugadores la primera pista de que hay algo siniestro y arcano ocurriendo en la casa que deben explorar más a fondo.Dado que nuestra escena es oscura y el ojo está muy alto en el cielo, era importante crear una silueta de piedra fracturada creíble, pero no era tan importante crear detalles de superficie de piedra creíbles porque los jugadores no podrían ver eso.Saber lo que es realista para tus jugadores ver dentro de la iluminación de tu escena antes de invertir mucho tiempo en detalles innecesarios puede ahorrarte muchos recursos en el proceso de desarrollo.


La distancia desde el jugador también significó que podíamos confiar completamente en un mapa normal para los detalles de la superficie del ojo para que la malla sea solo una esfera plana! Esculpimos los detalles en una malla de alta calidad y horneamos su mapa normal en una esfera poligonal mucho más baja para que pudiéramos obtener todos esos detalles hermosos sin el costo masivo de rendimiento.



Para agregar un sentimiento sobrenatural al ojo y enfatizar su presencia, decidimos crear un magma brillante y neón que se filtraría a través de sus grietas.Aunque no hay canal emisivo para la aspecto, lookde la superficie, superamos esta barrera al crear el ojo a partir de 2 esferas: una para la superficie rocosa exterior y una segunda, ligeramente más pequeña, para la magma resplandeciente.En Pintor de sustancia, creamos una textura de color base para la esfera exterior con transparencia en las áreas donde queríamos que el núcleo interno pasara.En Licuadora, "vertexamos" la esfera interna de una manera barata y fácil de obtener alguna variación de color en ella.

Otro desafío al que nos enfrentamos al crear el ojo fue impuesto por nuestro uso de streaming combinado con la distancia del ojo del jugador.Dada la centralidad de esta estructura, queríamos que siempre fuera visible a pesar de su distancia, pero sin ningún hackeo a su malla, los jugadores no podían ver el ojo a menos que estuvieran en el solarium.Pudimos forzar la constante presencia del ojo en la escena agregando algo de geometría al ojo y a sus anillos.Esta geometría se sienta justo debajo de la superficie del terreno, y esto es suficiente para engañar al motor para que piense que la esfera está más cerca del jugador de lo que es y siempre la transmita.Sin embargo, esto debería hacerse con bastante moderación ya que forzar a que demasiados objetos grandes se transmitan podría anular los beneficios de la transmisión habilitada y afectar negativamente el ejecucióndel juego.
Pudimos agregar movimiento al ojo y sus anillos gracias al mismo script que usábamos para rotar las mallas de la nube.Para un último toque, decidimos agregar una pista sobre la presencia de otro mundo más allá de las nubes, pero tuvimos que tomar un enfoque creativo para evitar agregar más geometría a la escena y además tener que lidiar con las dificultades mencionadas anteriormente causadas por el streaming habilitado.Creamos una escena que tenía mucha profundidad debido al tamaño y la distancia relativa de los objetos, renderizamos una imagen de esta escena, luego usamos dicha imagen como una etiqueta decal en una parte colocada justo detrás del ojo de la tormenta.Usamos el mismo método para rotar esta parte como usamos para el ojo y sus anillos.

Hacer la panadería expandida
Una de las cosas más divertidas de producir eran los espacios corruptos, donde podíamos subvertir las expectativas de los jugadores de la realidad cambiándola literalmente a su alrededor.Por ejemplo, en el rompecabezas del padre queríamos emular un momento similar a una pesadilla en la que no importa cuán rápido ejecutar, la habitación se siente como si se alargara cada vez más.Decidimos hacer un almacén expandible que se alejaría de los jugadores mientras buscaban ingredientes para devolver la habitación a su estado normal.
Configuramos esto con un simple movimiento de las paredes y un diseño inteligente de nuestras habitaciones que aparecería en cualquier lado de la despensa.En el estado normal de la sala, el almacén era un sencillo pasillo, pero en el espacio corrupto, en realidad era mucho más largo con varias alas y una pared falsa!


La pared falsa era un grupo de modelos que volveríamos al momento en que los jugadores ingresaran a un volumen de gatillo, que era una parte transparente antes en el almacén por la que caminarían.Ese gatillo también se utilizó en un script similar a los utilizados en todas nuestras puertas, que llamó al TweenService para moverse de un objetivo a otro.Utilizamos volúmenes de partes para decirle a la operación de intermedio dónde estaban las posiciones de inicio y fin para la pared.


Debido a que TweenService es un sistema tan general, todos nuestros modelos de datos de pared tenían que contener los mismos componentes.Por ejemplo, un script general de "Door_Script" reproduce un sonido definido por un "valor" debajo del aplicación de modelado"Grow_Wall".Ese mismo script, con algunas modificaciones en el siguiente ejemplo de código, también activó el audio para el movimiento del almacén.¡Esto añadió mucho al movimiento!
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, --Tiempo/Velocidad de Door Tween
Enum.EasingStyle.Quart, --Estilo de facilitación
Enum.EasingDirection.InOut, --Dirección de alivio
0, --Repetir recuento
false, --Reversión verdadera
0 --Retraso
)
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
--aplicación de modelado["Puerta"]:Jugar()
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
Una vez que tuvimos la pared falsa que se movía hacia atrás de la sala, necesitábamos que el resto del contenido se moviera con ella.Para hacer eso, necesitábamos que todos los artículos sueltos en el almacén se soldaran a la pared a medida que se movía.Al usar Contricciones de soldadura, pudimos soldar rápidamente todos los objetos a la pared del almacén para moverse como un solo objeto.¡Hacer esto significó que teníamos la opción de desoldar estos elementos para que los jugadores se tropiezen con ellos y los golpeen!
Haz la casa del árbol corrupta
Studio es un motor físicamente basado fantástico que puedes usar para crear todo, desde una puerta oscilante hasta una plataforma giratoria.Con nuestra demostración, queríamos usar la física para crear un sentido de realismo en un conjunto de ambientes de otro modo poco realistas.¡Usando solo algunas limitaciones **** , puedes crear algunas carreras de obstáculos divertidas y desafiantes dentro de tus propias experiencias!

Restricciones son un grupo de motores basados en física que alinean objetos y restringen comportamientos.Por ejemplo, puedes usar una restricción de varilla para conectarte a objetos a fin de mantenerlos a una distancia fija el uno del otro, o la restricción de cuerda para tener una lámpara colgante desde el final de la línea.Para el rompecabezas del hijo en el que los jugadores son transportados al estado corrupto del estudio, queríamos literalmente voltear el mundo de su lado.Hacerlo subvertiría las expectativas de los jugadores de la realidad y las reglas allí, ¡mientras todavía usa el sistema de físicas como se pretendía!

Una vez que los jugadores llegaron al área principal del rompecabezas, fueron recibidos con una vista familiar en Roblox: una carrera de obstáculos.Este curso de obstáculos en particular consistía en varias plataformas giratorias y paredes giratorias, junto con "áreas seguras" que progresaron la historia.Nos enfocaremos en los elementos giratorios/rotatorios.

¿Por qué usamos restricciones aquí? Porque TweenService o otros métodos no moverían al jugador mientras estaban parados sobre ellos.Sin que el objeto se mueva el jugador, alguien podría saltar a una plataforma y se desplegaría debajo de ellos.En cambio, queríamos que los jugadores navegaran por una plataforma giratoria mientras intentaban hacer su salto a la siguiente.Debido a este enfoque, los jugadores se sintieron arraigados donde se encontraban mientras tomaban una decisión con cómo proceder a través del curso, ¡y no necesitábamos hacer nada especial para asegurarnos de que se movieran con una superficie giratoria!

Para hacer esto, primero necesitábamos usar recursos de nuestro kit actual y agregar cualquier nuevo contenido para un efecto visual.Hicimos algunas paredes y plataformas incompletas con agujeros en ellas para contar la historia de la abuela que construyó la casa del árbol.Debido a que no queríamos crear un montón de plataformas únicas, creamos 4 piezas base y piezas de barandilla separadas.Esto nos permitió mezclar y combinar piezas de base e inicio individuales para tener mucha variedad.

Sabíamos que, ya que estábamos usando restricciones, no podríamos anclar estas mallas porque no se moverían incluso con la presencia de una restricción/motor que los impulsara.La restricción necesaria para ser un hijo de algo que se ancló para que la plataforma no solo se saliera del mundo.Resolvimos esto a través de una parte que llamamos Motor_Ancla que tenía una restricción de bisagra para impulsar el movimiento general de la plataforma.Después de eso, necesitábamos que las dos mallas se movieran como una, así que creamos una parte que llamamos Motor_Turn , luego soldamos las dos mallas a ella.De esta manera, la restricción podría trabajar en una sola parte, en contraposición a bisagras múltiples que trabajan con múltiples partes.

Ahora era el momento de configurar el comportamiento real de la propia restricción de bisagra, y agregar los accesorios que actuarían como la orientación de la pieza y la restricción juntos.Colocamos el accesorio giratorio en el Motor_Turn, al que se soldaron las piezas del camino, y otro accesorio para el comportamiento del ancla en el propio Motor_Anchor, al lado de la restricción de bisagra.Dado que esto necesitaba girar por sí en posesión, a diferencia de ser influenciado por el jugador (como un Bisagrade puerta), configuramos el HingeConstraint.ActuatorType al Motor , tratando la restricción como un motor que se mueve por sí solo.
Para mantener las plataformas girando a una velocidad constante, luego configuramos las propiedades HingeConstraint.AngularVelocity, HingeConstraint.MotorMaxAcceleration y HingeConstraint.MotorMaxTorque a valores que permitirían el movimiento y evitarían la interrupción si un jugador saltara sobre ella.

Ahora necesitábamos hacer las paredes giratorias.Las paredes necesitaban girar en su centro aparente, y sabíamos que queríamos que pudieran manejar cualquier orientación relativa al resto del nivel.Al igual que las plataformas, construimos estas para que todas las paredes estuvieran sin anclar y soldadas al Motor_Turn.

Utilizamos objetos Texture en la parte superior de objetos SurfaceAppearance para agregar algo de variación a nuestros materiales base.Texturas, similares a las imágenes, te permiten colocar una imagen en un plano de una malla.Esto puede ser útil si quieres agregar suciedad a una pared de ladrillos o hacer que la madera se vea envejecida mientras usas el mismo material de madera base. Texture los objetos tienen un comportamiento ligeramente diferente al de Decal los objetos en que puedes azulejar y desplazar la imagen como quieras, lo cual es muy útil si quieres poder escalar tu textura de superposición y no te importa que se repita!

Una vez que probamos algunas plataformas y paredes giratorias, hicimos varias variaciones y jugamos con su colocación para asegurarnos de que el curso de obstáculos fuera desafiante, sorprendente y también claro donde el jugador necesitaba ir! Se necesitó un ajuste para ambos sus valores y posiciones para que funcionaran bien.Tuvimos varios puntos en los que las plataformas y las paredes se estaban golpeando entre sí o los alrededores, pero con algún movimiento alrededor y pruebas frecuentes, pudimos aterrizar en las configuraciones que tenemos en la demostración!
Si no estás seguro de qué están golpeando tus objetos físicos, puedes alternar en fidelidad de colisión desde el widget Opciones de visualización en la esquina superior derecha de la ventanilla3D.



Como puedes ver a continuación, los agujeros de la puerta/ventana son visibles, pero los detalles más pequeños como el sub-paneling no lo son.Esto se debe a que la propiedad CollisionFidelity para las paredes se estableció en Caja .No necesitábamos la precisión de estos paneles, así que para ahorrar en el costo de rendimiento, esto era lo suficientemente detallado como para que los jugadores se subieran.Con las plataformas y paredes giratorias terminadas, solo necesitábamos agregar recursos de detallado como cajas y lámparas, ¡entonces estaba listo para jugar!
