Link Search Menu Expand Document

Project 4 – Simulator

Due: Thursday December 8 11:00am (no late days allowed)

Simulator screenshot

  1. Overview
  2. Getting Started
  3. Requirements
    1. Part A: Create a physics simulation system that:
    2. Part B: Recreate MazeGame using your physics engine:
    3. Part C: Rube Goldberg machine:
  4. Part A: Physics Simulation
    1. Program Overview
    2. Skeleton Code
      1. Implement viscous drag force
      2. The ComputeSphereMovement function
      3. The OnCollision function
        1. Detect SphereCollider collision
        2. Detect PlaneCollider collision
        3. Collision Response
  5. Part B: MazeGame with Custom Physics
    1. Overview
    2. MazeGame Requirements
      1. Handle collision detection and response
      2. Ensure the sphere always rolls on the ground
  6. SimTest References
  7. Part C: Open ended Rube Goldberg scene
    1. Overview
    2. Extra credit opporunity (optional):
  8. Turn In

Overview

In this project, you will implement your own physics simulation engine and use it to animate a new version of MazeGame from the beginning of the course.

This basic engine emits spheres and has them bounce off other sphere and plane colliders.

Simulator screenshot

Getting Started

To get started, clone the simulator-skeleton. The skeleton code has comments marked with // TODO denoting where to write your code. You are encouraged to read through this document and review the Particle System lectures carefully.

If you are having trouble cloning the repo (for example not all files show up), make sure to install git-lfs.

The Simulator project has scenes MazeGame and SimTest* which makes use of PhysicsSimulation.cs, PhysicsProperties.cs and MazeGameController.cs. When working on your physics engine, you’ll probably want to spend most of your time verifying your implementation in the SimTest* scenes first before integrating the engine into the MazeGame scene to make the sphere roll around.

You can compare your implementation with our provided reference behavior here

Help Slides


Requirements

Implement the features described below. Each part is described in more detail below.

Part A: Create a physics simulation system that:

  1. Has two distinct forces acting on a sphere:
    • In the script PhysicsProperties.cs, gravity (f=mg) is already implemented. You should implement viscous drag (f=-k_d*v) for this project.
  2. Updates the position and velocity of spheres:
    • In the script PhysicsSimulation.cs, compute a sphere’s position and velocity by solving the system of forces using Euler’s method in the function ComputeSphereMovement.
  3. Implements collision detection: You must detect collision and respond to other spheres and planes that can be added to your scene:
    • Plane collision: A natural position for the plane is to have it act as a ground for your scene, but it could be placed anywhere. Planes have a width and height which correspond to the x and z scale of the Transform component.
    • Sphere collision: Objects colliding with the sphere should reflect off the sphere depending on the sphere’s normal at the point of collision and the sphere’s incoming velocity direction.

Note:

  • Your spheres should bounce off of the SphereCollider and PlaneCollider GameObjects which use the CustomCollider.cs script to control the restitution constant. The restitution constant controls how much the normal component of the reflected velocity is attenuated. The constant is in the range of [0,1].
  • The collision between your spheres and other SphereCollider and PlaneCollider objects are one-sided, which means the collision response only applies to your spheres and the colliders should not move.
  • We also provide test scenes (Assets/Scene/SimTest*.unity) to help you to verify your implementation. Compare your results with the solution to see if the behavior is similar. You are free to create extra scenes to see whether your spheres bounce off other colliders at different angles correctly.

Part B: Recreate MazeGame using your physics engine:

With your physics engine working (spheres should collide and bounce off other colliders), navigate to the MazeGameController.cs script to implement the necessary steps to make the MazeGame scene work.

In the scene, the ball should roll around the maze and collide with the Collectibles (SphereCollider) and the walls (PlaneCollider), similar to the MazeGame tutorial at the beginning of the course.

Part C: Rube Goldberg machine:

In part C you will use your creativity to design and implement a Rube Goldberg machine. This part is more open ended. You will have to implement springs and force fields.


Part A: Physics Simulation

Program Overview

First, navigate to the SimTest1 scene. In the scene, there is an Emitter that uses the PhysicsSimulation.cs script to emit spheres. When playing this scene, spheres will be periodically emitted, but since you have not yet implemented any physics, they will not yet move. You can change the properties of the emitted spheres by changing the following parameters. Scale is the size of a sphere. Period controls how often a sphere is emitted. Max Spheres controls how many spheres exist in a scene simultaneously (for performance).

Note: changing these values during play does not update the simulation in real time. Any changes must be made before playing the scene.

Simulator screenshot

There are also PlaneCollider and SphereCollider gameobjects that use the CustomCollider.cs script to mark the respective plane and sphere primitives as colliders for our physics engine implementation. Each PlaneCollider or SphereCollider also has the restitution constant of the range [0,1] used in calculating collision force attenuation.

Simulator screenshot

If you want to add additional colliders, navigate to the Prefabs/Colliders folder and drag PlaneCollider/SphereCollider into the scene.

Skeleton Code

There are three major components in our physics simulation system:

  • Sphere objects (or balls) which have physics properties such as mass, position, and velocity
  • Forces
  • An engine for simulating the effect of the forces acting on the spheres that solve for the position and velocity of each sphere at every time step

The skeleton code already provides a general outline of a simulation engine. Specifically, at every frame, the engine first spawns the spheres by calling the EmitSpheres function. The spheres are spawned at the world coordinates of the Emitter Gameobject. Then, for each sphere, we need to:

  1. Calculate total forces acted on a sphere and solve for the sphere’s position and velocity using Euler’s method in the ComputeSphereMovement function.
  2. Detect collision and calculate collision response for the sphere in the OnCollision function.

Implement viscous drag force

Complete the TODO in PhysicsProperties.cs to implement a viscous drag force class. You must implement the constructor as well as the GetForce and SetDragCoefficient methods. The GetForce method should return a force vector which is the drag force exerted on a given Sphere. The SetDragCoefficient should update the coefficient of the drag force such that a subsequent call to GetForce will respect the new drag coefficient.

The ComputeSphereMovement function

The ComputeSphereMovement function is called every frame for each sphere (see FixedUpdate), and updates a Sphere’s velocity and position. Complete the TODO in ComputeSphereMovement in PhysicsSimulation.cs. Refer to lecture notes. In general, you will need to:

  1. Clear forces
  2. Calculate total forces acted on a sphere
  3. Solve the system of forces using Euler’s method (derivatives)
  4. Update sphere’s position and velocity

Hint: Use Unity’s Time.deltaTime which is the interval in seconds from the last frame to the current.

Once you finish this part, you should be able to see the emitted spheres move in the SimTest* scenes.

The OnCollision function

The OnCollision function in PhysicsSimulation.cs checks if a collision occurred between a ball and a collider, and also handles the collision interaction (bouncing). It returns a boolean indicating whether a collion occurred. Although we need to update a sphere’s position and velocity in world space, checking for collisions is much easier in a collider’s canonical (local) space. The skeleton already handles conversion from world to local space for both position and velocity of a sphere in the OnCollision function. Depending on whether the collider is a sphere or plane, we need separate checks.

Complete the three following 3 steps:

Detect SphereCollider collision

Implement the TODO: Detect collision with a sphere collider in OnCollision. Refer to lecture notes. You will need to check if a ball closely collides with a SphereCollider and if it is coming towards the collider.

In the OnCollision function:

  • The variable collisionOccured denotes whether our ball is within distance of collision with another SphereCollider.
  • The variable isEntering denotes whether our sphere is flying towards another SphereCollider.
  • The variable normal denotes the normal vector of the SphereCollider at the point of collision with the ball.

You will need to compute the two conditions above to determine whether a collision between our ball and another SphereCollider is possible. Remember that the position of our ball (localPos) is in the local frame of the SphereCollider, which means it can be used as a vector pointing from the SphereCollider towards the ball. The SphereCollider normal that you calculate here will be used for collision response calculation in 2.3.c

Detect PlaneCollider collision

Implement the TODO: Detect sphere collision with a plane collider in OnCollision. Refer to lecture notes.

Note: you don’t yet need to implement the TODO: Follow these steps to ensure the sphere always on top of the plane, since you will do that in part B: MazeGame. However it is possible some of your SimTest* scenes don’t match the solutions until you finish this, in particular when balls rolling on a plane or roll off an edge.

Since PlaneColliders are finite planes, you will also need to use the PlaneCollider’s width and height to determine collisions. By default, a 1x1 unit plane in Unity has an actual size of 10x10 units, thus the skeleton code already calculates the appropriate planeHeight and planeWidth by multiplying the x (height) and z(width) scale by 10. The default plane also has its normal pointing in the +y-axis, which is (0,1,0). This means that in the PlaneCollider’s local frame, its normal points in the (0,1,0) direction.

Simulator screenshot

In the OnCollision function:

  • The variable collisionOccured denotes whether our ball is within distance of collision with the PlaneCollider.
  • The variable isEntering denotes whether our sphere is flying towards the PlaneCollider.
  • The variable normal denotes the normal vector of the PlaneCollider at the point of collision with the ball. This will be the plane’s normal vector except when the collision occurs at an edge or at a corner. In those case, it will point from the edge/corner towards the ball.

You will need to compute the 2 conditions above to determine whether a collision between our ball and another PlaneCollider has occured. The PlaneCollider normal that you calculate here will also be used for collision response calculation in 2.3.c.

Note: a ball coming towards a PlaneCollider from underneath should not collide with the collider.

Collision Response

Implement the TODO: Update the sphere's velocity, remember to bring the velocity to world space in OnCollision.

At this point in the code, collisionOccurred and isEntering should be properly set for both the SphereCollider and the PlaneCollider case. Refer to lecture notes on how to calculate the new velocity vector and use the normal vector you calculated for both cases and calculate the new local velocity. Since we are operating in the collider’s local space, you will need to convert this local velocity to world velocity and assign it to the ball. [Hint: look at the function colliderTranform.TransformDirection() and colliderTransform.TransformPoint()].


Part B: MazeGame with Custom Physics

Overview

For this part, you will revisit your first-ever project in this class, MazeGame. However, there is a twist: you will not use Unity’s built-in Physics system (RigidBody, Colliders) but instead, you will try to recreate the game using the physics simulation engine that you just implemented.

Here is the link to the original MazeGame to re-familiarize yourself with all the components such as Collectible and EndWall. These 2 components use Unity’s collider system.

MazeGame Requirements

We have already created a simple MazeGame for you with all the necessary components such as the maze area, the control system, and the Emitter which spawns the player sphere. However, the game does not yet handle or detect collisions.

Handle collision detection and response

Use what you implemented in part A to complete the 2 TODOs in the MazeGameController.cs script to simulate the physics and handle the collisions so that the sphere rolls on the ground, picks up Collectible objects, and collides with the EndWall.

Ensure the sphere always rolls on the ground

Simulator screenshot

The reason why the sphere sinks to the ground is because we don’t consider the normal force from the plane. Consider a stationary sphere sitting on a plane. In the next timestep, the ball falls below the plane, but our mechanism of collision reflects the direction of velocity, which is zero. Hence, the sphere is not kept on the ground.

To fix this, we simply always position the sphere on top of the collider when there is a collision. Complete the TODO: Follow these steps to ensure the sphere always on top of the plane in OnCollision function in PhysicsSimulation.cs. The sphere should be able to roll around the maze after that.


SimTest References

In 16:9 aspect ratio, captured after both part A and B are completed.

1 Simulator screenshot 2 Simulator screenshot 3 Simulator screenshot 4 Simulator screenshot 5 (With the updated SimTest5 , make sure to pull the most recent skeleton code from Gitlab!) Simulator screenshot 6 Simulator screenshot

Part C: Open ended Rube Goldberg scene

Overview

Before you start working on part C, make sure you have finished part A and B, and check the TURN-IN instructions below. You will need to submit your code for part A and B without any code from part C. You can for example reclone the skeleton code, and copy over your solution to part A and B to the new project, to make sure you have an “untouched” backup of part A and B that you can use when you create your turn-in deliverable.

You will create a Unity scene where you simulate a ball physically interacting with a Rube Goldberg machine that you create. For this part, you will need to implement some more forces, in addition to forces you have created in previous parts. You will probably need to make major changes to the skeleton-code and you code in part A and B. Here are the requirements you need to include in your final deliverable. Note, you must not use Unity’s built in physics.

  • The scene must start with a single ball being emitted
  • The ball must interact with the following throughout the Rube Goldberg scene:
    • From previous A and B parts:
      • The ball must roll and bounce on walls and floors
      • The ball must have airtime (e.g using a ramp or bouncing)
      • The ball must have drag
    • New for this part
      • The ball must interact with a force field. Here are some examples
        • magnetic attraction or repulsion between the ball and some other object
        • wind from a fan
        • something else
        • NOTE: This force field cannot just be a constant force
      • The ball must interact with a springy object with force based collisions. Here is an example:
        • A squishy sponge. In this case, the sponge would exert force on the ball based on how far in the ball “squished into” the sponge (like a spring).
        • Note. The springy object must visually respond to the interaction. It doesn’t need to be fancy. For example for a squishy sponge, a ball should not just sink into a cube, be completely covered and then bounce out. Instead you can for example create a dent in the sponge, or lower one plane of the sponge as the ball is sinking into it.
      • The ball must interact with a compound object made from springs connecting multiple particles together. Here is an example
        • A cloth-like object made out of spheres, like a hammock, which the ball can roll through.
  • There must be a clear “goal” that the ball must reach by traversing through your rube goldberg machine (similar to the mazegame in part a)

Extra credit opporunity (optional):

You can choose to implement one or more of the following extra credit opportunities:

  1. Make a game out of it, where the game gives you a start and goal, along with a half-finished Rube Goldberg machine. The goal for the player is to place certain given objects (springs, cloth, walls, etc.) in order for the ball to reach the goal. Note: The game must be fully playable and contained within Unity a unity scene, which means when you press the “Unity play button”, the physical simulation shouldn’t start, but instead the game should start. You must create a ui for allowing the player to place objects within the scene. Note: The player is not allowed to use the built in Unity scene editing tools playing the games, the placing of the objects must be contained withih the game.
  2. Add levers (seesaws, hinged doors)
  3. Add object that are “alive”: actuated with “muscles”, and reacting to the external stimuli (e.g. when ball gets near start moving)

Turn In

Make a folder and rename it to your netid. In the folder, put the following items:

  • 6 screen recordings renamed SimTest1.mp4 to SimTest6.mp4 respectively. Each recording should be a screen capture of respective SimTest scene playing for at least 10 seconds and at most 15 seconds. SimTest4 should be played until the first emitted ball goes off screen (approximately 20 seconds). Your solution should not use any code from part C when you record these.
  • A screen recording renamed MazeGame.mp4 where you play the MazeGame scene, get 3 collectibles and then win (approximately 30-60s depending on your skills). Your solution should not use any code from part C when you record this.
  • PhysicsSimulation.cs (free of any code added in part C)
  • PhysicsProperties.cs (free of any code added in part C)
  • MazeGameController.cs (free of any code added in part C)
  • A subfolder called part_c containing a clear recording of your Rube Goldberg simulation (the recording must clearly show all requirements mentioned in part C above) named RubeGoldberg.mp4, along with all code for part C, as well as the Rube Goldberg scene itself.

For the recordings: Before you record, please make sure you have the most up-to-date versions of the SimTest scenes (if you are unsure, pull the skeleton-code branch). Make sure your game window in Unity is in 16x9 aspect ratio (top left drop-down after hitting play). Please make sure the resolution and frame rate of your screen capture is high enough to view the scene without problems.

Zip the folder and submit the file on Canvas. The zip file should contain exactly one level of folder nesting. (Once we open up your zip file, we should see a single folder named your netid, and we should see the listed items in the folder).