Programming Fundamentals
Lecture 05 Vectors, Physics and Collision
Detection
Edirlei Soares de Lima
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
function love.update(dt)
x = x + (xspeed * dt)
y = y + (yspeed * dt)
xspeed = xspeed * -1;
end
yspeed = yspeed * -1;
end
end
function love.draw()
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)
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
=
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)
function love.update(dt)
if (position.x > love.graphics.getWidth() - radius) or
(
velocity.x = velocity.x * -1;
end
if (position.y > love.graphics.getHeight() - radius) or
velocity.y = velocity.y * -1;
end
end
(
function love.draw()
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
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(),
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
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(),
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
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(),
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
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
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
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
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
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