Desarrollar un mundo en movimiento

*Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

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:

  1. TweenService - Para el movimiento de la nube.
  2. Cambios de iluminación - Para crear la iluminación de nube a nube.
  3. Rayos - Para la "iluminación volumétrica" y los rayos.
  4. Emisores de partículas - Para escombros que vuelan hasta el portal y vuelan alrededor debido a la corriente de aire.
  5. 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!

Una sola malla de nube.
Mallas de nubes en capas sin sus texturas!

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.

Una vez que comenzamos a agregar iluminación, necesitábamos agregar detalles a las mallas para que reaccionen mejor a la iluminación!

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 parentTransform
if parentObj:IsA("Model") then
if not parentObj.PrimaryPart then
-- la parte principal puede que aún no se transmita
continue -- espera a que la parte principal se re複ique
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

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:

  1. Los elementos de los golpes de rayo, como sus texturas, brillo y retrasos, se aleatorizan con cada golpe.
  2. Los cambios de audio y post FX están sincronizados con los efectos de ataque FX.
  3. 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)
-- Borrar
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()
-- sonido, audio
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 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.

El paquete Bosque contiene varios tipos de árboles, que te pueden ahorrar tiempo en tus propias experiencias.

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.

Los árboles tienen articulaciones primarias, secundarias y terciarias para que podamos tener movimientos creíbles desde ser soplados por el viento.

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.

La misma jerarquía dentro de Studio.

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!

La animación que importamos en el árbol de Redwood.

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!

El árbol de Beechwood tiene el mismo nombre exacto para sus articulaciones, solo no la misma cantidad.¡Esto está bien ya que el sistema de animaciones solo aplicará animaciones a esos puntos específicos que coincidan con el nombre en él! Por esta razón, podríamos aplicar las mismas animaciones a cualquier cosa que coincida con los nombres de los puntos!

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.

Usando el editor de animaciones, podríamos aplicar la misma animación de árbol de secoyas a el árbol de Beechwood!

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!

Usamos árboles animados inmediatamente alrededor de la casa donde el vórtice era más fuerte y el efecto visual sería el más impactante para los jugadores.

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.

Usamos varios volúmenes para obtener tanto la cantidad de lluvia, como la cobertura específica de lluvia que queríamos.

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!

Un valor de Squash de 3 comienza a estirar la textura más tiempo.
Un valor de Squash de 20 estira las partículas mucho más tiempo, pero también necesitábamos aumentar el valor de Tamaño también.

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.

Se acabó habiendo algunos volúmenes de partículas, así que no tuvimos partículas que entraran en la casa, y porque no sentimos que necesitaban moverse a través de los árboles como la niebla lo hizo.
El volumen de partículas de niebla era mucho mayor ya que las partículas eran grandes, y no necesitábamos ser tan precisos con la ubicación.

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.

Partícula de niebla
Partícula de hoja

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.

Configurar la iluminación final en tu escena temprano puede ahorrarte mucho trabajo innecesario.¡No podrías ver detalles de superficie en los anillos con la iluminación final de nuestra escena, por lo que no había necesidad de pasar el tiempo colocándolos allí!

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.

Escultura de alta calidad
Malla de bajo polígono
La malla de bajo polígono con la información normal de la escultura de alto polígono horneada en

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.

La pintura de vértice en la esfera interior.Creamos un gradiente que era más ligero alrededor del ojo para dar un mayor sentido de profundidad e interés visual.

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.

La imagen que usábamos para crear una ilusión de un mundo más allá de las nubes.Cuando los jugadores están lejos de algo, una imagen simple puede ser suficiente para crear la ilusión de más profundidad y complejidad en tu escena!

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!

El estado corrupto del almacén de la cocina.

La pared falsa se aleja de los jugadores.
>

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.

El volumen de parte activa una pared falsa detrás de ella para moverse a su punto final.Se hace visible en esta imagen con un tinte amarillo.
Target_Closed era una parte de objetivo genérica que usamos en todas nuestras puertas para donde deberían rotar.Aquí se reutilizó para decirle a la pared del pasillo a dónde ir.

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!

El rompecabezas del hijo comenzó con los jugadores en la misma habitación, pero todo estaba al revés.

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.

La apariencia que rompe la mente ocultó el hecho de que el juego aquí era muy simple.

¿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!

Puedes ver a tus amigos girar mientras intentan navegar por la carrera de obstáculos también.

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.

El archivo adjunto 0 era esencialmente el ancla para la bisagra y el archivo adjunto 1 representaba la bisagra misma.Teníamos la bisagra girando constantemente, pero también puedes usar una restricción de bisagra para las puertas.

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.

Queríamos reutilizar la mayor cantidad de mallas de la casa del árbol real para ahorrar en el ejecución, así que seguimos un camino similar al de las plataformas.Se hicieron varios tipos de paredes que podían pegarse juntas en diferentes combinaciones para alguna variación.

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!

Puedes ver tanto el comportamiento similar como la configuración para la restricción del bisagra y también cómo usamos objetos Texture .

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.

A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.
Cuando la visualización de colisión está deshabilitada, puedes ver la representación de geometría normal que se muestra en el juego.
Cuando la visualización de colisiones está habilitada, puedes ver que las hojas de los árboles no tienen colisiones, por lo que no interferirán con las plataformas giratorias o las paredes.

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!