Skip to main content

Velocity and Acceleration

The Foundation of Game Movement

Velocity and acceleration are the heartbeat of game physics! They transform static objects into dynamic entities that move, respond to forces, and feel alive. Let's master the fundamentals of motion! ๐Ÿš€๐Ÿ“ˆ

Understanding Motion

๐Ÿš— The Car Journey Analogy

Think of game physics like driving a car:

graph TD A["Motion Physics"] --> B["Position"] A --> C["Velocity"] A --> D["Acceleration"] A --> E["Forces"] B --> F["x, y coordinates"] B --> G["Rotation angle"] C --> H["Speed"] C --> I["Direction"] D --> J["Linear"] D --> K["Angular"] E --> L["Gravity"] E --> M["Thrust"] E --> N["Friction"]

Interactive Physics Playground

Explore velocity and acceleration concepts!

Demo: Constant Velocity

Velocity: 0, 0 | Acceleration: 0, 0

Basic Position, Velocity, and Acceleration

import pygame
import math

class PhysicsObject:
    def __init__(self, x, y):
        # Position (where the object is)
        self.position = pygame.math.Vector2(x, y)
        
        # Velocity (how fast position changes)
        self.velocity = pygame.math.Vector2(0, 0)
        
        # Acceleration (how fast velocity changes)
        self.acceleration = pygame.math.Vector2(0, 0)
        
        # Physical properties
        self.mass = 1.0
        self.radius = 10
    
    def apply_force(self, force):
        """Apply a force to the object (F = ma, so a = F/m)"""
        if self.mass > 0:
            self.acceleration += force / self.mass
    
    def update(self, dt):
        """Update physics simulation"""
        # Update velocity: v = v0 + a*t
        self.velocity += self.acceleration * dt
        
        # Update position: x = x0 + v*t
        self.position += self.velocity * dt
        
        # Reset acceleration (forces must be applied each frame)
        self.acceleration = pygame.math.Vector2(0, 0)
    
    def draw(self, screen):
        """Draw the object"""
        pygame.draw.circle(screen, (100, 200, 255), 
                         (int(self.position.x), int(self.position.y)), 
                         self.radius)

# Example: Basic motion
def basic_motion_example():
    pygame.init()
    screen = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    
    # Create object
    obj = PhysicsObject(100, 300)
    
    # Set initial velocity
    obj.velocity.x = 100  # pixels per second
    
    running = True
    while running:
        dt = clock.tick(60) / 1000.0  # Delta time in seconds
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
        
        # Apply constant acceleration (like gravity)
        obj.apply_force(pygame.math.Vector2(0, 200))  # Downward force
        
        # Update physics
        obj.update(dt)
        
        # Bounce off floor
        if obj.position.y > 580:
            obj.position.y = 580
            obj.velocity.y *= -0.8  # Bounce with energy loss
        
        # Draw
        screen.fill((30, 30, 30))
        obj.draw(screen)
        pygame.display.flip()
    
    pygame.quit()

Types of Motion

# Different motion patterns

class MotionExamples:
    @staticmethod
    def constant_velocity(obj, speed, direction):
        """Move at constant speed in a direction"""
        obj.velocity.x = speed * math.cos(direction)
        obj.velocity.y = speed * math.sin(direction)
    
    @staticmethod
    def constant_acceleration(obj, accel_x, accel_y):
        """Apply constant acceleration"""
        obj.apply_force(pygame.math.Vector2(accel_x, accel_y) * obj.mass)
    
    @staticmethod
    def circular_motion(obj, center, radius, angular_speed):
        """Move in a circle"""
        angle = pygame.time.get_ticks() * angular_speed / 1000.0
        obj.position.x = center[0] + radius * math.cos(angle)
        obj.position.y = center[1] + radius * math.sin(angle)
        
        # Set velocity tangent to circle
        obj.velocity.x = -radius * angular_speed * math.sin(angle)
        obj.velocity.y = radius * angular_speed * math.cos(angle)
    
    @staticmethod
    def oscillating_motion(obj, center, amplitude, frequency):
        """Simple harmonic motion"""
        time = pygame.time.get_ticks() / 1000.0
        offset = amplitude * math.sin(2 * math.pi * frequency * time)
        obj.position.x = center[0] + offset
        
        # Velocity is derivative of position
        obj.velocity.x = amplitude * 2 * math.pi * frequency * \
                        math.cos(2 * math.pi * frequency * time)
    
    @staticmethod
    def projectile_motion(obj, initial_velocity, gravity):
        """Projectile with gravity"""
        obj.velocity = initial_velocity.copy()
        obj.apply_force(pygame.math.Vector2(0, gravity * obj.mass))

# Advanced physics object with more features
class AdvancedPhysicsObject:
    def __init__(self, x, y):
        self.position = pygame.math.Vector2(x, y)
        self.velocity = pygame.math.Vector2(0, 0)
        self.acceleration = pygame.math.Vector2(0, 0)
        
        # Rotation
        self.angle = 0  # radians
        self.angular_velocity = 0  # radians per second
        self.angular_acceleration = 0
        
        # Properties
        self.mass = 1.0
        self.max_speed = 500
        self.max_force = 100
        
        # Damping (air resistance)
        self.linear_damping = 0.99
        self.angular_damping = 0.99
    
    def apply_force(self, force, point=None):
        """Apply force at a point (for torque)"""
        # Linear force
        force_vector = pygame.math.Vector2(force)
        
        # Limit force magnitude
        if force_vector.length() > self.max_force:
            force_vector.scale_to_length(self.max_force)
        
        self.acceleration += force_vector / self.mass
        
        # Calculate torque if force applied off-center
        if point:
            r = point - self.position
            torque = r.x * force[1] - r.y * force[0]
            self.angular_acceleration += torque / self.mass
    
    def update(self, dt):
        """Advanced physics update"""
        # Update linear motion
        self.velocity += self.acceleration * dt
        
        # Limit speed
        if self.velocity.length() > self.max_speed:
            self.velocity.scale_to_length(self.max_speed)
        
        # Apply damping
        self.velocity *= self.linear_damping
        
        # Update position
        self.position += self.velocity * dt
        
        # Update rotation
        self.angular_velocity += self.angular_acceleration * dt
        self.angular_velocity *= self.angular_damping
        self.angle += self.angular_velocity * dt
        
        # Reset accelerations
        self.acceleration = pygame.math.Vector2(0, 0)
        self.angular_acceleration = 0

Integration Methods

# Different integration methods for more accurate physics

class IntegrationMethods:
    @staticmethod
    def euler(obj, dt):
        """Simple Euler integration (less accurate but fast)"""
        obj.velocity += obj.acceleration * dt
        obj.position += obj.velocity * dt
    
    @staticmethod
    def velocity_verlet(obj, dt):
        """Velocity Verlet integration (more accurate)"""
        # Store old acceleration
        old_acceleration = obj.acceleration.copy()
        
        # Update position
        obj.position += obj.velocity * dt + old_acceleration * (0.5 * dt * dt)
        
        # Calculate new acceleration (would involve recalculating forces)
        # new_acceleration = calculate_acceleration()
        
        # Update velocity
        obj.velocity += (old_acceleration + obj.acceleration) * (0.5 * dt)
    
    @staticmethod
    def runge_kutta_4(obj, dt):
        """4th order Runge-Kutta (very accurate but slower)"""
        # Store initial state
        initial_pos = obj.position.copy()
        initial_vel = obj.velocity.copy()
        
        # Calculate k1
        k1_vel = obj.acceleration.copy()
        k1_pos = obj.velocity.copy()
        
        # Calculate k2
        obj.position = initial_pos + k1_pos * (dt/2)
        obj.velocity = initial_vel + k1_vel * (dt/2)
        k2_vel = obj.acceleration.copy()
        k2_pos = obj.velocity.copy()
        
        # Calculate k3
        obj.position = initial_pos + k2_pos * (dt/2)
        obj.velocity = initial_vel + k2_vel * (dt/2)
        k3_vel = obj.acceleration.copy()
        k3_pos = obj.velocity.copy()
        
        # Calculate k4
        obj.position = initial_pos + k3_pos * dt
        obj.velocity = initial_vel + k3_vel * dt
        k4_vel = obj.acceleration.copy()
        k4_pos = obj.velocity.copy()
        
        # Final update
        obj.position = initial_pos + (k1_pos + 2*k2_pos + 2*k3_pos + k4_pos) * (dt/6)
        obj.velocity = initial_vel + (k1_vel + 2*k2_vel + 2*k3_vel + k4_vel) * (dt/6)

Practical Movement Systems

# Player movement with acceleration
class PlayerPhysics:
    def __init__(self, x, y):
        self.position = pygame.math.Vector2(x, y)
        self.velocity = pygame.math.Vector2(0, 0)
        self.acceleration = pygame.math.Vector2(0, 0)
        
        # Movement parameters
        self.move_speed = 300
        self.jump_force = -500
        self.gravity = 980
        self.friction = 0.85
        self.air_resistance = 0.98
        
        # State
        self.on_ground = False
        self.facing_right = True
    
    def handle_input(self, keys):
        """Process player input"""
        # Reset horizontal acceleration
        self.acceleration.x = 0
        
        # Left/Right movement
        if keys[pygame.K_LEFT]:
            self.acceleration.x = -self.move_speed
            self.facing_right = False
        if keys[pygame.K_RIGHT]:
            self.acceleration.x = self.move_speed
            self.facing_right = True
        
        # Jump
        if keys[pygame.K_SPACE] and self.on_ground:
            self.velocity.y = self.jump_force
            self.on_ground = False
    
    def update(self, dt):
        """Update player physics"""
        # Apply gravity
        if not self.on_ground:
            self.acceleration.y = self.gravity
        else:
            self.acceleration.y = 0
            self.velocity.y = 0
        
        # Update velocity
        self.velocity += self.acceleration * dt
        
        # Apply friction/air resistance
        if self.on_ground:
            self.velocity.x *= self.friction
        else:
            self.velocity.x *= self.air_resistance
        
        # Update position
        self.position += self.velocity * dt
        
        # Ground collision (simple)
        if self.position.y > 500:  # Ground at y=500
            self.position.y = 500
            self.on_ground = True
            self.velocity.y = 0

# Car physics example
class CarPhysics:
    def __init__(self, x, y):
        self.position = pygame.math.Vector2(x, y)
        self.velocity = pygame.math.Vector2(0, 0)
        self.angle = 0  # Car's rotation
        
        # Car properties
        self.max_speed = 400
        self.acceleration_rate = 200
        self.brake_rate = 300
        self.turn_speed = 3
        self.drag_coefficient = 0.95
        
        # Current state
        self.speed = 0
        self.steering = 0
    
    def accelerate(self, throttle):
        """Apply acceleration (-1 to 1)"""
        if throttle > 0:
            self.speed += self.acceleration_rate * throttle
        else:
            self.speed += self.brake_rate * throttle
        
        self.speed = max(-self.max_speed/2, min(self.max_speed, self.speed))
    
    def steer(self, direction):
        """Steer the car (-1 to 1)"""
        self.steering = direction
    
    def update(self, dt):
        """Update car physics"""
        # Apply steering (only if moving)
        if abs(self.speed) > 10:
            self.angle += self.steering * self.turn_speed * dt * (self.speed / self.max_speed)
        
        # Calculate velocity from speed and angle
        self.velocity.x = math.cos(self.angle) * self.speed
        self.velocity.y = math.sin(self.angle) * self.speed
        
        # Apply drag
        self.speed *= self.drag_coefficient
        
        # Update position
        self.position += self.velocity * dt
        
        # Reset steering
        self.steering = 0

Complete Physics Demo

import pygame
import math
import random

class PhysicsDemo:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((800, 600))
        pygame.display.set_caption("Velocity and Acceleration Demo")
        self.clock = pygame.time.Clock()
        self.font = pygame.font.Font(None, 24)
        
        # Create physics objects
        self.objects = []
        self.create_demo_objects()
        
        # Demo settings
        self.show_vectors = True
        self.show_trails = True
        self.selected_object = 0
        self.gravity_enabled = True
    
    def create_demo_objects(self):
        """Create various demo objects"""
        # Bouncing ball
        ball = PhysicsObject(100, 100)
        ball.velocity = pygame.math.Vector2(150, 0)
        ball.color = (100, 200, 255)
        ball.name = "Bouncing Ball"
        self.objects.append(ball)
        
        # Orbiting object
        orbiter = PhysicsObject(400, 200)
        orbiter.velocity = pygame.math.Vector2(0, 150)
        orbiter.color = (255, 100, 100)
        orbiter.name = "Orbiter"
        orbiter.orbit_center = pygame.math.Vector2(400, 300)
        self.objects.append(orbiter)
        
        # Rocket
        rocket = PhysicsObject(600, 500)
        rocket.velocity = pygame.math.Vector2(0, 0)
        rocket.color = (100, 255, 100)
        rocket.name = "Rocket"
        rocket.has_thrust = True
        self.objects.append(rocket)
    
    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_TAB:
                    self.selected_object = (self.selected_object + 1) % len(self.objects)
                elif event.key == pygame.K_v:
                    self.show_vectors = not self.show_vectors
                elif event.key == pygame.K_t:
                    self.show_trails = not self.show_trails
                elif event.key == pygame.K_g:
                    self.gravity_enabled = not self.gravity_enabled
                elif event.key == pygame.K_r:
                    self.create_demo_objects()
                elif event.key == pygame.K_SPACE:
                    # Add random force to selected object
                    if self.objects:
                        obj = self.objects[self.selected_object]
                        force = pygame.math.Vector2(
                            random.uniform(-200, 200),
                            random.uniform(-200, 200)
                        )
                        obj.apply_force(force)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                # Create new object at mouse position
                pos = pygame.mouse.get_pos()
                new_obj = PhysicsObject(pos[0], pos[1])
                new_obj.velocity = pygame.math.Vector2(
                    random.uniform(-100, 100),
                    random.uniform(-100, 100)
                )
                new_obj.color = (
                    random.randint(100, 255),
                    random.randint(100, 255),
                    random.randint(100, 255)
                )
                new_obj.name = f"Object {len(self.objects) + 1}"
                self.objects.append(new_obj)
        
        # Continuous input
        keys = pygame.key.get_pressed()
        if self.objects:
            obj = self.objects[self.selected_object]
            
            # Apply forces with arrow keys
            force_amount = 100
            if keys[pygame.K_LEFT]:
                obj.apply_force(pygame.math.Vector2(-force_amount, 0))
            if keys[pygame.K_RIGHT]:
                obj.apply_force(pygame.math.Vector2(force_amount, 0))
            if keys[pygame.K_UP]:
                obj.apply_force(pygame.math.Vector2(0, -force_amount))
            if keys[pygame.K_DOWN]:
                obj.apply_force(pygame.math.Vector2(0, force_amount))
        
        return True
    
    def update(self, dt):
        """Update physics simulation"""
        for obj in self.objects:
            # Apply gravity if enabled
            if self.gravity_enabled:
                obj.apply_force(pygame.math.Vector2(0, 300 * obj.mass))
            
            # Special behaviors
            if hasattr(obj, 'orbit_center'):
                # Apply centripetal force
                to_center = obj.orbit_center - obj.position
                if to_center.length() > 0:
                    to_center.normalize_ip()
                    centripetal = to_center * (obj.velocity.length_squared() / 100)
                    obj.apply_force(centripetal)
            
            if hasattr(obj, 'has_thrust') and obj.has_thrust:
                # Apply upward thrust
                thrust = pygame.math.Vector2(0, -500)
                obj.apply_force(thrust)
            
            # Update physics
            obj.update(dt)
            
            # Boundary collisions
            if obj.position.x - obj.radius < 0:
                obj.position.x = obj.radius
                obj.velocity.x *= -0.8
            elif obj.position.x + obj.radius > 800:
                obj.position.x = 800 - obj.radius
                obj.velocity.x *= -0.8
            
            if obj.position.y - obj.radius < 0:
                obj.position.y = obj.radius
                obj.velocity.y *= -0.8
            elif obj.position.y + obj.radius > 600:
                obj.position.y = 600 - obj.radius
                obj.velocity.y *= -0.8
    
    def draw(self):
        """Draw everything"""
        self.screen.fill((20, 20, 30))
        
        # Draw grid
        for x in range(0, 800, 50):
            pygame.draw.line(self.screen, (30, 30, 40), (x, 0), (x, 600))
        for y in range(0, 600, 50):
            pygame.draw.line(self.screen, (30, 30, 40), (0, y), (800, y))
        
        # Draw objects
        for i, obj in enumerate(self.objects):
            # Draw trail
            if self.show_trails and hasattr(obj, 'trail'):
                if len(obj.trail) > 1:
                    pygame.draw.lines(self.screen, obj.color, False, obj.trail, 1)
            
            # Draw object
            color = obj.color
            if i == self.selected_object:
                # Highlight selected
                pygame.draw.circle(self.screen, (255, 255, 255),
                                 (int(obj.position.x), int(obj.position.y)),
                                 obj.radius + 3, 2)
            
            pygame.draw.circle(self.screen, color,
                             (int(obj.position.x), int(obj.position.y)),
                             obj.radius)
            
            # Draw vectors
            if self.show_vectors:
                # Velocity vector (green)
                vel_end = obj.position + obj.velocity * 0.2
                pygame.draw.line(self.screen, (0, 255, 0),
                               (obj.position.x, obj.position.y),
                               (vel_end.x, vel_end.y), 2)
                
                # Acceleration vector (red)
                acc_end = obj.position + obj.acceleration * 10
                pygame.draw.line(self.screen, (255, 0, 0),
                               (obj.position.x, obj.position.y),
                               (acc_end.x, acc_end.y), 2)
        
        # Draw UI
        self.draw_ui()
    
    def draw_ui(self):
        """Draw user interface"""
        y_offset = 10
        
        # Instructions
        texts = [
            "Tab: Select Object | V: Vectors | T: Trails | G: Gravity",
            "Arrow Keys: Apply Force | Space: Random Force | Click: New Object",
            f"Gravity: {'ON' if self.gravity_enabled else 'OFF'}",
            ""
        ]
        
        # Selected object info
        if self.objects:
            obj = self.objects[self.selected_object]
            texts.extend([
                f"Selected: {obj.name}",
                f"Position: ({obj.position.x:.1f}, {obj.position.y:.1f})",
                f"Velocity: ({obj.velocity.x:.1f}, {obj.velocity.y:.1f}) = {obj.velocity.length():.1f}",
                f"Acceleration: ({obj.acceleration.x:.1f}, {obj.acceleration.y:.1f})"
            ])
        
        for text in texts:
            rendered = self.font.render(text, True, (255, 255, 255))
            self.screen.blit(rendered, (10, y_offset))
            y_offset += 25
    
    def run(self):
        running = True
        dt = 0
        
        while running:
            running = self.handle_events()
            self.update(dt)
            self.draw()
            pygame.display.flip()
            dt = self.clock.tick(60) / 1000.0
        
        pygame.quit()

# Enhanced PhysicsObject for the demo
class PhysicsObject:
    def __init__(self, x, y):
        self.position = pygame.math.Vector2(x, y)
        self.velocity = pygame.math.Vector2(0, 0)
        self.acceleration = pygame.math.Vector2(0, 0)
        self.mass = 1.0
        self.radius = 15
        self.color = (100, 200, 255)
        self.name = "Object"
        self.trail = []
        self.max_trail_length = 50
    
    def apply_force(self, force):
        if self.mass > 0:
            self.acceleration += force / self.mass
    
    def update(self, dt):
        # Update physics
        self.velocity += self.acceleration * dt
        self.position += self.velocity * dt
        
        # Update trail
        self.trail.append((self.position.x, self.position.y))
        if len(self.trail) > self.max_trail_length:
            self.trail.pop(0)
        
        # Reset acceleration
        self.acceleration = pygame.math.Vector2(0, 0)

if __name__ == "__main__":
    demo = PhysicsDemo()
    demo.run()

Best Practices

โšก Physics Optimization Tips

Common Physics Patterns

๐ŸŽฎ Game Physics Applications

Practice Exercises

๐ŸŽฏ Physics Challenges!

  1. Lunar Lander: Control thrust to land safely
  2. Angry Birds Clone: Projectile motion with targets
  3. Racing Physics: Car with realistic acceleration/braking
  4. Pendulum: Swinging physics simulation
  5. Asteroid Field: Multiple objects with gravity
  6. Rocket Launch: Multi-stage rocket with fuel

Key Takeaways

๐Ÿ‹๏ธโ€โ™‚๏ธ Practice Exercise: Apply Force, See Motion

๐Ÿ‹๏ธโ€โ™‚๏ธ Exercise 1: F = ma + Euler Integration in 30 Lines

Objective: Build a tiny Pygame demo that exercises the three pillar patterns from this lesson in one program: F = ma via apply_force(force) dividing the input force by the object's mass to produce an acceleration delta; Euler integration via velocity += acceleration * dt; position += velocity * dt to advance state by one frame; and the critical reset-acceleration-each-frame rule (acceleration = Vector2(0, 0) at the end of update()) so accumulated forces don't carry into the next frame and exponentially launch the object off-screen. Hold ARROW keys to apply a 200-unit thrust force in that direction; release and the object drifts on inertia (proving velocity persists when no force is applied) โ€” exactly the behavior of the lesson's PhysicsObject.update() made interactive.

Instructions:

  1. Initialize a pygame.math.Vector2 for position (screen center), velocity (0, 0), and acceleration (0, 0); set mass = 1.0.
  2. In the game loop, read dt = clock.tick(60) / 1000.0 in seconds โ€” the lesson's frame-rate-independence rule.
  3. Poll pygame.key.get_pressed() and for each held arrow key, call apply_force(Vector2(ยฑ200, 0)) or Vector2(0, ยฑ200).
  4. Inside apply_force(force), do self.acceleration += force / self.mass โ€” the F = ma rearrangement the lesson teaches.
  5. Inside update(dt), integrate Euler-style: velocity += acceleration * dt then position += velocity * dt.
  6. At the very end of update(), reset self.acceleration = Vector2(0, 0) โ€” Best Practice 'Force Accumulation' from this lesson.
  7. Draw the object as a circle at position, plus a HUD line showing live velocity magnitude so inertia is visible.
๐Ÿ’ก Hint

The two easy ways to break this demo are both instructive: (a) skip the dt multiplications and the object moves the same number of pixels per frame instead of per second โ€” try it on a 30 FPS machine vs 60 FPS and the object literally moves twice as fast on the faster machine; (b) skip the acceleration reset at the end of update() and the acceleration accumulates across frames โ€” hold the arrow key for one second and the object launches off-screen exponentially (acceleration grows, velocity grows from acceleration, so velocity grows quadratically). Both bugs appear in real codebases.

โœ… Example Solution
import pygame
from pygame.math import Vector2

pygame.init()
W, H = 600, 400
screen = pygame.display.set_mode((W, H))
pygame.display.set_caption("Apply Force, See Motion")
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 24)

class PhysicsObject:
    def __init__(self, x, y):
        self.position = Vector2(x, y)
        self.velocity = Vector2(0, 0)
        self.acceleration = Vector2(0, 0)
        self.mass = 1.0

    def apply_force(self, force):
        # F = ma  =>  a += F / m
        self.acceleration += force / self.mass

    def update(self, dt):
        # Euler integration: v += a*dt; x += v*dt
        self.velocity += self.acceleration * dt
        self.position += self.velocity * dt
        # Critical: reset acceleration each frame
        # (forces are re-applied per frame, not persistent)
        self.acceleration = Vector2(0, 0)

obj = PhysicsObject(W // 2, H // 2)
THRUST = 200  # units of force per axis
running = True
while running:
    dt = clock.tick(60) / 1000.0  # seconds since last frame
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:  obj.apply_force(Vector2(-THRUST, 0))
    if keys[pygame.K_RIGHT]: obj.apply_force(Vector2( THRUST, 0))
    if keys[pygame.K_UP]:    obj.apply_force(Vector2(0, -THRUST))
    if keys[pygame.K_DOWN]:  obj.apply_force(Vector2(0,  THRUST))
    obj.update(dt)
    screen.fill((20, 20, 30))
    pygame.draw.circle(screen, (100, 200, 255),
                       (int(obj.position.x), int(obj.position.y)), 12)
    hud = font.render(f"|v| = {obj.velocity.length():.1f}", True, (255, 255, 255))
    screen.blit(hud, (10, 10))
    pygame.display.flip()
pygame.quit()

๐ŸŽฏ Quick Quiz

Question 1: Inside apply_force(force), the lesson's PhysicsObject does self.acceleration += force / self.mass. Which physical law does this implement?

Question 2: The lesson's update(dt) does velocity += acceleration * dt; position += velocity * dt. Why multiply by dt instead of just doing velocity += acceleration; position += velocity?

Question 3: The lesson's PhysicsObject.update() ends with self.acceleration = Vector2(0, 0) after integrating velocity and position. What goes wrong if you remove that reset line?

What's Next?

Now that you understand velocity and acceleration, next we'll add gravity to create more realistic physics simulations!