Between the Folds #009 - Abilities: Pearls


After we’ve covered Ernest’s glove in the last post, we will now take a closer look at our pearl magnets, Mrs Pull and Mr Push as we call them. While Ernest can only manipulate objects in his direct surroundings with the gloves, he can throw Mrs Pull and Mr Push which allows him to influence objects that are far away and otherwise unreachable.

(Mrs Pull and Mr Push in cooperation.)

Core

Custom radial force component

At its core, the pearl magnets consist of a custom RadialForceComponent. The RadialForceComponent provided by UE4 can exert a force based on the distance between the component’s center and the influenced object. Since we needed a little bit more control over the radial force, we created our own RadialForceComponent and adapted some of the code to our requirements. 

We determine which objects to influence by performing a query from the pearl’s center with its radius each frame. Since we only want to influence Whalium objects, we apply a filter for that in the query.

Once we’ve gathered all objects to influence, we iterate over them and apply a radial force to each. Mrs Pull attracts everything and therefore exerts a force towards her center; Mr Push repulses everything and exerts a force away from his center. We use the function PrimitiveComponent->AddRadialForce() for this.

State change

As we mentioned earlier, Ernest can throw his pearls to place them anywhere in the level. For this, we defined two different states for the pearls:

  • Spark state, where the pearl can be thrown and bounces off of surfaces but does not influence other objects. 
  • Pearl state, where the pearl is suspended in the air and can no longer be moved by any object or the player themselves. In this state, the pearl influences Whalium objects in its vicinity.

Ernest can decide freely when to manifest a spark into apearl, he can also recall the pearl at any time, at which point it returns to his gloves and can be thrown as a spark once again. Only one of each pearl can exist at the same time!

Swap

In the later stage of the game, Ernest discovers an additional ability for the pearls: the pearl swap. This ability allows him to instantly swap the locations and states of Mrs Pull and Mr Push. If the pearls are in different states, e.g. Mrs Pull is in pearl state and Mr Push is in spark state, their states are swapped as well. Mrs Pull is changed into the spark state and receives the location and velocity of Mr Push. Mr Push on the other hand activates his pearl state and is placed where Mrs Pull has been previously, causing it to immediately push away all objects that Mrs Pull had previously attracted.

(The swap can be used to instantly push away all objects that had previously been attracted by Mrs Pull.)

Iterations

From two actors to one

Initially, we had the different states of the pearls implemented as individual actors that each had their own logic and behavior. Whenever a spark was turned into a pearl, we hid the spark and placed the corresponding pearl in its position. However, this became quite complicated and cumbersome once we introduced the swap ability. It meant that we always had to consider four different actors of two different classes and all the possible combinations when one pearl was in state X and the other pearl was in state Y (plus another state if one of the pearls wasn’t spawned at all).

Therefore, we decided to combine all pearl states into a single actor and represent their current state using an enum. The enum can take on the following values:

The new pearl actor has all required components for each state, most importantly a ProjectileMovementComponent for the spark state and our RadialForceComponent for the pearl state. In the function TrySetState(), we enable and disable the corresponding components for the requirements of each state.

This approach simplifies the swap procedure immensely, because we only need to swap the pearls’ locations and call TrySetState() for each pearl with the other pearl’s current state. 

Throwing

In early tests, players noted that they found placing the pearls accurately in the level difficult. Placing objects in 3D space is challenging, considering the players can only view the scene from a 2D perspective on the screen. 

Aim mode

We attempted to mitigate this by providing the player with more depth information, for which we introduced the aim mode. The player can activate the aim mode by holding down the throw button. The aim mode displays the path which the spark would take, were it to be thrown right now. This path, represented by a series of spheres, is updated in real time according to the player’s location and rotation, and can therefore be used to accurately aim at the desired target. UE4 provides the very useful function PredictProjectilePathByTraceChannel() for exactly this use case. We use the OutPathPositions array and draw one InstancedStaticMesh-Sphere for each predicted position.

(The aim mode helps estimating where the perl will land when throwing.)

Highlighting 

Another way we convey the position and distance of the pearl sparks is by highlighting surrounding objects. When the spark flies by an influenceable Whalium object, that object lights up and signals that it would be influenced if the player were to manifest the pearl right now. The player can therefore concentrate on the object they want to influence, and once it lights up, they can manifest their pearl and the desired object will be attracted or repulsed.

(Objects light up if they are in range of the spark.)

Influencing large objects

Another issue that was brought forward to us was that the pearls felt too weak when influencing particularly large and bulky objects. This was because our RadialForceComponent would always apply its force at the pivot of the influenced PrimitiveComponent, instead of the closest point between pearl and object. This was especially frustrating for objects with physics constraints that prevent movement in one direction. If the player wanted to push/pull a specific segment of an object, but the pearl actually calculated the force vector to the object’s center, the object would frequently move in unexpected directions or not at all if the force was pointing against the physics constraint.

We combated this by adding the component tag AddForceAtLocation. If this tag is set on an influenceable PrimitiveComponent, our RadialForceComponent first calculates the closest point between pearl and influenced object using PrimitiveComponent->GetClosestPointOnCollision() and then PrimitiveComponent->AddForceAtLocation() instead of AddRadialForce()

( 1.) Force is applied at the pivot, against the actor's PhysicsConstraint - the object doesn't move. 2.) Force is applied at the nearest point between pearl and mesh - the object moves to the side as expected.)

That’s all for our pearl showcase, as always feel free to ask any remaining questions! Next time we are going to cover the part that makes our objects influenceable in the first place: the Whalium component.

- Andi, Ralf

Leave a comment

Log in with itch.io to leave a comment.