zyps 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/bin/zyps +266 -16
  2. data/bin/zyps-175 +514 -0
  3. data/bin/zyps_demo +12 -2
  4. data/lib/zyps/views/canvas/wx.rb +12 -26
  5. metadata +52 -58
data/bin/zyps CHANGED
@@ -22,6 +22,7 @@ gems_loaded = false
22
22
  begin
23
23
  require 'logger'
24
24
  require 'wx'
25
+ require 'yaml'
25
26
  require 'zyps'
26
27
  require 'zyps/actions'
27
28
  require 'zyps/conditions'
@@ -79,7 +80,7 @@ class Application < Wx::App
79
80
  :uri => nil,
80
81
  }.merge(options)
81
82
  @log.debug "options: #{@options.inspect}"
82
-
83
+
83
84
  end
84
85
 
85
86
 
@@ -98,19 +99,29 @@ class Application < Wx::App
98
99
  frame.evt_close {on_exit}
99
100
 
100
101
  @log.debug "Create a view and canvas."
101
- drawing_area = Wx::Window.new(frame)
102
- drawing_area.min_size = [0, 0]
103
- frame.sizer.add(drawing_area, 1, Wx::GROW)
102
+ @drawing_area = Wx::Window.new(frame)
103
+ @drawing_area.min_size = [0, 0]
104
+ frame.sizer.add(@drawing_area, 1, Wx::GROW)
104
105
  @view = TrailsView.new(
105
106
  :width => @options[:width],
106
107
  :height => @options[:height],
107
- :canvas => WxCanvas.new(drawing_area)
108
+ :canvas => WxCanvas.new
108
109
  )
109
110
  @log.debug "Set up mouse click/drag handlers for canvas."
110
111
  @press_location = nil
111
- drawing_area.evt_left_down {|event| on_mouse_press(event)}
112
- drawing_area.evt_left_up {|event| on_mouse_release(event)}
113
- @log.debug "Resize view and enclosure when canvas changes size."
112
+ @drawing_area.evt_left_down {|event| on_mouse_press(event)}
113
+ @drawing_area.evt_left_up {|event| on_mouse_release(event)}
114
+ #Whenever the drawing area needs updating, copy the view's buffer over.
115
+ @drawing_area.evt_paint {|event| @drawing_area.paint {|dc| render(dc)}}
116
+
117
+ @log.debug "Create menus."
118
+ @menus = MenuSet.new(frame)
119
+ evt_menu(@menus.open_menu) {|event| on_open_menu(event)}
120
+ evt_menu(@menus.save_menu) {|event| on_save_menu(event)}
121
+ evt_menu(@menus.save_as_menu) {|event| on_save_as_menu(event)}
122
+ evt_menu(@menus.exit_menu) {|event| on_exit}
123
+ evt_menu(@menus.using_zyps_menu) {|event| on_using_zyps_menu(event)}
124
+ evt_menu(@menus.about_menu) {|event| on_about_menu(event)}
114
125
 
115
126
  @log.debug "Create an interface."
116
127
  @controls = ControlPanel.new(frame)
@@ -134,8 +145,8 @@ class Application < Wx::App
134
145
  @enclosure = Enclosure.new(
135
146
  :left => 0,
136
147
  :bottom => 0,
137
- :top => drawing_area.size.height,
138
- :right => drawing_area.size.width
148
+ :top => @drawing_area.size.height,
149
+ :right => @drawing_area.size.width
139
150
  )
140
151
  @environment.environmental_factors << @enclosure
141
152
  end
@@ -163,14 +174,19 @@ class Application < Wx::App
163
174
  #Update environment and view.
164
175
  def on_timer
165
176
  #Resize playfield if window has been resized.
166
- if @view.height != @view.canvas.drawing_area.size.height || @view.width != @view.canvas.drawing_area.size.width
167
- @view.height = @view.canvas.drawing_area.size.height
168
- @view.width = @view.canvas.drawing_area.size.width
169
- @enclosure.top = @view.canvas.drawing_area.size.height
170
- @enclosure.right = @view.canvas.drawing_area.size.width
177
+ if @view.height != @drawing_area.size.height || @view.width != @drawing_area.size.width
178
+ @view.height = @drawing_area.size.height
179
+ @view.width = @drawing_area.size.width
180
+ @enclosure.top = @drawing_area.size.height
181
+ @enclosure.right = @drawing_area.size.width
171
182
  end
172
183
  #Update environment.
173
184
  @environment.interact
185
+ #Update viewing area.
186
+ @drawing_area.paint do |dc|
187
+ #Copy from View to drawing area.
188
+ render(dc)
189
+ end
174
190
  #Keeps dead objects from accumulating and causing hiccups later.
175
191
  GC.start
176
192
  end
@@ -217,6 +233,150 @@ class Application < Wx::App
217
233
  end
218
234
 
219
235
 
236
+ #Takes a Wx::DC to copy the view to.
237
+ def render(dc)
238
+ #Copy the buffer to the device context.
239
+ dc.draw_bitmap(@view.canvas.buffer, 0, 0, false)
240
+ end
241
+
242
+
243
+ #Let user choose an environment save file.
244
+ def on_open_menu(event)
245
+ @log.debug "Set up a dialog to open a .zyp file. Retain the directory the user moves to."
246
+ dialog = Wx::FileDialog.new(
247
+ @frame,
248
+ :wildcard => "*.zyp",
249
+ :style => Wx::FD_OPEN | Wx::FD_CHANGE_DIR
250
+ )
251
+ @log.debug "Act only if user presses OK."
252
+ if dialog.show_modal == Wx::ID_OK
253
+ begin
254
+ @environment.objects = YAML.load_file(dialog.path)
255
+ @object_file = dialog.path
256
+ rescue Exception => exception
257
+ show_alert(exception.message)
258
+ end
259
+ end
260
+ end
261
+
262
+
263
+ #Save environment to file, or choose a file if none is selected.
264
+ def on_save_menu(event)
265
+ if @object_file
266
+ begin
267
+ save_objects(@object_file)
268
+ rescue Exception => exception
269
+ show_alert(exception.message)
270
+ on_save_as_menu(event)
271
+ end
272
+ else
273
+ on_save_as_menu(event)
274
+ end
275
+ end
276
+
277
+
278
+ #Let user provide a file name to save the environment to.
279
+ def on_save_as_menu(event)
280
+ @log.debug "Set up a dialog to save a .zyp file."
281
+ @log.debug "Retain the directory the user moves to."
282
+ @log.debug "Prompt before overwriting a file."
283
+ dialog = Wx::FileDialog.new(
284
+ @frame,
285
+ :wildcard => "*.zyp",
286
+ :style => Wx::FD_SAVE | Wx::FD_CHANGE_DIR | Wx::FD_OVERWRITE_PROMPT
287
+ )
288
+ @log.debug "Act only if user presses OK."
289
+ if dialog.show_modal == Wx::ID_OK
290
+ begin
291
+ save_objects(dialog.path)
292
+ @object_file = dialog.path
293
+ rescue Exception => exception
294
+ show_alert(exception.message)
295
+ end
296
+ end
297
+ end
298
+
299
+
300
+ #Display program info.
301
+ def on_about_menu(event)
302
+ show_message(<<EOD)
303
+ Zyps
304
+
305
+ http://jay.mcgavren.com/zyps
306
+
307
+ Copyright Jay McGavren, jay@mcgavren.com
308
+
309
+ Zyps is free software; you can redistribute it and/or modify
310
+ it under the terms of the GNU Lesser General Public License as published by
311
+ the Free Software Foundation; either version 3 of the License, or
312
+ (at your option) any later version.
313
+
314
+ This program is distributed in the hope that it will be useful,
315
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
316
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
317
+ GNU General Public License for more details.
318
+
319
+ You should have received a copy of the GNU Lesser General Public License
320
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
321
+ EOD
322
+ end
323
+
324
+
325
+ #Display program info.
326
+ def on_using_zyps_menu(event)
327
+ show_message(<<EOD)
328
+ Creating Creatures
329
+ Use the check boxes at the right to select the actions you want new creatures to perform. Then click inside the playfield at the left to create a creature.
330
+ You can "launch" a creature by clicking and dragging the mouse in the playfield, then releasing. The new creature will travel in the direction you drag the mouse cursor.
331
+
332
+ Controlling the View
333
+ The "Length" slider controls the length of the tail behind each creature.
334
+ Check the "Trails" checkbox to have creatures leave a winding trail behind them as they travel. Uncheck it to make all trails disappear.
335
+
336
+ Controlling the Environment
337
+ Click the "Clear" button to clear all creatures from the environment (any trails will still be left behind).
338
+
339
+ Saving and Loading
340
+ Use the "Save As..." and "Save" menus under the File menu to save the environment to a ".zyp" file. When you later reopen the file via the "Load" menu, all your creatures will be brought back.
341
+ EOD
342
+ end
343
+
344
+
345
+ #Save environment objects to the given path.
346
+ def save_objects(path)
347
+ File.open(path, "w") {|file| YAML.dump(@environment.objects, file)}
348
+ end
349
+
350
+
351
+ #Display an alert to the user.
352
+ def show_alert(message)
353
+ Wx::MessageDialog.new(
354
+ @frame,
355
+ :message => message,
356
+ :caption => "Alert",
357
+ :style => Wx::OK
358
+ ).show_modal
359
+ end
360
+
361
+
362
+ #Display a message to the user.
363
+ def show_message(message)
364
+ dialog = Wx::Dialog.new(@frame)
365
+ dialog.sizer = Wx::BoxSizer.new(Wx::VERTICAL)
366
+ text = Wx::TextCtrl.new(
367
+ dialog,
368
+ :style => Wx::TE_READONLY | Wx::TE_MULTILINE,
369
+ :value => message
370
+ )
371
+ dialog.sizer.add(text, 1, Wx::EXPAND)
372
+ ok_button = Wx::Button.new(dialog, Wx::ID_OK, "OK")
373
+ dialog.sizer.add(ok_button, 0, Wx::ALIGN_CENTER_HORIZONTAL)
374
+ ok_button.set_default
375
+ ok_button.set_focus
376
+ dialog.show_modal
377
+ end
378
+
379
+
220
380
  end
221
381
 
222
382
 
@@ -232,6 +392,93 @@ end
232
392
 
233
393
 
234
394
 
395
+ #Application menu system.
396
+ class MenuSet
397
+
398
+
399
+ attr_accessor :open_menu
400
+ attr_accessor :save_menu
401
+ attr_accessor :save_as_menu
402
+ attr_accessor :exit_menu
403
+ attr_accessor :using_zyps_menu
404
+ attr_accessor :about_menu
405
+
406
+
407
+ #Takes the frame that will be its parent.
408
+ def initialize(parent)
409
+
410
+ menu_bar = Wx::MenuBar.new
411
+
412
+ file_menu = Wx::Menu.new
413
+ @open_menu = add_item(
414
+ file_menu,
415
+ :label => "&Open...\tCtrl+O",
416
+ :id => Wx::ID_OPEN
417
+ )
418
+ @save_menu = add_item(
419
+ file_menu,
420
+ :label => "&Save\tCtrl+S",
421
+ :id => Wx::ID_SAVE
422
+ )
423
+ @save_as_menu = add_item(
424
+ file_menu,
425
+ :label => "Save &As...\tCtrl+Shift+S",
426
+ :id => Wx::ID_SAVEAS
427
+ )
428
+ file_menu.append_separator
429
+ @exit_menu = add_item(
430
+ file_menu,
431
+ :label => "E&xit",
432
+ :id => Wx::ID_EXIT
433
+ )
434
+ menu_bar.append(file_menu, "&File")
435
+
436
+ help_menu = Wx::Menu.new
437
+ @using_zyps_menu = add_item(
438
+ help_menu,
439
+ :label => "&Using Zyps",
440
+ :id => Wx::ID_HELP_CONTENTS
441
+ )
442
+ help_menu.append_separator
443
+ @about_menu = add_item(
444
+ help_menu,
445
+ :label => "&About",
446
+ :id => Wx::ID_ABOUT
447
+ )
448
+ menu_bar.append(help_menu, "&Help")
449
+
450
+ parent.set_menu_bar(menu_bar)
451
+
452
+ end
453
+
454
+
455
+ private
456
+
457
+
458
+ def add_item(parent, options = {})
459
+ options = {
460
+ :id => Wx::ID_ANY,
461
+ :label => "",
462
+ :help_string => "",
463
+ :kind => Wx::ITEM_NORMAL,
464
+ :sub_menu => nil,
465
+ }.merge(options)
466
+ item = Wx::MenuItem.new(
467
+ parent,
468
+ options[:id],
469
+ options[:label],
470
+ options[:help_string],
471
+ options[:kind],
472
+ options[:sub_menu]
473
+ )
474
+ parent.append_item(item)
475
+ item
476
+ end
477
+
478
+ end
479
+
480
+
481
+
235
482
  #Controls for manipulating the environment.
236
483
  class ControlPanel < Wx::Panel
237
484
 
@@ -248,6 +495,7 @@ class ControlPanel < Wx::Panel
248
495
  attr_accessor :clear_button
249
496
 
250
497
 
498
+ #Takes the frame/control that will be its parent.
251
499
  def initialize(parent)
252
500
 
253
501
  super(parent)
@@ -304,7 +552,9 @@ class ControlPanel < Wx::Panel
304
552
 
305
553
  #Create a button with the given options, and add it to the given container.
306
554
  def add_button(parent, options = {})
307
- add_control(Wx::Button.new(parent, options))
555
+ control = Wx::Button.new(parent, options)
556
+ control.parent.sizer.add(control, 0, Wx::ALIGN_CENTER_HORIZONTAL|Wx::ALL, 3)
557
+ control
308
558
  end
309
559
 
310
560
  #Create the given label, and add it to the given container.
data/bin/zyps-175 ADDED
@@ -0,0 +1,514 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ # Copyright 2007 Jay McGavren, jay@mcgavren.com.
4
+ #
5
+ # This file is part of Zyps.
6
+ #
7
+ # Zyps is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published by
9
+ # the Free Software Foundation; either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+
21
+ gems_loaded = false
22
+ begin
23
+ require 'gtk2'
24
+ require 'optparse'
25
+ require 'zyps'
26
+ require 'zyps/actions'
27
+ require 'zyps/conditions'
28
+ require 'zyps/environmental_factors'
29
+ require 'zyps/remote'
30
+ require 'zyps/views/trails'
31
+ require 'zyps/views/canvas/gtk2'
32
+ rescue LoadError
33
+ if gems_loaded == false
34
+ require 'rubygems'
35
+ gems_loaded = true
36
+ retry
37
+ else
38
+ raise
39
+ end
40
+ end
41
+
42
+
43
+ include Zyps
44
+
45
+
46
+ DEFAULT_VIEW_WIDTH = 800
47
+ DEFAULT_VIEW_HEIGHT = 600
48
+ DEFAULT_MAX_SPEED = 200
49
+ DEFAULT_MAX_POPULATION = 100
50
+ DEFAULT_FPS = 60
51
+
52
+
53
+ class Application
54
+
55
+ #Port to open service on.
56
+ attr_accessor :uri
57
+ #Maximum allowed number of objects.
58
+ attr_accessor :max_population
59
+ #View dimensions.
60
+ attr_accessor :view_width, :view_height
61
+
62
+ #Create app window, game environment, and view.
63
+ #Set up default values.
64
+ #Takes a hash with these keys and defaults:
65
+ # :uri => nil,
66
+ # :view_width => DEFAULT_VIEW_WIDTH,
67
+ # :view_height => DEFAULT_VIEW_HEIGHT,
68
+ # :fps => DEFAULT_FPS,
69
+ # :max_population => DEFAULT_MAX_POPULATION,
70
+ # :max_speed => DEFAULT_MAX_SPEED,
71
+ # :enclosure => true
72
+ def initialize(options = {})
73
+
74
+ options = {
75
+ :uri => nil,
76
+ :view_width => DEFAULT_VIEW_WIDTH,
77
+ :view_height => DEFAULT_VIEW_HEIGHT,
78
+ :fps => DEFAULT_FPS,
79
+ :max_population => DEFAULT_MAX_POPULATION,
80
+ :max_speed => DEFAULT_MAX_SPEED,
81
+ :enclosure => true
82
+ }.merge(options)
83
+
84
+ @uri, @view_width, @view_height, @fps, @max_population, @max_speed, @enclosure =
85
+ options[:uri], options[:view_width], options[:view_height], options[:fps], options[:max_population], options[:max_speed], options[:enclosure]
86
+
87
+ end
88
+
89
+
90
+ def main
91
+
92
+ #Create a window, and set GTK up to quit when it is closed.
93
+ window = Gtk::Window.new
94
+ window.resizable = false
95
+ window.signal_connect("delete_event") {false}
96
+ window.signal_connect("destroy") {Gtk.main_quit}
97
+
98
+ #Create environment.
99
+ @environment = Environment.new
100
+
101
+ #Set up controls.
102
+ #Also initializes @view.
103
+ window.add(
104
+ create_controls(
105
+ :view_width => @view_width,
106
+ :view_height => @view_height,
107
+ :spacing => 2
108
+ )
109
+ )
110
+
111
+ #Show all widgets.
112
+ window.show_all
113
+
114
+ #Point view at environment.
115
+ @environment.add_observer(@view)
116
+
117
+ #Keep objects on screen.
118
+ if @enclosure
119
+ enclosure = Enclosure.new(
120
+ :left => 0,
121
+ :bottom => 0,
122
+ :top => @view_height,
123
+ :right => @view_width
124
+ )
125
+ @environment.environmental_factors << enclosure
126
+ end
127
+
128
+ #Keep all objects under a certain speed.
129
+ @environment.environmental_factors << SpeedLimit.new(@max_speed) if @max_speed
130
+
131
+ #Limit population.
132
+ @environment.environmental_factors << PopulationLimit.new(@max_population) if @max_population
133
+
134
+ #Set up a creature generator.
135
+ @generator = CreatureGenerator.new(@environment)
136
+
137
+ #Create thread to update environment.
138
+ thread = Thread.new do
139
+
140
+ begin
141
+
142
+ drawing_clock = Clock.new
143
+ time_per_frame = 1.0 / @fps
144
+
145
+ loop do
146
+
147
+ @environment.interact
148
+
149
+ #Determine how much time is left in this frame.
150
+ time_left_in_frame = (time_per_frame) - drawing_clock.elapsed_time
151
+ #Sleep for the remaining time.
152
+ if time_left_in_frame > 0
153
+ sleep time_left_in_frame
154
+ #Skip a frame if things are going too slow.
155
+ else
156
+ sleep time_per_frame
157
+ end
158
+
159
+ end
160
+
161
+ rescue Exception => exception
162
+ puts exception, exception.backtrace
163
+ end
164
+
165
+ end
166
+
167
+
168
+ #Start a network service.
169
+ if @uri
170
+ server = EnvironmentServer.new(@environment, @uri)
171
+ server.start
172
+ #Disable file system access.
173
+ $SAFE = 2
174
+ end
175
+
176
+
177
+ #Activate the GUI.
178
+ Gtk.main
179
+
180
+ end
181
+
182
+
183
+ #Create a view and controls.
184
+ #Takes a hash with these defaults:
185
+ # :view_width => @view_width
186
+ # :view_height => @view_height
187
+ # :homogeneous => false
188
+ # :spacing => 0
189
+ # :expand => false
190
+ # :fill => false
191
+ # :padding => 0
192
+ def create_controls(options = {})
193
+
194
+ options = {
195
+ :view_width => @view_width,
196
+ :view_height => @view_height,
197
+ :homogeneous => false,
198
+ :spacing => 0,
199
+ :expand => false,
200
+ :fill => false,
201
+ :padding => 0,
202
+ }.merge(options)
203
+
204
+ #Create a container for the view and controls.
205
+ interface = Gtk::HBox.new(options[:homogeneous], options[:spacing])
206
+
207
+ #Add view to interface.
208
+ @view = TrailsView.new(
209
+ :width => options[:view_width],
210
+ :height => options[:view_height],
211
+ :trail_length => 5
212
+ )
213
+ drawing_window = Gtk::DrawingArea.new
214
+ @view.canvas = GTK2Canvas.new(drawing_window)
215
+ interface.pack_start(drawing_window, options[:expand], options[:fill], options[:padding])
216
+
217
+ #When mouse button pressed, record location for use in release event handler.
218
+ drawing_window.add_events(Gdk::Event::BUTTON_PRESS_MASK)
219
+ drawing_window.signal_connect("button-press-event") do |canvas, event|
220
+ @press_location = Location.new(event.x, event.y)
221
+ end
222
+
223
+ #Create a creature on button release.
224
+ drawing_window.add_events(Gdk::Event::BUTTON_RELEASE_MASK)
225
+ drawing_window.signal_connect("button-release-event") do |canvas, event|
226
+ #Ensure the mouse was pressed within the canvas.
227
+ if @press_location
228
+ #Create creature at release location.
229
+ @release_location = Location.new(event.x, event.y)
230
+ @generator.create_creature(
231
+ :x => event.x,
232
+ :y => event.y,
233
+ :speed => Utility.find_distance(@press_location, @release_location) * 2, #Use distance dragged as speed.
234
+ :pitch => Utility.find_angle(@press_location, @release_location), #Move in direction of drag.
235
+ :turn => @turn_flag.active?,
236
+ :approach => @approach_flag.active?,
237
+ :flee => @flee_flag.active?,
238
+ :push => @push_flag.active?,
239
+ :pull => @pull_flag.active?,
240
+ :breed => @breed_flag.active?,
241
+ :eat => @eat_flag.active?
242
+ )
243
+ #Nullify press location, as it is no longer applicable.
244
+ @press_location = nil
245
+ end
246
+ end
247
+
248
+ #Create a VBox for all controls.
249
+ control_panel = Gtk::VBox.new(options[:homogeneous], options[:spacing])
250
+
251
+ #Create a group for the actions.
252
+ action_controls = Gtk::VBox.new(options[:homogeneous], options[:spacing])
253
+ action_controls.pack_start(Gtk::Label.new("Actions"), options[:expand], options[:fill], options[:padding])
254
+ @turn_flag = Gtk::CheckButton.new("Turn")
255
+ action_controls.pack_start(@turn_flag, options[:expand], options[:fill], options[:padding])
256
+ @approach_flag = Gtk::CheckButton.new("Chase")
257
+ action_controls.pack_start(@approach_flag, options[:expand], options[:fill], options[:padding])
258
+ @flee_flag = Gtk::CheckButton.new("Flee")
259
+ action_controls.pack_start(@flee_flag, options[:expand], options[:fill], options[:padding])
260
+ @push_flag = Gtk::CheckButton.new("Push")
261
+ action_controls.pack_start(@push_flag, options[:expand], options[:fill], options[:padding])
262
+ @pull_flag = Gtk::CheckButton.new("Pull")
263
+ action_controls.pack_start(@pull_flag, options[:expand], options[:fill], options[:padding])
264
+ @breed_flag = Gtk::CheckButton.new("Breed")
265
+ action_controls.pack_start(@breed_flag, options[:expand], options[:fill], options[:padding])
266
+ @eat_flag = Gtk::CheckButton.new("Eat")
267
+ action_controls.pack_start(@eat_flag, options[:expand], options[:fill], options[:padding])
268
+ #Add the action controls to the panel.
269
+ control_panel.pack_start(action_controls, options[:expand], options[:fill], options[:padding])
270
+
271
+ #Create a group for environment controls.
272
+ environment_controls = Gtk::VBox.new(options[:homogeneous], options[:spacing])
273
+ environment_controls.pack_start(Gtk::Label.new("Length"), options[:expand], options[:fill], options[:padding])
274
+ @trail_length_slider = Gtk::HScale.new(2, 100, 1)
275
+ @trail_length_slider.signal_connect("value-changed") {
276
+ @view.trail_length = @trail_length_slider.value
277
+ }
278
+ @trail_length_slider.value = 5
279
+ environment_controls.pack_start(@trail_length_slider, options[:expand], options[:fill], options[:padding])
280
+ @trails_flag = Gtk::CheckButton.new("Trails")
281
+ @trails_flag.signal_connect("toggled") {
282
+ @view.erase_flag = ! @trails_flag.active?
283
+ }
284
+ @trails_flag.active = false
285
+ environment_controls.pack_start(@trails_flag, options[:expand], options[:fill], options[:padding])
286
+ @clear_button = Gtk::Button.new("Clear")
287
+ @clear_button.signal_connect("clicked") {
288
+ @environment.objects = []
289
+ }
290
+ environment_controls.pack_start(@clear_button, options[:expand], options[:fill], options[:padding])
291
+ #Add the environment controls to the BOTTOM of the panel.
292
+ control_panel.pack_end(environment_controls, options[:expand], options[:fill], options[:padding])
293
+
294
+ #Add the control panel to the interface.
295
+ interface.pack_start(control_panel, options[:expand], options[:fill], options[:padding])
296
+
297
+ interface
298
+
299
+ end
300
+
301
+
302
+ #Set attributes according to command-line arguments.
303
+ def process_options(arguments)
304
+
305
+ #Set up option parser.
306
+ options = OptionParser.new
307
+
308
+ #Define valid options.
309
+ options.on("-h", "--help", TrueClass, "Display program help.") {
310
+ puts options.help
311
+ exit
312
+ }
313
+ options.on(
314
+ "-m",
315
+ "--max-population [number]",
316
+ Integer,
317
+ "The maximum number of allowed game objects. #{DEFAULT_MAX_POPULATION} by default."
318
+ ) {|value| @max_population = value}
319
+ options.on(
320
+ "-s",
321
+ "--max-speed [number]",
322
+ Integer,
323
+ "The fastest an object can go. #{DEFAULT_MAX_SPEED ? DEFAULT_MAX_SPEED : 'No limit'} by default."
324
+ ) {|value| @max_speed = value}
325
+ options.on(
326
+ "-n",
327
+ "--no-enclosure",
328
+ "Disables the barrier that normally keeps objects on the screen."
329
+ ) {|value| @enclosure = false}
330
+ options.on(
331
+ "-u",
332
+ "--uri [uri]",
333
+ String,
334
+ "URI to serve the environment on via dRuby. If not specified, no server will be started."
335
+ ) {|value| @uri = value}
336
+ options.on(
337
+ "-f",
338
+ "--fps [frames]",
339
+ Integer,
340
+ "Number of frames to draw per second. #{DEFAULT_FPS} by default."
341
+ ) {|value| @fps = value}
342
+ options.on(
343
+ "--view-width [pixels]",
344
+ Integer,
345
+ "Window width. #{DEFAULT_VIEW_WIDTH} by default."
346
+ ) {|value| @view_width = value}
347
+ options.on(
348
+ "--view-height [pixels]",
349
+ Integer,
350
+ "Window height. #{DEFAULT_VIEW_HEIGHT} by default."
351
+ ) {|value| @view_height = value}
352
+
353
+ #Parse the options, printing usage if parsing fails.
354
+ options.parse(arguments) rescue puts "#{$!}\nType '#{$0} --help' for valid options."
355
+
356
+ end
357
+
358
+
359
+ end
360
+
361
+
362
+
363
+ class CreatureGenerator
364
+
365
+ #Environment creatures will be added to.
366
+ attr_accessor :environment
367
+ #Default required proximity for actions.
368
+ attr_accessor :default_proximity
369
+ #Rate of new TurnActions.
370
+ attr_accessor :turn_rate
371
+ #Acceleration rate of new ApproachActions.
372
+ attr_accessor :approach_rate
373
+ #Acceleration rate of new FleeActions.
374
+ attr_accessor :flee_rate
375
+ #Strength of new PullActions.
376
+ attr_accessor :pull_strength
377
+ #Strength of new PushActions.
378
+ attr_accessor :push_strength
379
+
380
+ def initialize(environment)
381
+
382
+ @environment = environment
383
+
384
+ #Set up defaults for attributes.
385
+ @default_proximity = 200
386
+ @approach_rate = 200
387
+ @flee_rate = @approach_rate
388
+ @push_strength = @approach_rate * 2
389
+ @pull_strength = @push_strength * 0.75
390
+ @turn_rate = @approach_rate * 1.1
391
+ @turn_angle = 90
392
+ @breed_rate = 10
393
+
394
+ end
395
+
396
+
397
+ #Create a creature and add it to the environment.
398
+ def create_creature(options = {})
399
+
400
+ options = {
401
+ :x => 0,
402
+ :y => 0,
403
+ :speed => 1,
404
+ :pitch => 0,
405
+ :action_proximity => @default_proximity,
406
+ :turn => false,
407
+ :approach => false,
408
+ :flee => false,
409
+ :push => false,
410
+ :pull => false,
411
+ :breed => false,
412
+ :eat => false,
413
+ }.merge(options)
414
+
415
+ #Create a creature.
416
+ creature = Creature.new(
417
+ :location => Location.new(options[:x], options[:y]),
418
+ :vector => Vector.new(options[:speed], options[:pitch]),
419
+ :size => 5
420
+ )
421
+
422
+ #Set up actions and merge colors according to selected behaviors.
423
+ color = Color.new(0.25, 0.25, 0.25)
424
+ if options[:turn]
425
+ color.blue += 1
426
+ creature.behaviors << Behavior.new(
427
+ :actions => [TurnAction.new(@turn_rate, @turn_angle)],
428
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 2)]
429
+ )
430
+ end
431
+ if options[:approach]
432
+ color.red += 1
433
+ creature.behaviors << create_behavior(
434
+ :actions => [ApproachAction.new(@approach_rate)],
435
+ :conditions => [ProximityCondition.new(options[:action_proximity])]
436
+ )
437
+ end
438
+ if options[:flee]
439
+ color.red += 0.5; color.green += 0.5 #Yellow.
440
+ creature.behaviors << create_behavior(
441
+ :actions => [FleeAction.new(@flee_rate)],
442
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.5)]
443
+ )
444
+ end
445
+ if options[:push]
446
+ color.red += 0.5; color.blue += 0.5 #Purple.
447
+ creature.behaviors << create_behavior(
448
+ :actions => [PushAction.new(@push_strength)],
449
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.25)]
450
+ )
451
+ end
452
+ if options[:pull]
453
+ color.blue += 0.75; color.green += 0.75 #Aqua.
454
+ creature.behaviors << create_behavior(
455
+ :actions => [PullAction.new(@pull_strength)],
456
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.75)]
457
+ )
458
+ end
459
+ if options[:breed]
460
+ color.green -= 0.1 #Make a bit redder.
461
+ color.blue -= 0.1
462
+ creature.behaviors << create_behavior(
463
+ :actions => [BreedAction.new(@environment, @breed_rate)],
464
+ :conditions => [CollisionCondition.new] #The default ProximityCondition won't do.
465
+ )
466
+ end
467
+ if options[:eat]
468
+ color.green += 1
469
+ creature.behaviors << create_behavior(
470
+ :actions => [EatAction.new(@environment)],
471
+ :conditions => [
472
+ CollisionCondition.new, #The default ProximityCondition won't do.
473
+ StrengthCondition.new #The eater should be as strong or stronger than its dinner.
474
+ ]
475
+ )
476
+ end
477
+
478
+ creature.color = color
479
+
480
+ @environment.objects << creature
481
+
482
+ end
483
+
484
+
485
+ def create_behavior(options = {})
486
+
487
+ options = {
488
+ :actions => [],
489
+ :conditions => [ProximityCondition.new(@default_proximity)],
490
+ }.merge(options)
491
+
492
+ behavior = Behavior.new
493
+ behavior.actions = options[:actions]
494
+ behavior.conditions = options[:conditions]
495
+ behavior
496
+
497
+ end
498
+
499
+
500
+ end
501
+
502
+
503
+
504
+ begin
505
+ #Create a server.
506
+ application = Application.new
507
+ #Parse the command line.
508
+ application.process_options(ARGV)
509
+ #Start the server.
510
+ application.main
511
+ rescue => exception
512
+ #Print error to STDERR and exit with an abnormal status.
513
+ abort "Error: " + exception.message + exception.backtrace.join("\n")
514
+ end
data/bin/zyps_demo CHANGED
@@ -70,7 +70,6 @@ class Demo < Wx::App
70
70
  say "First, we need to create a Frame (window) to hold everything."
71
71
  frame = Wx::Frame.new(nil, :size => [WIDTH, HEIGHT], :title => "Zyps Demo")
72
72
  frame.evt_close {|event| exit}
73
- frame.show
74
73
 
75
74
  say "Zyps environments are displayed using Views."
76
75
  say "A TrailsView shows game objects with little light trails behind them."
@@ -80,7 +79,11 @@ class Demo < Wx::App
80
79
  )
81
80
  say "We also assign the View a Canvas to draw to."
82
81
  say "Since our framework is wxWidgets, we'll use a WxCanvas."
83
- view.canvas = WxCanvas.new(frame)
82
+ view.canvas = WxCanvas.new
83
+
84
+ say "We need a place to draw our View to."
85
+ say "We'll add a Wx::Window to the Frame."
86
+ window = Wx::Window.new(frame)
84
87
 
85
88
  say "The world is called an Environment in Zyps. Let's create a new one."
86
89
  @environment = Environment.new
@@ -96,14 +99,21 @@ class Demo < Wx::App
96
99
  timer_id = Wx::ID_HIGHEST + 1
97
100
  timer = Wx::Timer.new(self, timer_id)
98
101
  say "The timer will trigger the environment update."
102
+ say "Then it copies the updated view's buffer to our window."
99
103
  say "We also call the Ruby garbage collector with each update."
100
104
  say "This keeps dead objects from accumulating and causing hiccups later."
101
105
  evt_timer(timer_id) do
102
106
  @environment.interact
107
+ window.paint do |surface|
108
+ surface.draw_bitmap(view.canvas.buffer, 0, 0, false)
109
+ end
103
110
  GC.start
104
111
  end
105
112
  timer.start(milliseconds_per_frame)
106
113
 
114
+ say "Our final setup step is to show the window that will display it all."
115
+ frame.show
116
+
107
117
  end
108
118
 
109
119
 
@@ -23,34 +23,25 @@ module Zyps
23
23
 
24
24
 
25
25
  #Called by View objects for use in wxRuby applications.
26
- #Assign an instance to a View, then add the drawing_area attribute to a GUI container object.
27
- #The drawing area will be updated whenever the View is.
26
+ #Assign an instance to a View, and the drawing_area will be updated whenever the View is.
28
27
  class WxCanvas
29
28
 
30
29
 
31
- #A wxWidgets window that will be painted on.
32
- attr_reader :drawing_area
30
+ #A Wx::Bitmap that will be painted on.
31
+ attr_reader :buffer
33
32
  #Dimensions of the drawing area.
34
33
  #Control should normally be left to the owner View object.
35
34
  attr_reader :width, :height
36
35
 
37
- #Takes the wxRuby GUI object that will be its parent.
38
- def initialize (drawing_area)
39
-
40
- @drawing_area = drawing_area
36
+
37
+ def initialize
41
38
 
42
39
  #Will be resized later.
43
- @width = @drawing_area.size.width
44
- @height = @drawing_area.size.height
40
+ @width, @height = 1, 1
45
41
 
46
42
  #Set to correct size.
47
43
  resize
48
44
 
49
- #Whenever the drawing area needs updating...
50
- @drawing_area.evt_paint do |event|
51
- render
52
- end
53
-
54
45
  #Arrays of shapes that will be painted when render() is called.
55
46
  @rectangle_queue = []
56
47
  @line_queue = []
@@ -111,14 +102,15 @@ class WxCanvas
111
102
  #Draw all queued lines.
112
103
  render_lines(surface)
113
104
  end
114
- #Copy offscreen bitmap to screen.
115
- @drawing_area.paint do |dc|
116
- #Copy the buffer to the viewable window.
117
- dc.draw_bitmap(buffer, 0, 0, false)
118
- end
119
105
  end
120
106
 
121
107
 
108
+ #The Wx::Bitmap to draw to.
109
+ def buffer
110
+ @buffer ||= Wx::Bitmap.new(@width, @height)
111
+ end
112
+
113
+
122
114
  private
123
115
 
124
116
 
@@ -136,12 +128,6 @@ class WxCanvas
136
128
  def resize
137
129
  @buffer = nil #Causes buffer to reset its size next time it's accessed.
138
130
  end
139
-
140
-
141
- #The Wx::Bitmap to draw to.
142
- def buffer
143
- @buffer ||= Wx::Bitmap.new(@width, @height)
144
- end
145
131
 
146
132
 
147
133
  #Draw all queued rectangles to the given GC.
metadata CHANGED
@@ -1,88 +1,82 @@
1
1
  --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
2
4
  name: zyps
3
5
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
6
+ version: 0.7.2
7
+ date: 2008-01-29 00:00:00 -07:00
8
+ summary: A game library for Ruby
9
+ require_paths:
10
+ - lib
11
+ email: jay@mcgavren.com
12
+ homepage: http://jay.mcgavren.com/zyps/
13
+ rubyforge_project: zyps
14
+ description:
15
+ autorequire: zyps
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
5
25
  platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
6
29
  authors:
7
30
  - Jay McGavren
8
- autorequire: zyps
9
- bindir: bin
10
- cert_chain: []
11
-
12
- date: 2008-01-25 00:00:00 -07:00
13
- default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: wxruby
17
- version_requirement:
18
- version_requirements: !ruby/object:Gem::Requirement
19
- requirements:
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 1.9.2
23
- version:
24
- description:
25
- email: jay@mcgavren.com
26
- executables:
27
- - zyps
28
- extensions: []
29
-
30
- extra_rdoc_files:
31
- - README.txt
32
- - COPYING.LESSER.txt
33
- - COPYING.txt
34
31
  files:
35
32
  - COPYING.LESSER.txt
36
33
  - COPYING.txt
37
34
  - README.txt
38
- - bin/zyps_demo
39
35
  - bin/zyps
36
+ - bin/zyps-175
37
+ - bin/zyps_demo
40
38
  - lib/zyps
39
+ - lib/zyps/actions.rb
40
+ - lib/zyps/conditions.rb
41
+ - lib/zyps/environmental_factors.rb
42
+ - lib/zyps/remote.rb
41
43
  - lib/zyps/views
42
44
  - lib/zyps/views/canvas
43
45
  - lib/zyps/views/canvas/gtk2.rb
44
46
  - lib/zyps/views/canvas/wx.rb
45
47
  - lib/zyps/views/trails.rb
46
- - lib/zyps/remote.rb
47
- - lib/zyps/environmental_factors.rb
48
- - lib/zyps/actions.rb
49
- - lib/zyps/conditions.rb
50
48
  - lib/zyps.rb
49
+ - test/test_zyps.rb
51
50
  - test/zyps
51
+ - test/zyps/test_actions.rb
52
52
  - test/zyps/test_behaviors.rb
53
+ - test/zyps/test_conditions.rb
53
54
  - test/zyps/test_environmental_factors.rb
54
55
  - test/zyps/test_remote.rb
55
- - test/zyps/test_actions.rb
56
- - test/zyps/test_conditions.rb
56
+ test_files:
57
57
  - test/test_zyps.rb
58
- has_rdoc: true
59
- homepage: http://jay.mcgavren.com/zyps/
60
- post_install_message:
61
58
  rdoc_options:
62
59
  - --title
63
60
  - Zyps - A game library for Ruby
64
61
  - --main
65
62
  - README.txt
66
- require_paths:
67
- - lib
68
- required_ruby_version: !ruby/object:Gem::Requirement
69
- requirements:
70
- - - ">="
71
- - !ruby/object:Gem::Version
72
- version: "0"
73
- version:
74
- required_rubygems_version: !ruby/object:Gem::Requirement
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- version: "0"
79
- version:
63
+ extra_rdoc_files:
64
+ - README.txt
65
+ - COPYING.LESSER.txt
66
+ - COPYING.txt
67
+ executables:
68
+ - zyps
69
+ extensions: []
70
+
80
71
  requirements: []
81
72
 
82
- rubyforge_project: zyps
83
- rubygems_version: 1.0.1
84
- signing_key:
85
- specification_version: 2
86
- summary: A game library for Ruby
87
- test_files:
88
- - test/test_zyps.rb
73
+ dependencies:
74
+ - !ruby/object:Gem::Dependency
75
+ name: wxruby
76
+ version_requirement:
77
+ version_requirements: !ruby/object:Gem::Version::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 1.9.2
82
+ version: