three a simple 2d game engine: input handling and collision detection

43
three a simple 2D game engine: input handling and collision detection

Upload: ross-lynch

Post on 28-Dec-2015

224 views

Category:

Documents


1 download

TRANSCRIPT

three

a simple 2D game engine: input handling and collision detection

Getting input from the user

We want to keep track of Where the mouse is Whether a mouse button has

been pressed What keys have been pressed

We’ll do this by storing the information in global variables

For mouse position and state And a list of states (up/down)

for each key

[define mouse-location [point 0 0]]

[define mouse-in-window? false]

[define mouse-pressed? false]

[define key-states [Array.CreateInstance Boolean 256]]

[define key-pressed?[keyvalue →

[get key-states keyvalue]]]

Event handling

The operating system tells you what the user is doing by signaling events KeyDown/KeyUp

A key went down/up on the keyboard MouseDown/MouseUp

A button went up/down on the mouse MouseMove

The mouse moved inside the window MouseEnter/MouseLeave

The user dragged the mouse into/out of the window

So we need to set up handlers for them

Setting up input handling

First, we initialize all those global variables Set all the elements of

the list of key states to false (i.e. not pressed)

Assume the mouse buttons are up

Assume the mouse is out of the window

[define initialize-input [form →

[up-to 256

[i → [[get key-states i] ← false]]]

[mouse-location ← [point 0 0]]

[mouse-down? ← false]

[mouse-in-window? ← false] … ]]

Setting up event handlers

When the MouseEnter event happens Set mouse-in-window? to

be true When MouseLead

happens Set it to be false

[define initialize-input [form → … [form.MouseEnter.Add

[ignore ignore → [mouse-in-window? ← true]]] [form.MouseLeave.Add …] [form.MouseMove.Add

[ignore e → [update-mouse e]]] [form.MouseDown.Add

[ignore e → [mouse-down? ← true] [update-mouse e]]] [form.MouseUp.Add …]]

Setting up event handlers

When MouseMove happens The OS passes a

MouseEventArgs object This object contains the

current coordinates of the mouse

So call update-mouse to read the coordinates out of the object and update the mouse-location variable

[define initialize-input [form → … [form.MouseEnter.Add

[ignore ignore → [mouse-in-window? ← true]]] [form.MouseLeave.Add …] [form.MouseMove.Add

[ignore e → [update-mouse e]]] [form.MouseDown.Add

[ignore e → [mouse-down? ← true] [update-mouse e]]] [form.MouseUp.Add …]]

update-mouse

The .X and .Y fields of the MouseEventArgs object give the location of the mouse

Make a point out of them and update mouse-location

[define update-mouse [e →

[mouse-location ← [point e.X e.Y]]]]

Setting up event handlers

MouseDown/MouseUp events are the same way The MouseEventArgs

object also has a field called Button that tells which button was pressed

For simplicity, we’ll ignore that

Update the mouse-down? Variable

Call update-mouse to update mouse-location

[define initialize-input

[form → … [form.MouseDown.Add

[ignore e → [mouse-down? ← true] [update-mouse e]]] [form.MouseUp.Add …]]

Setting up the keyboard handlers

The OS passes a KeyEventArgs object to keyboard handlers It’s KeyValue field has

a number corresponding to the key that was pressed

[define initialize-input [form →

[form.KeyDown.Add [ignore e → [set-key-state e.KeyValue true]]]

[form.KeyUp.Add [ignore e → [set-key-state e.KeyValue false]]] …]]

Implementing set-key-state

Basically, we Take the key number

(keyvalue) And set the corresponding

element of key-states to state (true or false)

However, we need to do the bitwise-and thingy to “fix” the number We’ll explain this later For now, trust me

[define set-key-state[keyvalue state →

[[get key-states

[bitwise-and keyvalue 255]]

← state]]]

Getting the magic key numbers

There’s an object called Keys It has fields with all the key

numbers They’re represented as a

funny kind of object So you need to do the

Convert.ToInt32 thing to make them “real” numbers

You can get any keyboard key you want this way (e.g. using Keys.A for the A key)

[define up-arrow[System.Convert.ToInt32 Keys.Up]]

[define down-arrow[System.Convert.ToInt32 Keys.Down]]

[define left-arrow[System.Convert.ToInt32 Keys.Left]]

[define right-arrow[System.Convert.ToInt32 Keys.Right]]

Example: controlling a car

A simple car that you can steer with the arrow keys Up – go forward Down – go back Left, right - steer

Example: controlling a car tank

A simple car that you can steer with the arrow keys

Unfortunately, it’s hard to tell which way the car is facing

So we’ll use a tank

The Tank class

Basically like the Obstacle class, but a slightly different draw method

[define Tank [class [Tank position]

GameObject]]

[define-method [draw [Tank t] g]

[g.DrawRectangle black-pen −12 −10 24 20]

[g.DrawRectangle black-pen 12 −2 10 4]]

Controlling speed

Simple version: If they hold down the

up-arrow key Move right at 10 pixels

per second

[define-method [tick [Tank t] Δt]

[t.velocity ←

[if [key-pressed? up-arrow]

[point 10 0]

[point 0 0]]]]]

Controlling speed

Simple version: If they hold down the

up-arrow key Move right at 10 pixels

per second Slightly fancier

Let them move backward

[define-method [tick [Tank t] Δt]

[t.velocity ←

[if [key-pressed? up-arrow]

[point 10 0]

[if [key-pressed? down-arrow] [point -10 0]

[point 0 0]]]]]

Controlling speed

Simple version: If they hold down the

up-arrow key Move right at 10 pixels

per second Slightly fancier

Let them move backward

Oh, yea. Let them steer.

[define-method [tick [Tank t] Δt]

[t.velocity ← [rotate-vector-degrees

[if [key-pressed? up-arrow] [point 10 0] [if [key-pressed? down-arrow]

[point -10 0] [point 0 0]]]

t.orientation]]]

Controlling speed

Writing it this way makes it easier to change the speed if we want to Only one number to

change

[define-method [tick [Tank t] Δt]

[t.velocity ← [× 10

[if [key-pressed? up-arrow]

1

[if [key-pressed? down-arrow]

-1

0]]

[rotate-vector-degrees [point 1 0]

t.orientation]]]]

Steering

To steer Just do the same trick

with the angular velocity

Only, remember it’s a number, not a vector

[define-method [tick [Tank t] Δt]

[t.velocity ← ...]

[t.angular-velocity ← [× 30

[if [key-pressed? left-arrow]

-1

[if [key-pressed? right-arrow]

1

0]]]]

Sticking to the mouse

We can force an object to follow the mouse by just setting its position to the mouse position

[define dragger [class [dragger position]

GameObject]]

[define-method [draw [dragger o] g]

[g.DrawEllipse black-pen −20 −20 40 40]]

[define-method [tick [dragger o] Δt]

[o.position ← mouse-location]]

Following the mouse

We can make the object follow the mouse by setting its speed to

always be some fraction of the vector between the object and the mouse

[define Mouser [class [Mouser position]

GameObject]]

[define-method [draw [Mouser o] g]

[g.DrawEllipse black-pen −20 −20 40 40]]

[define-method [tick [Mouser o] Δt]

[o.velocity ← [× [− mouse-location

o.position]

0.5]]]

Making it more interesting

We can complicate the dynamics of the ball by: Accelerating it toward

the mouse

[define SpringMouser[class [SpringMouser position]

GameObject]]

[define-method [tick [SpringMouser o] Δt]

[o.velocity ← [+ o.velocity

[× [− mouse-location

o.position]

0.1]

[× −0.001 o.velocity]]]]

Making it more interesting

We can complicate the dynamics of the ball by: Accelerating it toward

the mouse Rather than moving

straight toward it

[define SpringMouser[class [SpringMouser position]

GameObject]]

[define-method [tick [SpringMouser o] Δt]

[o.velocity ← [+ o.velocity

[× [− mouse-location

o.position]

0.1]

[× −0.001 o.velocity]]]]

Making it more interesting

We can complicate the dynamics of the ball by: Accelerating it toward

the mouse Rather than moving

straight toward it And adding some

damping to slow it down

[define SpringMouser[class [SpringMouser position]

GameObject]]

[define-method [tick [SpringMouser o] Δt]

[o.velocity ← [+ o.velocity

[× [− mouse-location

o.position]

0.1]

[× −0.001 o.velocity]]]]

Changing the appearance slightly

We can make a filled circle rather than a normal circle

By substituting FillEllipse

For DrawEllipse

And a Brush object For a Pen

[define green-brush [new SolidBrush [color “green”]]]

[define-method [draw [SpringMouser o] g]

[g.FillEllipse green-brush

−20 −20 40 40]]

Collision handling

Many games need to notice when two objects collide This is an expensive

operation It’s an on-going area

of research We’ll just talk about

the absolute dumbest version of it

[define update-objects[form →

[with* current-time = DateTime.Now Δt = [current-time.Subtract

last-time].TotalSeconds [for-all-game-objects [o → [tick o Δt]]] [for-all-game-objects

[o → [o.position ← [+ o.position [× o.velocity Δt]]] [o.orientation ← [+ o.orientation [× o.angular-

velocity Δt]]]]]

[check-collisions] [last-time ← current-time] [form.Invalidate]]]]

Naïve algorithm

Compare every object to every object

Check if they’ve collided Deal with it if they have

Notes: We haven’t said how to

check whether two objects have collided

Or what to do about it

[define check-collisions [→

[for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2]

]]]]]]]

Computational complexity

Suppose there are n objects in the game

[define check-collisions [→

[for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2]

]]]]]]]

Computational complexity

Suppose there are n objects in the game Then this procedure

runs n times

[define check-collisions [→

[for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2]

]]]]]]]

Computational complexity

Suppose there are n objects in the game Then this procedure

runs n times But each time it runs,

this procedure runs n times!

[define check-collisions [→

[for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2]

]]]]]]]

Computational complexity

Suppose there are n objects in the game Then this procedure

runs n times But each time it runs,

this procedure runs n times!

So that means we do n2 collision checks

[define check-collisions [→

[for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2]

]]]]]]]

Computational complexity

Suppose there are n objects in the game Then this procedure

runs n times But each time it runs,

this procedure runs n times!

So that means we do n2 collision checks

Very expensive

[define check-collisions [→ [for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2] ]]]]]]]

Computational complexity

The time complexity of a program is The rate at which the running time

increases With the size of its input

A linear algorithm increases about as fast as n

We then say the algorithm is O(n) A quadratic algorithm increases as

fast as n2

We then say it’s O(n2) An exponential time algorithm

increases exponentially with n Exponential algorithms are useless

unless n is very small

0

10

20

30

40

50

60

70

80

90

100

1 2 3 4 5 6 7 8 9 10

2 n̂

n 3̂

n 2̂

n

Problems with the algorithm

This actually has some bugs

[define check-collisions [→

[for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2]

]]]]]]]

Problem 1

This will check whether each object has collided with itself It probably has …

[define check-collisions [→

[for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2]

]]]]]]]

Problem 1

For any two objects, x and y, it will call both [collided? x y], and [collided? y x]

[define check-collisions [→

[for-all-game-objects

[o1 → [for-all-game-objects

[o2 → [if [collided? o1 o2]

[handle-collision o1 o2]

]]]]]]]

Fancy version

This version only compares objects Against objects that are later

in the list of game objects So we only compare each

pair once

However, it turns out it still has some issues, so the version we hand out may be different

[define check-collisions[→

[if collision-detection-enabled?

[with i = 0

[while [< i [length all-game-objects]]

[with o1 = [get all-game-objects i]

j = [+ i 1]

[while [< j [length all-game-objects]]

[with o2 = [get all-game-objects j]

[if [collided? o1 o2]

[handle-collision o1 o2]]

[j ← [+ j 1]]]]]

[i ← [+ i 1]]]]]]]

Adding collision handling to the SpringBall example

Two balls have collided if

[define collided? [o1 o2 →

[< [magnitude [− o1.position

o2.position]]

10]]]]

[define-method [handle-collision [SpringBall o1] [SpringBall o2]]

[destroy o1]

[destroy o2]]

Adding collision handling to the SpringBall example

Two balls have collided if The length

[define collided? [o1 o2 →

[< [magnitude [− o1.position

o2.position]]

10]]]]

[define-method [handle-collision [SpringBall o1] [SpringBall o2]]

[destroy o1]

[destroy o2]]

Adding collision handling to the SpringBall example

Two balls have collided if The length Of the space between

them

[define collided? [o1 o2 →

[< [magnitude [− o1.position

o2.position]]

10]]]]

[define-method [handle-collision [SpringBall o1] [SpringBall o2]]

[destroy o1]

[destroy o2]]

Adding collision handling to the SpringBall example

Two balls have collided if The length Of the space between

them Is less than 10 pixels

[define collided? [o1 o2 →

[< [magnitude [− o1.position

o2.position]]

10]]]]

[define-method [handle-collision [SpringBall o1] [SpringBall o2]]

[destroy o1]

[destroy o2]]

Adding collision handling to the SpringBall example

When two ball collide [define collided? [o1 o2 →

[< [magnitude [− o1.position

o2.position]]

10]]]]

[define-method [handle-collision [SpringBall o1] [SpringBall o2]]

[destroy o1]

[destroy o2]]

Adding collision handling to the SpringBall example

When two ball collide Blow the little buggers

away

[define collided? [o1 o2 →

[< [magnitude [− o1.position

o2.position]]

10]]]]

[define-method [handle-collision [SpringBall o1] [SpringBall o2]]

[destroy o1]

[destroy o2]]