Motion in Computer Games: Lecture #11 Movement

 
 
CSE 420
CSE 420
Computer Games
Computer Games
Lecture #
Lecture #
11 Movement
11 Movement
L
 
L
i
n
e
L
Lecture #11 Movement
Objectives
Understanding basic rules of motion
Getting continuous input from the
 keyboard
Firing missiles from sprites
Using vector projection
Calculating the distance and angle
 between points
Lecture #11 Movement
Objectives
 (cont.)
Following the mouse
Handling basic gravity
Building a vehicle model with power,
 turning rate, and drag
Modeling spacecraft motion
Handling orbital physics
Gaming and Physics
Games are about things moving around.
To get motion right, you must understand
how motion works in the real world.
Basic physics describes these phenomena.
You should take a more formal physics
course.
Lecture #11 Movement
Newton's Laws
These Laws describe how things move.
The three primary Laws:
An object at rest stays at rest.
Force = mass * acceleration.
Every action has an equal and opposite reaction.
Understanding these principles gives you the
ability to model motion.
Lecture #11 Movement
Vectors
V
e
c
t
o
r
:
 
H
a
s
 
d
i
r
e
c
t
i
o
n
 
a
n
d
 
m
a
g
n
i
t
u
d
e
.
 
O
b
j
e
c
t
m
o
t
i
o
n
 
i
s
 
a
 
v
e
c
t
o
r
 
b
e
c
a
u
s
e
 
t
h
e
 
m
a
g
n
i
t
u
d
e
 
i
s
t
h
e
 
s
p
e
e
d
 
a
n
d
 
t
h
e
 
d
i
r
e
c
t
i
o
n
 
i
s
 
a
n
 
a
n
g
l
e
.
V
e
c
t
o
r
 
c
o
m
p
o
n
e
n
t
s
:
 
A
 
v
e
c
t
o
r
 
c
a
n
 
a
l
s
o
 
b
e
d
e
s
c
r
i
b
e
d
 
i
n
 
d
x
/
d
y
 
t
e
r
m
s
.
 
C
o
n
v
e
r
t
i
n
g
 
f
r
o
m
s
p
e
e
d
/
d
i
r
e
c
t
i
o
n
 
t
o
 
d
x
/
d
y
 
i
s
 
c
a
l
l
e
d
 
v
e
c
t
o
r
p
r
o
j
e
c
t
i
o
n
.
Lecture #11 Movement
M
o
t
i
o
n
 
T
e
r
m
s
P
o
s
i
t
i
o
n
:
 
T
h
e
 
c
u
r
r
e
n
t
 
p
o
s
i
t
i
o
n
 
o
f
 
a
n
 
o
b
j
e
c
t
,
u
s
u
a
l
l
y
 
a
n
 
(
x
,
 
y
)
 
c
o
o
r
d
i
n
a
t
e
 
p
a
i
r
.
V
e
l
o
c
i
t
y
:
 
A
 
c
h
a
n
g
e
 
i
n
 
p
o
s
i
t
i
o
n
 
d
u
r
i
n
g
 
a
 
t
i
m
e
f
r
a
m
e
.
 
O
f
t
e
n
 
d
e
t
e
r
m
i
n
e
d
 
a
s
 
a
 
v
e
c
t
o
r
 
a
n
d
c
o
n
v
e
r
t
e
d
 
t
o
 
d
x
/
d
y
 
c
o
m
p
o
n
e
n
t
s
 
f
o
r
 
c
o
n
v
e
n
i
e
n
c
e
.
M
e
a
s
u
r
e
d
 
i
n
 
p
i
x
e
l
s
 
p
e
r
 
f
r
a
m
e
 
(
p
p
f
)
.
A
c
c
e
l
e
r
a
t
i
o
n
:
 
A
 
c
h
a
n
g
e
 
i
n
 
v
e
l
o
c
i
t
y
.
 
A
 
v
e
c
t
o
r
o
f
t
e
n
 
c
h
a
n
g
e
d
 
t
o
 
d
d
x
/
d
d
y
.
 
S
o
m
e
t
i
m
e
s
 
c
a
l
l
e
d
 
a
f
o
r
c
e
 
v
e
c
t
o
r
.
Lecture #11 Movement
M
u
l
t
i
p
l
e
 
F
o
r
c
e
 
V
e
c
t
o
r
s
Motion is a combination of various forces.
A balloon has weight pulling it down.
Helium adds a buoyant force pulling it up.
Wind adds a sideways force.
How will the balloon move?
Lecture #11 Movement
A
d
d
i
n
g
 
F
o
r
c
e
 
V
e
c
t
o
r
s
It's easy to calculate multiple force vectors.
Convert each force into dx/dy components.
Add all dx values to get a total dx.
Add all dy values to get a total dy.
Lecture #11 Movement
F
o
r
c
e
 
V
e
c
t
o
r
 
E
x
a
m
p
l
e
Lecture #11 Movement
G
e
t
t
i
n
g
 
C
o
n
t
i
n
u
o
u
s
 
K
e
y
b
o
a
r
d
I
n
p
u
t
So far, keyboard input has been one event
per key press.
Sometimes, you want continuous action as
long as a key is pressed down.
This uses a technique called 
hardware
polling.
Lecture #11 Movement
Advantages of Hardware Polling
A sprite can handle its own key presses.
Keyboard input can be continuous.
Motion appears much smoother.
Hardware polling is pretty easy to implement.
See 
turret.py
.
Continuous keyboard input
Very smooth rotation
Lecture #11 Movement
Building a Rotating Turret
def __init__(self):
    pygame.sprite.Sprite.__init__(self)
    self.imageMaster =
pygame.image.load("turret.gif")
    self.imageMaster = self.imageMaster.convert()
    self.rect = self.imageMaster.get_rect()
    self.rect.center = (320, 240)
    self.turnRate = 10
    self.dir = 0
def update(self):
    self.checkKeys()
    self.rotate()
Lecture #11 Movement
Notes on the Turret Class
To preserve quality when rotating, load the
image into a master image.
Keep the turret in the center of the screen.
Set up the turnRate constant. Smaller is slower
and smoother.
Set a dir constant.
Update involves
Checking keyboard
Rotating turret
Lecture #11 Movement
Checking the Keyboard
Done in the turret's checkKeys() method
def checkKeys(self):
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        self.dir += self.turnRate
        if self.dir > 360:
            self.dir = self.turnRate
    if keys[pygame.K_RIGHT]:
        self.dir -= self.turnRate
        if self.dir < 0:
            self.dir = 360 - self.turnRate
Lecture #11 Movement
How Checking the
Keyboard Works
Store pygame.keyPressed() in a variable.
It’s a tuple of binary values, one for each key
on the keyboard.
Use a keyboard constant to check a
particular key.
Checking for left arrow:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
Lecture #11 Movement
Rotating the Turret
The steps are just like 
Lecture #10 (
Chapter
8
)
:
Save center.
Transform the new image from imgMaster.
Place back in center.
def rotate(self):
    oldCenter = self.rect.center
    self.image = pygame.transform.rotate(self.imageMaster,
                 self.dir)
    self.rect = self.image.get_rect()
    self.rect.center = oldCenter
Lecture #11 Movement
Building the Main Loop
No keyboard handling!
Note: You still need the event.get()
mechanism.
keepGoing = True
while keepGoing:
    clock.tick(30)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            keepGoing = False
    allSprites.clear(screen, background)
    allSprites.update()
    allSprites.draw(screen)
    pygame.display.flip()
Lecture #11 Movement
Vector Projection
Chapter 8 
(Lecture #10) 
projects a vector in
eight directions.
What if you want any direction, any speed?
You should be able to convert any vector into
its dx/dy components.
This process is 
vector projection.
Lecture #11 Movement
Looking at the Problem
Lecture #11 Movement
Building a Triangle
Lecture #11 Movement
Using Trig Terms
Lecture #11 Movement
Solving for dy and dx
Lecture #11 Movement
Dealing with Radians
Lecture #11 Movement
How vecProj.py Works
Written in text mode for simplicity
Imports math module to get trig functions
Begins a loop
Inputs values for angle and distance
Converts angle to radians
Calculates dx and dy
Inverts dy (in math, y increases upward)
Outputs value
Asks user if he or she wants to repeat
Lecture #11 Movement
The vecProj.py Program
 
""" vecProj
    given any angle and distance,
    converts to dx and dy
    No GUI.
"""
import math
def main():
    keepGoing = True
    while keepGoing:
        print
        print "Give me an angle and distance,"
        print "and I'll convert to dx and dy values"
        print
        r = float(raw_input("distance: "))
        degrees = float(raw_input("angle (degrees): "))
Lecture #11 Movement
The vecProj.py Program (Cont’d)
        theta = degrees * math.pi / 180
        dx = r * math.cos(theta)
        dy = r * math.sin(theta)
        # compensate for inverted y axis
        dy *= -1
        print "dx: %f, dy: %f" % (dx, dy)
        response = raw_input("Again? (Y/N)")
        if response.upper() != "Y":
            keepGoing = False
Lecture #11 Movement
Making the Turret Fire
With vector projection, you can make the
turret fire a projectile.
The bullet gets dx and dy by projecting the
turret's direction.
A label is added to output direction and
charge.
See 
turretFire.py
.
Lecture #11 Movement
Adding a Label Class
class Label(pygame.sprite.Sprite):
    """ Label Class (simplest version)
        Properties:
            font: any pygame font object
            text: text to display
            center: desired position of label center (x, y)
    """
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.font = pygame.font.SysFont("None", 30)
        self.text = ""
        self.center = (320, 240)
    def update(self):
        self.image = self.font.render(self.text, 1, (0, 0, 0))
        self.rect = self.image.get_rect()
        self.rect.center = self.center
Lecture #11 Movement
Updating the Shell Class
The turret instance needs to know about the
shell instance.
Pass the shell as a parameter to the turret
constructor.
Store the shell as an attribute of the turret.
class Turret(pygame.sprite.Sprite):
    def __init__(self, shell):
        self.shell = shell
        # … other init code here…
Lecture #11 Movement
Add a charge Property
to the Turret
The turret doesn't move, so it isn't correct to
call it speed.
The charge indicates the speed of the shell
when it’s fired.
Add a charge attribute in the turret
constructor.
class Turret(pygame.sprite.Sprite):
    def __init__(self, shell):
        # … other init code here …
        self.charge = 5
Lecture #11 Movement
Modify the
checkKeys() Method
The up and down keys change the charge.
The spacebar fires a shell.
if keys[pygame.K_UP]:
    self.charge += 1
    if self.charge > 20:
        self.charge = 20
if keys[pygame.K_DOWN]:
    self.charge -= 1
    if self.charge < 0:
        self.charge = 0
if keys[pygame.K_SPACE]:
    self.shell.x = self.rect.centerx
    self.shell.y = self.rect.centery
    self.shell.speed = self.charge
    self.shell.dir = self.dir
Lecture #11 Movement
Firing the Shell
Set the shell's position equal to the turret's
position so that the shell seems to be coming
from the turret.
Set the shell's speed to the turret's charge.
Set the shell's direction to the turret's
direction.
Lecture #11 Movement
Creating the Shell Class
The shell itself is very ordinary.
I drew a circle instead of importing an image.
It hides offstage (-100, -100) when not
needed.
It doesn't really get created by the turret 
 it's
always around, just offstage.
This kind of illusion is very common.
Lecture #11 Movement
Calculating the Shell's Vector
Use trig functions to determine dx
and dy.
Don't forget to invert dy to compensate for y
increasing downward.
 def calcVector(self):
        radians = self.dir * math.pi / 180
        self.dx = self.speed * math.cos(radians)
        self.dy = self.speed * math.sin(radians)
        self.dy *= -1
Lecture #11 Movement
Resetting the Shell
Move the shell offstage.
Set the speed to 0 so the shell doesn't
wander back.
def reset(self):
    """ move off stage and stop"""
    self.x = -100
    self.y = -100
    self.speed = 0
Lecture #11 Movement
Modifying the Main Code
Create all three sprites.
Update the label in the main loop.
 
shell = Shell(screen)
    turret = Turret(shell)
    lblOutput = Label()
    lblOutput.center = (100, 20)
    allSprites = pygame.sprite.Group(shell, turret, lblOutput)
lblOutput.text = "dir: %d  speed %d" % (turret.dir,
                  turret.charge)
allSprites.clear(screen, background)
allSprites.update()
allSprites.draw(screen)
pygame.display.flip()
Lecture #11 Movement
Following the Mouse
Vector projection converts angle and distance
to dx/dy.
Sometimes, you have dx/dy and want angle
and distance.
More often, you have two points and want the
angle and distance between them.
See 
followMouse.py
.
Lecture #11 Movement
Converting Components Back to
Vectors
Lecture #11 Movement
Modifying the Turret Class
The turret class no longer reads the
keyboard, so now its update calls a
followMouse() method.
def update(self):
    self.followMouse()
    self.rotate()
Lecture #11 Movement
Making a Turret Point towards the
Mouse
Compare the turret position with the mouse
position.
Subtract x values for dx.
Subtract y values for dy.
Use the atan() function to find the inverse
tangent.
Convert to degrees.
Rotate the turret.
Lecture #11 Movement
Following the Mouse
def followMouse(self):
    (mouseX, mouseY) = pygame.mouse.get_pos()
    dx = self.rect.centerx - mouseX
    dy = self.rect.centery - mouseY
    dy *= -1
    radians = math.atan2(dy, dx)
    self.dir = radians * 180 / math.pi
    self.dir += 180
    #calculate distance
    self.distance = math.sqrt((dx * dx) + (dy * dy))
Lecture #11 Movement
How followMouse() Works
Subtract positions to get dx, dy.
Compensate for the inverted y axis.
Calculate the angle between the turret and the
mouse. (The atan2() function works even when
dy is 0.)
Convert the radian result to degrees.
Offset by 180. (The result of atan2() is between -
180 and 180.)
Calculate the distance by using the Pythagorean
theorem.
Lecture #11 Movement
Basic Gravity Concepts
Trajectories normally (near a planet) follow
parabolic paths.
The horizontal speed of a projectile stays nearly
constant (at least, in the game).
The vertical velocity decreases.
The object appears to pause at the top of the
arc.
It hits the ground at the same speed it left the
cannon.
Lecture #11 Movement
Simulating Basic Gravity
See 
turretGrav.py
.
The turret is now viewed from the side.
The shell follows a realistic arc.
The arc is drawn on the screen as the shell
moves.
Lecture #11 Movement
Adding Gravity to
the Shell Class
Add a gravitational constant to the shell.
Set the initial dx and dy when the shell is
fired.
Add a call to calcPos() in update().
self.gravity = .5
def update(self):
    self.calcPos()
    self.checkBounds()
    self.rect.center = (self.x, self.y)
Lecture #11 Movement
Adding the
Gravitational Force
Compensate for gravity in calcPos().
Draw the shell's path.
def calcPos(self):
    #compensate for gravity
    self.dy += self.gravity
    #get old position for drawing
    oldx = self.x
    oldy = self.y
    self.x += self.dx
    self.y += self.dy
    pygame.draw.line(self.background,
    (0,0,0), (oldx, oldy), (self.x, self.y))
Lecture #11 Movement
Drawing on the Background
Drawing on the screen is temporary.
To keep the drawings, draw on the
background instead.
Pass the background to the shell as a
parameter.
The shell draws its path on the background.
To erase the drawing, refill the background
surface.
Lecture #11 Movement
Building a Vector-Based Vehicle
Vector projection can add realism to vehicle
models.
Write sprite code like in 
Lecture #10 (
Chapter
8
)
.
Change the calcVector() method to
incorporate vector projection.
See 
carVec.py
.
Lecture #11 Movement
Creating a Vector-Based
Car Sprite
Add turnRate and accel properties to the car
in its init() method.
Design an update() method to pass control
around.
    self.turnRate = 3
    self.accel = .1
    self.x = 320
    self.y = 240
def update(self):
    self.checkKeys()
    self.rotate()
    self.calcVector()
    self.checkBounds()
    self.rect.center = (self.x, self.y)
Lecture #11 Movement
Checking the Keys
 
 def checkKeys(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_RIGHT]:
            self.dir -= self.turnRate
            if self.dir < 0:
                self.dir = 360 - self.turnRate
        if keys[pygame.K_LEFT]:
            self.dir += self.turnRate
            if self.dir > 360:
                self.dir = self.turnRate
        if keys[pygame.K_UP]:
            self.speed += self.accel
            if self.speed > 10:
                self.speed = 10
        if keys[pygame.K_DOWN]:
            self.speed -= self.accel
            if self.speed < -3:
                self.speed = -3
Lecture #11 Movement
Calculating the Vector
Use vector projection.
Check the code for other details 
 they’re
just like 
Lecture # 10 (
Chapter 8
)
.
def calcVector(self):
    radians = self.dir * math.pi / 180
    self.dx = math.cos(radians)
    self.dy = math.sin(radians)
    self.dx *= self.speed
    self.dy *= self.speed
    self.dy *= -1
    self.x += self.dx
    self.y += self.dy
Lecture #11 Movement
Making a More Versatile Vehicle
Model
New parameters can make a better-behaved
vehicle that's easier to tune:
P
o
w
e
r
:
 
S
t
r
e
n
g
t
h
 
o
f
 
t
h
e
 
v
e
h
i
c
l
e
'
s
 
e
n
g
i
n
e
 
i
n
 
r
e
l
a
t
i
o
n
s
h
i
p
t
o
 
i
t
s
 
m
a
s
s
.
D
r
a
g
:
 
T
h
e
 
s
u
m
 
o
f
 
a
l
l
 
t
h
e
 
f
o
r
c
e
s
 
t
h
a
t
 
c
a
u
s
e
 
a
 
v
e
h
i
c
l
e
 
t
o
s
l
o
w
 
d
o
w
n
.
T
u
r
n
 
r
a
t
e
:
 
T
h
e
 
s
p
e
e
d
 
a
t
 
w
h
i
c
h
 
a
 
v
e
h
i
c
l
e
 
t
u
r
n
s
.
S
p
e
e
d
:
 
N
o
 
l
o
n
g
e
r
 
s
e
t
 
d
i
r
e
c
t
l
y
;
 
a
 
f
u
n
c
t
i
o
n
 
o
f
 
p
o
w
e
r
 
a
n
d
d
r
a
g
.
See 
carParam.py
.
Lecture #11 Movement
The miniGUI Module
carParam.py
 makes use of several Graphic
User Interface (GUI) widgets.
I created them in a module called
miniGUI.py
.
They aren’t central to this discussion, so I
won't dwell on them here.
They’re fully developed as part of a gaming
library in Chapter 10.
Lecture #11 Movement
Building the User Interface
The 
carParam.py
 program communicates with
the user through labels and scrollers from
miniGUI.py
.
Copy 
miniGUI.py
 to the current directory.
Import 
miniGUI.py
 at the top of the program.
Each element is a sprite.
They're added to the sprite group like any other
sprites.
Lecture #11 Movement
Building the GUI Elements
Import 
miniGUI.py
.
Create the label.
Create the scroller.
lblPower = miniGUI.Label()
lblPower.center = (80, 20)
lblPower.text = "Power"
scrPower = miniGUI.Scroller()
scrPower.center = (250, 20)
scrPower.bgColor = (0xFF, 0xFF, 0xFF)
scrPower.value = 5
scrPower.maxValue = 10
scrPower.increment = .5
Lecture #11 Movement
Communicating
with the User
In the main loop, get power, turnRate, and
drag from the scrollers.
Copy the speed to the label for output.
    car.power = scrPower.value
    car.turnRate = scrTurn.value
    car.drag = scrDrag.value
    lblSpeed1.text = "%.2f" % car.speed
Lecture #11 Movement
Checking the Keys in carParam
def checkKeys(self):
    keys = pygame.key.get_pressed()
    if keys[pygame.K_RIGHT]:
        self.dir -= self.turnRate
        if self.dir < 0:
            self.dir = 360 - self.turnRate
    if keys[pygame.K_LEFT]:
        self.dir += self.turnRate
        if self.dir > 360:
            self.dir = self.turnRate
    if keys[pygame.K_UP]:
        self.speed += self.power
        #no need to check for a max speed anymore
    if keys[pygame.K_DOWN]:
        self.speed -= self.power
        if self.speed < -3:
            self.speed = -3
Lecture #11 Movement
Changes in checkKeys()
The up and down keys affect self.power
rather than self.accel.
No need to check for maximum speed 
 that
will be handled later.
power handles braking, as well as
acceleration (but brakes are now much less
important because the car will slow down on
its own).
Lecture #11 Movement
Compensating for Drag
The drag coefficient is a float between 0 and
1.
A smaller value means more efficient drag.
Subtract the drag from 1.
Multiply the speed by the resulting
percentage.
Set slow speeds to 0.
Lecture #11 Movement
Drag Code in calcVector()
def calcVector(self):
    radians = self.dir * math.pi / 180
    self.dx = math.cos(radians)
    self.dy = math.sin(radians)
    #compensate for drag
    self.speed *= (1 - self.drag)
    if self.speed < .5:
        if self.speed > -.5:
            self.speed = 0
    self.dx *= self.speed
    self.dy *= self.speed
    self.dy *= -1
Lecture #11 Movement
Building a Spacecraft Model
Cars tend to go in the direction they're
pointing.
Spacecraft travel differently.
When you fire a thruster, it adds thrust in a
particular direction.
You can easily fly perpendicular to the
direction of travel in space.
See 
space.py
.
Lecture #11 Movement
Creating a Multi-State Ship
It's useful to see thrust.
The spaceship has four master sprites:
i
m
g
C
r
u
i
s
e
:
 
N
o
 
t
h
r
u
s
t
i
m
g
T
h
r
u
s
t
:
 
M
a
i
n
 
t
h
r
u
s
t
e
r
s
i
m
g
L
e
f
t
:
 
T
h
r
u
s
t
 
r
o
t
a
t
i
n
g
 
t
o
 
l
e
f
t
i
m
g
R
i
g
h
t
:
 
T
h
r
u
s
t
 
r
o
t
a
t
i
n
g
 
t
o
 
r
i
g
h
t
Lecture #11 Movement
Checking Keys in Space
Set the image to the default (imgCruise).
Change the image when turning or
applying thrust.
Turn left or right the normal way. (Turning
just changes the dir property).
Give thrust a small value when the up
arrow is pressed.
Otherwise, set the thrust to 0.
Lecture #11 Movement
space.py checkKeys()
 def checkKeys(self):
        keys = pygame.key.get_pressed()
        self.imageMaster = self.imageCruise
        if keys[pygame.K_RIGHT]:
            self.dir -= self.turnRate
            if self.dir < 0:
                self.dir = 360 - self.turnRate
            self.imageMaster = self.imageRight
        if keys[pygame.K_LEFT]:
            self.dir += self.turnRate
            if self.dir > 360:
                self.dir = self.turnRate
            self.imageMaster = self.imageLeft
        if keys[pygame.K_UP]:
            self.thrust = .1
            self.imageMaster = self.imageThrust
        else:
            self.thrust = 0
Lecture #11 Movement
Creating a Motion Vector
Movement in space is reflective of Newton's
First Law.
The ship's current motion is a vector.
If the user thrusts, add a small motion vector
in the current direction.
Lecture #11 Movement
The calcVector() Method
Calculate the thrust vector. (The vector
will be 0 if the thrust is 0.)
Add thrustDX to self.dx and thrustDY to
self.dy.
def calcVector(self):
    radians = self.dir * math.pi / 180
    thrustDx = self.thrust * math.cos(radians)
    thrustDy = self.thrust * math.sin(radians)
    thrustDy *= -1
    self.dx += thrustDx
    self.dy += thrustDy
    self.speed = math.sqrt((self.dx * self.dx) + 
<cont'd>
    (self.dy * self.dy))
Lecture #11 Movement
Introducing Orbits
Orbits are a staple feature of space games.
Objects exert a force on each other.
Small stationary objects will be pulled towards
larger objects.
With enough sideways velocity, an object will
"skip past" instead of being drawn in.
An orbit happens when the small object's
sideways speed is fast enough to avoid being
pulled in but not fast enough to escape.
Lecture #11 Movement
Characteristics of
an Orbital Path
Lower orbits are faster than higher orbits.
Raise an orbit by thrusting in the orbital
direction.
Lower an orbit by thrusting against the orbital
direction.
Thrust affects the opposite side of the orbit.
See 
orbit.py
.
Lecture #11 Movement
Newton's Law of Universal
Gravitation
Lecture #11 Movement
Working with
Planetary Gravity
The mass of the two objects is important.
The distance between the objects is also
critical.
There's a gravitational constant, G.
Both objects exert a force on each other.
Lecture #11 Movement
Simplifying Newton's Formula
For an arcade game, you can simplify things
quite a bit.
One object is usually much smaller than the
other, so I'll give the ship a mass of 1.
The force of the small object on the large one
is so miniscule it will be ignored.
G can be left out for now.
Lecture #11 Movement
Modifying the Ship to Orbit
Add a mass attribute. (Set the mass to 1
initially.)
The ship no longer needs a checkbounds()
method because bouncing or wrapping would
change the orbit.
The ship draws its own path, which makes it
easier to visualize the orbits.
Lecture #11 Movement
Building a Planet
The planet also needs a mass property.
It also has a gravitate() method that
calculates the planet's pull on any object fed
as a parameter.
The orbiting object's dx and dy attributes are
adjusted to compensate for the gravitational
pull.
This method is called 
once per frame.
Lecture #11 Movement
The gravitate() Method
def gravitate(self, body):
    """ calculates gravitational pull on
        object """
    (self.x, self.y) = self.rect.center
    #get dx, dy, distance
    dx = self.x - body.x
    dy = self.y - body.y
    distance = math.sqrt((dx * dx) + (dy * dy))
    #normalize dx and dy
    dx /= distance
    dy /= distance
    force = (body.mass * self.mass)/(math.pow(distance, 2))
    dx *= force
    dy *= force
    body.dx += dx
    body.dy += dy
Lecture #11 Movement
How gravitate() Works
Accept a sprite as a parameter (the sprite
must have a mass attribute).
Determine the planet's x and y values.
Calculate the dx and dy between the objects.
Calculate the distance between them.
Divide dx and dy by the distance. This
creates a motion vector of length 1 drawing
the ship and planet together.
Lecture #11 Movement
More on gravitate()
Calculate the force of gravity.
Use a simplified form of the gravitation
formula.
Multiply dx and dy by the gravitational force.
Add the new gravity vector to the ship's
motion vector.
force = (body.mass * self.mass)/(math.pow(distance, 2))
Lecture #11 Movement
Discussion Questions
How do Newton's Laws affect game
development?
When is continuous keyboard motion
preferred?
How does vector projection simplify motion
code?
How might you make a system with a ship
orbiting between a planet and the planet's
moon?
Lecture #11 Movement
Lecture #11 Movement
Summary
You should now understand
B
asic rules of motion
Getting continuous input from the keyboard
Using vector projection
Calculating the distance and angle
 
between points
Following the mouse
Handling basic gravity
 and 
orbital physics
Modeling spacecraft motion
 
Next Lecture
 
Game Engine
Lecture #11 Movement
References
Andy Harris, “Game Programming, The L Line,
The Express Line to Learning”; Wiley, 2007
Lecture #11 Movement
Slide Note
Embed
Share

Explore the fundamentals of motion in computer games through Lecture #11 Movement. Delve into concepts like Newton's Laws, vectors, motion terms, and handling various forces to enhance your grasp on gaming physics. Discover how to apply basic rules of motion, obtain continuous input from keyboards, fire missiles, calculate distances and angles, and handle gravity within game development.

  • Motion Physics
  • Computer Games
  • Game Development
  • Vectors
  • Newtons Laws

Uploaded on Aug 23, 2024 | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. Download presentation by click this link. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

E N D

Presentation Transcript


  1. L Computer Games Lecture #11 Movement

  2. Objectives Understanding basic rules of motion Getting continuous input from the keyboard Firing missiles from sprites Using vector projection Calculating the distance and angle between points Lecture #11 Movement

  3. Objectives (cont.) Following the mouse Handling basic gravity Building a vehicle model with power, turning rate, and drag Modeling spacecraft motion Handling orbital physics Lecture #11 Movement

  4. Gaming and Physics Games are about things moving around. To get motion right, you must understand how motion works in the real world. Basic physics describes these phenomena. You should take a more formal physics course. Lecture #11 Movement

  5. Newton's Laws These Laws describe how things move. The three primary Laws: An object at rest stays at rest. Force = mass * acceleration. Every action has an equal and opposite reaction. Understanding these principles gives you the ability to model motion. Lecture #11 Movement

  6. Vectors Vector: Has direction and magnitude. Object motion is a vector because the magnitude is the speed and the direction is an angle. Vector components: A vector can also be described in dx/dy terms. Converting from speed/direction to dx/dy is called vector projection. Lecture #11 Movement

  7. Motion Terms Position: The current position of an object, usually an (x, y) coordinate pair. Velocity: A change in position during a time frame. Often determined as a vector and converted to dx/dy components for convenience. Measured in pixels per frame (ppf). Acceleration: A change in velocity. A vector often changed to ddx/ddy. Sometimes called a force vector. Lecture #11 Movement

  8. Multiple Force Vectors Motion is a combination of various forces. A balloon has weight pulling it down. Helium adds a buoyant force pulling it up. Wind adds a sideways force. How will the balloon move? Lecture #11 Movement

  9. Adding Force Vectors It's easy to calculate multiple force vectors. Convert each force into dx/dy components. Add all dx values to get a total dx. Add all dy values to get a total dy. Lecture #11 Movement

  10. Force Vector Example Force X Component Y Component Gravity 0 1 Buoyancy 0 -3 Wind 4 0 Total 4 2 Lecture #11 Movement

  11. Getting Continuous Keyboard Input So far, keyboard input has been one event per key press. Sometimes, you want continuous action as long as a key is pressed down. This uses a technique called hardware polling. Lecture #11 Movement

  12. Advantages of Hardware Polling A sprite can handle its own key presses. Keyboard input can be continuous. Motion appears much smoother. Hardware polling is pretty easy to implement. See turret.py. Continuous keyboard input Very smooth rotation Lecture #11 Movement

  13. Building a Rotating Turret def __init__(self): pygame.sprite.Sprite.__init__(self) self.imageMaster = pygame.image.load("turret.gif") self.imageMaster = self.imageMaster.convert() self.rect = self.imageMaster.get_rect() self.rect.center = (320, 240) self.turnRate = 10 self.dir = 0 def update(self): self.checkKeys() self.rotate() Lecture #11 Movement

  14. Notes on the Turret Class To preserve quality when rotating, load the image into a master image. Keep the turret in the center of the screen. Set up the turnRate constant. Smaller is slower and smoother. Set a dir constant. Update involves Checking keyboard Rotating turret Lecture #11 Movement

  15. Checking the Keyboard Done in the turret's checkKeys() method def checkKeys(self): keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: self.dir += self.turnRate if self.dir > 360: self.dir = self.turnRate if keys[pygame.K_RIGHT]: self.dir -= self.turnRate if self.dir < 0: self.dir = 360 - self.turnRate Lecture #11 Movement

  16. How Checking the Keyboard Works Store pygame.keyPressed() in a variable. It s a tuple of binary values, one for each key on the keyboard. Use a keyboard constant to check a particular key. Checking for left arrow: keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: Lecture #11 Movement

  17. Rotating the Turret The steps are just like Lecture #10 (Chapter 8): Save center. Transform the new image from imgMaster. Place back in center. def rotate(self): oldCenter = self.rect.center self.image = pygame.transform.rotate(self.imageMaster, self.dir) self.rect = self.image.get_rect() self.rect.center = oldCenter Lecture #11 Movement

  18. Building the Main Loop No keyboard handling! Note: You still need the event.get() mechanism. keepGoing = True while keepGoing: clock.tick(30) for event in pygame.event.get(): if event.type == pygame.QUIT: keepGoing = False allSprites.clear(screen, background) allSprites.update() allSprites.draw(screen) pygame.display.flip() Lecture #11 Movement

  19. Vector Projection Chapter 8 (Lecture #10) projects a vector in eight directions. What if you want any direction, any speed? You should be able to convert any vector into its dx/dy components. This process is vector projection. Lecture #11 Movement

  20. Looking at the Problem Lecture #11 Movement

  21. Building a Triangle Lecture #11 Movement

  22. Using Trig Terms Lecture #11 Movement

  23. Solving for dy and dx Lecture #11 Movement

  24. Dealing with Radians Lecture #11 Movement

  25. How vecProj.py Works Written in text mode for simplicity Imports math module to get trig functions Begins a loop Inputs values for angle and distance Converts angle to radians Calculates dx and dy Inverts dy (in math, y increases upward) Outputs value Asks user if he or she wants to repeat Lecture #11 Movement

  26. The vecProj.py Program """ vecProj given any angle and distance, converts to dx and dy No GUI. """ import math def main(): keepGoing = True while keepGoing: print print "Give me an angle and distance," print "and I'll convert to dx and dy values" print r = float(raw_input("distance: ")) degrees = float(raw_input("angle (degrees): ")) Lecture #11 Movement

  27. The vecProj.py Program (Contd) theta = degrees * math.pi / 180 dx = r * math.cos(theta) dy = r * math.sin(theta) # compensate for inverted y axis dy *= -1 print "dx: %f, dy: %f" % (dx, dy) response = raw_input("Again? (Y/N)") if response.upper() != "Y": keepGoing = False Lecture #11 Movement

  28. Making the Turret Fire With vector projection, you can make the turret fire a projectile. The bullet gets dx and dy by projecting the turret's direction. A label is added to output direction and charge. See turretFire.py. Lecture #11 Movement

  29. Adding a Label Class class Label(pygame.sprite.Sprite): """ Label Class (simplest version) Properties: font: any pygame font object text: text to display center: desired position of label center (x, y) """ def __init__(self): pygame.sprite.Sprite.__init__(self) self.font = pygame.font.SysFont("None", 30) self.text = "" self.center = (320, 240) def update(self): self.image = self.font.render(self.text, 1, (0, 0, 0)) self.rect = self.image.get_rect() self.rect.center = self.center Lecture #11 Movement

  30. Updating the Shell Class The turret instance needs to know about the shell instance. Pass the shell as a parameter to the turret constructor. Store the shell as an attribute of the turret. class Turret(pygame.sprite.Sprite): def __init__(self, shell): self.shell = shell # other init code here Lecture #11 Movement

  31. Add a charge Property to the Turret The turret doesn't move, so it isn't correct to call it speed. The charge indicates the speed of the shell when it s fired. Add a charge attribute in the turret constructor. class Turret(pygame.sprite.Sprite): def __init__(self, shell): # other init code here self.charge = 5 Lecture #11 Movement

  32. Modify the checkKeys() Method The up and down keys change the charge. if keys[pygame.K_UP]: self.charge += 1 if self.charge > 20: self.charge = 20 if keys[pygame.K_DOWN]: self.charge -= 1 if self.charge < 0: self.charge = 0 The spacebar fires a shell. if keys[pygame.K_SPACE]: self.shell.x = self.rect.centerx self.shell.y = self.rect.centery self.shell.speed = self.charge self.shell.dir = self.dir Lecture #11 Movement

  33. Firing the Shell Set the shell's position equal to the turret's position so that the shell seems to be coming from the turret. Set the shell's speed to the turret's charge. Set the shell's direction to the turret's direction. Lecture #11 Movement

  34. Creating the Shell Class The shell itself is very ordinary. I drew a circle instead of importing an image. It hides offstage (-100, -100) when not needed. It doesn't really get created by the turret it's always around, just offstage. This kind of illusion is very common. Lecture #11 Movement

  35. Calculating the Shell's Vector def calcVector(self): radians = self.dir * math.pi / 180 self.dx = self.speed * math.cos(radians) self.dy = self.speed * math.sin(radians) self.dy *= -1 Use trig functions to determine dx and dy. Don't forget to invert dy to compensate for y increasing downward. Lecture #11 Movement

  36. Resetting the Shell def reset(self): """ move off stage and stop""" self.x = -100 self.y = -100 self.speed = 0 Move the shell offstage. Set the speed to 0 so the shell doesn't wander back. Lecture #11 Movement

  37. Modifying the Main Code Create all three sprites. shell = Shell(screen) turret = Turret(shell) lblOutput = Label() lblOutput.center = (100, 20) allSprites = pygame.sprite.Group(shell, turret, lblOutput) Update the label in the main loop. lblOutput.text = "dir: %d speed %d" % (turret.dir, turret.charge) allSprites.clear(screen, background) allSprites.update() allSprites.draw(screen) pygame.display.flip() Lecture #11 Movement

  38. Following the Mouse Vector projection converts angle and distance to dx/dy. Sometimes, you have dx/dy and want angle and distance. More often, you have two points and want the angle and distance between them. See followMouse.py. Lecture #11 Movement

  39. Converting Components Back to Vectors Lecture #11 Movement

  40. Modifying the Turret Class The turret class no longer reads the keyboard, so now its update calls a followMouse() method. def update(self): self.followMouse() self.rotate() Lecture #11 Movement

  41. Making a Turret Point towards the Mouse Compare the turret position with the mouse position. Subtract x values for dx. Subtract y values for dy. Use the atan() function to find the inverse tangent. Convert to degrees. Rotate the turret. Lecture #11 Movement

  42. Following the Mouse def followMouse(self): (mouseX, mouseY) = pygame.mouse.get_pos() dx = self.rect.centerx - mouseX dy = self.rect.centery - mouseY dy *= -1 radians = math.atan2(dy, dx) self.dir = radians * 180 / math.pi self.dir += 180 #calculate distance self.distance = math.sqrt((dx * dx) + (dy * dy)) Lecture #11 Movement

  43. How followMouse() Works Subtract positions to get dx, dy. Compensate for the inverted y axis. Calculate the angle between the turret and the mouse. (The atan2() function works even when dy is 0.) Convert the radian result to degrees. Offset by 180. (The result of atan2() is between - 180 and 180.) Calculate the distance by using the Pythagorean theorem. Lecture #11 Movement

  44. Basic Gravity Concepts Trajectories normally (near a planet) follow parabolic paths. The horizontal speed of a projectile stays nearly constant (at least, in the game). The vertical velocity decreases. The object appears to pause at the top of the arc. It hits the ground at the same speed it left the cannon. Lecture #11 Movement

  45. Simulating Basic Gravity See turretGrav.py. The turret is now viewed from the side. The shell follows a realistic arc. The arc is drawn on the screen as the shell moves. Lecture #11 Movement

  46. Adding Gravity to the Shell Class Add a gravitational constant to the shell. self.gravity = .5 Set the initial dx and dy when the shell is fired. Add a call to calcPos() in update(). def update(self): self.calcPos() self.checkBounds() self.rect.center = (self.x, self.y) Lecture #11 Movement

  47. Adding the Gravitational Force Compensate for gravity in calcPos(). def calcPos(self): #compensate for gravity self.dy += self.gravity Draw the shell's path. #get old position for drawing oldx = self.x oldy = self.y self.x += self.dx self.y += self.dy pygame.draw.line(self.background, (0,0,0), (oldx, oldy), (self.x, self.y)) Lecture #11 Movement

  48. Drawing on the Background Drawing on the screen is temporary. To keep the drawings, draw on the background instead. Pass the background to the shell as a parameter. The shell draws its path on the background. To erase the drawing, refill the background surface. Lecture #11 Movement

  49. Building a Vector-Based Vehicle Vector projection can add realism to vehicle models. Write sprite code like in Lecture #10 (Chapter 8). Change the calcVector() method to incorporate vector projection. See carVec.py. Lecture #11 Movement

  50. Creating a Vector-Based Car Sprite Add turnRate and accel properties to the car in its init() method. self.turnRate = 3 self.accel = .1 self.x = 320 self.y = 240 Design an update() method to pass control around. def update(self): self.checkKeys() self.rotate() self.calcVector() self.checkBounds() self.rect.center = (self.x, self.y) Lecture #11 Movement

More Related Content

giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#