For this class, we looked at a few more advanced techniques to enhance our platform games. The first is Animation Effects, which allow us to call a scripted event from inside an animation clip, which we used to make a “coin box”. Next we looked at 2D Effectors, physics components that let us add behaviors to our collider objects. Let’s go!
Part 1: Animation Events
Today we created a classic element from the old side-scrollers, the coin box. Super Mario Brothers featured these boxes throughout their levels, and they were a wonderful devices. A player would jump from underneath, the box would shift up slightly to indicate that your actions caused a reaction, and a spinning coin would pop out, increasing your coin count and playing a happy coin sound to reinforce that a good thing happened, and your brain would release just a little more dopamine. Ahhh….
To construct our coin box, I first created a prefab element that contains a box sprite from our environment sprite sheet, and that sprite has a box collider to receive the impact event, and the animator component so that we can make it move. (Remember, if you make the moving object a child of the prefab parent object, then the transform animation will be in relationship to its steady parent object. This way the box will work the same wherever the prefab is placed and we do not need to use Apply Root Motion to keep it in place)
I generated two animation clips, a quick upward bump animation (at a sample rate of 12 frames per second) and a single frame idle animation that will be our default state. I generated a Trigger parameter that we can fire off when the player touches our Box Collider 2D. This will move us to the Bounce animation. Rather than set an exit condition to this clip, we select “Has Exit Time”. This way, once the clip has run, the exit transition will automatically be processed and we will return to the idle state.
We wrote a quick script (BoxScript.cs) to attach to the box object that uses OnCollisionEnter2D( ) to detect a collision with the player, and the Animator component to set the trigger. First step completed!
Next, we created a jumping coin. We built this with our spinning coin sequence, running at a higher sample rate of 24fps, and animated the position so it made a quick jump up and back down. We made this a prefab object as well, so that we could call it into existence whenever a box is bounced.
Now it is time to connect the two objects. In our box script, we added a public function LaunchCoin( ) that would instantiate our coin prefab at the box position when called (and then destroy it momentarily afterwards because it’s always good to clean up after ourselves). Because this BoxScript class is attached to the same object as our Animation, and because the function was made public, it is now available to us as an animation event.
At the height of our boxes bounce clip, I added an Animation Event to the timeline. You can see it up there just under the number 3 – it’s the little white marker. To create an event, you hit the “Add Event” button which is located just next to the “Add Keyframe” marker in the Animation window. (Just under the Sample Rate field).
The Event itself can be configured in the Inspector. Select the event marker in the timeline and then use the dropdown in the Inspector to associate it with a public available function attached to that object.
Here our animation event takes no arguments, but you can configure one parameter to be passed when the event is called. This can be either a float, int, string, or object reference. Now we have a coin that pops out of our box. Our coin sticks around a little longer than we would like, so we disable looping on the Animation Clip, and also add the Sprite Renderer’s “enabled” property to the clip so that we can turn off the visual once it has landed.
Finally we created a separate CoinScript that registers the scoring event and commands an attached AudioSource to play a coin sound. Using Animation Events again, we can fine tune the timing of that sound in relation to our animation. Timing sounds to events is one of the more common use case that I have found for these type of event calls, as it allows you to synchronize a sound with a particular frame or feature of animation, such as making a “footfall” sound in a walking or running animation.
Part 2: 2D Effectors
Sometimes our standard platforms don’t quite do what we want them to. Or there are effects we would like to create such as wind, or a conveyor belt, or floating on water. Thankfully, Unity includes a number of “effector” components that modify a standard 2D collider and turn it into a specialty object or zone.
The important thing to keep in mind is that in order for an effector to work, it must be a component on the same object as a 2D collider, and that collider must have the Used by Effector checkbox ticked. Some effectors (like the Platform Effector and Surface Effector) require that the collider be a solid object. Others like the Area Effector and Buoyancy Effector require that the collider be set up as a Trigger. I have a short description of each of these types below, but for more information on how to use them I recommend watching the recording of today’s class.
Platform Effector
The Platform Effector allow us to create platforms that can be accessed from one direction but solid from another. The most common use of this is a platform that you can jump to from directly underneath, such as in the image below. While these platforms were created in a Tilemap, using the TilemapCollider would result in an impervious surface that my player object cannot jump through.
Instead, I have created a series of empty objects with Box Colliders that fit over these platform tiles, and then added the Platform Effector component to each of these objects. I have set the colliders to Used by Effector, and now I have a one-way platform that will let me pass through as long as I am moving in an upward direction. This is the default setting of 180 degrees, and is represented by the arc in the scene window. This arc and the rotation offset of that arc can be set in the component. You can also mask the physics layer of this effect.
Area Effector
The Area Effector take a Collider that has the Is Trigger property checked, and converts it into a force applying zone of influence. This creates a force applied in a single direction, and you can alter the magnitude of the force, and introduce variations to that magnitude that will apply some randomness into the effect. The Force Target determines whether the force is applied directly to the Rigidbody, or to the Collider itself. If it is the collider, this can generate torque (rotation) if not coinciding the the center of mass. Applying directly to the Rigidbody is the same as applying to the center of mass and so no torque will be generated.
Buoyancy Effector
The Buoyancy Effector is useful for creating water-like settings, where you have a character or objects that have to swim or float within a substance. Here, there is a defined Surface Level and a Density that define how objects should float. The more mass an object has across it’s collider, the less buoyant it is considered to be, and will sink further into the area. You can also define a Flow force to be applied as well, if you want your body of water to have a current.
Surface Effector
Finally, the Surface Effector creates a conveyor like system, where a tangent force is applied along the surface of the collider, at a set speed. The Force Scale is used to adjust how the effector attempts to get the colliding object to arrive at the set speed. A value of 1 will override all other movement such as walking, so a lesser value is recommended. The Use Contact Force option is similar to the Area Effector’s Force Target. If checked, the force is applied at the contact point of the collider, if unchecked the force is applied to the collider as a whole. (Point of Contact can cause rotational forces to be applied, which made the box in the bottom image spin)