Game Frameworks  
Lecture 03: 2D Physics in Unity  
Edirlei Soares de Lima  
<edirlei.lima@universidadeeuropeia.pt>  
Unity Physics  
Unity simulates physics in 2D and 3D to ensure that objects  
correctly accelerate and respond to collisions, gravity, and  
various other forces.  
3D physics: Nvidia PhysX engine integration  
2D physics: Box2D engine integration  
Main components:  
Rigidbodies  
Colliders  
Rigidbody 2D  
Rigidbody is the main component that  
enables physical behavior for a GameObject.  
With a Rigidbody attached, the object will  
immediately respond to gravity.  
If a Collider component is also added, the  
GameObject will be moved by incoming collisions.  
Important: since a Rigidbody component controls the  
movement of the GameObject, never try to move it  
from a script by changing the Transform properties  
such as position and rotation.  
Instead, use the physics engine to apply forces to push the  
GameObject and let the physics engine calculate the results.  
Rigidbody 2D  
Body Type:  
Dynamic: for objects designed to move under  
simulation. It uses the full set of properties, such  
as finite mass and drag, and is affected by gravity  
and forces.  
A Dynamic body will collide with every other body type.  
It is the most performance-expensive body type.  
Never use a script to set the position or rotation of a  
Dynamic Rigidbody using its Transform component.  
Rigidbody 2D  
Body Type:  
Kinematic: for objects designed to move under  
simulation, but only under very explicit user  
control. It is not affected by gravity and forces.  
It does not collide with other Kinematic or Static  
Rigidbodies (only collides with Dynamic Rigidbodies).  
It has a lower demand on system resources than a  
Dynamic Rigidbody.  
It is designed to be repositioned explicitly via  
MovePosition or MoveRotation functions.  
Rigidbody 2D  
Body Type:  
Static: for objects designed to not move under  
simulation at all. If anything collides with it, the  
object behaves like an immovable object.  
Only collides with Dynamic Rigidbodies.  
The least resource-intensive body type.  
All GameObjects that have a Collider component and do  
not have a Rigidbody, are automatically considered Static  
RigidBodies.  
Collider 2D  
A Collider component defines the shape of a  
GameObject for the purposes of physical  
collisions.  
A collider is invisible and does not need to be the  
exact same shape as the object.  
Rough approximations are often more efficient and  
indistinguishable in gameplay).  
The simplest (and least processor-intensive)  
colliders are primitive collider types:  
Box, Circle, and Capsule.  
More complex and customizable colliders:  
Edge and Polygon.  
Physics Example 1  
Example 1: Hello Physics World  
Add Box Colliders 2D to the platforms.  
Add a Circle Collider 2D to the ball.  
Add a RigidBody 2D to the Ball.  
Physics Material 2D  
A Physics Material is used to adjust the  
friction and bounce that occurs between  
physics objects when they collide.  
Example: Ball bouncing  
Scripting Add Force  
Example 2: Moving the Ball with a Force  
public class BallMovement : MonoBehaviour  
{
public float moveForce = 20;  
private Rigidbody2D rigidBody;  
private void Start()  
{
rigidBody = GetComponent<Rigidbody2D>();  
}
private void FixedUpdate()  
{
float horizontalInput = Input.GetAxis("Horizontal");  
rigidBody.AddForce(Vector2.right * horizontalInput *  
moveForce);  
}
}
Important: always use FixedUpdate when manipulating a Rigidbody.  
Scripting Vector2 Class  
The Vector2 class represents 2D vectors.  
It also contains functions and properties for common vector  
operations: magnitude, normalization, dot product, cross product,  
angle, etc.  
Main Constants:  
Vector2.down: shorthand for writing Vector2(0, -1).  
Vector2.left: shorthand for writing Vector2(-1, 0).  
Vector2.one: shorthand for writing Vector2(1, 1).  
Vector2.right: shorthand for writing Vector2(1, 0).  
Vector2.up: shorthand for writing Vector2(0, 1).  
Vector2.zero: shorthand for writing Vector2(0, 0).  
Scripting Vector2 Class  
Main functions:  
Vector2.Angle(Vector2 from, Vector2 to): returns the unsigned angle in degrees  
between from and to.  
Vector2.ClampMagnitude(Vector2 vector, float maxLength): returns a copy of  
vector with its magnitude clamped to maxLength.  
Vector2.Distance(Vector2 a, Vector2 b): returns the distance between a and b.  
Vector2.Dot(Vector2 lhs, Vector2 rhs): dot Product of two vectors.  
Vector2.Lerp(Vector2 a, Vector2 b, float t): linearly interpolates between  
vectors a and b by t.  
Vector2.MoveTowards(Vector2 current, Vector2 target, float maxDistanceDelta):  
moves a point current towards target.  
Vector2.SmoothDamp(Vector2 current, Vector2 target, ref Vector2  
currentVelocity, float smoothTime, float maxSpeed = Mathf.Infinity, float  
deltaTime = Time.deltaTime): Gradually changes a vector towards a desired goal  
over time.  
Scripting Velocity Change  
Example 3: Player Movement (Platform Game)  
public class PlayerMovement : MonoBehaviour  
{
public float moveForce = 6;  
private Rigidbody2D rigidBody;  
private void Start()  
{
rigidBody = GetComponent<Rigidbody2D>();  
}
private void FixedUpdate()  
{
float horizontalInput = Input.GetAxis("Horizontal");  
rigidBody.velocity = new Vector2(horizontalInput *  
moveForce, rigidBody.velocity.y);  
}
}
Effectors 2D  
Effector 2D are components that control the  
forces of physics when GameObject colliders  
come into contact with each other.  
Effectors:  
PlatformEffector2D: creates platform behaviors  
such as one-way collisions and no side friction;  
SurfaceEffector2D: simulates conveyor belts;  
PointEffector2D: attracts or repulses against a  
given source point;  
BuoyancyEffector2D: makes GameOjects float;  
AreaEffector2D: randomly vary force and angle  
magnitude;  
Scripting Impulse Force & Raycast  
Example 4: Player Jump (Platform Game)  
public class PlayerMovement : MonoBehaviour{  
public float moveForce = 6;  
public float jumpForce = 7.2f;  
private Rigidbody2D rigidBody;  
private float feetOffset = 0;  
private void Start(){  
rigidBody = GetComponent<Rigidbody2D>();  
BoxCollider2D collider = GetComponent<BoxCollider2D>();  
feetOffset = ((collider.size.y * transform.localScale.y) / 2)+ 0.01f;  
}
private bool IsOnGround(){  
Vector2 playerFeet = new Vector2(transform.position.x,  
transform.position.y - feetOffset);  
RaycastHit2D hit = Physics2D.Raycast(playerFeet, Vector2.down, 2.0f);  
if (hit.collider != null){  
float distance = Mathf.Abs(hit.point.y - playerFeet.y);  
if (distance < 0.1){  
return true;  
}
}
return false;  
}
Scripting Impulse Force & Raycast  
Example 4: Player Jump (Platform Game)  
...  
private void FixedUpdate()  
{
float horizontalInput = Input.GetAxis("Horizontal");  
float jumpInput = Input.GetAxis("Jump");  
if ((jumpInput > 0) && (IsOnGround()))  
{
rigidBody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);  
}
rigidBody.velocity = new Vector2(horizontalInput * moveForce,  
rigidBody.velocity.y);  
}
}
Physics 2D Raycast  
The Raycast function casts a ray against colliders in the Scene.  
RaycastHit2D Raycast(Vector2 origin, Vector2 direction, float distance =  
Mathf.Infinity, int layerMask = DefaultRaycastLayers,  
float minDepth = -Mathf.Infinity, float maxDepth =  
Mathf.Infinity);  
Returns a RaycastHit object with a reference to the collider that is hit  
by the ray.  
The collider property of the result will be NULL if nothing was hit.  
The layerMask can be used to detect objects selectively only on  
certain layers.  
In last example, the function IsOnGround() can be optimized by defining a Layer on  
objects that represent ground. The Layer can then be used in the layer mask  
parameter to only check collisions with relevant objects.  
RaycastHit2D hit = Physics2D.Raycast(playerFeet, Vector2.down, 2.0f,  
LayerMask.GetMask("Ground"));  
Physics 2D Settings  
Edit -> Project Settings  
Physics 2D settings can be used  
to change global settings for 2D  
physics simulation.  
Scripting Top-Down Movement  
Example 5: Player Movement (Top-Down Game)  
public class TopDownPlayerMovement : MonoBehaviour  
{
public float moveForce = 6;  
private Rigidbody2D rigidBody;  
private void Start()  
{
rigidBody = GetComponent<Rigidbody2D>();  
}
private void FixedUpdate()  
{
float horizontalInput = Input.GetAxis("Horizontal");  
float verticalInput = Input.GetAxis("Vertical");  
rigidBody.velocity = new Vector2(horizontalInput * moveForce,  
verticalInput * moveForce);  
}
}
Scripting Top-Down Movement  
Example 6: Pushing Objects (Top-Down Game)  
Dynamic objects must have a RigidBody2D.  
Adjust the Linear Drag to make them stop faster.  
For the physics engine, the objects are in the air, so there  
is not friction.  
Adjust the Mass to make objects heavier.  
Scripting Top-Down Movement  
Example 7: Player Movement based on Mouse Position  
public class SpaceshipMovement : MonoBehaviour{  
public float moveForce = 3;  
public float minMouseDistance = 50;  
private Rigidbody2D rigidBody;  
private void Start(){  
rigidBody = GetComponent<Rigidbody2D>();  
}
private void FixedUpdate(){  
float horizontalInput = Input.GetAxis("Horizontal");  
float verticalInput = Input.GetAxis("Vertical");  
Vector2 mouseDirection = Input.mousePosition -  
Camera.main.WorldToScreenPoint(transform.position);  
if (mouseDirection.magnitude > minMouseDistance){  
float rotationAngle = Mathf.Atan2(mouseDirection.normalized.y,  
mouseDirection.normalized.x) * Mathf.Rad2Deg;  
rigidBody.SetRotation(rotationAngle - 90);  
}
rigidBody.velocity = (((transform.up * verticalInput) +  
(transform.right * horizontalInput)) * moveForce);  
}
}
Scripting 2D Physics Events  
Collision Events:  
void OnCollisionEnter2D(Collision collision) is called when the  
collider/rigidbody has begun touching another rigidbody/collider.  
void OnCollisionStay2D(Collision collision) is called once per frame for every  
collider/rigidbody that is touching the rigidbody/collider.  
void OnCollisionExit2D(Collision collision) is called when the collider/rigidbody  
has stopped touching another rigidbody/collider.  
Trigger Events:  
void OnTriggerEnter2D(Collider collider) is called when the collider/rigidbody  
has begun touching another collider that is a trigger.  
void OnTriggerStay2D(Collider collider) is called once per frame for every  
trigger collider that is touching the rigidbody/collider.  
void OnTriggerExit2D(Collider collider) is called when the collider/rigidbody  
has stopped touching another collider that is a trigger.  
Scripting 2D Physics Events  
Example 8: Destroy the Spaceship on Collision  
public class SpaceshipMovement : MonoBehaviour{  
...  
private void OnCollisionEnter2D(Collision2D collision)  
{
if (collision.collider.tag == "Asteroid")  
{
Destroy(gameObject);  
}
}
}
Scripting 2D Physics Events  
Example 9: Change the color of the Spaceship when it enters the heal area.  
public class HealArea : MonoBehaviour  
{
private void OnTriggerEnter2D(Collider2D collision)  
{
if (collision.gameObject.tag == "Player")  
{
collision.gameObject.GetComponent<SpriteRenderer>().color = Color.blue;  
}
}
private void OnTriggerExit2D(Collider2D collision)  
{
if (collision.gameObject.tag == "Player")  
{
collision.gameObject.GetComponent<SpriteRenderer>().color = Color.white;  
}
}
}
Scripting 2D Physics Events  
Example 10: Shooting and Destroying Asteroids  
public class TopDownPlayerMovement : MonoBehaviour  
{
public float moveForce = 3;  
public float minMouseDistance = 50;  
public GameObject bulletPrefab;  
public float bulletForce = 12;  
public float bulletStartOffset = 1.2f;  
public float cooldownTime = 0.5f;  
private Rigidbody2D rigidBody;  
private float shootTimer = 0;  
private void Start(){  
rigidBody = GetComponent<Rigidbody2D>();  
}
private void OnCollisionEnter2D(Collision2D collision){  
if (collision.collider.tag == "Asteroid"){  
Destroy(gameObject);  
}
}
...  
Scripting 2D Physics Events  
Example 10: Shooting and Destroying Asteroids  
...  
private void Update()  
{
float fireInput = Input.GetAxis("Fire1");  
if ((fireInput > 0) && (shootTimer <= 0))  
{
Vector3 mouseDirection = Input.mousePosition –  
Camera.main.WorldToScreenPoint(transform.position);  
GameObject bullet = Instantiate(bulletPrefab, transform.position +  
(mouseDirection.normalized * bulletStartOffset),  
Quaternion.identity);  
bullet.GetComponent<Rigidbody2D>().velocity = mouseDirection.normalized  
bulletForce;  
*
shootTimer = cooldownTime;  
}
if (shootTimer >= 0)  
{
shootTimer -= Time.deltaTime;  
}
}
...  
Scripting 2D Physics Events  
Example 10: Shooting and Destroying Asteroids  
private void FixedUpdate()  
{
float horizontalInput = Input.GetAxis("Horizontal");  
float verticalInput = Input.GetAxis("Vertical");  
Vector2 mouseDirection = Input.mousePosition –  
Camera.main.WorldToScreenPoint(transform.position);  
if (mouseDirection.magnitude > minMouseDistance)  
{
float rotationAngle = Mathf.Atan2(mouseDirection.normalized.y,  
mouseDirection.normalized.x) * Mathf.Rad2Deg;  
rigidBody.SetRotation(rotationAngle - 90);  
}
rigidBody.velocity = (((transform.up * verticalInput) + (transform.right *  
horizontalInput)) * moveForce);  
}
}
Exercise 1  
1
) Create an enemy for a 2D platform game that can only be  
killed by jumping on his head (like some enemies of Super  
Mario games).  
Start with the code created for the player jump example.  
If the player touches the sides of the enemy, the player dies.  
Enemy  
Enemy  
Kill the enemy  
Player dies  
2
D Joints  
Joints can be used to attach GameObjects together.  
There are different types of 2D joints:  
Distance Joint 2D - attaches two GameObjects and keeps them a certain distance apart.  
Fixed Joint 2D - keeps two objects in a position relative to each other, so the objects are  
always offset at a given position and angle.  
Friction Joint 2D - reduces both the linear and angular velocities between two  
GameObjects to zero  
Hinge Joint 2D - allows a GameObject to be attached to a point in space around which it  
can rotate.  
Relative Joint 2D - allows two GameObjects to maintain a position based on each other’s  
location.  
Slider Joint 2D - allows a GameObject to slide along a line in space.  
Spring Joint 2D - allows two GameObjects to be attached together by a spring.  
Target Joint 2D - connects to a specified target, rather than another rigid body.  
Wheel Joint 2D - simulates wheels and suspension.  
Distance Joint 2D  
Distance Joint 2D Attaches two GameObjects and keeps  
them a certain distance apart.  
Update the Line Renderer positions do show a line:  
private void Update()  
{
Rigidbody2D attachedBall = GetComponent<DistanceJoint2D>().connectedBody;  
LineRenderer line = GetComponent<LineRenderer>();  
line.SetPosition(0, transform.position);  
line.SetPosition(1, attachedBall.transform.position);  
}
Hinge Joint 2D  
Hinge Joint 2D - allows a GameObject to be attached to a  
point in space around which it can rotate.  
Update the Line Renderer positions do show a line:  
private void Update()  
{
HingeJoint2D hinge = GetComponent<HingeJoint2D>();  
LineRenderer line = GetComponent<LineRenderer>();  
line.SetPosition(0, transform.position);  
line.SetPosition(1, hinge.connectedBody.position);  
}
Spring Joint 2D  
Spring Joint 2D - allows two GameObjects to be attached  
together by a spring.  
Update the Line Renderer positions do show a line:  
private void Update(){  
SpringJoint2D hinge = GetComponent<SpringJoint2D>();  
if (hinge){  
LineRenderer line = GetComponent<LineRenderer>();  
line.SetPosition(0, transform.position);  
line.SetPosition(1, hinge.connectedBody.position);  
}
else{  
Destroy(GetComponent<LineRenderer>());  
}
}
Further Reading  
Unity User Manual, Available at: https://docs.unity3d.com/2020.1/  
Documentation/Manual/  
2D  
Gameplay in 2D  
Physics Reference 2D  
Rigidbody 2D  
Collider 2D  
Effectors 2D  
2D Joints  
Books  
Jeremy G. (2017). Introduction to Game Design, Prototyping, and  
Development: from the Concept to Playable Game - with Unity and C# (2nd  
ed.). Boston, MA: Addison-Wesley Professional. ISBN: 978-0134659862  
Hocking, J. (2018). Unity in Action: Multiplatform Game Development in C#  
(2nd ed.). Shelter Island, NY: Manning Publications. ISBN: 978-1617294969