Spanish (Español) translation by Ricardo Mayén (you can also view the original English article)

En este tutorial aprenderás cómo crear plataformas móviles y te asegurarás que los objetos que viajan en ellas preservarán su posición relativa. También retomaremos el caso de ser aplastado entre una plataforma y el suelo.
Prerequisitos
Este tutorial esta basado en la serie Física Plataformera Básica. Específicamente, usaremos el código basado en la octava parte del tutorial como punto de partida con unas pocas modificaciones. Revisa la serie tutorial, y particularmente la última parte. Los principios detrás de la implementación serán válidos aun si estás usando una solución física diferente, pero el código será compatible con la versión presentada en esta serie tutorial.
Demo
Puedes descargar el demo de los adjuntos del tutorial. Usa las teclas WASD para mover el persona, Espacio para recrear un personaje clon, y P para recrear una plataforma móvil. El botón derecho del ratón crea una baldosa. Puedes usar la rueda de desplazamiento y las teclas de flecha para seleccionar una baldoza que quieres colocar. Los deslizadores cambian la medida del personaje del jugador.
El demo ha sido publicado bajo Unity 2017 .2b4, y el código fuente también es compatible con esta versión de Unity.
Implementación
Plataformas Móviles
Antes que todo, creemos un libreto para una plataforma móvil.
Inicialización
Comencemos por crear la clase del objeto.
public class MovingPlatform : MovingObject { }
Ahora inicialicemos algunos parámetros básicos del objeto en la función init.
public void Init() { mAABB.HalfSize = new Vector2(32.0f, 8.0f); mSlopeWallHeight = 0; mMovingSpeed = 100.0f; mIsKinematic = true; mSpeed.x = mMovingSpeed; }
COnfiguramos el tamaño y la velocidad, y hacemos el colisionador cinemático, lo cual significa que no será movido por objetos regulares.. También establecemos el mSlopeWallHeight
a 0, lo cual significa que la plataforma no escalará las pendientes—siempre las tratará como muros.
Comportamiento
El comportamiento para esta plataforma móvil en particular será justo de esta manera: comenzar el movimiento a la dereche, y siempre que encuentres un obstáculo. cambia la dirección 90 grados en sentido de las agujas del reloj.
public void CustomUpdate() { if (mPS.pushesRightTile && !mPS.pushesBottomTile) mSpeed.y = -mMovingSpeed; else if (mPS.pushesBottomTile && !mPS.pushesLeftTile) mSpeed.x = -mMovingSpeed; else if (mPS.pushesLeftTile && !mPS.pushesTopTile) mSpeed.y = mMovingSpeed; else if (mPS.pushesTopTile && !mPS.pushesRightTile) mSpeed.x = mMovingSpeed; UpdatePhysics(); }
Aquí está el patrón visualizado:

Pegando el Personaje a la Plataforma
En este momento, si un personaje permanece de pie en una plataforma, la plataforma simplemente se deslizará por debajo, como si no hubiera fricción entre los objetos. Trataremos de remediar esto, al copiar el intervalo de la plataforma.

Determina el Objeto Padre
Antes que todo, queremos estar concientes de en qué objeto, si hay alguno, nuestro personaje está parado. Declaremos una referencia para ese objeto en la clase MovingObject
.
public MovingObject mMountParent = null;
Ahora en el UpdatePhysicsResponse
, si detectamos que estamos colisionando con un objeto debajo de nosotros, podemos asignar esta referencia. Creemos una función que asignará la referencia primero.
public void TryAutoMount(MovingObject platform) { if (mMountParent == null) { mMountParent = platform; } }
Ahora usémoslo en lugares apropiados; es decir, donde sea que digamos que nuestro objeto está colisionando con otro objeto debajo de él.
else if (overlap.y == 0.0f) { if (other.mAABB.Center.y > mAABB.Center.y) { mPS.pushesTopObject = true; mSpeed.y = Mathf.Min(mSpeed.y, 0.0f); } else { TryAutoMount(other); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max(mSpeed.y, 0.0f); } continue; }
El primer lugar es cuando revisamos si los objetos se están tocando.
if (overlap.y < 0.0f) { mPS.pushesTopObject = true; mSpeed.y = Mathf.Min(mSpeed.y, 0.0f); } else { TryAutoMount(other); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max(mSpeed.y, 0.0f); }
El segundo lugar es donde ellos se superponen.
Ahora que hemos cubierto esto, manipulemos el movimiento para nuestro objeto. Modifiquemos la función UpdatePhysics
del tutorial previo.
Declaremos una variable de clase para el intervalo que necesitamos mover nuestro personaje.
public Vector2 mOffset;
Ahora reemplacemos el viejo intervalo local con el de clase.
mOffset = mSpeed * Time.deltaTime;
En caso que el objeto esté en una plataforma, agreguemos el movimiento de la plataforma al intervalo.
mOffset = mSpeed * Time.deltaTime; if (mMountParent != null) { if (HasCollisionDataFor(mMountParent)) mOffset += mMountParent.mPosition - mMountParent.mOldPosition; else mMountParent = null; }
Fijate que aquí estamos también revisando si aún estamos en contacto con el objeto. Si no es el caso, entonces configuramos el mMountParent
a null
, para marcar que esté objeto ya no está montado sobre cualquier otro.
A continuación, movamos la posición de nuestro objeto cerca de ese intervalo. No vamos a usar nuestra función Move
, sino simplemente cambiar la posición. Entonces en la colisión revisa entre los objetos, lo cual se lleva a cabo justo después del UpdatePhysics
, obtendremos el resultado para las posiciones en este marco en vez del previo.
mOffset = mSpeed * Time.deltaTime; if (mMountParent != null) { if (HasCollisionDataFor(mMountParent)) mOffset += mMountParent.mPosition - mMountParent.mOldPosition; else mMountParent = null; } mPosition += RoundVector(mOffset + mReminder); mAABB.Center = mPosition;
Ahora movamonos al UpdatePhysicsP2
, el cual es convocado después de que las colisiones entre los objetos han sido resueltas. Aquí deshacemos nuestro movimiento previo, el cual no ha sido revisado si es válido o no.
public void UpdatePhysicsP2() { mPosition -= RoundVector(mOffset + mReminder); mAABB.Center = mPosition;
Luego procedemos al UpdatePhysicsResponse
a aplicar un movimiento fuera de la superposición con otros objetos. Aquí, previamente estabamos modificando la posición directamente, pero ahora en cambio modifiquemos el mOffset
, así que este cambio de posición se resuelve luego cuando usamos nuestra función Move
.
if (smallestOverlap == Mathf.Abs(overlap.x)) { float offsetX = overlap.x * speedRatioX; mOffset.x += offsetX; offsetSum.x += offsetX; if (overlap.x < 0.0f) { mPS.pushesRightObject = true; mSpeed.x = Mathf.Min(mSpeed.x, 0.0f); } else { mPS.pushesLeftObject = true; mSpeed.x = Mathf.Max(mSpeed.x, 0.0f); } } else { float offsetY = overlap.y * speedRatioY; mOffset.y += offsetY; offsetSum.y += offsetY; if (overlap.y < 0.0f) { mPS.pushesTopObject = true; mSpeed.y = Mathf.Min(mSpeed.y, 0.0f); } else { TryAutoMount(other); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max(mSpeed.y, 0.0f); } }
Ahora podemos regresar al UpdatePhysicsP2
, donde simplemente convocamos las funciones UpdatePhysicsResponse
y Move
como hicimos antes, para obtener el estado de posición correcto.
mPosition -= RoundVector(mOffset + mReminder); mAABB.Center = mPosition; UpdatePhysicsResponse(); if (mOffset != Vector2.zero) Move(mOffset, mSpeed, ref mPosition, ref mReminder, mAABB, ref mPS);
Corrige el Orden de la Actualización
Debido a la manera en que ordenamos las actualizaciones de física, si el objeto hijo es actualizado antes que el objeto padre, el hijo constantemente estará perdiendo contacto con la plataforma cuando viajen de arriba hacia abajo.

Para corregir esto, siempre que configuramos el mMountParent
, si la plataforma está detrás del hijo en la línea de actualización, cambiamos a estos dos, de manera que el objeto padre siempre actualice primero. Hagamos esa modificación en la función TryAutoMount
.
public void TryAutoMount(MovingObject platform) { if (mMountParent == null) { mMountParent = platform; if (platform.mUpdateId > mUpdateId) mGame.SwapUpdateIds(this, platform); } }
Como puedes ver, si la identificación de actualización del objeto de la plataforma es más grande que el hijo, el orden de actualización de los objetos se cambia de lugar, removiendo el problema.

Eso es básicamente todo cuando se trata de pegar el personaje a la plataforma móvil.
Detecta Cuando Eres Aplastado
El detectar ser aplastado es bastante simple. En la UpdatePhysicsResponse
, necesitamos ver si la superposición contra un objeto cinemático nos mueve hasta una pared.
Encarguemonos del eje X primero:
if (smallestOverlap == Mathf.Abs(overlap.x)) { float offsetX = overlap.x * speedRatioX; mOffset.x += offsetX; offsetSum.x += offsetX; if (overlap.x < 0.0f) { mPS.pushesRightObject = true; mSpeed.x = Mathf.Min(mSpeed.x, 0.0f); } else { mPS.pushesLeftObject = true; mSpeed.x = Mathf.Max(mSpeed.x, 0.0f); } }
Si el objeto está a nuestra derecha y ya estamos presionando contra una pared a la izquierda, entonces llamemos una función Crush
, la cual implementaremos posteriormente. Haz lo mismo para el otro lado.
if (overlap.x < 0.0f) { if (other.mIsKinematic && mPS.pushesLeftTile) Crush(); mPS.pushesRightObject = true; mSpeed.x = Mathf.Min(mSpeed.x, 0.0f); } else { if (other.mIsKinematic && mPS.pushesRightTile) Crush(); mPS.pushesLeftObject = true; mSpeed.x = Mathf.Max(mSpeed.x, 0.0f); }
Repitamos eso para el eje Y.
if (overlap.y < 0.0f) { if (other.mIsKinematic && mPS.pushesBottomTile) Crush(); mPS.pushesTopObject = true; mSpeed.y = Mathf.Min(mSpeed.y, 0.0f); } else { if (other.mIsKinematic && mPS.pushesTopTile) Crush(); TryAutoMount(other); mPS.pushesBottomObject = true; mSpeed.y = Mathf.Max(mSpeed.y, 0.0f); }
La función Crush
simplemente moverá al personaje al centro del mapa para el demo.
public void Crush() { mPosition = mMap.mPosition + new Vector3(mMap.mWidth / 2 * Map.cTileSize, mMap.mHeight / 2 * Map.cTileSize); }
El resultado es que el personaje es teleportado cuando está siendo aplastado por una plataforma.

Resumen
Este fue un tutorial corto porque agregar plataformas móviles no es un reto grande, especialmente si conoces bien el sistema de física. Al tomar prestado de todo el código en la serie tutorial de física, este fue un proceso muy fluido.
¡Este tutorial ha sido solicitado unas cuantas veces, así que espero que lo encuentres útil! ¡Gracias por leer, y nos vemos la próxima vez!
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post