Rubygame tutorial #3: In control
Welcome back. Due to (somewhat) popular demand, I’m going to continue the rubygame tutorials. In this tutorial we will look at a keyboard event: KeyDownEvent.
Looking back for a second or two
Let us remind ourselves of the steps we need to take to create a very simple Rubygame program:
- Create a screen
- Create a event queue
- Create a clock
- Enter the main loop. The main loop does drawing and contains an event loop.
- The event loop should have some exit condition
That is really it. Armed with only this knowledge and a few drawing methods we can do a lot already.
A single pixel
Let’s start off with a program that plots a single pixel. The following program will just plot a single pixel at the center of the screen.
1 #! /usr/bin/ruby
2
3 # A pixel.
4
5 require ‘rubygame‘
6 include Rubygame
7
8 screen = Screen.new([640, 400])
9 events = EventQueue.new
10 clock = Clock.new
11 clock.target_framerate = 120
12 running = true
13
14 while running
15 events.each do |event|
16 case event
17 when QuitEvent
18 running = false
19 end
20 end
21
22 screen.fill([0, 0, 0])
23 screen.set_at([screen.w / 2, screen.h / 2], [255, 255, 255])
24 screen.update
25 clock.tick
26 end
27
Not too interesting I guess. I single pixel and it doesn’t even move! Let’s change that.
Exercises
1. Modify the program to display a blue pixel.
2. Modify the progam to plot the pixel at the position 100, 100.
3. Change the screen size to 500, 500.
4. Write a program that takes the pixel coordinates as command-line arguments. Provide the center as a default position if no arguments are passed. Give an error if the coordinates are beyond the size of the screen.
Preparing for motion
Let’s give our pixel a life of its own. We will create a pixel class. Here is a new version of the program.
1 #! /usr/bin/ruby
2
3 # Another pixel.
4
5 require ‘rubygame‘
6 include Rubygame
7
8 class Pixel
9 attr_reader
, :y
10
11 def initialize(x, y, surface)
12 @x, @y = x, y
13 @surface = surface
14 end
15
16 def plot
17 @surface.set_at [@x, @y], [255, 255, 255]
18 end
19 end
20
21 screen = Screen.new [640, 400]
22 events = EventQueue.new
23 clock = Clock.new
24 clock.target_framerate = 120
25 pix = Pixel.new(screen.w/2, screen.h/2, screen)
26 running = true
27
28 while running
29 events.each { |event|
30 case event
31 when QuitEvent
32 running = false
33 end
34 }
35
36 pix.plot
37 screen.update
38 clock.tick
39 end
We still only see a single pixel at the middle of the screen. Still no fun!
On the other hand, we now have a pixel that knows its own position and is able to “draw itself” onto its target surface.
Exercises
1. Add a color attribute to the Pixel class. Set the color attribute to red before plotting the pixel.
2. Create five Pixel instances, each with different coordinates and plot each of them.
Now, move it!
So far we have used QuitEvent to determine if the user has closed the Rubygame screen. Now let’s look at another event: KeyDownEvent. This event is triggered any time a key is pressed down. We can find out which key via KeyDownEvent#key. Let’s use this to add the ability to move the pixel around.
We give our Pixel class some new methods: up, down, left, right and stop. Internally, it will also have two new fields: one to determine the horizontal direction and one to determine the vertical direction.
1 #! /usr/bin/ruby
2
3 # Moving pixel.
4
5 require ‘rubygame‘
6 include Rubygame
7
8 class Pixel
9 attr_reader
, :y
10
11 def initialize(x, y, surface)
12 @x, @y = x, y
13 @dx, @dy = 0, 0
14 @surface = surface
15 end
16
17 def up
18 @dx, @dy = 0, -1
19 end
20
21 def down
22 @dx, @dy = 0, 1
23 end
24
25 def left
26 @dx, @dy = -1, 0
27 end
28
29 def right
30 @dx, @dy = 1, 0
31 end
32
33 def stop
34 @dx = @dy = 0
35 end
36
37 def move
38 tx, ty = @x + @dx, @y + @dy
39 return unless (0..@surface.w-1).include? tx
40 return unless (0..@surface.h-1).include? ty
41
42 @x, @y = tx, ty
43 end
44
45 def plot
46 @surface.set_at [@x, @y], [255, 255, 255]
47 end
48 end
49
50 screen = Screen.new [640, 400]
51 events = EventQueue.new
52 clock = Clock.new
53 clock.target_framerate = 120
54 pix = Pixel.new(screen.w/2, screen.h/2, screen)
55 running = true
56
57 while running
58 events.each { |event|
59 case event
60 when QuitEvent
61 running = false
62 when KeyDownEvent
63 case event.key
64 when K_UP: pix.up
65 when K_DOWN: pix.down
66 when K_LEFT: pix.left
67 when K_RIGHT: pix.right
68 when K_SPACE: pix.stop
69 end
70 end
71 }
72
73 screen.fill [0, 0, 0]
74 pix.move
75 pix.plot
76 screen.update
77 clock.tick
78 end
Exercises
1. Try to comment out the screen.fill line inside the drawing loop. What happens?
2. Currently, the pixel can move in four directions: up, down, left and right. How could you modify the program to allow the pixel to move diagonally. (There are several ways, experiment as much as you can.)
3. Modify the program to create three Pixels. Give each a different starting position. When one of the motion keys is pressed, all three should move.
4. What if you wanted to create three Pixels that each used a different set of keys to control its motion, how would you go about that? Would you make any changes to the Pixel class itself?
5. Rewrite the program to check the KeyUpEvent instead of KeyDown. How does this affect the program?
Conclusion
In this tutorial you have learned how to use the KeyDown event. We will continue with our moving pixel in the next tutorial (hey, I told you I was going to go real slow).
In the meantime, experiment with what you have learned so far. There are loads of things you can do to get extra practice. For instance you could modify the program so that the pixel only moves while a key is pressed down. Once the key is released it should stop (use both KeyDownEvent and KeyUpEvent). The more you practice the better you will become.



ARGH! Stupid smiley expansion! The
is supposed to be : x (without a space between the colon and the x).
Comment by Lorenzo E. Danielsson — 2008.02.22 @ 07:38