rubygame 2.3.0-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- data/CREDITS +60 -0
- data/LICENSE +504 -0
- data/NEWS +252 -0
- data/README +123 -0
- data/ROADMAP +109 -0
- data/Rakefile +440 -0
- data/doc/extended_readme.rdoc +49 -0
- data/doc/getting_started.rdoc +47 -0
- data/doc/macosx_install.rdoc +70 -0
- data/doc/windows_install.rdoc +123 -0
- data/ext/rubygame/rubygame_core.so +0 -0
- data/ext/rubygame/rubygame_event.c +644 -0
- data/ext/rubygame/rubygame_event.h +48 -0
- data/ext/rubygame/rubygame_event.obj +0 -0
- data/ext/rubygame/rubygame_gfx.c +942 -0
- data/ext/rubygame/rubygame_gfx.h +101 -0
- data/ext/rubygame/rubygame_gfx.obj +0 -0
- data/ext/rubygame/rubygame_gfx.so +0 -0
- data/ext/rubygame/rubygame_gl.c +154 -0
- data/ext/rubygame/rubygame_gl.h +32 -0
- data/ext/rubygame/rubygame_gl.obj +0 -0
- data/ext/rubygame/rubygame_image.c +252 -0
- data/ext/rubygame/rubygame_image.h +41 -0
- data/ext/rubygame/rubygame_image.obj +0 -0
- data/ext/rubygame/rubygame_image.so +0 -0
- data/ext/rubygame/rubygame_joystick.c +247 -0
- data/ext/rubygame/rubygame_joystick.h +41 -0
- data/ext/rubygame/rubygame_joystick.obj +0 -0
- data/ext/rubygame/rubygame_main.c +155 -0
- data/ext/rubygame/rubygame_main.h +36 -0
- data/ext/rubygame/rubygame_main.obj +0 -0
- data/ext/rubygame/rubygame_mixer.c +1024 -0
- data/ext/rubygame/rubygame_mixer.h +36 -0
- data/ext/rubygame/rubygame_mixer.obj +0 -0
- data/ext/rubygame/rubygame_mixer.so +0 -0
- data/ext/rubygame/rubygame_music.c +1017 -0
- data/ext/rubygame/rubygame_music.h +29 -0
- data/ext/rubygame/rubygame_music.obj +0 -0
- data/ext/rubygame/rubygame_screen.c +448 -0
- data/ext/rubygame/rubygame_screen.h +43 -0
- data/ext/rubygame/rubygame_screen.obj +0 -0
- data/ext/rubygame/rubygame_shared.c +272 -0
- data/ext/rubygame/rubygame_shared.h +68 -0
- data/ext/rubygame/rubygame_shared.obj +0 -0
- data/ext/rubygame/rubygame_sound.c +863 -0
- data/ext/rubygame/rubygame_sound.h +29 -0
- data/ext/rubygame/rubygame_sound.obj +0 -0
- data/ext/rubygame/rubygame_surface.c +1151 -0
- data/ext/rubygame/rubygame_surface.h +62 -0
- data/ext/rubygame/rubygame_surface.obj +0 -0
- data/ext/rubygame/rubygame_time.c +183 -0
- data/ext/rubygame/rubygame_time.h +32 -0
- data/ext/rubygame/rubygame_time.obj +0 -0
- data/ext/rubygame/rubygame_ttf.c +599 -0
- data/ext/rubygame/rubygame_ttf.h +69 -0
- data/ext/rubygame/rubygame_ttf.obj +0 -0
- data/ext/rubygame/rubygame_ttf.so +0 -0
- data/lib/rubygame.rb +41 -0
- data/lib/rubygame/clock.rb +128 -0
- data/lib/rubygame/color.rb +79 -0
- data/lib/rubygame/color/models/base.rb +111 -0
- data/lib/rubygame/color/models/hsl.rb +153 -0
- data/lib/rubygame/color/models/hsv.rb +149 -0
- data/lib/rubygame/color/models/rgb.rb +78 -0
- data/lib/rubygame/color/palettes/css.rb +49 -0
- data/lib/rubygame/color/palettes/palette.rb +100 -0
- data/lib/rubygame/color/palettes/x11.rb +177 -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/named_resource.rb +254 -0
- data/lib/rubygame/queue.rb +288 -0
- data/lib/rubygame/rect.rb +612 -0
- data/lib/rubygame/sfont.rb +223 -0
- data/lib/rubygame/sprite.rb +511 -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 +302 -0
- data/samples/demo_gl.rb +151 -0
- data/samples/demo_gl_tex.rb +197 -0
- data/samples/demo_music.rb +77 -0
- data/samples/demo_rubygame.rb +296 -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/song.ogg +0 -0
- data/samples/term16.png +0 -0
- data/samples/whiff.wav +0 -0
- metadata +164 -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,511 @@
|
|
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:
|
275
|
+
# collide_group(group, &block) -> Hash
|
276
|
+
# collide_group(group, killa=false, killb=false) -> Hash # deprecated
|
277
|
+
#
|
278
|
+
# Check collision between each member of the calling Group and each
|
279
|
+
# member of +group+. Returns a Hash table with each member of the calling
|
280
|
+
# Group as a key, and as a value an Array of all members of +group+ that
|
281
|
+
# it collided with.
|
282
|
+
#
|
283
|
+
# If a block is given, that block is executed for every pair of colliding
|
284
|
+
# sprites. For example, if a1 collides with b1 and b2, the block will
|
285
|
+
# be called twice: once with [ a1, b1 ] and once with [ a1, b2 ].
|
286
|
+
#
|
287
|
+
# Example:
|
288
|
+
#
|
289
|
+
# # 'kills' both sprites when they collide
|
290
|
+
#
|
291
|
+
# groupA,collide_group(groupB) do |a, b|
|
292
|
+
# a.kill
|
293
|
+
# b.kill
|
294
|
+
# end
|
295
|
+
#
|
296
|
+
# *NOTE*: +killa+ and +killb+ are deprecated and will be removed in the future.
|
297
|
+
# It is highly recommended that you use the block argument instead.
|
298
|
+
#
|
299
|
+
# *IMPORTANT*: +killa+ and +killb+ will be ignored if a block is given!
|
300
|
+
#
|
301
|
+
# If +killa+ is true and a sprite in group A collides with a sprite in group B,
|
302
|
+
# the sprite in group A will have its #kill method called; the same goes for
|
303
|
+
# +killb+ and group B.
|
304
|
+
#
|
305
|
+
def collide_group(group, killa=false, killb=false, &block)
|
306
|
+
sprites = {}
|
307
|
+
self.each { |sprite|
|
308
|
+
col = sprite.collide_group(group)
|
309
|
+
sprites[sprite] = col if col.length > 0
|
310
|
+
}
|
311
|
+
|
312
|
+
if block_given?
|
313
|
+
sprites.each_pair do |a, bs|
|
314
|
+
bs.each { |b| yield(a, b) }
|
315
|
+
end
|
316
|
+
else
|
317
|
+
# killa and killb only work if no block is given
|
318
|
+
if killa
|
319
|
+
sprites.each_key { |sprite| sprite.kill }
|
320
|
+
end
|
321
|
+
if killb
|
322
|
+
sprites.each_value do |array|
|
323
|
+
array.each { |sprite| sprite.kill }
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
return sprites
|
329
|
+
end
|
330
|
+
|
331
|
+
# Remove each sprite in +sprites+ from the Group. Each sprite is notified
|
332
|
+
# so that it can remove this Group from its list of parent Groups.
|
333
|
+
# Note that this will not work correctly if fed a list of its
|
334
|
+
# own sprites (use Array.dup if you want correct behavior)
|
335
|
+
def delete(*sprites)
|
336
|
+
sprites.each { |sprite|
|
337
|
+
if self.include? sprite
|
338
|
+
super(sprite)
|
339
|
+
sprite.remove(self)
|
340
|
+
end
|
341
|
+
}
|
342
|
+
return self
|
343
|
+
end
|
344
|
+
|
345
|
+
# Draw every sprite on Surface +dest+. Calls Sprite#draw for every member
|
346
|
+
# sprite, passing +dest+ as the argument. See also #call and #update.
|
347
|
+
def draw(dest)
|
348
|
+
self.each { |sprite| sprite.draw(dest) }
|
349
|
+
end
|
350
|
+
|
351
|
+
# Add each sprite in +sprites+ to the Group. Each sprite is notified so
|
352
|
+
# that it can add this Group to its list of parent Groups. See also #<<.
|
353
|
+
def push(*sprites)
|
354
|
+
sprites.each { |sprite|
|
355
|
+
self << sprite
|
356
|
+
}
|
357
|
+
return self
|
358
|
+
end
|
359
|
+
|
360
|
+
# Update every member sprite. Calls Sprite#update for every member
|
361
|
+
# sprite, passing on all arguments. See also #call and #draw.
|
362
|
+
def update(*args)
|
363
|
+
self.each { |sprite|
|
364
|
+
sprite.update(*args)
|
365
|
+
}
|
366
|
+
end
|
367
|
+
|
368
|
+
end #class Group
|
369
|
+
|
370
|
+
# UpdateGroup is a mix-in module that extends Group to allow it to "erase"
|
371
|
+
# (i.e. draw over with a background Surface) and re-draw each sprite in its
|
372
|
+
# new position. This eliminates the "trail" of images that sprites would
|
373
|
+
# otherwise leave as they move around.
|
374
|
+
#
|
375
|
+
# UpdateGroup adds a new attribute, @dirty_rects, which is an Array storing
|
376
|
+
# all Rects which need to be updated, including the old positions of the
|
377
|
+
# sprites. This attribute is returned when UpdateGroup#draw is called,
|
378
|
+
# so that it can be used to update the parts of the Screen that have
|
379
|
+
# changed.
|
380
|
+
#
|
381
|
+
# The general order of calls each frame should be:
|
382
|
+
# 1. #undraw; clear the old positions of the sprites.
|
383
|
+
# 2. #update; update the sprites to their new positions.
|
384
|
+
# 3. #draw; draw the sprites in their new positions.
|
385
|
+
#
|
386
|
+
# This module can extend either a class or an already-existing Group
|
387
|
+
# instance (either empty or with members) without any special preparation.
|
388
|
+
module UpdateGroup
|
389
|
+
attr_accessor :dirty_rects
|
390
|
+
|
391
|
+
# Defines @dirty_rects when an existing object is extended.
|
392
|
+
def UpdateGroup.extend_object(obj)
|
393
|
+
super
|
394
|
+
obj.dirty_rects = []
|
395
|
+
end
|
396
|
+
|
397
|
+
# Initialize the Group, calling +super+ and defining @dirty_rects.
|
398
|
+
def initialize
|
399
|
+
super
|
400
|
+
@dirty_rects = []
|
401
|
+
end
|
402
|
+
|
403
|
+
# call-seq: draw(dest) -> Array
|
404
|
+
#
|
405
|
+
# Draw every sprite on Surface +dest+. See Group#draw.
|
406
|
+
# Returns an Array of Rects representing the portions of +dest+ which
|
407
|
+
# were affected by the last #undraw and this #draw.
|
408
|
+
def draw(dest)
|
409
|
+
self.each { |sprite|
|
410
|
+
@dirty_rects.push( sprite.draw(dest) )
|
411
|
+
}
|
412
|
+
rects = @dirty_rects
|
413
|
+
@dirty_rects = []
|
414
|
+
return rects
|
415
|
+
end
|
416
|
+
|
417
|
+
# Draw over part of +dest+ with image data from the corresponding part
|
418
|
+
# of +background+. For best results, +background+ should be at least as
|
419
|
+
# big as +dest+ (or, rather, the part of +dest+ that will ever be drawn
|
420
|
+
# over).
|
421
|
+
def undraw(dest,background)
|
422
|
+
self.each { |sprite|
|
423
|
+
@dirty_rects.push( sprite.undraw(dest, background) )
|
424
|
+
}
|
425
|
+
end
|
426
|
+
end # module UpdateGroup
|
427
|
+
|
428
|
+
|
429
|
+
# LimitGroup is a mix-in module that extends Group to limit the number
|
430
|
+
# of sprites it can contain. If the limit has been reached, each new sprite
|
431
|
+
# will "push out" the oldest sprite, on a First-In-First-Out basis.
|
432
|
+
#
|
433
|
+
# The limit can be set either when the LimitGroup is created, or
|
434
|
+
# at any time during execution. However, if you reduce the limit,
|
435
|
+
# excess sprites will not be removed until the next time a sprite is
|
436
|
+
# added. (You can work around this by pushing the last sprite in the
|
437
|
+
# group again; the duplicate will be removed.)
|
438
|
+
#
|
439
|
+
# Please note that, because Group#push is defined in terms of Group#<<,
|
440
|
+
# both LimitGroup#<< and LimitGroup#push work properly, even though only
|
441
|
+
# LimitGroup#<< is defined.
|
442
|
+
module LimitGroup
|
443
|
+
attr_accessor :limit
|
444
|
+
|
445
|
+
# Defines and sets @limit = 1 when an existing object is extended.
|
446
|
+
def LimitGroup.extend_object(obj)
|
447
|
+
super
|
448
|
+
obj.limit = 1
|
449
|
+
end
|
450
|
+
|
451
|
+
# Initialize the LimitGroup and define @limit (as 1, by default).
|
452
|
+
def initialize(limit=1)
|
453
|
+
@limit = limit
|
454
|
+
end
|
455
|
+
|
456
|
+
# Add +sprite+ to the LimitGroup, removing the oldest member if
|
457
|
+
# necessary to keep under the limit. If +sprite+ is already in
|
458
|
+
# the LimitGroup, it will be moved to the top of the queue.
|
459
|
+
def <<(sprite)
|
460
|
+
if not include? sprite
|
461
|
+
super(sprite)
|
462
|
+
while length > @limit
|
463
|
+
self.slice!(0)
|
464
|
+
end
|
465
|
+
else # move sprite to the back of the queue
|
466
|
+
self.delete(sprite)
|
467
|
+
super(sprite)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end # module LimitGroup
|
471
|
+
|
472
|
+
# DepthSortGroup is a mix-in module that extends Group to sort its
|
473
|
+
# sprites by their @depth attribute, so that sprites with low depths
|
474
|
+
# will appear on top of sprites with higher depths. A sprite's depth
|
475
|
+
# can be any Numeric, or nil (which will be counted as 0).
|
476
|
+
#
|
477
|
+
# If two sprites have exactly the same depth, there is no guarantee about
|
478
|
+
# which one will be drawn on top of the other. (But, whichever one is on
|
479
|
+
# top will stay on top, at least until the group is re-sorted.)
|
480
|
+
#
|
481
|
+
# If a sprite's depth changes after it has been added to the group, you
|
482
|
+
# must use the #sort_sprites method for the change to have any effect.
|
483
|
+
#
|
484
|
+
module DepthSortGroup
|
485
|
+
# Add a single sprite to the group. For efficiency reasons, this
|
486
|
+
# method doesn't re-sort sprites afterwards. You should use #sort_sprites
|
487
|
+
# after you're done adding sprites. Or, better yet, just use #push.
|
488
|
+
def <<(sprite)
|
489
|
+
super
|
490
|
+
end
|
491
|
+
|
492
|
+
# Add multiple sprites to the group, then re-sort all sprites.
|
493
|
+
def push(*sprites)
|
494
|
+
sprites.each { |sprite|
|
495
|
+
self << sprite
|
496
|
+
}
|
497
|
+
sort_sprites()
|
498
|
+
return self
|
499
|
+
end
|
500
|
+
|
501
|
+
# Sort sprites by depth, in descending order, so that sprites with low depths
|
502
|
+
# will be drawn on top of sprites with high depths.
|
503
|
+
#
|
504
|
+
# If a sprite has a depth of nil, it is sorted as if its depth were 0 (zero).
|
505
|
+
def sort_sprites
|
506
|
+
self.sort! { |a,b| (b.depth or 0) <=> (a.depth or 0) }
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
end #module Sprite
|
511
|
+
end # module Rubygame
|