Programming Fundamentals  
Lecture 05 Vectors, Physics and Collision  
Detection  
Edirlei Soares de Lima  
<edirlei.lima@universidadeeuropeia.pt>  
What is a Vector?  
The word vector can mean a lot of different things…  
Biology/epidemiology: an organism that transmits infection from one host  
to another;  
Computer science: a one-dimensional array;  
Mathematics and physics: an entity that has both magnitude and  
direction.  
A vector is typically represented as an arrow, where the direction  
is indicated by where the arrow is pointing, and the magnitude  
by the length of the arrow itself.  
Bouncing Ball Without Vectors  
local x = 100  
local y = 100  
local xspeed = 300  
local yspeed = 500  
local radius = 30  
function love.update(dt)  
x = x + (xspeed * dt)  
y = y + (yspeed * dt)  
if (x > love.graphics.getWidth() - radius) or (x < radius) then  
xspeed = xspeed * -1;  
end  
if (y > love.graphics.getHeight() - radius) or (y < radius) then  
yspeed = yspeed * -1;  
end  
end  
function love.draw()  
love.graphics.circle("fill", x, y, radius, 30)  
end  
Bouncing Ball Without Vectors  
In the code, the ball has some properties that are represented  
by variables:  
Location  
Velocity  
x and y  
xspeed and yspeed  
Both location and velocity can be described as vectors:  
0
, 0  
Defining a Vector  
To better represent vectors in Lua, we can create a table to  
store both x and y:  
vector2 = {}  
vector2.lua  
function vector2.new(px, py)  
return {x = px, y = py}  
end  
We can save it in a separated module (file) and handle it as a  
new data type.  
What is a Table?  
A Lua table is defined by a set of pairs key-data, where the data  
is referenced by the key.  
The key (index) can be of any type of data (number, text, …);  
The data can also be of any type of data (number, text, function, image);  
With Lua tables we can associate names to its elements and  
create structures to store related data:  
player1 = {  
name = "John",  
score = 1000,  
lives = 3  
}
We can access an element of a table by its name:  
io.write(player1.score)  
Vector Addition  
Now that we have two vector objects (location and velocity),  
we are ready to implement the algorithm for motion:  
position = position + velocity  
Code without vectors:  
x = x + (xspeed * dt)  
y = y + (yspeed * dt)  
However, we are not allowed to do that directly, we need a  
function to add two vectors:  
=
function vector2.add(vec, inc)  
vector2.lua  
local result = vector2.new(0, 0)  
result.x = vec.x + inc.x  
result.y = vec.y + inc.y  
return result  
end  
Bouncing Ball With Vectors  
require "vector2"  
main.lua  
local position = vector2.new(100, 100)  
local velocity = vector2.new(300, 500)  
local radius = 30  
function love.update(dt)  
position = vector2.add(position, vector2.mult(velocity, dt))  
if (position.x > love.graphics.getWidth() - radius) or  
(
position.x < radius) then  
velocity.x = velocity.x * -1;  
end  
if (position.y > love.graphics.getHeight() - radius) or  
position.y < radius) then  
velocity.y = velocity.y * -1;  
end  
end  
(
function love.draw()  
love.graphics.circle("fill", position.x, position.y, radius, 30)  
end  
Vector Subtraction  
=
Implementation:  
function vector2.sub(vec, dec)  
local result = vector2.new(0, 0)  
result.x = vec.x - dec.x  
result.y = vec.y - dec.y  
return result  
vector2.lua  
end  
Vector Multiplication and Division  
=
Multiplication implementation:  
function vector2.mult(vec, n)  
local result = vector2.new(0, 0)  
result.x = vec.x * n  
result.y = vec.y * n  
return result  
vector2.lua  
end  
Division implementation:  
function vector2.div(vec, n)  
local result = vector2.new(0, 0)  
result.x = vec.x / n  
result.y = vec.y / n  
return result  
vector2.lua  
end  
Vector Magnitude  
Implementation:  
function vector2.magnitude(vec)  
return math.sqrt(vec.x * vec.x + vec.y * vec.y)  
end  
vector2.lua  
Vector Normalization  
Implementation:  
function vector2.normalize(vec)  
local m = vector2.magnitude(vec)  
vector2.lua  
if m ~= 0 then  
return vector2.div(vec, m)  
end  
return vec  
end  
Löve 2D Physics Engine  
Löve 2D uses the Box2D physics engine: https://box2d.org  
Box2D is a 2D rigid body simulation library for games.  
Basic concepts:  
Shape: shapes are solid 2D geometrical objects which handle the mass  
and collision of a rigid body. Example of shape types: circle, rectangle,  
polygon.  
Body: bodies are objects with velocity and position.  
Fixture: fixtures are used to attach shapes to bodies. Fixtures also add  
material properties to objects such as density, friction, and restitution.  
Contact: contacts are objects created to manage collisions. Contacts  
are created automatically by the physics engine.  
Joint: attach multiple bodies together to interact in unique ways.  
World: an object that contains all bodies and joints.  
Bouncing Ball With Physics  
local world  
local ball  
main.lua  
local wallleft  
local wallright  
local walltop  
local wallbottom  
function love.load()  
world = love.physics.newWorld(0, 0, true)  
ball = {}  
ball.body = love.physics.newBody(world, 100, 100, "dynamic")  
ball.shape = love.physics.newCircleShape(30)  
ball.fixture = love.physics.newFixture(ball.body, ball.shape, 1)  
ball.fixture:setRestitution(1)  
ball.fixture:setFriction(0)  
wallleft = {}  
wallleft.body = love.physics.newBody(world, 5, 300, "static")  
wallleft.shape = love.physics.newRectangleShape(10, 600)  
wallleft.fixture = love.physics.newFixture(wallleft.body,  
wallleft.shape, 1)  
Bouncing Ball With Physics  
main.lua  
wallright = {}  
wallright.body = love.physics.newBody(world, 795, 300, "static")  
wallright.shape = love.physics.newRectangleShape(10, 600)  
wallright.fixture = love.physics.newFixture(wallright.body,  
wallright.shape, 1)  
wallbottom = {}  
wallbottom.body = love.physics.newBody(world, 400, 595, "static")  
wallbottom.shape = love.physics.newRectangleShape(800, 10)  
wallbottom.fixture = love.physics.newFixture(wallbottom.body,  
wallbottom.shape, 1)  
walltop = {}  
walltop.body = love.physics.newBody(world, 400, 5, "static")  
walltop.shape = love.physics.newRectangleShape(800, 10)  
walltop.fixture = love.physics.newFixture(walltop.body,  
walltop.shape, 1)  
ball.body:setLinearVelocity(400, 400)  
end  
Bouncing Ball With Physics  
main.lua  
function love.update(dt)  
world:update(dt)  
end  
function love.draw()  
love.graphics.circle("fill", ball.body:getX(), ball.body:getY(),  
ball.shape:getRadius())  
love.graphics.polygon("fill", wallleft.body:getWorldPoints(  
wallleft.shape:getPoints()))  
love.graphics.polygon("fill", wallright.body:getWorldPoints(  
wallright.shape:getPoints()))  
love.graphics.polygon("fill", wallbottom.body:getWorldPoints(  
wallbottom.shape:getPoints()))  
love.graphics.polygon("fill", walltop.body:getWorldPoints(  
walltop.shape:getPoints()))  
end  
Example 1: Linear Velocity (Moving  
Towards the Mouse)  
require "vector2"  
main.lua  
local world  
local ball  
function love.load()  
world = love.physics.newWorld(0, 0, true)  
ball = {}  
ball.body = love.physics.newBody(world, 100, 100, "dynamic")  
ball.shape = love.physics.newCircleShape(30)  
ball.fixture = love.physics.newFixture(ball.body, ball.shape, 1)  
ball.fixture:setFriction(0)  
end  
function love.update(dt)  
local mouseposition = vector2.new(love.mouse.getX(),love.mouse.getY())  
local mousedirection = vector2.sub(mouseposition,  
vector2.new(ball.body:getPosition()))  
.
..  
Example 1: Linear Velocity (Moving Towards the  
Mouse)  
main.lua  
if vector2.magnitude(mousedirection) > 2 then  
mousedirection = vector2.normalize(mousedirection)  
local velocity = vector2.mult(mousedirection, 400)  
ball.body:setLinearVelocity(velocity.x, velocity.y)  
else  
ball.body:setLinearVelocity(0, 0)  
end  
world:update(dt)  
end  
function love.draw()  
love.graphics.circle("fill", ball.body:getX(), ball.body:getY(),  
ball.shape:getRadius())  
end  
Forces  
A force can be represented by a vector that causes an object  
with mass to accelerate.  
Newton’s Laws of Motion:  
1
2
3
. An object at rest stays at rest and an object in motion stays in motion.  
. Force equals mass times acceleration.  
. For every action there is an equal and opposite reaction.  
Friction  
Friction is a dissipative force, which is a type of force where the  
total energy of the system decreases when an object is in  
motion.  
= coefficient of friction (strength of a friction force for a particular surface)  
N = normal force (the force perpendicular to the object’s motion along a surface)  
= velocity unit vector  
Example: Gravity and Wind Forces  
require "vector2"  
main.lua  
local world  
local ball  
local ground  
local windforce  
function love.load()  
love.physics.setMeter(64)  
world = love.physics.newWorld(0, 9.81 * love.physics.getMeter(),  
true)  
ball = {}  
ball.body = love.physics.newBody(world, 100, 100, "dynamic")  
ball.shape = love.physics.newCircleShape(30)  
ball.fixture = love.physics.newFixture(ball.body, ball.shape, 5)  
ball.fixture:setRestitution(0.6)  
ball.fixture:setFriction(0.8)  
.
..  
Example: Gravity and Wind Forces  
ground = {}  
ground.body = love.physics.newBody(world, 400, 575, "static")  
ground.shape = love.physics.newRectangleShape(800, 50)  
main.lua  
ground.fixture = love.physics.newFixture(ground.body, ground.shape, 1)  
windforce = vector2.new(10, 0)  
end  
function love.update(dt)  
world:update(dt)  
ball.body:applyForce(windforce.x, windforce.y)  
end  
function love.draw()  
love.graphics.setColor(1,1,1)  
love.graphics.circle("fill", ball.body:getX(), ball.body:getY(),  
ball.shape:getRadius())  
love.graphics.setColor(0,1,0)  
love.graphics.polygon("fill", ground.body:getWorldPoints(  
ground.shape:getPoints()))  
end  
Exercise 1  
1
) Write the code to accelerate a spaceship towards the mouse  
position.  
You need to use the applyForcefunction  
to move the spaceship.  
Hint:  
o First, you need to calculate a vector that points  
from the spaceship to the mouse location  
(direction).  
The spaceship must rotate towards the  
direction of its movement.  
Hint:  
o Use the math.atan2 function to calculate the  
arc tangent value from the y and x variables of  
the direction of the velocity or the mouse  
direction.  
Example: Player Movement and Jump  
require "vector2"  
main.lua  
local world  
local player  
local ground  
function love.load()  
love.physics.setMeter(30)  
world = love.physics.newWorld(0, 9.81 * love.physics.getMeter(), true)  
ground = {}  
ground.body = love.physics.newBody(world, 400, 575, "static")  
ground.shape = love.physics.newRectangleShape(800, 50)  
ground.fixture = love.physics.newFixture(ground.body, ground.shape, 1)  
player = {}  
player.body = love.physics.newBody(world, 400, 100, "dynamic")  
player.shape = love.physics.newRectangleShape(30, 60)  
player.fixture = love.physics.newFixture(player.body, player.shape, 1)  
.
..  
Example: Player Movement and Jump  
.
..  
main.lua  
player.maxvelocity = 200  
player.onground = false  
player.jumped = false  
player.fixture:setFriction(1)  
player.body:setFixedRotation(true)  
end  
function love.update(dt)  
world:update(dt)  
-
-player movement  
if love.keyboard.isDown("right") then  
local moveForce = vector2.new(700, 0)  
player.body:applyForce(moveForce.x, moveForce.y)  
end  
if love.keyboard.isDown("left") then  
local moveForce = vector2.new(-700, 0)  
player.body:applyForce(moveForce.x, moveForce.y)  
end  
.
..  
Example: Player Movement and Jump  
main.lua  
.
..  
if love.keyboard.isDown("up") and player.onground == true and  
player.jumped == false then  
local jumpForce = vector2.new(0, -500)  
player.body:applyLinearImpulse(jumpForce.x, jumpForce.y)  
player.jumped = true  
end  
-
-max velocity  
local velocity = vector2.new(player.body:getLinearVelocity())  
if velocity.x > 0 then  
player.body:setLinearVelocity(math.min(velocity.x,  
player.maxvelocity), velocity.y)  
else  
player.body:setLinearVelocity(math.max(velocity.x,  
-
player.maxvelocity), velocity.y)  
end  
.
..  
Example: Player Movement and Jump  
main.lua  
.
-
..  
-check if player is on ground  
local contacts = player.body:getContacts()  
if #contacts == 0 then  
player.onground = false  
else  
local groundcontact = false  
for i = 1, #contacts, 1 do  
local normal = vector2.new(contacts[i]:getNormal())  
if normal.y == -1 then  
groundcontact = true  
end  
end  
player.onground = groundcontact  
end  
end  
.
..  
Example: Player Movement and Jump  
main.lua  
.
..  
function love.keyreleased(key)  
if key == "up" then  
player.jumped = false  
end  
end  
function love.draw()  
love.graphics.setColor(1,1,1)  
love.graphics.polygon("fill", player.body:getWorldPoints(  
player.shape:getPoints()))  
love.graphics.setColor(0,1,0)  
love.graphics.polygon("fill", ground.body:getWorldPoints(  
ground.shape:getPoints()))  
end  
Example: Responsive Player Movement  
..  
.
main.lua  
function love.update(dt)  
world:update(dt)  
local playerGravityForce = vector2.new(0, 1200)  
player.body:applyForce(playerGravityForce.x, playerGravityForce.y)  
local playerVelocity = vector2.new(player.body:getLinearVelocity())  
if love.keyboard.isDown("right") then  
player.body:setLinearVelocity(300, playerVelocity.y)  
elseif love.keyboard.isDown("left") then  
player.body:setLinearVelocity(-300, playerVelocity.y)  
else  
player.body:setLinearVelocity(0, playerVelocity.y)  
end  
if love.keyboard.isDown("up") and player.onground == true  
and player.jumped == false then  
local jumpForce = vector2.new(0, -1000)  
player.body:applyLinearImpulse(jumpForce.x, jumpForce.y)  
player.jumped = true  
end  
...  
Exercise 2  
2
) Modify the code of the player movement and jump example  
to allow the player to double jump.  
The player must be able to jump only two  
times.  
After double jumping, a new jump can only  
be performed when the player touches the  
ground.  
Hint:  
o Use the callback love.keyreleased(key,  
scancode)to detect when the player releases  
the jump key, which indicates that he/she can  
jump again (if it was the first jump).  
Dot Product  
ꢂ ∙ =,, =+ꢅ  
The dot product has several applications in game programming.  
Example: sight/view angle  
Example: View Angle and Modularization  
require "vector2"  
player.lua  
local player  
function CreatePlayer(world)  
player = {}  
player.sprite = love.graphics.newImage("spaceship.png")  
player.body = love.physics.newBody(world, 400, 300, "dynamic")  
player.shape = love.physics.newRectangleShape(  
player.sprite:getWidth() * 0.3,  
player.sprite:getHeight() * 0.3)  
player.fixture = love.physics.newFixture(player.body,  
player.shape, 1)  
player.body:setLinearDamping(0.3)  
end  
.
..  
Example: View Angle and Modularization  
..  
.
player.lua  
function UpdatePlayer(dt)  
local mouseposition = vector2.new(love.mouse.getX(),  
love.mouse.getY())  
local mousedirection = vector2.sub(mouseposition,  
vector2.new(player.body:getPosition()))  
mousedirection = vector2.normalize(mousedirection)  
local rotation = math.atan2(mousedirection.y, mousedirection.x)  
player.body:setAngle(rotation)  
if (love.mouse.isDown(1)) then  
local engineForce = vector2.mult(mousedirection, 800)  
player.body:applyForce(engineForce.x, engineForce.y)  
end  
end  
.
..  
Example: View Angle and Modularization  
..  
.
player.lua  
function DrawPlayer()  
love.graphics.draw(player.sprite, player.body:getX(),  
player.body:getY(), player.body:getAngle(),  
0.3, 0.3, player.sprite:getWidth() / 2,  
player.sprite:getHeight() / 2)  
end  
function GetPlayerPosition()  
return vector2.new(player.body:getPosition())  
end  
Example: View Angle and Modularization  
require "vector2"  
enemy.lua  
local enemy  
function CreateEnemy(world)  
enemy = {}  
enemy.sprite = love.graphics.newImage("spaceship.png")  
enemy.body = love.physics.newBody(world, 700, 500, "dynamic")  
enemy.shape = love.physics.newRectangleShape(  
enemy.sprite:getWidth() * 0.3,  
enemy.sprite:getHeight() * 0.3)  
enemy.fixture = love.physics.newFixture(enemy.body, enemy.shape, 1)  
enemy.body:setLinearDamping(0.3)  
enemy.direction = vector2.new(-1, 0)  
enemy.body:setAngle(math.atan2(enemy.direction.y,  
enemy.direction.x))  
enemy.viewangle = 30  
enemy.chasing = false  
end  
.
..  
Example: View Angle and Modularization  
enemy.lua  
.
..  
function UpdateEnemy(dt, playerposition)  
enemy.direction = vector2.new(math.cos(enemy.body:getAngle()),  
math.sin(enemy.body:getAngle()))  
if (CanSee(vector2.new(enemy.body:getPosition()), enemy.direction,  
playerposition, enemy.viewangle)) then  
enemy.chasing = true  
end  
if enemy.chasing then  
local playerdirection = vector2.normalize(vector2.sub(  
playerposition, vector2.new(enemy.body:getPosition())))  
local engineForce = vector2.mult(playerdirection, 800)  
enemy.body:applyForce(engineForce.x, engineForce.y)  
local enemyvelocity = vector2.new(enemy.body:getLinearVelocity())  
enemy.body:setAngle(math.atan2(enemyvelocity.y, enemyvelocity.x))  
end  
end  
.
..  
Example: View Angle and Modularization  
..  
.
enemy.lua  
function DrawEnemy()  
love.graphics.draw(enemy.sprite, enemy.body:getX(),  
enemy.body:getY(), enemy.body:getAngle(), 0.3,  
.3, enemy.sprite:getWidth() / 2,  
enemy.sprite:getHeight() / 2)  
end  
0
function CanSee(p1, p1lookdir, p2, viewangle)  
local direction = vector2.normalize(vector2.sub(p2, p1))  
local angle = math.acos(vector2.dot(p1lookdir, direction))  
if (math.deg(angle) < viewangle) then  
return true  
end  
return false  
end  
Example: View Angle and Modularization  
require "player"  
require "enemy"  
main.lua  
local world  
function love.load()  
world = love.physics.newWorld(0, 0, true)  
CreatePlayer(world)  
CreateEnemy(world)  
end  
function love.update(dt)  
world:update(dt)  
UpdatePlayer(dt)  
UpdateEnemy(dt, GetPlayerPosition())  
end  
function love.draw()  
DrawPlayer()  
DrawEnemy()  
end  
Collisions  
No Collision  
Collision  
Box2  
Box2  
Box1  
Box1  
Collisions  
No Collision  
Collision  
Circle2  
Circle2  
Circle1  
Circle1  
Collisions vs Triggers  
Collision  
Trigger  
Collisions:  
Static or dynamic solid objects (can block the movement of other  
objects).  
Triggers:  
Visible or invisible areas that are used to trigger certain game events  
(objects can pass through trigger areas).  
Static vs Dynamic  
Static  
Dynamic  
Static objects:  
Static objects cannot be moved by forces.  
Dynamic objects:  
Can be moved by forces (forces directly applied over the object or  
generated by other objects that collide with the dynamic object).  
Example: Static and Dynamic Objects  
require "vector2"  
local world  
local player  
local ground  
local box1, box2, box3  
function love.load()  
love.physics.setMeter(30)  
world = love.physics.newWorld(0, 9.81 * love.physics.getMeter(), true)  
ground = {}  
ground.body = love.physics.newBody(world, 400, 575, "static")  
ground.shape = love.physics.newRectangleShape(800, 50)  
ground.fixture = love.physics.newFixture(ground.body, ground.shape, 1)  
box1 = {}  
box1.body = love.physics.newBody(world, 600, 525, "dynamic")  
box1.shape = love.physics.newRectangleShape(50, 50)  
box1.fixture = love.physics.newFixture(box1.body, box1.shape, 2)  
.
..  
Example: Static and Dynamic Objects  
..  
.
box2 = {}  
box2.body = love.physics.newBody(world, 600, 475, "dynamic")  
box2.shape = love.physics.newRectangleShape(50, 50)  
box2.fixture = love.physics.newFixture(box2.body, box2.shape, 2)  
box3 = {}  
box3.body = love.physics.newBody(world, 600, 425, "dynamic")  
box3.shape = love.physics.newRectangleShape(50, 50)  
box3.fixture = love.physics.newFixture(box3.body, box3.shape, 2)  
player = {}  
player.body = love.physics.newBody(world, 400, 100, "dynamic")  
player.shape = love.physics.newRectangleShape(30, 60)  
player.fixture = love.physics.newFixture(player.body, player.shape, 1)  
player.maxvelocity = 200  
player.onground = false  
player.jumped = false  
player.fixture:setFriction(1)  
player.body:setFixedRotation(true)  
end  
...  
Example: Static and Dynamic Objects  
.
..  
function love.update(dt)  
end  
- use here the same code of the player  
-- movement and jump example  
function love.keyreleased(key) - use here the same code of the player  
end  
-- movement and jump example  
function love.draw()  
love.graphics.setColor(1,1,1)  
love.graphics.polygon("fill", player.body:getWorldPoints(  
player.shape:getPoints()))  
love.graphics.setColor(0,1,0)  
love.graphics.polygon("fill", ground.body:getWorldPoints(  
ground.shape:getPoints()))  
love.graphics.setColor(0,0,1)  
love.graphics.polygon("fill", box1.body:getWorldPoints(  
box1.shape:getPoints()))  
love.graphics.polygon("fill", box2.body:getWorldPoints(  
box2.shape:getPoints()))  
love.graphics.polygon("fill", box3.body:getWorldPoints(  
box3.shape:getPoints()))  
end  
Exercise 3  
3
) Create a level for a platform game like the one illustrated  
bellow.  
Green and gray platforms are static.  
Red objects are dynamic (the player must be able to push them).  
Use the code create for the last example as the starting point.  
Collision Detection Callbacks  
The physics engine of Löve 2D uses callbacks to handle collision  
events.  
There are 4 collision detection callbacks:  
beginContact: gets called when two fixtures begin to overlap.  
endContact: gets called when two fixtures cease to overlap.  
preSolve: gets called before a collision gets resolved.  
postSolve: gets called after the collision has been resolved.  
The collision detection callbacks are set in the World object  
using the function:  
World:setCallbacks(beginContact, endContact, preSolve, postSolve)  
Collision Detection Callbacks  
function love.load()  
love.physics.setMeter(30)  
world = love.physics.newWorld(0, 9.81 * love.physics.getMeter(), true)  
world:setCallbacks(BeginContact, EndContact, PreSolve, PostSolve)  
.
..  
end  
function BeginContact(fixtureA, fixtureB, contact)  
end  
function EndContact(fixtureA, fixtureB, contact)  
end  
function PreSolve(fixtureA, fixtureB, contact)  
end  
function PostSolve(fixtureA, fixtureB, contact, normal, tangent)  
end  
Example: Collision Callbacks and Triggers  
require "vector2"  
local world  
local player  
local ground  
local platform1  
local trigger1  
local endgame = false  
function love.load()  
love.physics.setMeter(30)  
world = love.physics.newWorld(0, 9.81 * love.physics.getMeter(), true)  
world:setCallbacks(BeginContact, nil, nil, nil)  
ground = {}  
ground.body = love.physics.newBody(world, 400, 575, "static")  
ground.shape = love.physics.newRectangleShape(800, 50)  
ground.fixture = love.physics.newFixture(ground.body, ground.shape, 1)  
ground.fixture:setUserData("platform")  
.
..  
Example: Collision Callbacks and Triggers  
.
..  
platform1 = {}  
platform1.body = love.physics.newBody(world, 500, 475, "static")  
platform1.shape = love.physics.newRectangleShape(150, 25)  
platform1.fixture = love.physics.newFixture(platform1.body,  
platform1.shape, 2)  
platform1.fixture:setUserData("platform")  
trigger1 = {}  
trigger1.body = love.physics.newBody(world, 700, 275, "static")  
trigger1.shape = love.physics.newRectangleShape(50, 550)  
trigger1.fixture = love.physics.newFixture(trigger1.body,  
trigger1.shape, 2)  
trigger1.fixture:setSensor(true)  
trigger1.fixture:setUserData("endlevel")  
player = {}  
player.body = love.physics.newBody(world, 200, 100, "dynamic")  
player.shape = love.physics.newRectangleShape(30, 60)  
player.fixture = love.physics.newFixture(player.body, player.shape, 1)  
.
..  
Example: Collision Callbacks and Triggers  
..  
.
player.maxvelocity = 200  
player.onground = false  
player.fixture:setFriction(1)  
player.fixture:setUserData("player")  
player.body:setFixedRotation(true)  
end  
function BeginContact(fixtureA, fixtureB, contact)  
if fixtureA:getUserData() == "endlevel" and  
fixtureB:getUserData() == "player" then  
endgame = true  
end  
if fixtureA:getUserData() == "platform" and  
fixtureB:getUserData() == "player" then  
local normal = vector2.new(contact:getNormal())  
if normal.y == -1 then  
player.onground = true  
end  
end  
end  
.
..  
Example: Collision Callbacks and Triggers  
..  
.
function love.update(dt)  
world:update(dt)  
if love.keyboard.isDown("right") then  
local moveForce = vector2.new(700, 0)  
player.body:applyForce(moveForce.x, moveForce.y)  
end  
if love.keyboard.isDown("left") then  
local moveForce = vector2.new(-700, 0)  
player.body:applyForce(moveForce.x, moveForce.y)  
end  
if love.keyboard.isDown("up") and player.onground == true then  
local jumpForce = vector2.new(0, -500)  
player.body:applyLinearImpulse(jumpForce.x, jumpForce.y)  
player.onground = false  
end  
local velocity = vector2.new(player.body:getLinearVelocity())  
if velocity.x > 0 then  
player.body:setLinearVelocity(math.min(velocity.x,  
player.maxvelocity), velocity.y)  
else  
.
..  
Example: Collision Callbacks and Triggers  
player.body:setLinearVelocity(math.max(velocity.x,  
-
player.maxvelocity), velocity.y)  
end  
end  
function love.draw()  
if not endgame then  
love.graphics.setColor(1,1,1)  
love.graphics.polygon("fill", player.body:getWorldPoints(  
player.shape:getPoints()))  
love.graphics.setColor(0,1,0)  
love.graphics.polygon("fill", ground.body:getWorldPoints(  
ground.shape:getPoints()))  
love.graphics.polygon("fill", platform1.body:getWorldPoints(  
platform1.shape:getPoints()))  
love.graphics.setColor(0,0,1)  
love.graphics.polygon("fill", trigger1.body:getWorldPoints(  
trigger1.shape:getPoints()))  
else  
love.graphics.setColor(1,1,1)  
love.graphics.print("Game Completed!", 350, 300)  
end  
end  
Exercise 4  
4
) 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).  
If the player touches the sides of the enemy, a “game over” message  
must be displayed.  
If the player touches the top of the enemy, a “you win” message must  
be displayed.  
Enemy  
Enemy  
Player wins  
Player dies