rubygame 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CREDITS +50 -0
- data/Changelog +162 -0
- data/LICENSE +504 -0
- data/README +122 -0
- data/Rakefile +380 -0
- data/TODO +45 -0
- data/doc/extended_readme.rdoc +49 -0
- data/doc/getting_started.rdoc +47 -0
- data/doc/macosx_install.rdoc +74 -0
- data/doc/windows_install.rdoc +124 -0
- data/ext/rubygame/MANIFEST +25 -0
- data/ext/rubygame/rubygame_event.c +644 -0
- data/ext/rubygame/rubygame_event.h +48 -0
- data/ext/rubygame/rubygame_gfx.c +951 -0
- data/ext/rubygame/rubygame_gfx.h +102 -0
- data/ext/rubygame/rubygame_gl.c +154 -0
- data/ext/rubygame/rubygame_gl.h +32 -0
- data/ext/rubygame/rubygame_image.c +108 -0
- data/ext/rubygame/rubygame_image.h +41 -0
- data/ext/rubygame/rubygame_joystick.c +247 -0
- data/ext/rubygame/rubygame_joystick.h +41 -0
- data/ext/rubygame/rubygame_main.c +155 -0
- data/ext/rubygame/rubygame_main.h +33 -0
- data/ext/rubygame/rubygame_mixer.c +764 -0
- data/ext/rubygame/rubygame_mixer.h +62 -0
- data/ext/rubygame/rubygame_screen.c +420 -0
- data/ext/rubygame/rubygame_screen.h +41 -0
- data/ext/rubygame/rubygame_shared.c +152 -0
- data/ext/rubygame/rubygame_shared.h +54 -0
- data/ext/rubygame/rubygame_surface.c +1107 -0
- data/ext/rubygame/rubygame_surface.h +62 -0
- data/ext/rubygame/rubygame_time.c +183 -0
- data/ext/rubygame/rubygame_time.h +32 -0
- data/ext/rubygame/rubygame_ttf.c +600 -0
- data/ext/rubygame/rubygame_ttf.h +69 -0
- data/lib/rubygame.rb +40 -0
- data/lib/rubygame/MANIFEST +12 -0
- data/lib/rubygame/clock.rb +128 -0
- data/lib/rubygame/constants.rb +238 -0
- data/lib/rubygame/event.rb +313 -0
- data/lib/rubygame/ftor.rb +370 -0
- data/lib/rubygame/hotspot.rb +265 -0
- data/lib/rubygame/keyconstants.rb +237 -0
- data/lib/rubygame/mediabag.rb +94 -0
- data/lib/rubygame/queue.rb +288 -0
- data/lib/rubygame/rect.rb +614 -0
- data/lib/rubygame/sfont.rb +223 -0
- data/lib/rubygame/sprite.rb +477 -0
- data/samples/FreeSans.ttf +0 -0
- data/samples/GPL.txt +340 -0
- data/samples/README +40 -0
- data/samples/chimp.bmp +0 -0
- data/samples/chimp.rb +313 -0
- data/samples/demo_gl.rb +151 -0
- data/samples/demo_gl_tex.rb +197 -0
- data/samples/demo_music.rb +75 -0
- data/samples/demo_rubygame.rb +279 -0
- data/samples/demo_sfont.rb +52 -0
- data/samples/demo_ttf.rb +193 -0
- data/samples/demo_utf8.rb +53 -0
- data/samples/fist.bmp +0 -0
- data/samples/load_and_blit.rb +22 -0
- data/samples/panda.png +0 -0
- data/samples/punch.wav +0 -0
- data/samples/ruby.png +0 -0
- data/samples/term16.png +0 -0
- data/samples/whiff.wav +0 -0
- metadata +123 -0
@@ -0,0 +1,223 @@
|
|
1
|
+
#--
|
2
|
+
# Rubygame -- Ruby code and bindings to SDL to facilitate game creation
|
3
|
+
# Copyright (C) 2004-2007 John Croisant
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
|
+
#++
|
19
|
+
|
20
|
+
module Rubygame
|
21
|
+
|
22
|
+
# *NOTE*: you must require 'rubygame/sfont' manually to gain access to
|
23
|
+
# Rubygame::SFont. It is not imported with Rubygame by default!
|
24
|
+
#
|
25
|
+
# SFont is a type of bitmapped font, which is loaded from an image file
|
26
|
+
# with a meaningful top row of pixels, and the font itself below that. The
|
27
|
+
# top row provides information about what parts of of the lower area
|
28
|
+
# contains font data, and which parts are empty.
|
29
|
+
#
|
30
|
+
# The image file should contain all of the glyphs on one row, with the
|
31
|
+
# colorkey color at the bottom-left pixel and the "skip" color at the
|
32
|
+
# top-left pixel.
|
33
|
+
#
|
34
|
+
# The colorkey color is applied to the image file when it is loaded, so
|
35
|
+
# that all pixels of that color appear transparent. Alternatively, if the
|
36
|
+
# alpha value of pixel [0,0] is 0 (that is, if it is fully transparent),
|
37
|
+
# the image file is assumed to have a proper alpha channel, and no colorkey
|
38
|
+
# is applied. The skip color is used in the top row of pixels to indicate
|
39
|
+
# that all the pixels below it contain empty space, and that the next
|
40
|
+
# pixel that is not the skip color marks the beginning of the next glyph.
|
41
|
+
#
|
42
|
+
# The official SFont homepage, with information on SFont and sample font
|
43
|
+
# files, can be found at http://www.linux-games.com/sfont/. More
|
44
|
+
# information on SFont, and a useful utility for automatically generating
|
45
|
+
# the top row for a font, can be found at: http://www.nostatic.org/sfont/
|
46
|
+
|
47
|
+
class SFont
|
48
|
+
@@default_glyphs = [\
|
49
|
+
"!",'"',"#","$","%","&","'","(",")","*","+",",","-",".","/","0",
|
50
|
+
"1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@",
|
51
|
+
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
|
52
|
+
"Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_","`",
|
53
|
+
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p",
|
54
|
+
"q","r","s","t","u","v","w","x","y","z","{","|","}","~"]
|
55
|
+
|
56
|
+
# Returns an array of strings, each string a single ASCII character
|
57
|
+
# from ! (33) to ~ (126). This is the default set of characters in a
|
58
|
+
# SFont. The full set is as follows:
|
59
|
+
#
|
60
|
+
# ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @
|
61
|
+
# A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ `
|
62
|
+
# a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
|
63
|
+
def SFont.default_glyphs
|
64
|
+
@@default_glyphs
|
65
|
+
end
|
66
|
+
|
67
|
+
# Create a now SFont instance.
|
68
|
+
#
|
69
|
+
# This function takes these arguments:
|
70
|
+
# filename:: the name of the image file from which the font should be
|
71
|
+
# loaded. Or, a Surface with a font image on it. The
|
72
|
+
# Surface will be copied onto separate Surfaces for each
|
73
|
+
# glyph, so the original Surface can be recycled.
|
74
|
+
# glyphs:: array of strings, one for each glyph, in the order they
|
75
|
+
# are found in the image file. If glyphs is not provided,
|
76
|
+
# or is nil, it is assumed to be the normal SFont order;
|
77
|
+
# that is, ASCII characters ! (33) to ~ (126). See
|
78
|
+
# SFont.default_glyphs for a full list.
|
79
|
+
# spacew:: represents the width of a space character ( ). You can
|
80
|
+
# either specify the width in pixels, or specify a
|
81
|
+
# character whose width, as found in the image, should be
|
82
|
+
# used. Alternatively, you could add a space character to
|
83
|
+
# the list of glyphs, and to the image file. If +spacew+
|
84
|
+
# is not given or is nil, and the space character is not
|
85
|
+
# in the list of glyphs, it will have the same width as
|
86
|
+
# the double-quote character (").
|
87
|
+
def initialize(filename,glyphs=nil,spacew=nil)
|
88
|
+
# load the surface containing all the glyphs
|
89
|
+
surface = nil
|
90
|
+
if filename.is_a? String
|
91
|
+
surface = Surface.load_image(filename)
|
92
|
+
elsif filename.is_a? Surface
|
93
|
+
surface = filename
|
94
|
+
end
|
95
|
+
@height = surface.height
|
96
|
+
colorkey = surface.get_at(0,@height-1)
|
97
|
+
|
98
|
+
# set colorkey if "transparent" color is not actually transparent
|
99
|
+
if colorkey[3] != 0
|
100
|
+
surface.set_colorkey(colorkey[0..2])
|
101
|
+
end
|
102
|
+
|
103
|
+
@glyphs = {}
|
104
|
+
@skip = surface.get_at(0,0)[0..2]
|
105
|
+
|
106
|
+
# split the glyphs into separate surfaces
|
107
|
+
glyphs = (glyphs or @@default_glyphs)
|
108
|
+
start_x = 2
|
109
|
+
glyphs.each{ |glyph| start_x = load_glyph(surface,glyph,start_x) }
|
110
|
+
|
111
|
+
if not glyphs.include?(" ")
|
112
|
+
if spacew == nil
|
113
|
+
spacew = @glyphs['"'].width
|
114
|
+
elsif spacew.kind_of? Numeric
|
115
|
+
spacew = spacew.to_i
|
116
|
+
elsif spacew.kind_of? String
|
117
|
+
if glyphs.include? spacew
|
118
|
+
spacew = @glyphs[spacew].width
|
119
|
+
else
|
120
|
+
spacew = @glyphs['"'].width
|
121
|
+
end
|
122
|
+
else
|
123
|
+
raise(ArgumentError,"spacew must be Numeric, String, \
|
124
|
+
or nil (got %s)"%[spacew.class])
|
125
|
+
end
|
126
|
+
@glyphs[" "] = Surface.new([spacew,@height])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Return the height of the font, in pixels. This is the same as the
|
131
|
+
# height of the image file (all glyphs are the same height).
|
132
|
+
attr_reader :height
|
133
|
+
|
134
|
+
# This is a private function which is used to parse the font image.
|
135
|
+
#
|
136
|
+
# Create a Surface for a single glyph, and store it as a value in the
|
137
|
+
# +@glyphs+ hash, indexed by the glyph (string) it represents.
|
138
|
+
#
|
139
|
+
# Starting at a pixel in the "skip" region to the left of the glyph.
|
140
|
+
# Scans to the right along the top row until it finds a non-skip pixel
|
141
|
+
# (this is where the glyph starts) and then scans until it finds a skip
|
142
|
+
# pixel (this is where the glyph ends.
|
143
|
+
#
|
144
|
+
# Returns the x value it stops at, plus 1. This should be fed back in
|
145
|
+
# for the +start_x+ of the next glyph.
|
146
|
+
#
|
147
|
+
# This _private_ method takes these arguments:
|
148
|
+
# surface:: the Surface containing all glyph image data.
|
149
|
+
# glyph:: a string containing the current glyph
|
150
|
+
# start_x:: the x position to start scanning at.
|
151
|
+
def load_glyph(surface,glyph,start_x) # :doc:
|
152
|
+
# find where this glyph starts
|
153
|
+
begin
|
154
|
+
while(surface.get_at(start_x,0)[0..2] == @skip)
|
155
|
+
start_x += 1
|
156
|
+
end
|
157
|
+
rescue IndexError
|
158
|
+
return -1
|
159
|
+
end
|
160
|
+
|
161
|
+
end_x = start_x
|
162
|
+
|
163
|
+
# find how wide this glyph is
|
164
|
+
begin
|
165
|
+
while(surface.get_at(end_x,0)[0..2] != @skip)
|
166
|
+
end_x += 1
|
167
|
+
end
|
168
|
+
rescue IndexError
|
169
|
+
return -1
|
170
|
+
end
|
171
|
+
|
172
|
+
# make a new surface for the glyph and blit the image onto it
|
173
|
+
rect = Rect.new(start_x, 0, end_x-start_x, surface.h)
|
174
|
+
@glyphs[glyph] = Surface.new(rect.size)
|
175
|
+
surface.blit(@glyphs[glyph],[0,0],rect)
|
176
|
+
|
177
|
+
return end_x+1
|
178
|
+
end
|
179
|
+
private :load_glyph
|
180
|
+
|
181
|
+
# This is a private function which is used to render a string.
|
182
|
+
#
|
183
|
+
# Blit a single glyph to a Surface at the given position.
|
184
|
+
#
|
185
|
+
# This _private_ method takes these arguments:
|
186
|
+
# glyph:: a string containing the glyph to blit.
|
187
|
+
# surface:: the target surface to blit onto.
|
188
|
+
# pos:: an Array of the x and y values to blit the glyph to.
|
189
|
+
def blit_glyph(glyph,surface,pos) # :doc:
|
190
|
+
@glyphs[glyph].blit(surface,pos)
|
191
|
+
end
|
192
|
+
private :blit_glyph
|
193
|
+
|
194
|
+
# Pretends to render the given string, and returns the width in pixels
|
195
|
+
# of the surface that would be created if it were rendered using
|
196
|
+
# SFont#render. If you want the height too, you can get it with
|
197
|
+
# SFont#height (the height is contant).
|
198
|
+
#
|
199
|
+
# This method takes this argument:
|
200
|
+
# string:: the string to pretend to render.
|
201
|
+
def string_width(string)
|
202
|
+
w = 0
|
203
|
+
string.each_byte { |glyph| w += @glyphs["%c"%[glyph]].width }
|
204
|
+
return w
|
205
|
+
end
|
206
|
+
|
207
|
+
# Renders the given string to a Surface, and returns that surface.
|
208
|
+
#
|
209
|
+
# This method takes this argument:
|
210
|
+
# string:: the text string to render.
|
211
|
+
def render(string)
|
212
|
+
size = [self.string_width(string),self.height]
|
213
|
+
render = Surface.new(size)
|
214
|
+
x = 0
|
215
|
+
string.each_byte { |glyph|
|
216
|
+
blit_glyph("%c"%[glyph],render,[x,0])
|
217
|
+
x += @glyphs["%c"%[glyph]].width
|
218
|
+
}
|
219
|
+
return render
|
220
|
+
end
|
221
|
+
end # class SFont
|
222
|
+
|
223
|
+
end # module Rubygame
|
@@ -0,0 +1,477 @@
|
|
1
|
+
#--
|
2
|
+
# Rubygame -- Ruby code and bindings to SDL to facilitate game creation
|
3
|
+
# Copyright (C) 2004-2007 John Croisant
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
|
+
#++
|
19
|
+
|
20
|
+
module Rubygame
|
21
|
+
|
22
|
+
# The Sprites module provides classes and mix-in modules for managing
|
23
|
+
# sprites. A Sprite is, fundamentally, a Surface with an associated
|
24
|
+
# Rect which defines the position of that Surface on the Screen.
|
25
|
+
# Additionally, sprites can have some behavior associated with them
|
26
|
+
# via the Sprite#update method, including movement and collision detection;
|
27
|
+
# because of this, sprites can be the foundation of most on-screen objects,
|
28
|
+
# such as characters, items, missiles, and even user-interface elements.
|
29
|
+
#
|
30
|
+
# There are several classes/modules under the Sprites module:
|
31
|
+
# Sprite:: mix-in module to turn an individual object or class into
|
32
|
+
# a Sprite.
|
33
|
+
# Group:: class for containing and manipulating many Sprite objects.
|
34
|
+
# UpdateGroup:: mix-in module for Group to allow undrawing/redrawing of
|
35
|
+
# member Sprite objects.
|
36
|
+
# LimitGroup:: mix-in module for Group to limit the number of Sprite objects
|
37
|
+
# the Group can hold at once.
|
38
|
+
#
|
39
|
+
# More mix-in modules to extend the functionality of Group or Sprite are
|
40
|
+
# planned for the future. Do not hesitate to change either (or both) Group
|
41
|
+
# or Sprite within your application to fit your needs! That's what they are
|
42
|
+
# here for!
|
43
|
+
#
|
44
|
+
module Sprites
|
45
|
+
|
46
|
+
# The Sprite mix-in module (not to be confused with its parent module,
|
47
|
+
# Sprites) can be used to extend a class or object to behave as a
|
48
|
+
# sprite. Specifically, a sprite can:
|
49
|
+
# - #draw (blit) itself onto a Surface in its proper position
|
50
|
+
# - detect bounding-box collision with Groups (#collide_group) and/or other
|
51
|
+
# sprites (#collide_sprite).
|
52
|
+
# - #update its own state based on arbitrary rules.
|
53
|
+
#
|
54
|
+
# A Sprite is, fundamentally, a Surface with an associated
|
55
|
+
# Rect which defines the position of that Surface on the Screen.
|
56
|
+
# Additionally, sprites can have some behavior associated with them
|
57
|
+
# via the #update method, including movement and collision detection;
|
58
|
+
# because of this, sprites can be the foundation of most on-screen objects,
|
59
|
+
# such as characters, items, missiles, and even user-interface elements.
|
60
|
+
#
|
61
|
+
# In order to work properly as a Sprite, the extended object or class must
|
62
|
+
# have two methods defined (by default, these are defined as accessors to
|
63
|
+
# attributes of the same names):
|
64
|
+
# image:: return a Surface with the sprite's image.
|
65
|
+
# rect:: returns a Rect with the position and dimensions of the sprite.
|
66
|
+
#
|
67
|
+
# Normally, the value returned by rect is used to draw the sprite
|
68
|
+
# onto a Surface as well as to detect collision between sprites. However,
|
69
|
+
# if @col_rect will be used for collision detection instead, if it is
|
70
|
+
# defined. See also #col_rect.
|
71
|
+
#
|
72
|
+
# Additionally, if you are extending an already-existing instance (rather
|
73
|
+
# than a class), that instance must have an attribute @groups, which is
|
74
|
+
# an Array containing all Groups to which the sprite belongs.
|
75
|
+
#
|
76
|
+
#
|
77
|
+
module Sprite
|
78
|
+
attr_reader :groups
|
79
|
+
attr_accessor :image, :rect, :depth
|
80
|
+
|
81
|
+
# Initialize the Sprite, defining @groups and @depth.
|
82
|
+
def initialize
|
83
|
+
@groups = []
|
84
|
+
@depth = 0
|
85
|
+
end
|
86
|
+
|
87
|
+
# Add the Sprite to each given Group.
|
88
|
+
def add(*groups)
|
89
|
+
groups.each { |group|
|
90
|
+
unless @groups.include? group
|
91
|
+
@groups.push(group)
|
92
|
+
group.push(self)
|
93
|
+
end
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
# call-seq: alive? -> true or false
|
98
|
+
#
|
99
|
+
# True if the Sprite belongs to at least one Group.
|
100
|
+
def alive?
|
101
|
+
return @groups.length > 0
|
102
|
+
end
|
103
|
+
|
104
|
+
# Set an alternative Rect to use for collision detection. If undefined or
|
105
|
+
# set to nil, the Sprite's #rect is used instead.
|
106
|
+
attr_writer :col_rect
|
107
|
+
|
108
|
+
# call-seq: col_rect -> Rect
|
109
|
+
#
|
110
|
+
# Returns @col_rect if it is defined, otherwise calls #rect.
|
111
|
+
# This method is used by #collide, #collide_group, and #collide_sprite
|
112
|
+
# to get the bounding box for collision detection.
|
113
|
+
def col_rect
|
114
|
+
if defined? @col_rect
|
115
|
+
return (@col_rect or rect)
|
116
|
+
else
|
117
|
+
return rect
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# call-seq: collide(other) -> Array
|
122
|
+
#
|
123
|
+
# Check collision between the caller and a Group or another Sprite. See
|
124
|
+
# also #collide_group and #collide_sprite. This method uses the value
|
125
|
+
# of #col_rect as the bounding box when detecting collision.
|
126
|
+
#
|
127
|
+
# If +other+ is a Group, returns an Array of every Sprite in that
|
128
|
+
# Group that collides with the caller. If +other+ is a Sprite, returns
|
129
|
+
# an Array containing +other+ if it collides with the caller. Otherwise,
|
130
|
+
# returns an empty Array.
|
131
|
+
def collide(other)
|
132
|
+
if other.class.is_a? Group
|
133
|
+
return collide_group(other)
|
134
|
+
elsif collide_sprite?(other)
|
135
|
+
return [other]
|
136
|
+
else
|
137
|
+
return []
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# call-seq: collide_group(group) -> Array
|
142
|
+
#
|
143
|
+
# Check collision between the caller and all members of a Group. This
|
144
|
+
# method uses the value of #col_rect as the bounding box when detecting
|
145
|
+
# collision.
|
146
|
+
#
|
147
|
+
# Returns an Array of every Sprite in +group+ that collides with the
|
148
|
+
# caller. If none collide, returns an empty Array.
|
149
|
+
def collide_group(group)
|
150
|
+
sprites = []
|
151
|
+
group.each { |sprite|
|
152
|
+
if self.collide_sprite?(sprite) and (not sprites.include?(sprite))
|
153
|
+
sprites.push(sprite)
|
154
|
+
end
|
155
|
+
}
|
156
|
+
return sprites
|
157
|
+
end
|
158
|
+
|
159
|
+
# call-seq: collide_sprite?(sprite) -> true or false or nil
|
160
|
+
#
|
161
|
+
# Check collision between the caller and another Sprite. This method uses
|
162
|
+
# the value of #col_rect as the bounding box when detecting collision.
|
163
|
+
# Returns true if +sprite+ collides with the caller, false if they do
|
164
|
+
# not, or nil if +sprite+ does not respond to either :col_rect or :rect
|
165
|
+
# (meaning it was not possible to detect collision).
|
166
|
+
def collide_sprite?(sprite)
|
167
|
+
if sprite.respond_to?(:col_rect)
|
168
|
+
return self.col_rect.collide_rect?(sprite.col_rect)
|
169
|
+
elsif sprite.respond_to?(:rect)
|
170
|
+
return self.col_rect.collide_rect?(sprite.rect)
|
171
|
+
else
|
172
|
+
return nil
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# call-seq: draw(surface) -> Rect
|
177
|
+
#
|
178
|
+
# Blit the Surface returned by #image onto +surface+ at the position
|
179
|
+
# given by the Rect returned by #rect. Returns a Rect representing
|
180
|
+
# the area of +surface+ which was affected by the draw.
|
181
|
+
def draw(destination)
|
182
|
+
self.image.blit(destination, self.rect)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Remove the caller from every Group that it belongs to.
|
186
|
+
def kill
|
187
|
+
@groups.each { |group| group.delete(self) }
|
188
|
+
@groups = []
|
189
|
+
end
|
190
|
+
|
191
|
+
# Remove the caller from each given Group.
|
192
|
+
def remove(*groups)
|
193
|
+
groups.each { |group|
|
194
|
+
if @groups.include? group
|
195
|
+
@groups.delete(group)
|
196
|
+
group.delete(self)
|
197
|
+
end
|
198
|
+
}
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
# call-seq: undraw(surface, background) -> Rect
|
203
|
+
#
|
204
|
+
# 'Erase' the sprite from +surface+ by drawing over it with part of
|
205
|
+
# +background+. For best results, +background+ should be the same size
|
206
|
+
# as +surface+.
|
207
|
+
#
|
208
|
+
# Returns a Rect representing the area of +surface+ which was affected.
|
209
|
+
#
|
210
|
+
def undraw(dest, background)
|
211
|
+
background.blit(dest, @rect, @rect)
|
212
|
+
end
|
213
|
+
|
214
|
+
# This method is meant to be overwritten by Sprite-based classes to
|
215
|
+
# define meaningful behavior. It can take any number of arguments you
|
216
|
+
# want, be called however often you want, and do whatever you want.
|
217
|
+
# It may return something, but Group#update will not (by default) use,
|
218
|
+
# or even collect, return values from this method.
|
219
|
+
#
|
220
|
+
# An example definition might take the amount of time that has passed
|
221
|
+
# since the last update; the Sprite could then update its position
|
222
|
+
# accordingly. Game logic, collision detection, and animation would also
|
223
|
+
# fit in here, if appropriate to your class.
|
224
|
+
def update( *args )
|
225
|
+
super
|
226
|
+
end
|
227
|
+
|
228
|
+
end # module Sprite
|
229
|
+
|
230
|
+
# The Group class is a special container, based on Array, with supplemental
|
231
|
+
# methods for handling multiple Sprite objects. Group can draw, update,
|
232
|
+
# and check collision for all its member sprites with one call.
|
233
|
+
#
|
234
|
+
# All members of a Group must be unique (duplicates will be refused), and
|
235
|
+
# should be a Sprite (or functionally equivalent).
|
236
|
+
class Group < Array
|
237
|
+
|
238
|
+
# Add +sprite+ to the Group. +sprite+ is notified so that it can
|
239
|
+
# add this Group to its list of parent Groups. See also #push.
|
240
|
+
def <<(sprite)
|
241
|
+
unless self.include? sprite
|
242
|
+
super(sprite)
|
243
|
+
sprite.add(self)
|
244
|
+
end
|
245
|
+
return self
|
246
|
+
end
|
247
|
+
|
248
|
+
# Call the method represented by +symbol+ for every member sprite,
|
249
|
+
# giving +args+ as the arguments to the method. This method uses
|
250
|
+
# Object#send, which does not hesitate to call private methods, so
|
251
|
+
# use this wisely! See also #draw and #update.
|
252
|
+
def call(symbol,*args)
|
253
|
+
self.each { |sprite|
|
254
|
+
sprite.send(symbol,*args)
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
# Remove every member sprite from the Group. Each sprite is notified
|
259
|
+
# so that it can remove this Group from its list of parent Groups.
|
260
|
+
# See also #delete.
|
261
|
+
def clear
|
262
|
+
self.dup.each { |sprite| sprite.remove(self) }
|
263
|
+
end
|
264
|
+
|
265
|
+
# call-seq: collide_sprite(sprite) -> Array
|
266
|
+
#
|
267
|
+
# Check collision between each member of the Group and +sprite+. Returns
|
268
|
+
# an Array of all member sprites that collided with +sprite+. If none
|
269
|
+
# collided, returns an empty Array.
|
270
|
+
def collide_sprite(sprite)
|
271
|
+
sprite.collide_group(self)
|
272
|
+
end
|
273
|
+
|
274
|
+
# call-seq: collide_group(group, klla=false, killb=false) -> Hash
|
275
|
+
#
|
276
|
+
# Check collision between each member of the calling Group and each
|
277
|
+
# member of +group+. Returns a Hash table with each member of the calling
|
278
|
+
# Group as a key, and as a value an Array of all members of +group+ that
|
279
|
+
# it collided with.
|
280
|
+
def collide_group(group, killa=false, killb=false)
|
281
|
+
sprites = {}
|
282
|
+
self.each { |sprite|
|
283
|
+
col = sprite.collide_group(group)
|
284
|
+
sprites[sprite] = col if col.length > 0
|
285
|
+
}
|
286
|
+
if killa
|
287
|
+
sprites.each_key { |sprite| sprite.kill }
|
288
|
+
end
|
289
|
+
if killb
|
290
|
+
sprites.each_value do |array|
|
291
|
+
array.each { |sprite| sprite.kill }
|
292
|
+
end
|
293
|
+
end
|
294
|
+
return sprites
|
295
|
+
end
|
296
|
+
|
297
|
+
# Remove each sprite in +sprites+ from the Group. Each sprite is notified
|
298
|
+
# so that it can remove this Group from its list of parent Groups.
|
299
|
+
# Note that this will not work correctly if fed a list of its
|
300
|
+
# own sprites (use Array.dup if you want correct behavior)
|
301
|
+
def delete(*sprites)
|
302
|
+
sprites.each { |sprite|
|
303
|
+
if self.include? sprite
|
304
|
+
super(sprite)
|
305
|
+
sprite.remove(self)
|
306
|
+
end
|
307
|
+
}
|
308
|
+
return self
|
309
|
+
end
|
310
|
+
|
311
|
+
# Draw every sprite on Surface +dest+. Calls Sprite#draw for every member
|
312
|
+
# sprite, passing +dest+ as the argument. See also #call and #update.
|
313
|
+
def draw(dest)
|
314
|
+
self.each { |sprite| sprite.draw(dest) }
|
315
|
+
end
|
316
|
+
|
317
|
+
# Add each sprite in +sprites+ to the Group. Each sprite is notified so
|
318
|
+
# that it can add this Group to its list of parent Groups. See also #<<.
|
319
|
+
def push(*sprites)
|
320
|
+
sprites.each { |sprite|
|
321
|
+
self << sprite
|
322
|
+
}
|
323
|
+
return self
|
324
|
+
end
|
325
|
+
|
326
|
+
# Update every member sprite. Calls Sprite#update for every member
|
327
|
+
# sprite, passing on all arguments. See also #call and #draw.
|
328
|
+
def update(*args)
|
329
|
+
self.each { |sprite|
|
330
|
+
sprite.update(*args)
|
331
|
+
}
|
332
|
+
end
|
333
|
+
|
334
|
+
end #class Group
|
335
|
+
|
336
|
+
# UpdateGroup is a mix-in module that extends Group to allow it to "erase"
|
337
|
+
# (i.e. draw over with a background Surface) and re-draw each sprite in its
|
338
|
+
# new position. This eliminates the "trail" of images that sprites would
|
339
|
+
# otherwise leave as they move around.
|
340
|
+
#
|
341
|
+
# UpdateGroup adds a new attribute, @dirty_rects, which is an Array storing
|
342
|
+
# all Rects which need to be updated, including the old positions of the
|
343
|
+
# sprites. This attribute is returned when UpdateGroup#draw is called,
|
344
|
+
# so that it can be used to update the parts of the Screen that have
|
345
|
+
# changed.
|
346
|
+
#
|
347
|
+
# The general order of calls each frame should be:
|
348
|
+
# 1. #undraw; clear the old positions of the sprites.
|
349
|
+
# 2. #update; update the sprites to their new positions.
|
350
|
+
# 3. #draw; draw the sprites in their new positions.
|
351
|
+
#
|
352
|
+
# This module can extend either a class or an already-existing Group
|
353
|
+
# instance (either empty or with members) without any special preparation.
|
354
|
+
module UpdateGroup
|
355
|
+
attr_accessor :dirty_rects
|
356
|
+
|
357
|
+
# Defines @dirty_rects when an existing object is extended.
|
358
|
+
def UpdateGroup.extend_object(obj)
|
359
|
+
super
|
360
|
+
obj.dirty_rects = []
|
361
|
+
end
|
362
|
+
|
363
|
+
# Initialize the Group, calling +super+ and defining @dirty_rects.
|
364
|
+
def initialize
|
365
|
+
super
|
366
|
+
@dirty_rects = []
|
367
|
+
end
|
368
|
+
|
369
|
+
# call-seq: draw(dest) -> Array
|
370
|
+
#
|
371
|
+
# Draw every sprite on Surface +dest+. See Group#draw.
|
372
|
+
# Returns an Array of Rects representing the portions of +dest+ which
|
373
|
+
# were affected by the last #undraw and this #draw.
|
374
|
+
def draw(dest)
|
375
|
+
self.each { |sprite|
|
376
|
+
@dirty_rects.push( sprite.draw(dest) )
|
377
|
+
}
|
378
|
+
rects = @dirty_rects
|
379
|
+
@dirty_rects = []
|
380
|
+
return rects
|
381
|
+
end
|
382
|
+
|
383
|
+
# Draw over part of +dest+ with image data from the corresponding part
|
384
|
+
# of +background+. For best results, +background+ should be at least as
|
385
|
+
# big as +dest+ (or, rather, the part of +dest+ that will ever be drawn
|
386
|
+
# over).
|
387
|
+
def undraw(dest,background)
|
388
|
+
self.each { |sprite|
|
389
|
+
@dirty_rects.push( sprite.undraw(dest, background) )
|
390
|
+
}
|
391
|
+
end
|
392
|
+
end # module UpdateGroup
|
393
|
+
|
394
|
+
|
395
|
+
# LimitGroup is a mix-in module that extends Group to limit the number
|
396
|
+
# of sprites it can contain. If the limit has been reached, each new sprite
|
397
|
+
# will "push out" the oldest sprite, on a First-In-First-Out basis.
|
398
|
+
#
|
399
|
+
# The limit can be set either when the LimitGroup is created, or
|
400
|
+
# at any time during execution. However, if you reduce the limit,
|
401
|
+
# excess sprites will not be removed until the next time a sprite is
|
402
|
+
# added. (You can work around this by pushing the last sprite in the
|
403
|
+
# group again; the duplicate will be removed.)
|
404
|
+
#
|
405
|
+
# Please note that, because Group#push is defined in terms of Group#<<,
|
406
|
+
# both LimitGroup#<< and LimitGroup#push work properly, even though only
|
407
|
+
# LimitGroup#<< is defined.
|
408
|
+
module LimitGroup
|
409
|
+
attr_accessor :limit
|
410
|
+
|
411
|
+
# Defines and sets @limit = 1 when an existing object is extended.
|
412
|
+
def LimitGroup.extend_object(obj)
|
413
|
+
super
|
414
|
+
obj.limit = 1
|
415
|
+
end
|
416
|
+
|
417
|
+
# Initialize the LimitGroup and define @limit (as 1, by default).
|
418
|
+
def initialize(limit=1)
|
419
|
+
@limit = limit
|
420
|
+
end
|
421
|
+
|
422
|
+
# Add +sprite+ to the LimitGroup, removing the oldest member if
|
423
|
+
# necessary to keep under the limit. If +sprite+ is already in
|
424
|
+
# the LimitGroup, it will be moved to the top of the queue.
|
425
|
+
def <<(sprite)
|
426
|
+
if not include? sprite
|
427
|
+
super(sprite)
|
428
|
+
while length > @limit
|
429
|
+
self.slice!(0)
|
430
|
+
end
|
431
|
+
else # move sprite to the back of the queue
|
432
|
+
self.delete(sprite)
|
433
|
+
super(sprite)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end # module LimitGroup
|
437
|
+
|
438
|
+
# DepthSortGroup is a mix-in module that extends Group to sort its
|
439
|
+
# sprites by their @depth attribute, so that sprites with low depths
|
440
|
+
# will appear on top of sprites with higher depths. A sprite's depth
|
441
|
+
# can be any Numeric, or nil (which will be counted as 0).
|
442
|
+
#
|
443
|
+
# If two sprites have exactly the same depth, there is no guarantee about
|
444
|
+
# which one will be drawn on top of the other. (But, whichever one is on
|
445
|
+
# top will stay on top, at least until the group is re-sorted.)
|
446
|
+
#
|
447
|
+
# If a sprite's depth changes after it has been added to the group, you
|
448
|
+
# must use the #sort_sprites method for the change to have any effect.
|
449
|
+
#
|
450
|
+
module DepthSortGroup
|
451
|
+
# Add a single sprite to the group. For efficiency reasons, this
|
452
|
+
# method doesn't re-sort sprites afterwards. You should use #sort_sprites
|
453
|
+
# after you're done adding sprites. Or, better yet, just use #push.
|
454
|
+
def <<(sprite)
|
455
|
+
super
|
456
|
+
end
|
457
|
+
|
458
|
+
# Add multiple sprites to the group, then re-sort all sprites.
|
459
|
+
def push(*sprites)
|
460
|
+
sprites.each { |sprite|
|
461
|
+
self << sprite
|
462
|
+
}
|
463
|
+
sort_sprites()
|
464
|
+
return self
|
465
|
+
end
|
466
|
+
|
467
|
+
# Sort sprites by depth, in descending order, so that sprites with low depths
|
468
|
+
# will be drawn on top of sprites with high depths.
|
469
|
+
#
|
470
|
+
# If a sprite has a depth of nil, it is sorted as if its depth were 0 (zero).
|
471
|
+
def sort_sprites
|
472
|
+
self.sort! { |a,b| (b.depth or 0) <=> (a.depth or 0) }
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
end #module Sprite
|
477
|
+
end # module Rubygame
|