Physics aren’t a native language feature of C#, but Unity provides enough functionality for both 2D and 3D physics that the topic can easily justify its own lesson. In this post I will discuss methods of programmatically managing Rigidbodies, Colliders, and Triggers to name a few.
3D Scene Setup
We will need a scene in order to quickly set up some interactions. So create a new scene. Add a plane (From the menu bar choose GameObject->3D Object->Plane). The default object already has a “Mesh Collider” which ties it into the physics engine, although you could replace it with a “Box Collider” for better efficiency. This object will serve as the ground, and because it won’t move it is called a “static collider” – it will not need a RigidBody component attached.
Next, create a Cube (From the menu bar choose GameObject->3D Object->Cube). In the Cube’s inspector, set the Transform Position XYZ to 0,3,0. Set the Rotation to 30,45,0. Finally, add a Rigidbody component (From the menu bar choose Component->Physics->Rigidbody). The Rigidbody component is what allows colliders to become dynamic – they are able to be affected by gravity and other colliders for example.
Even without programming, our scene is now set up enough that if you press play, you can see the cube drop to the ground and bounce a bit before settling into position!
Collision Events
Review the MonoBehaviour documentation http://docs.unity3d.com/ScriptReference/MonoBehaviour.html and you will see a category called “Messages” where several of the methods I have introduced before (Awake, Update, etc) are declared. You can think of these as “virtual” methods (methods which you are not required to override or implement) although their actual implementation uses an advanced language feature called “reflection”. Because of this, you do not include the keyword “override” in your method signatures, and several of the methods can be implemented with different return data types (void or IEnumerator etc) and optional parameters as well.
Three of these messages relate specifically to events with Rigidbody collisions: OnCollisionEnter, OnCollisionStay, and OnCollisionExit.
For example, I can have a method be invoked whenever the cube collides with the floor. You might take advantage of this to play a sound, or apply “damage“ to a custom script etc.
using UnityEngine; using System.Collections; public class Hittable : MonoBehaviour { void OnCollisionEnter (Collision collision) { Debug.Log(name + " detected a collision"); } void OnCollisionStay(Collision collisionInfo) { Debug.Log(name + " collision stay"); } void OnCollisionExit(Collision collisionInfo) { Debug.Log(name + " collision exit"); } }
Attach this script both to the cube and the plane and then press play. Note that these “Messages” are sent to both objects, not just the object with the Rigidbody. The enter method may have been triggered twice (because the cube bounces), the stay method will be triggered continually while the cube is at rest on the floor (once per frame) and the exit method was probably only triggered once (in between the bounce).
The messages pass along a “Collision” parameter which provides convenient access to all sorts of information including the locations of the collisions (you could use this to spawn particles like sparks) and the velocity of the colliding objects (you might modify sound fx volume or amount of damage done based on this value). Note that if you don’t need access to the collision info parameter, leave it out of the method signature for optimization sake.
Trigger Events
Trigger events are very similar to collision events, but a collider marked as a “trigger” isn’t “solid” – it won’t be moved by Rigidbodies or be able to move Rigidbodies. It still generates enter, stay and exit events, so you can know when things reach certain places in your game without stopping them from reaching those places. For example, you could have a trigger near a doorway that causes the door to automatically open, but does not stop the player from approaching. You could have another trigger that when crossed indicates a player has solved a maze. Some people will use a large trigger area to encompass their game world and delete any object which exits the trigger (like bullets, etc.)
Add the following methods to our demo script:
void OnTriggerEnter(Collider other) { Debug.Log(name + " detected a trigger"); } void OnTriggerStay(Collider other) { Debug.Log(name + " trigger stay"); } void OnTriggerExit(Collider other) { Debug.Log(name + " trigger exit"); }
Head back over to unity and select the Cube. In the inspector for the Rigidbody component, enable the “Is Kinematic” toggle. Select the Plane. In the inspector for the collider, enable the “Is Trigger” toggle. When you press play the cube will no longer fall to the ground by gravity. This is because kinematic objects are moved by code. They can push other rigid bodies but will not be pushed by them.
Reselect the Cube. In the inspector for the Transform component, move your cursor over the Position’s letter “Y” and you should see arrows pointing left and right. Click and drag left and right to watch the cube be dragged up and down from the camera view. You will see messages print to the console window at the bottom. Now our “Trigger” methods are invoked but not our “Collision” methods.
Programming for Rigidbodies
Until now we have really only used normal coroutines and the “Update” method of MonoBehaviour. Update is called as rapidly as possible (based on the current fps of your game). For example, if you use the following snippet in a scene and press play you should see that the time between frames varies:
void Update () { Debug.Log("Tick " + Time.deltaTime); }
When you use physics, you should update your objects in a different “loop” called “FixedUpdate” as in the following snippet:
void FixedUpdate () { Debug.Log("Tick " + Time.fixedDeltaTime); }
Attach this snippet to a scene and press play and you will see that the time between frames is now consistent. The default for me was 0.02 seconds between frames, somewhat slower than the ~0.016 seconds reported in the normal update loop. The reason for this is that physics is an intensive part of an engine, and by updating at a slightly lower resolution, it allows better performance.
From the menu bar choose “Edit->Project Settings->Time”. In the inspector you will see an entry for “Fixed Timestep” which matches the time reported by Time.fixedDeltaTime from earlier. You can modify the value here to be lower (for more frequent physics updates) or higher (for less frequent physics updates) based on the needs of your project.
When moving game objects by constant rates in a normal update loop, you had to multiply your speed values by Time.deltaTime in order to maintain frame-rate independent animation. This is not true when animating via physics. You should not apply less force to an object when you have more updates per second, or more force when you have less updates per second.
Disable the “Is Kinematic” flag of the Cube’s RigidBody and disable the “Is Trigger” flag of the Plane’s Collider. Create a new script called Demo, and attach it to your scene. Make sure to assign the reference to the cube Rigidbody before pressing Play.
using UnityEngine; using System.Collections; public class Demo : MonoBehaviour { [SerializeField] Rigidbody cube; public Vector3 force; public Vector3 torque; void FixedUpdate () { cube.AddForce(force); cube.AddTorque(torque); } }
Because the force of gravity is -9.81 on the Y Axis, if you were to set the vertical force of this script to anything greater than 9.81 the cube will begin to float. Enter a value of 9.82 and watch for yourself. This is true regardless of the Fixed Timestep value mentioned earlier. Try changing the update interval from 0.02 to 0.01 and play the scene again. Even though the update rate occurs more frequently, the cube moves the same way – it doesn’t float any faster. The cube still requires the same amount of force to override the force of gravity (applying less force but more frequently doesn’t make up the difference).
The reason you WOULD want to refer to Time.fixedDeltaTime is if you were animating the object directly. Re-enable the “Is Kinematic” flag of the Cube’s RigidBody and re-enable the “Is Trigger” flag of the Plane’s Collider. Note that you should only modify the position of a transform on a Kinematic Rigidbody (applying force wouldn’t apply here anyway so it isn’t an option). Modify the Demo script again to the following:
using UnityEngine; using System.Collections; public class Demo : MonoBehaviour { [SerializeField] Rigidbody cube; float angle = 0; float offset = 2f; float speed = Mathf.PI; void FixedUpdate () { angle += speed * Time.fixedDeltaTime; float yPos = Mathf.Sin(angle) * offset; cube.transform.localPosition = new Vector3(0, yPos, 0); } }
This code sample moves our kinematic cube up and down through the trigger plane programmatically, just like we were doing manually in the previous section. In this case we DO need to reference the Time.fixedDeltaTime, or the cube would animate faster or slower depending on modified project settings for “Fixed Timestep”.
The Physics Class
The physics class exposes several static variables that may be helpful, such as a Vector3 representing gravity. What I find even more useful are the various “Cast” methods. You can tell the Physics engine to cast a line, ray, sphere or capsule from one point in your scene to another (or in a direction) and return information such as whether or not any physics objects were hit.
There are a variety of use cases for these methods, such as “line of sight” for an AI – cast a line from the enemies head in a sight range and see if any objects are hit, and if so, you might want to check to see if the object is the player! You might use CapsuleCast to see if your player can move through a desired path (just because a line cast might succeed, doesn’t mean that a thicker object can fit).
Another useful method is “OverlapSphere”. You could do this to see what objects are in range of an explosion so you can apply damage to them. Or, you could have raycast from a player’s input point on the screen into the world and then use a sphere cast from that point to see what objects are nearby. I use this sometimes to make sure that a mobile touch input can be a little “looser” but get objects that a player might have intended to select.
Check out the Unity docs for more on these (including sample code)
http://docs.unity3d.com/ScriptReference/Physics.html
2D Physics
The 2D physics in Unity is a separate system from 3D (not simply ignore the Y values or something equivalent). They implemented it by creating duplicate components for most of the 3D counterparts, but the 2D version has “2D” appended to the name. For example, you would use “Rigidbody” for 3D and “Rigidbody2D” for the 2D version. It’s not a direct one-to-one, there are some components and joints etc that make sense in 2D or 3D that don’t make sense in the other.
Check out the Unity docs for more on 2D physics:
http://docs.unity3d.com/ScriptReference/Physics2D.html
Summary
In this lesson I very briefly introduced the topics of Physics programming. I mentioned a few of the components you will use such as Rigidbody and Collider, explained the difference between Collision and Trigger events, and discussed Kinematic vs non-kinematic Rigidbodies. Even with all of that, I barely scratched the surface of what can be done. Additionally Unity 5 has just come out and I haven’t had a chance to use it yet, so we may need to revisit this topic again.
Reblogged this on marubyquinones and commented:
Physics
Physics aren’t a native language feature of C#, but Unity provides enough functionality for both 2D and 3D physics that the topic can easily justify its own lesson.