gosu-examples 1.0.3
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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +14 -0
- data/bin/gosu-examples +97 -0
- data/examples/chipmunk_and_rmagick.rb +152 -0
- data/examples/chipmunk_integration.rb +278 -0
- data/examples/cptn_ruby.rb +229 -0
- data/examples/media/BrokenPNG.png +0 -0
- data/examples/media/Cursor.png +0 -0
- data/examples/media/JingleBells.mp3 +0 -0
- data/examples/media/JingleBells.ogg +0 -0
- data/examples/media/Loop.wav +0 -0
- data/examples/media/Sample.wav +0 -0
- data/examples/media/SquareTexture.png +0 -0
- data/examples/media/Wallpaper.png +0 -0
- data/examples/media/WallpaperXXL.png +0 -0
- data/examples/media/WhiteAlpha.png +0 -0
- data/examples/media/audio_formats/aiff_32bit_float.aiff +0 -0
- data/examples/media/audio_formats/au_16bit_pcm.au +0 -0
- data/examples/media/audio_formats/caf_be_16bit_44khz.caf +0 -0
- data/examples/media/audio_formats/caf_le_16bit_44khz.caf +0 -0
- data/examples/media/audio_formats/caf_le_8bit_44khz.caf +0 -0
- data/examples/media/audio_formats/general_midi.mid +0 -0
- data/examples/media/audio_formats/impulse_tracker.it +0 -0
- data/examples/media/audio_formats/mp3_128k_stereo.mp3 +0 -0
- data/examples/media/audio_formats/mp3_avg_96kbit_jointstereo.mp3 +0 -0
- data/examples/media/audio_formats/ogg_vorbis.ogg +0 -0
- data/examples/media/audio_formats/wav_16bit_pcm.wav +0 -0
- data/examples/media/audio_formats/wav_32bit_pcm.wav +0 -0
- data/examples/media/audio_formats/wav_4bit_ms_adpcm.wav +0 -0
- data/examples/media/beep.wav +0 -0
- data/examples/media/cptn_ruby.png +0 -0
- data/examples/media/cptn_ruby_map.txt +25 -0
- data/examples/media/earth.png +0 -0
- data/examples/media/explosion.wav +0 -0
- data/examples/media/gem.png +0 -0
- data/examples/media/header.psd +0 -0
- data/examples/media/image_formats/test.jpg +0 -0
- data/examples/media/image_formats/test.psd +0 -0
- data/examples/media/landscape.svg +10 -0
- data/examples/media/large_star.png +0 -0
- data/examples/media/smoke.png +0 -0
- data/examples/media/soldier.png +0 -0
- data/examples/media/space.png +0 -0
- data/examples/media/star.png +0 -0
- data/examples/media/starfighter.bmp +0 -0
- data/examples/media/tileset.png +0 -0
- data/examples/media/vera.ttf +0 -0
- data/examples/opengl_integration.rb +224 -0
- data/examples/rmagick_integration.rb +413 -0
- data/examples/tutorial.rb +129 -0
- data/examples/welcome.rb +59 -0
- data/lib/gosu-examples/example.rb +82 -0
- data/lib/gosu-examples/sidebar.rb +60 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8eb4367ee4c008b79167bda5845113c7513eedd4
|
4
|
+
data.tar.gz: 9b6fe668a5ed7f24da588abdb4f6f9dcbe600f81
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cacab6fb2e19440bb1b1b84043f4a3936c5b84c48734809a3e8452252ac5509cd9b743d117afc81fa2d7a300c5593a35e32dfb13a0d030ffeb8bef0efe5367eb
|
7
|
+
data.tar.gz: aed9c85c7dc6c65a7bf585867f77344a6617273310bcba784f1a15ca0c932ab8eb9984fe3900c3bbf1d693371e10b8fb58e13a7b7325f23d52c14a5ca363869f
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Julian Raschke
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gosu Examples
|
2
|
+
=============
|
3
|
+
|
4
|
+
This is a collection of Ruby example games for the [Gosu media library](https://libgosu.org/).
|
5
|
+
|
6
|
+
To run the examples, simply install the `gosu-examples` Ruby gem and then run `gosu-examples` in the terminal.
|
7
|
+
|
8
|
+
Some examples require the following libraries to be installed:
|
9
|
+
|
10
|
+
`gem install chipmunk`
|
11
|
+
`gem install rmagick`
|
12
|
+
`gem install opengl`
|
13
|
+
|
14
|
+
(This is a very rough first version of this gem. Feel free to contribute to all parts of this repository via Pull Requests - media files, more examples, cleaning up code... - as long as we can use your contributions under the MIT license.)
|
data/bin/gosu-examples
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'gosu'
|
5
|
+
|
6
|
+
Dir.chdir "#{File.dirname __FILE__}/../examples"
|
7
|
+
|
8
|
+
$LOAD_PATH << "#{File.dirname __FILE__}/../lib/gosu-examples"
|
9
|
+
|
10
|
+
require 'example'
|
11
|
+
require 'sidebar'
|
12
|
+
|
13
|
+
Example.load_examples "{.,../features}/*.rb" # TODO - should be *.rb
|
14
|
+
|
15
|
+
class ExampleBox < Gosu::Window
|
16
|
+
# TODO - the ExampleWindow should resize to fit once Gosu::Window#resize has been added.
|
17
|
+
# See https://github.com/jlnr/gosu/issues/255
|
18
|
+
EXAMPLE_WIDTH = 600
|
19
|
+
EXAMPLE_HEIGHT = 600
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super Sidebar::WIDTH + EXAMPLE_WIDTH, EXAMPLE_HEIGHT, :fullscreen => ARGV.include?('--fullscreen')
|
23
|
+
|
24
|
+
@sidebar = Sidebar.new { |example| change_example(example) }
|
25
|
+
|
26
|
+
welcome_class = Example.examples.find { |example| example.name =~ /::Welcome$/ }
|
27
|
+
@current_example = welcome_class.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
self.caption = "Gosu Example Box - #{@current_example.caption} (#{Gosu::fps} FPS)"
|
32
|
+
|
33
|
+
@current_example.update
|
34
|
+
end
|
35
|
+
|
36
|
+
def draw
|
37
|
+
@current_example.draw
|
38
|
+
|
39
|
+
flush
|
40
|
+
|
41
|
+
translate(EXAMPLE_WIDTH, 0) do
|
42
|
+
current_filename = @current_example.class.source_file
|
43
|
+
@sidebar.draw(current_filename)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def button_down(id)
|
48
|
+
case id
|
49
|
+
when Gosu::KbEscape
|
50
|
+
close
|
51
|
+
when char_to_button_id('S')
|
52
|
+
if filename = @current_example.class.source_file then
|
53
|
+
open_file_or_folder filename
|
54
|
+
end
|
55
|
+
when char_to_button_id('O')
|
56
|
+
if filename = @current_example.class.source_file then
|
57
|
+
open_file_or_folder File.dirname(filename)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
if id == Gosu::MsLeft and mouse_x >= EXAMPLE_WIDTH then
|
61
|
+
@sidebar.click(mouse_x - EXAMPLE_WIDTH, mouse_y)
|
62
|
+
else
|
63
|
+
@current_example.button_down(id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def button_up(id)
|
69
|
+
@current_example.button_up(id)
|
70
|
+
end
|
71
|
+
|
72
|
+
def needs_cursor?
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def change_example(example)
|
79
|
+
if @current_example.class != example then
|
80
|
+
@current_example = nil
|
81
|
+
GC.start
|
82
|
+
@current_example = example.new
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def open_file_or_folder(filename)
|
87
|
+
if RUBY_PLATFORM =~ /darwin[0-9]*$/ then
|
88
|
+
`open '#{filename}'`
|
89
|
+
elsif RUBY_PLATFORM =~ /mingw[0-9]*$/ then
|
90
|
+
`explorer '#{filename}'`
|
91
|
+
else
|
92
|
+
`xdg-open '#{filename}'`
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
ExampleBox.new.show
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# Encoding: UTF-8
|
2
|
+
|
3
|
+
# Based on the C Demo3 demonstration distributed with Chipmunk.
|
4
|
+
# Also with some help from the chipmunk_integration.rb program.
|
5
|
+
#
|
6
|
+
# License: Same as for Gosu (MIT)
|
7
|
+
# Created on 21/10/2007, 00:05:19 by Robert Sheehan
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'gosu'
|
11
|
+
require 'chipmunk'
|
12
|
+
require 'rmagick'
|
13
|
+
|
14
|
+
# Layering of sprites
|
15
|
+
module ZOrder
|
16
|
+
Background, Box = *0..1
|
17
|
+
end
|
18
|
+
|
19
|
+
WIDTH = 600
|
20
|
+
HEIGHT = 600
|
21
|
+
TICK = 1.0/60.0
|
22
|
+
NUM_POLYGONS = 80
|
23
|
+
NUM_SIDES = 4
|
24
|
+
EDGE_SIZE = 15
|
25
|
+
|
26
|
+
class ChipmunkAndRMagick < (Example rescue Gosu::Window)
|
27
|
+
def radians_to_vec2(radians)
|
28
|
+
CP::Vec2.new(Math::cos(radians), Math::sin(radians))
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
super WIDTH, HEIGHT
|
33
|
+
|
34
|
+
self.caption = "Chipmunk, RMagick and Gosu"
|
35
|
+
|
36
|
+
@space = CP::Space.new
|
37
|
+
@space.iterations = 5
|
38
|
+
@space.gravity = CP::Vec2.new(0, 100)
|
39
|
+
|
40
|
+
# you can replace the background with any image with this line
|
41
|
+
# background = Magick::ImageList.new("media/space.png")
|
42
|
+
fill = Magick::TextureFill.new(Magick::ImageList.new("granite:"))
|
43
|
+
background = Magick::Image.new(WIDTH, HEIGHT, fill)
|
44
|
+
setup_triangles(background)
|
45
|
+
@background_image = Gosu::Image.new(background, :tileable => true) # turn the image into a Gosu one
|
46
|
+
@boxes = create_boxes(NUM_POLYGONS)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create all of the static triangles.
|
50
|
+
# Adds them to the space and the background image.
|
51
|
+
def setup_triangles(background)
|
52
|
+
gc = Magick::Draw.new
|
53
|
+
gc.stroke_width(2)
|
54
|
+
gc.stroke('red')
|
55
|
+
gc.fill('blue')
|
56
|
+
# all the triangles are part of the same body
|
57
|
+
body = CP::Body.new(Float::MAX, Float::MAX)
|
58
|
+
base = 15
|
59
|
+
height = 10
|
60
|
+
shape_vertices = [CP::Vec2.new(-base, base), CP::Vec2.new(base, base), CP::Vec2.new(0, -height)]
|
61
|
+
# make shapes and images
|
62
|
+
8.times do |i|
|
63
|
+
8.times do |j|
|
64
|
+
stagger = (j % 2) * 40
|
65
|
+
x = i * 80 + stagger
|
66
|
+
y = j * 70 + 80
|
67
|
+
shape = CP::Shape::Poly.new(body, shape_vertices, CP::Vec2.new(x, y))
|
68
|
+
shape.e = 1
|
69
|
+
shape.u = 1
|
70
|
+
@space.add_static_shape(shape)
|
71
|
+
gc.polygon(x - base + 1, y + base - 1, x + base - 1, y + base - 1, x, y - height + 1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
# do the drawing
|
75
|
+
gc.draw(background)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Produces the vertices of a regular polygon.
|
79
|
+
def polygon_vertices(sides, size)
|
80
|
+
vertices = []
|
81
|
+
sides.times do |i|
|
82
|
+
angle = -2 * Math::PI * i / sides
|
83
|
+
vertices << radians_to_vec2(angle) * size
|
84
|
+
end
|
85
|
+
return vertices
|
86
|
+
end
|
87
|
+
|
88
|
+
# Produces the image of a polygon.
|
89
|
+
def polygon_image(vertices)
|
90
|
+
box_image = Magick::Image.new(EDGE_SIZE * 2, EDGE_SIZE * 2) { self.background_color = 'transparent' }
|
91
|
+
gc = Magick::Draw.new
|
92
|
+
gc.stroke('red')
|
93
|
+
gc.fill('plum')
|
94
|
+
draw_vertices = vertices.map { |v| [v.x + EDGE_SIZE, v.y + EDGE_SIZE] }.flatten
|
95
|
+
gc.polygon(*draw_vertices)
|
96
|
+
gc.draw(box_image)
|
97
|
+
return Gosu::Image.new(box_image)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Produces the polygon objects and adds them to the space.
|
101
|
+
def create_boxes(num)
|
102
|
+
box_vertices = polygon_vertices(NUM_SIDES, EDGE_SIZE)
|
103
|
+
box_image = polygon_image(box_vertices)
|
104
|
+
boxes = []
|
105
|
+
num.times do
|
106
|
+
body = CP::Body.new(1, CP::moment_for_poly(1.0, box_vertices, CP::Vec2.new(0, 0))) # mass, moment of inertia
|
107
|
+
body.p = CP::Vec2.new(rand(WIDTH), rand(40) - 50)
|
108
|
+
shape = CP::Shape::Poly.new(body, box_vertices, CP::Vec2.new(0, 0))
|
109
|
+
shape.e = 0.0
|
110
|
+
shape.u = 0.4
|
111
|
+
boxes << Box.new(box_image, body)
|
112
|
+
@space.add_body(body)
|
113
|
+
@space.add_shape(shape)
|
114
|
+
end
|
115
|
+
return boxes
|
116
|
+
end
|
117
|
+
|
118
|
+
# All the simulation is done here.
|
119
|
+
def update
|
120
|
+
@space.step(TICK)
|
121
|
+
@boxes.each { |box| box.check_off_screen }
|
122
|
+
end
|
123
|
+
|
124
|
+
# All the updating of the screen is done here.
|
125
|
+
def draw
|
126
|
+
@background_image.draw(0, 0, ZOrder::Background)
|
127
|
+
@boxes.each { |box| box.draw }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# The falling boxes class.
|
132
|
+
# Nothing more than a body and an image.
|
133
|
+
class Box
|
134
|
+
def initialize(image, body)
|
135
|
+
@image = image
|
136
|
+
@body = body
|
137
|
+
end
|
138
|
+
|
139
|
+
# If it goes offscreen we put it back to the top.
|
140
|
+
def check_off_screen
|
141
|
+
pos = @body.p
|
142
|
+
if pos.y > HEIGHT + EDGE_SIZE or pos.x > WIDTH + EDGE_SIZE or pos.x < -EDGE_SIZE
|
143
|
+
@body.p = CP::Vec2.new(rand * WIDTH, 0)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def draw
|
148
|
+
@image.draw_rot(@body.p.x, @body.p.y, ZOrder::Box, @body.a.radians_to_gosu)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
ChipmunkAndRMagick.new.show if __FILE__ == $0
|
@@ -0,0 +1,278 @@
|
|
1
|
+
# Encoding: UTF-8
|
2
|
+
|
3
|
+
## File: ChipmunkIntegration.rb
|
4
|
+
## Author: Dirk Johnson
|
5
|
+
## Version: 1.0.0
|
6
|
+
## Date: 2007-10-05
|
7
|
+
## License: Same as for Gosu (MIT)
|
8
|
+
## Comments: Based on the Gosu Ruby Tutorial, but incorporating the Chipmunk Physics Engine
|
9
|
+
## See https://github.com/jlnr/gosu/wiki/Ruby-Chipmunk-Integration for the accompanying text.
|
10
|
+
|
11
|
+
require 'rubygems'
|
12
|
+
require 'gosu'
|
13
|
+
require 'chipmunk'
|
14
|
+
|
15
|
+
WIDTH = 600
|
16
|
+
HEIGHT = 600
|
17
|
+
|
18
|
+
# The number of steps to process every Gosu update
|
19
|
+
# The Player ship can get going so fast as to "move through" a
|
20
|
+
# star without triggering a collision; an increased number of
|
21
|
+
# Chipmunk step calls per update will effectively avoid this issue
|
22
|
+
SUBSTEPS = 6
|
23
|
+
|
24
|
+
# Layering of objects
|
25
|
+
module ZOrder
|
26
|
+
Background, Stars, Player, UI = *0..3
|
27
|
+
end
|
28
|
+
|
29
|
+
# This game will have one Player in the form of a ship
|
30
|
+
class Player
|
31
|
+
attr_reader :shape
|
32
|
+
|
33
|
+
def initialize(shape)
|
34
|
+
@image = Gosu::Image.new("media/starfighter.bmp")
|
35
|
+
@shape = shape
|
36
|
+
@shape.body.p = CP::Vec2.new(0.0, 0.0) # position
|
37
|
+
@shape.body.v = CP::Vec2.new(0.0, 0.0) # velocity
|
38
|
+
|
39
|
+
# Keep in mind that down the screen is positive y, which means that PI/2 radians,
|
40
|
+
# which you might consider the top in the traditional Trig unit circle sense is actually
|
41
|
+
# the bottom; thus 3PI/2 is the top
|
42
|
+
@shape.body.a = (3*Math::PI/2.0) # angle in radians; faces towards top of screen
|
43
|
+
end
|
44
|
+
|
45
|
+
# Directly set the position of our Player
|
46
|
+
def warp(vect)
|
47
|
+
@shape.body.p = vect
|
48
|
+
end
|
49
|
+
|
50
|
+
# Apply negative Torque; Chipmunk will do the rest
|
51
|
+
# SUBSTEPS is used as a divisor to keep turning rate constant
|
52
|
+
# even if the number of steps per update are adjusted
|
53
|
+
def turn_left
|
54
|
+
@shape.body.t -= 400.0/SUBSTEPS
|
55
|
+
end
|
56
|
+
|
57
|
+
# Apply positive Torque; Chipmunk will do the rest
|
58
|
+
# SUBSTEPS is used as a divisor to keep turning rate constant
|
59
|
+
# even if the number of steps per update are adjusted
|
60
|
+
def turn_right
|
61
|
+
@shape.body.t += 400.0/SUBSTEPS
|
62
|
+
end
|
63
|
+
|
64
|
+
# Apply forward force; Chipmunk will do the rest
|
65
|
+
# SUBSTEPS is used as a divisor to keep acceleration rate constant
|
66
|
+
# even if the number of steps per update are adjusted
|
67
|
+
# Here we must convert the angle (facing) of the body into
|
68
|
+
# forward momentum by creating a vector in the direction of the facing
|
69
|
+
# and with a magnitude representing the force we want to apply
|
70
|
+
def accelerate
|
71
|
+
@shape.body.apply_force((radians_to_vec2(@shape.body.a) * (3000.0/SUBSTEPS)), CP::Vec2.new(0.0, 0.0))
|
72
|
+
end
|
73
|
+
|
74
|
+
# Apply even more forward force
|
75
|
+
# See accelerate for more details
|
76
|
+
def boost
|
77
|
+
@shape.body.apply_force((radians_to_vec2(@shape.body.a) * (3000.0)), CP::Vec2.new(0.0, 0.0))
|
78
|
+
end
|
79
|
+
|
80
|
+
# Apply reverse force
|
81
|
+
# See accelerate for more details
|
82
|
+
def reverse
|
83
|
+
@shape.body.apply_force(-(radians_to_vec2(@shape.body.a) * (1000.0/SUBSTEPS)), CP::Vec2.new(0.0, 0.0))
|
84
|
+
end
|
85
|
+
|
86
|
+
# Wrap to the other side of the screen when we fly off the edge
|
87
|
+
def validate_position
|
88
|
+
l_position = CP::Vec2.new(@shape.body.p.x % WIDTH, @shape.body.p.y % HEIGHT)
|
89
|
+
@shape.body.p = l_position
|
90
|
+
end
|
91
|
+
|
92
|
+
def draw
|
93
|
+
@image.draw_rot(@shape.body.p.x, @shape.body.p.y, ZOrder::Player, @shape.body.a.radians_to_gosu)
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# Convenience method for converting from radians to a Vec2 vector.
|
99
|
+
def radians_to_vec2(radians)
|
100
|
+
CP::Vec2.new(Math::cos(radians), Math::sin(radians))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# See how simple our Star is?
|
105
|
+
# Of course... it just sits around and looks good...
|
106
|
+
class Star
|
107
|
+
attr_reader :shape
|
108
|
+
|
109
|
+
def initialize(animation, shape)
|
110
|
+
@animation = animation
|
111
|
+
@color = Gosu::Color.new(0xff_000000)
|
112
|
+
@color.red = rand(255 - 40) + 40
|
113
|
+
@color.green = rand(255 - 40) + 40
|
114
|
+
@color.blue = rand(255 - 40) + 40
|
115
|
+
@shape = shape
|
116
|
+
@shape.body.p = CP::Vec2.new(rand * WIDTH, rand * HEIGHT) # position
|
117
|
+
@shape.body.v = CP::Vec2.new(0.0, 0.0) # velocity
|
118
|
+
@shape.body.a = (3*Math::PI/2.0) # angle in radians; faces towards top of screen
|
119
|
+
end
|
120
|
+
|
121
|
+
def draw
|
122
|
+
img = @animation[Gosu::milliseconds / 100 % @animation.size];
|
123
|
+
img.draw(@shape.body.p.x - img.width / 2.0, @shape.body.p.y - img.height / 2.0, ZOrder::Stars, 1, 1, @color, :add)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# The Gosu::Window is always the "environment" of our game
|
128
|
+
# It also provides the pulse of our game
|
129
|
+
class ChipmunkIntegration < (Example rescue Gosu::Window)
|
130
|
+
def initialize
|
131
|
+
super WIDTH, HEIGHT
|
132
|
+
|
133
|
+
self.caption = "Gosu & Chipmunk Integration Demo"
|
134
|
+
|
135
|
+
@background_image = Gosu::Image.new("media/space.png", :tileable => true)
|
136
|
+
|
137
|
+
# Put the beep here, as it is the environment now that determines collision
|
138
|
+
@beep = Gosu::Sample.new("media/beep.wav")
|
139
|
+
|
140
|
+
# Put the score here, as it is the environment that tracks this now
|
141
|
+
@score = 0
|
142
|
+
@font = Gosu::Font.new(20)
|
143
|
+
|
144
|
+
# Time increment over which to apply a physics "step" ("delta t")
|
145
|
+
@dt = (1.0/60.0)
|
146
|
+
|
147
|
+
# Create our Space and set its damping
|
148
|
+
# A damping of 0.8 causes the ship bleed off its force and torque over time
|
149
|
+
# This is not realistic behavior in a vacuum of space, but it gives the game
|
150
|
+
# the feel I'd like in this situation
|
151
|
+
@space = CP::Space.new
|
152
|
+
@space.damping = 0.8
|
153
|
+
|
154
|
+
# Create the Body for the Player
|
155
|
+
body = CP::Body.new(10.0, 150.0)
|
156
|
+
|
157
|
+
# In order to create a shape, we must first define it
|
158
|
+
# Chipmunk defines 3 types of Shapes: Segments, Circles and Polys
|
159
|
+
# We'll use s simple, 4 sided Poly for our Player (ship)
|
160
|
+
# You need to define the vectors so that the "top" of the Shape is towards 0 radians (the right)
|
161
|
+
shape_array = [CP::Vec2.new(-25.0, -25.0), CP::Vec2.new(-25.0, 25.0), CP::Vec2.new(25.0, 1.0), CP::Vec2.new(25.0, -1.0)]
|
162
|
+
shape = CP::Shape::Poly.new(body, shape_array, CP::Vec2.new(0,0))
|
163
|
+
|
164
|
+
# The collision_type of a shape allows us to set up special collision behavior
|
165
|
+
# based on these types. The actual value for the collision_type is arbitrary
|
166
|
+
# and, as long as it is consistent, will work for us; of course, it helps to have it make sense
|
167
|
+
shape.collision_type = :ship
|
168
|
+
|
169
|
+
@space.add_body(body)
|
170
|
+
@space.add_shape(shape)
|
171
|
+
|
172
|
+
@player = Player.new(shape)
|
173
|
+
@player.warp(CP::Vec2.new(320, 240)) # move to the center of the window
|
174
|
+
|
175
|
+
@star_anim = Gosu::Image.load_tiles("media/star.png", 25, 25)
|
176
|
+
@stars = Array.new
|
177
|
+
|
178
|
+
# Here we define what is supposed to happen when a Player (ship) collides with a Star
|
179
|
+
# I create a @remove_shapes array because we cannot remove either Shapes or Bodies
|
180
|
+
# from Space within a collision closure, rather, we have to wait till the closure
|
181
|
+
# is through executing, then we can remove the Shapes and Bodies
|
182
|
+
# In this case, the Shapes and the Bodies they own are removed in the Gosu::Window.update phase
|
183
|
+
# by iterating over the @remove_shapes array
|
184
|
+
# Also note that both Shapes involved in the collision are passed into the closure
|
185
|
+
# in the same order that their collision_types are defined in the add_collision_func call
|
186
|
+
@remove_shapes = []
|
187
|
+
@space.add_collision_func(:ship, :star) do |ship_shape, star_shape|
|
188
|
+
@score += 10
|
189
|
+
@beep.play
|
190
|
+
@remove_shapes << star_shape
|
191
|
+
end
|
192
|
+
|
193
|
+
# Here we tell Space that we don't want one star bumping into another
|
194
|
+
# The reason we need to do this is because when the Player hits a Star,
|
195
|
+
# the Star will travel until it is removed in the update cycle below
|
196
|
+
# which means it may collide and therefore push other Stars
|
197
|
+
# To see the effect, remove this line and play the game, every once in a while
|
198
|
+
# you'll see a Star moving
|
199
|
+
@space.add_collision_func(:star, :star, &nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
def update
|
203
|
+
# Step the physics environment SUBSTEPS times each update
|
204
|
+
SUBSTEPS.times do
|
205
|
+
# This iterator makes an assumption of one Shape per Star making it safe to remove
|
206
|
+
# each Shape's Body as it comes up
|
207
|
+
# If our Stars had multiple Shapes, as would be required if we were to meticulously
|
208
|
+
# define their true boundaries, we couldn't do this as we would remove the Body
|
209
|
+
# multiple times
|
210
|
+
# We would probably solve this by creating a separate @remove_bodies array to remove the Bodies
|
211
|
+
# of the Stars that were gathered by the Player
|
212
|
+
@remove_shapes.each do |shape|
|
213
|
+
@stars.delete_if { |star| star.shape == shape }
|
214
|
+
@space.remove_body(shape.body)
|
215
|
+
@space.remove_shape(shape)
|
216
|
+
end
|
217
|
+
@remove_shapes.clear # clear out the shapes for next pass
|
218
|
+
|
219
|
+
# When a force or torque is set on a Body, it is cumulative
|
220
|
+
# This means that the force you applied last SUBSTEP will compound with the
|
221
|
+
# force applied this SUBSTEP; which is probably not the behavior you want
|
222
|
+
# We reset the forces on the Player each SUBSTEP for this reason
|
223
|
+
@player.shape.body.reset_forces
|
224
|
+
|
225
|
+
# Wrap around the screen to the other side
|
226
|
+
@player.validate_position
|
227
|
+
|
228
|
+
# Check keyboard
|
229
|
+
if Gosu::button_down? Gosu::KbLeft
|
230
|
+
@player.turn_left
|
231
|
+
end
|
232
|
+
if Gosu::button_down? Gosu::KbRight
|
233
|
+
@player.turn_right
|
234
|
+
end
|
235
|
+
|
236
|
+
if Gosu::button_down? Gosu::KbUp
|
237
|
+
if Gosu::button_down?(Gosu::KbRightShift) or Gosu::button_down?(Gosu::KbLeftShift)
|
238
|
+
@player.boost
|
239
|
+
else
|
240
|
+
@player.accelerate
|
241
|
+
end
|
242
|
+
elsif Gosu::button_down? Gosu::KbDown
|
243
|
+
@player.reverse
|
244
|
+
end
|
245
|
+
|
246
|
+
# Perform the step over @dt period of time
|
247
|
+
# For best performance @dt should remain consistent for the game
|
248
|
+
@space.step(@dt)
|
249
|
+
end
|
250
|
+
|
251
|
+
# Each update (not SUBSTEP) we see if we need to add more Stars
|
252
|
+
if rand(100) < 4 and @stars.size < 25 then
|
253
|
+
body = CP::Body.new(0.0001, 0.0001)
|
254
|
+
shape = CP::Shape::Circle.new(body, 25/2, CP::Vec2.new(0.0, 0.0))
|
255
|
+
shape.collision_type = :star
|
256
|
+
|
257
|
+
@space.add_body(body)
|
258
|
+
@space.add_shape(shape)
|
259
|
+
|
260
|
+
@stars.push(Star.new(@star_anim, shape))
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def draw
|
265
|
+
@background_image.draw(0, 0, ZOrder::Background)
|
266
|
+
@player.draw
|
267
|
+
@stars.each { |star| star.draw }
|
268
|
+
@font.draw("Score: #{@score}", 10, 10, ZOrder::UI, 1.0, 1.0, 0xff_ffff00)
|
269
|
+
end
|
270
|
+
|
271
|
+
def button_down(id)
|
272
|
+
if id == Gosu::KbEscape
|
273
|
+
close
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
ChipmunkIntegration.new.show if __FILE__ == $0
|