Created
July 29, 2009 00:48
-
-
Save jacius/157785 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/env ruby | |
# One way of making an object start and stop moving gradually: | |
# make user input affect acceleration, not velocity. | |
require "rubygame" | |
# Include these modules so we can type "Surface" instead of | |
# "Rubygame::Surface", etc. Purely for convenience/readability. | |
include Rubygame | |
include Rubygame::Events | |
include Rubygame::EventActions | |
include Rubygame::EventTriggers | |
# A class representing the player's ship moving in "space". | |
class Ship | |
include Sprites::Sprite | |
include EventHandler::HasEventHandler | |
def initialize( px, py ) | |
@px, @py = px, py # Current Position | |
@vx, @vy = 0, 0 # Current Velocity | |
@ax, @ay = 0, 0 # Current Acceleration | |
@max_speed = 400.0 # Max speed on an axis | |
@accel = 1200.0 # Max Acceleration on an axis | |
@slowdown = 800.0 # Deceleration when not accelerating | |
@keys = [] # Keys being pressed | |
# The ship's appearance. A white square for demonstration. | |
@image = Surface.new([20,20]) | |
@image.fill(:white) | |
@rect = @image.make_rect | |
# Create event hooks in the easiest way. | |
make_magic_hooks( | |
# Send keyboard events to #key_pressed() or #key_released(). | |
KeyPressed => :key_pressed, | |
KeyReleased => :key_released, | |
# Send ClockTicked events to #update() | |
ClockTicked => :update | |
) | |
end | |
private | |
# Add it to the list of keys being pressed. | |
def key_pressed( event ) | |
@keys += [event.key] | |
end | |
# Remove it from the list of keys being pressed. | |
def key_released( event ) | |
@keys -= [event.key] | |
end | |
# Update the ship state. Called once per frame. | |
def update( event ) | |
dt = event.seconds # Time since last update | |
update_accel | |
update_vel( dt ) | |
update_pos( dt ) | |
end | |
# Update the acceleration based on what keys are pressed. | |
def update_accel | |
x, y = 0,0 | |
x -= 1 if @keys.include?( :left ) | |
x += 1 if @keys.include?( :right ) | |
y -= 1 if @keys.include?( :up ) # up is down in screen coordinates | |
y += 1 if @keys.include?( :down ) | |
# Scale to the acceleration rate. This is a bit unrealistic, since | |
# it doesn't consider magnitude of x and y combined (diagonal). | |
x *= @accel | |
y *= @accel | |
@ax, @ay = x, y | |
end | |
# Update the velocity based on the acceleration and the time since | |
# last update. | |
def update_vel( dt ) | |
@vx = update_vel_axis( @vx, @ax, dt ) | |
@vy = update_vel_axis( @vy, @ay, dt ) | |
end | |
# Calculate the velocity for one axis. | |
# v = current velocity on that axis (e.g. @vx) | |
# a = current acceleration on that axis (e.g. @ax) | |
# | |
# Returns what the new velocity (@vx) should be. | |
# | |
def update_vel_axis( v, a, dt ) | |
# Apply slowdown if not accelerating. | |
if a == 0 | |
if v > 0 | |
v -= @slowdown * dt | |
v = 0 if v < 0 | |
elsif v < 0 | |
v += @slowdown * dt | |
v = 0 if v > 0 | |
end | |
end | |
# Apply acceleration | |
v += a * dt | |
# Clamp speed so it doesn't go too fast. | |
v = @max_speed if v > @max_speed | |
v = -@max_speed if v < -@max_speed | |
return v | |
end | |
# Update the position based on the velocity and the time since last | |
# update. | |
def update_pos( dt ) | |
@px += @vx * dt | |
@py += @vy * dt | |
@rect.center = [@px, @py] | |
end | |
end | |
# The Game class helps organize thing. It takes events | |
# from the queue and handles them, sometimes performing | |
# its own action (e.g. Escape key = quit), but also | |
# passing the events to the pandas to handle. | |
# | |
class Game | |
include EventHandler::HasEventHandler | |
def initialize() | |
make_screen | |
make_clock | |
make_queue | |
make_event_hooks | |
make_ship | |
end | |
# The "main loop". Repeat the #step method | |
# over and over and over until the user quits. | |
def go | |
catch(:quit) do | |
loop do | |
step | |
end | |
end | |
end | |
private | |
# Create a new Clock to manage the game framerate | |
# so it doesn't use 100% of the CPU | |
def make_clock | |
@clock = Clock.new() | |
@clock.target_framerate = 50 | |
@clock.calibrate | |
@clock.enable_tick_events | |
end | |
# Set up the event hooks to perform actions in | |
# response to certain events. | |
def make_event_hooks | |
hooks = { | |
:escape => :quit, | |
:q => :quit, | |
QuitRequested => :quit | |
} | |
make_magic_hooks( hooks ) | |
end | |
# Create an EventQueue to take events from the keyboard, etc. | |
# The events are taken from the queue and passed to objects | |
# as part of the main loop. | |
def make_queue | |
# Create EventQueue with new-style events (added in Rubygame 2.4) | |
@queue = EventQueue.new() | |
@queue.enable_new_style_events | |
# Don't care about mouse movement, so let's ignore it. | |
@queue.ignore = [MouseMoved] | |
end | |
# Create the Rubygame window. | |
def make_screen | |
@screen = Screen.open( [640, 480] ) | |
@screen.title = "Square! In! Space!" | |
end | |
# Create the player ship in the middle of the screen | |
def make_ship | |
@ship = Ship.new( @screen.w/2, @screen.h/2 ) | |
# Make event hook to pass all events to @ship#handle(). | |
make_magic_hooks_for( @ship, { YesTrigger.new() => :handle } ) | |
end | |
# Quit the game | |
def quit | |
puts "Quitting!" | |
throw :quit | |
end | |
# Do everything needed for one frame. | |
def step | |
# Clear the screen. | |
@screen.fill( :black ) | |
# Fetch input events, etc. from SDL, and add them to the queue. | |
@queue.fetch_sdl_events | |
# Tick the clock and add the TickEvent to the queue. | |
@queue << @clock.tick | |
# Process all the events on the queue. | |
@queue.each do |event| | |
handle( event ) | |
end | |
# Draw the ship in its new position. | |
@ship.draw( @screen ) | |
# Refresh the screen. | |
@screen.update() | |
end | |
end | |
# Start the main game loop. It will repeat forever | |
# until the user quits the game! | |
Game.new.go | |
# Make sure everything is cleaned up properly. | |
Rubygame.quit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment