zyps 0.3.1 → 0.4.1

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.
Files changed (115) hide show
  1. data/bin/zyps +22 -346
  2. data/bin/zyps_demo +116 -188
  3. data/doc/classes/AccelerateAction.html +182 -0
  4. data/doc/classes/{Vector.src/M000006.html → AccelerateAction.src/M000009.html} +5 -5
  5. data/doc/classes/AccelerateAction.src/M000010.html +18 -0
  6. data/doc/classes/Action.html +118 -0
  7. data/doc/classes/AgeCondition.html +181 -0
  8. data/doc/classes/AgeCondition.src/M000039.html +18 -0
  9. data/doc/classes/AgeCondition.src/M000040.html +18 -0
  10. data/doc/classes/ApproachAction.html +189 -0
  11. data/doc/classes/ApproachAction.src/M000048.html +19 -0
  12. data/doc/classes/ApproachAction.src/M000049.html +38 -0
  13. data/doc/classes/Behavior.html +23 -26
  14. data/doc/classes/Behavior.src/{M000003.html → M000001.html} +1 -1
  15. data/doc/classes/Behavior.src/{M000004.html → M000002.html} +2 -2
  16. data/doc/classes/BlendAction.html +181 -0
  17. data/doc/classes/BlendAction.src/M000043.html +18 -0
  18. data/doc/classes/BlendAction.src/M000044.html +18 -0
  19. data/doc/classes/Clock.html +10 -10
  20. data/doc/classes/Clock.src/{M000026.html → M000041.html} +0 -0
  21. data/doc/classes/Clock.src/{M000027.html → M000042.html} +0 -0
  22. data/doc/classes/CollisionCondition.html +148 -0
  23. data/doc/classes/CollisionCondition.src/M000053.html +18 -0
  24. data/doc/classes/Color.html +36 -36
  25. data/doc/classes/Color.src/{M000015.html → M000022.html} +0 -0
  26. data/doc/classes/Color.src/{M000016.html → M000023.html} +0 -0
  27. data/doc/classes/Color.src/{M000017.html → M000024.html} +0 -0
  28. data/doc/classes/Color.src/{M000018.html → M000025.html} +0 -0
  29. data/doc/classes/Color.src/{M000019.html → M000026.html} +0 -0
  30. data/doc/classes/Color.src/{M000020.html → M000027.html} +3 -3
  31. data/doc/classes/Condition.html +118 -0
  32. data/doc/classes/Creature.html +28 -13
  33. data/doc/classes/Creature.src/{M000005.html → M000011.html} +4 -4
  34. data/doc/classes/{Responsive.src/M000030.html → Creature.src/M000012.html} +2 -2
  35. data/doc/classes/DestroyAction.html +181 -0
  36. data/doc/classes/DestroyAction.src/M000030.html +18 -0
  37. data/doc/classes/DestroyAction.src/M000031.html +18 -0
  38. data/doc/classes/EatAction.html +149 -0
  39. data/doc/classes/EatAction.src/M000045.html +21 -0
  40. data/doc/classes/Enclosure.html +206 -0
  41. data/doc/classes/Enclosure.src/M000005.html +18 -0
  42. data/doc/classes/Enclosure.src/M000006.html +31 -0
  43. data/doc/classes/Environment.html +11 -11
  44. data/doc/classes/Environment.src/{M000028.html → M000046.html} +1 -1
  45. data/doc/classes/Environment.src/{M000029.html → M000047.html} +0 -0
  46. data/doc/classes/EnvironmentalFactor.html +2 -51
  47. data/doc/classes/{Responsive.html → FaceAction.html} +21 -16
  48. data/doc/classes/FaceAction.src/M000052.html +18 -0
  49. data/doc/classes/FleeAction.html +189 -0
  50. data/doc/classes/FleeAction.src/M000054.html +19 -0
  51. data/doc/classes/FleeAction.src/M000055.html +38 -0
  52. data/doc/classes/GameObject.html +47 -21
  53. data/doc/classes/GameObject.src/{M000022.html → M000032.html} +3 -4
  54. data/doc/classes/GameObject.src/M000033.html +16 -0
  55. data/doc/classes/GameObject.src/{M000023.html → M000034.html} +1 -1
  56. data/doc/classes/GameObject.src/{M000024.html → M000035.html} +1 -1
  57. data/doc/classes/GameObject.src/{M000025.html → M000036.html} +1 -1
  58. data/doc/classes/Location.html +5 -5
  59. data/doc/classes/Location.src/{M000014.html → M000021.html} +1 -1
  60. data/doc/classes/ProximityCondition.html +182 -0
  61. data/doc/classes/ProximityCondition.src/M000037.html +18 -0
  62. data/doc/classes/ProximityCondition.src/M000038.html +18 -0
  63. data/doc/classes/TagAction.html +181 -0
  64. data/doc/classes/{EnvironmentalFactor.src/M000021.html → TagAction.src/M000028.html} +4 -4
  65. data/doc/classes/TagAction.src/M000029.html +18 -0
  66. data/doc/classes/TagCondition.html +181 -0
  67. data/doc/classes/TagCondition.src/M000007.html +18 -0
  68. data/doc/classes/TagCondition.src/M000008.html +18 -0
  69. data/doc/classes/TrailsView.html +13 -21
  70. data/doc/classes/TrailsView.src/{M000001.html → M000003.html} +3 -3
  71. data/doc/classes/TrailsView.src/{M000002.html → M000004.html} +4 -2
  72. data/doc/classes/TurnAction.html +182 -0
  73. data/doc/classes/TurnAction.src/M000050.html +19 -0
  74. data/doc/classes/TurnAction.src/M000051.html +18 -0
  75. data/doc/classes/Utility.html +48 -48
  76. data/doc/classes/Utility.src/{M000031.html → M000056.html} +0 -0
  77. data/doc/classes/Utility.src/{M000032.html → M000057.html} +0 -0
  78. data/doc/classes/Utility.src/{M000033.html → M000058.html} +0 -0
  79. data/doc/classes/Utility.src/{M000034.html → M000059.html} +0 -0
  80. data/doc/classes/Utility.src/{M000035.html → M000060.html} +0 -0
  81. data/doc/classes/Utility.src/{M000036.html → M000061.html} +0 -0
  82. data/doc/classes/Utility.src/M000062.html +21 -0
  83. data/doc/classes/Vector.html +41 -41
  84. data/doc/classes/Vector.src/M000013.html +5 -11
  85. data/doc/classes/Vector.src/{M000007.html → M000014.html} +0 -0
  86. data/doc/classes/Vector.src/{M000008.html → M000015.html} +0 -0
  87. data/doc/classes/Vector.src/{M000009.html → M000016.html} +0 -0
  88. data/doc/classes/Vector.src/{M000010.html → M000017.html} +0 -0
  89. data/doc/classes/Vector.src/{M000011.html → M000018.html} +0 -0
  90. data/doc/classes/Vector.src/{M000012.html → M000019.html} +0 -0
  91. data/doc/classes/Vector.src/M000020.html +25 -0
  92. data/doc/created.rid +1 -1
  93. data/doc/files/COPYING_LESSER_txt.html +1 -1
  94. data/doc/files/COPYING_txt.html +1 -1
  95. data/doc/files/README_txt.html +1 -1
  96. data/doc/files/lib/zyps/actions_rb.html +134 -0
  97. data/doc/files/lib/zyps/conditions_rb.html +134 -0
  98. data/doc/files/lib/zyps/environmental_factors_rb.html +134 -0
  99. data/doc/files/lib/zyps/views/trails_rb.html +1 -1
  100. data/doc/files/lib/zyps_rb.html +1 -1
  101. data/doc/fr_class_index.html +16 -1
  102. data/doc/fr_file_index.html +3 -0
  103. data/doc/fr_method_index.html +62 -37
  104. data/doc/index.html +2 -2
  105. data/lib/zyps.rb +46 -47
  106. data/lib/zyps/actions.rb +186 -0
  107. data/lib/zyps/conditions.rb +69 -0
  108. data/lib/zyps/environmental_factors.rb +55 -0
  109. data/lib/zyps/views/trails.rb +6 -6
  110. data/test/test_zyps.rb +159 -69
  111. data/test/zyps/test_actions.rb +183 -0
  112. data/test/zyps/test_conditions.rb +83 -0
  113. data/test/zyps/test_environmental_factors.rb +88 -0
  114. metadata +137 -75
  115. data/doc/classes/Utility.src/M000037.html +0 -22
data/bin/zyps CHANGED
@@ -20,279 +20,20 @@
20
20
 
21
21
  begin
22
22
  require 'zyps'
23
+ require 'zyps/actions'
24
+ require 'zyps/conditions'
25
+ require 'zyps/environmental_factors'
23
26
  require 'zyps/views/trails'
24
27
  rescue LoadError
25
28
  require 'rubygems'
26
29
  require 'zyps'
30
+ require 'zyps/actions'
31
+ require 'zyps/conditions'
32
+ require 'zyps/environmental_factors'
27
33
  require 'zyps/views/trails'
28
34
  end
29
35
 
30
36
 
31
- #Generates new creatures and places them in an environment.
32
- class Generator
33
-
34
- #Environment creatures will be spawned into.
35
- attr_accessor :environment
36
- #Boundary that all new objects must be inside.
37
- attr_accessor :min_x, :min_y, :max_x, :max_y
38
- #Array of tags to choose from when generating new one.
39
- attr_accessor :tag_pool
40
- #Array of colors to choose from when generating new one.
41
- attr_accessor :color_pool
42
- #The maximum speed a new creature should be going.
43
- attr_accessor :max_speed
44
- #The maximum number of behaviors a creature can have.
45
- attr_accessor :max_behaviors
46
- #The maximum number of conditions a Behavior can have.
47
- attr_accessor :max_conditions
48
- #The maximum number of actions a Behavior can have.
49
- attr_accessor :max_actions
50
-
51
- def initialize(environment, min_x, min_y, max_x, max_y)
52
- @environment, @min_x, @min_y, @max_x, @max_y = environment, min_x, min_y, max_x, max_y
53
- @tag_pool = %q{foo bar baz}
54
- @color_pool = []
55
- @max_behaviors = 2
56
- @max_conditions = 2
57
- @max_actions = 2
58
- @max_speed = 100
59
- end
60
-
61
- #Create a creature.
62
- def create_creature()
63
-
64
- creature = Creature.new()
65
- #Create a Color.
66
- creature.color = create_color
67
- #Create a Location.
68
- creature.location = create_location
69
- #Create a Vector.
70
- creature.vector = create_vector()
71
- #Create a random tag.
72
- creature.tags << create_tag()
73
- #Create a random number of behaviors.
74
- generate_number(0, @max_behaviors).to_i.times {creature.behaviors << create_behavior()}
75
-
76
- creature
77
-
78
- end
79
-
80
- #Create a random tag.
81
- #Chooses from tag_pool.
82
- def create_tag()
83
- @tag_pool[generate_number(0, @tag_pool.length).to_i]
84
- end
85
- #Create a random Color.
86
- #Chooses from color_pool, or creates a random one from scratch if color_pool is empty.
87
- def create_color()
88
- if (color_pool.length != 0) then
89
- return @color_pool[generate_number(0, @color_pool.length).to_i]
90
- else
91
- return Color.new(generate_random_number(0, 1), generate_random_number(0, 1), generate_random_number(0, 1))
92
- end
93
- end
94
- #Create a random Location.
95
- def create_location()
96
- Location.new(
97
- generate_random_number(0, @max_x - @min_x) + @min_x,
98
- generate_random_number(0, @max_y - @min_y) + @min_y
99
- )
100
- end
101
- #Create a random Vector.
102
- def create_vector()
103
- Vector.new(generate_number(0, @max_speed), generate_number(0, 360))
104
- end
105
- #Create a random Behavior.
106
- def create_behavior()
107
- behavior = Behavior.new
108
- action_count = generate_number(0, @max_actions).to_i
109
- action_count.times do
110
- behavior.actions << create_action()
111
- end
112
- #Only add conditions if there's an action.
113
- if action_count > 0 then
114
- #There should always be a proximity condition.
115
- behavior.conditions << create_proximity_condition()
116
- #Add some additional conditions.
117
- generate_random_number(0, @max_conditions).to_i.times do
118
- behavior.conditions << create_condition()
119
- end
120
- end
121
- behavior
122
- end
123
- #Create a random condition.
124
- def create_condition()
125
- case generate_number(0, 2).to_i
126
- when 0
127
- return create_tag_condition()
128
- when 1
129
- return create_age_condition()
130
- else
131
- raise "Invalid condition"
132
- end
133
- end
134
- #Create a condition that looks for a random tag.
135
- def create_tag_condition()
136
- tag = create_tag()
137
- lambda {|creature, target| target.tags.include?(tag)}
138
- end
139
- #Create a condition that looks for a random age.
140
- def create_age_condition()
141
- age = generate_number(0, 60)
142
- lambda {|creature, target| target.age > age}
143
- end
144
- #Create a condition that looks for a random proximity.
145
- def create_proximity_condition()
146
- proximity = generate_number(0, 100)
147
- lambda do |creature, target|
148
- Utility.find_distance(creature.location, target.location) < proximity
149
- end
150
- end
151
- #Create a random action.
152
- def create_action()
153
- case generate_random_number(0, 9).to_i
154
- when 0
155
- return create_accelerate_action()
156
- when 1
157
- return create_approach_action()
158
- when 2
159
- return create_flee_action()
160
- when 3
161
- return create_blend_action()
162
- when 4
163
- return create_mate_action()
164
- when 5
165
- return create_spawn_action()
166
- when 6
167
- return create_turn_action()
168
- when 7
169
- return create_eat_action()
170
- when 8
171
- return create_tag_action()
172
- else
173
- raise "Invalid action"
174
- end
175
- end
176
- #Create an action that accelerates at a random rate.
177
- def create_accelerate_action()
178
- clock = Clock.new()
179
- rate = generate_number(0, 10)
180
- lambda {|creature, target| creature.vector.speed += rate * clock.elapsed_time}
181
- end
182
- #Create an action that turns in a circle.
183
- def create_turn_action()
184
- clock = Clock.new()
185
- rate = generate_number(0, 360)
186
- lambda {|creature, target| creature.vector.pitch += rate * clock.elapsed_time}
187
- end
188
- #Create an action that approaches the target.
189
- def create_approach_action()
190
- heading = Vector.new
191
- rate = generate_number(0, 20)
192
- lambda do |creature, target|
193
- #Find the difference between the current heading and the angle to the target.
194
- turn_angle = Utility.find_angle(creature.location, target.location) - heading.pitch
195
- #If the angle is the long way around from the current heading, change it to the smaller angle.
196
- if turn_angle > 180 then
197
- turn_angle -= 360.0
198
- elsif turn_angle < -180 then
199
- turn_angle += 360.0
200
- end
201
- #If turn angle is greater than allowed turn speed, reduce it.
202
- turn_angle = Utility.constrain_value(turn_angle, rate)
203
- #Turn the appropriate amount.
204
- heading.pitch += turn_angle
205
- #Apply the heading to the creature's movement vector.
206
- creature.vector += heading
207
- end
208
- end
209
- #Create an action that flees from the target.
210
- def create_flee_action()
211
- heading = Vector.new
212
- rate = generate_number(0, 20)
213
- lambda do |creature, target|
214
- #Find the difference between the current heading and the angle AWAY from the target.
215
- turn_angle = Utility.find_angle(creature.location, target.location) - heading.pitch + 180
216
- #If the angle is the long way around from the current heading, change it to the smaller angle.
217
- if turn_angle > 180 then
218
- turn_angle -= 360.0
219
- elsif turn_angle < -180 then
220
- turn_angle += 360.0
221
- end
222
- #If turn angle is greater than allowed turn speed, reduce it.
223
- turn_angle = Utility.constrain_value(turn_angle, rate)
224
- #Turn the appropriate amount.
225
- heading.pitch += turn_angle
226
- #Apply the heading to the creature's movement vector.
227
- creature.vector += heading
228
- end
229
- end
230
- #Create an action that shifts the subject's color to match the target.
231
- def create_blend_action()
232
- lambda {|creature, target| creature.color += target.color}
233
- end
234
- #Create an action that mates with the target.
235
- def create_mate_action()
236
- #TODO
237
- lambda {|creature, target| }
238
- end
239
- #Create an action that spawns a new creature.
240
- def create_spawn_action()
241
- #TODO
242
- lambda {|creature, target| }
243
- end
244
- #Create an action that eats the target.
245
- def create_eat_action()
246
- lambda {|creature, target| @environment.objects.delete(target)}
247
- end
248
- #Create an action that applies a tag to the target.
249
- def create_tag_action()
250
- tag = create_tag()
251
- lambda {|creature, target| target.tags << tag unless target.tags.include?(tag)}
252
- end
253
- #Generate a number between the given minimum and maximum, based on the current system time.
254
- def generate_number(minimum, maximum)
255
- (Time.new.to_f % (maximum - minimum)) + minimum
256
- end
257
- #Generate a random number between the given minimum and maximum.
258
- def generate_random_number(minimum, maximum)
259
- value = rand * (maximum - minimum) + minimum
260
- end
261
- end
262
-
263
-
264
-
265
- #Keeps all objects within a set of walls.
266
- class EncloseBehavior < Behavior
267
-
268
- #Positions of walls.
269
- attr_accessor :left, :top, :right, :bottom
270
-
271
- def initialize
272
- super
273
- @left, @top, @right, @bottom = 0, 0, 0, 0
274
- @actions << lambda do |boundary, object|
275
- #If object is beyond a boundary, set its position equal to the boundary and reflect it.
276
- if (object.location.x < @left) then
277
- object.location.x = @left
278
- object.vector.pitch = Utility.find_reflection_angle(90, object.vector.pitch)
279
- elsif (object.location.x > @right) then
280
- object.location.x = @right
281
- object.vector.pitch = Utility.find_reflection_angle(270, object.vector.pitch)
282
- end
283
- if (object.location.y < @top) then
284
- object.location.y = @top
285
- object.vector.pitch = Utility.find_reflection_angle(0, object.vector.pitch)
286
- elsif (object.location.y > @bottom) then
287
- object.location.y = @bottom
288
- object.vector.pitch = Utility.find_reflection_angle(180, object.vector.pitch)
289
- end
290
- end
291
- end
292
-
293
- end
294
-
295
-
296
37
 
297
38
  class Application
298
39
 
@@ -334,15 +75,12 @@ class Application
334
75
 
335
76
 
336
77
  #Keep all objects within a boundary.
337
- enclose = EncloseBehavior.new()
338
- enclose.left = 0
339
- enclose.right = @width
340
- enclose.top = 0
341
- enclose.bottom = @height
342
- @environment.environmental_factors << EnvironmentalFactor.new([enclose])
343
-
344
- #Create a creature generator.
345
- @generator = Generator.new(@environment, 0, 0, @width, @height)
78
+ enclosure = Enclosure.new
79
+ enclosure.left = 0
80
+ enclosure.bottom = 0
81
+ enclosure.top = @height
82
+ enclosure.right = @width
83
+ @environment.environmental_factors << enclosure
346
84
 
347
85
  #Create thread to update environment.
348
86
  thread = Thread.new do
@@ -350,9 +88,7 @@ class Application
350
88
  begin
351
89
 
352
90
  drawing_clock = Clock.new
353
- birth_clock = Clock.new
354
91
  time_per_frame = 1.0 / @fps
355
- time_since_birth = 0
356
92
 
357
93
  loop do
358
94
 
@@ -411,90 +147,30 @@ class Application
411
147
  Utility.find_angle(@press_location, @release_location) #Move in direction of drag.
412
148
  ),
413
149
  0,
150
+ 1,
414
151
  @tags_box.text.split(/,\s*/)
415
152
  )
416
153
  behavior = Behavior.new
417
154
  condition_tags = @tag_condition_box.text
418
155
  condition_tags.split(/,\s*/).each do |tag|
419
- behavior.conditions << lambda do |creature, target|
420
- target.tags.include?(tag)
421
- end
156
+ behavior.conditions << TagCondition.new(tag)
422
157
  end
423
158
  proximity = @proximity_condition_slider.value
424
- if proximity > 0
425
- behavior.conditions << lambda do |creature, target|
426
- Utility.find_distance(creature.location, target.location) < proximity
427
- end
428
- end
159
+ behavior.conditions << ProximityCondition.new(proximity) if proximity > 0
429
160
  age = @age_condition_slider.value
430
- if age > 0
431
- behavior.conditions << lambda do |creature, target|
432
- target.age > age
433
- end
434
- end
161
+ behavior.conditions << AgeCondition.new(age) if age > 0
435
162
  acceleration = @acceleration_slider.value
436
- if acceleration != 0
437
- behavior.actions << lambda do |creature, target|
438
- creature.vector.speed += acceleration
439
- creature.vector.speed = 0 if creature.vector.speed < 0
440
- end
441
- end
163
+ behavior.actions << AccelerateAction.new(acceleration) if acceleration != 0
442
164
  turn = @turn_slider.value
443
- if turn != 0
444
- behavior.actions << lambda do |creature, target|
445
- creature.vector.pitch += turn
446
- end
447
- end
165
+ behavior.actions << TurnAction.new(turn) if turn != 0
448
166
  approach_angle = @approach_slider.value
449
- if approach_angle > 0
450
- heading = creature.vector.clone
451
- behavior.actions << lambda do |creature, target|
452
- #Find the difference between the current heading and the angle to the target.
453
- turn_angle = Utility.find_angle(creature.location, target.location) - heading.pitch
454
- #If the angle is the long way around from the current heading, change it to the smaller angle.
455
- if turn_angle > 180 then
456
- turn_angle -= 360.0
457
- elsif turn_angle < -180 then
458
- turn_angle += 360.0
459
- end
460
- #If turn angle is greater than allowed turn speed, reduce it.
461
- turn_angle = Utility.constrain_value(turn_angle, approach_angle)
462
- #Turn the appropriate amount.
463
- heading.pitch += turn_angle
464
- #Apply the heading to the creature's movement vector.
465
- creature.vector += heading
466
- end
467
- end
167
+ behavior.actions << ApproachAction.new(Vector.new(1, creature.vector.pitch), approach_angle) if approach_angle > 0
468
168
  flee_angle = @flee_slider.value
469
- if flee_angle > 0
470
- heading = creature.vector.clone
471
- behavior.actions << lambda do |creature, target|
472
- #Find the difference between the current heading and the angle to the target.
473
- turn_angle = Utility.find_angle(creature.location, target.location) - heading.pitch + 180
474
- #If the angle is the long way around from the current heading, change it to the smaller angle.
475
- if turn_angle > 180 then
476
- turn_angle -= 360.0
477
- elsif turn_angle < -180 then
478
- turn_angle += 360.0
479
- end
480
- #If turn angle is greater than allowed turn speed, reduce it.
481
- turn_angle = Utility.constrain_value(turn_angle, flee_angle)
482
- #Turn the appropriate amount.
483
- heading.pitch += turn_angle
484
- #Apply the heading to the creature's movement vector.
485
- creature.vector += heading
486
- end
487
- end
488
- if @eat_flag.active?
489
- behavior.actions << lambda do |creature, target|
490
- @environment.objects.delete(target)
491
- end
492
- end
169
+ behavior.actions << FleeAction.new(Vector.new(5, creature.vector.pitch), flee_angle) if flee_angle > 0
170
+ behavior.actions << EatAction.new(@environment) if @eat_flag.active?
493
171
  apply_tags = @tag_action_box.text
494
172
  apply_tags.split(/,\s*/).each do |tag|
495
- behavior.conditions << lambda do |creature, target|
496
- target.tags << tag unless target.tags.include?(tag)
497
- end
173
+ behavior.actions << TagAction.new(tag)
498
174
  end
499
175
  creature.behaviors << behavior
500
176
  @environment.objects << creature
data/bin/zyps_demo CHANGED
@@ -20,10 +20,14 @@
20
20
 
21
21
  begin
22
22
  require 'zyps'
23
+ require 'zyps/actions'
24
+ require 'zyps/conditions'
23
25
  require 'zyps/views/trails'
24
26
  rescue LoadError
25
27
  require 'rubygems'
26
28
  require 'zyps'
29
+ require 'zyps/actions'
30
+ require 'zyps/conditions'
27
31
  require 'zyps/views/trails'
28
32
  end
29
33
 
@@ -38,6 +42,8 @@ class Demo
38
42
  FRAME_COUNT = 80
39
43
  #Number of frames per second.
40
44
  FRAMES_PER_SECOND = 30
45
+ #Default size of game objects.
46
+ DEFAULT_OBJECT_SIZE = 78.5 #5 units in radius.
41
47
 
42
48
  #Set up a window, a canvas, and an object environment, then run the given block.
43
49
  def demo
@@ -102,10 +108,12 @@ class Demo
102
108
  count.times do |i|
103
109
  multiplier = i / count.to_f
104
110
  environment.objects << Creature.new(
105
- i,
111
+ i, #Name.
106
112
  Location.new(multiplier * @view.width, multiplier * @view.height),
107
113
  Color.new(multiplier, 1 - multiplier, multiplier / 2 + 0.5),
108
- Vector.new(100 * multiplier, multiplier * 360)
114
+ Vector.new(100 * multiplier, multiplier * 360),
115
+ 0, #Age.
116
+ DEFAULT_OBJECT_SIZE #Size.
109
117
  )
110
118
  end
111
119
  end
@@ -125,7 +133,9 @@ class Demo
125
133
  say("The things that populate an environment are called GameObjects. Each object has:")
126
134
  object = GameObject.new
127
135
  say("...a name")
128
- object.name = "Clancy"
136
+ object.name = "Huey"
137
+ say("...a size")
138
+ object.size = DEFAULT_OBJECT_SIZE
129
139
  say("...a Location with x and y coordiates")
130
140
  object.location = Location.new(@view.width/2, @view.height/2)
131
141
  say("...a Color with red, green and blue components ranging from 0 to 1")
@@ -136,14 +146,28 @@ class Demo
136
146
  say("Once your object is ready, add it to the environment.")
137
147
  @environment.objects << object
138
148
 
139
- say("Call a view's render(objects) method to draw the objects.")
149
+ say("Add a view as an observer of an Environment, and it will draw the objects.")
140
150
  say("Call an environment's interact() method to have the objects in it move around.")
141
- say("Our demo's animate() method does both these things for us.")
151
+ say("Our demo's animate() method does this for us.")
142
152
  animate(FRAME_COUNT)
143
153
 
144
154
  say("Let's add a couple more objects with different colors and vectors.")
145
- @environment.objects << GameObject.new(nil, Location.new(@view.width/2, @view.height/2), Color.new(0, 1, 0), Vector.new(20, 135))
146
- @environment.objects << GameObject.new(nil, Location.new(@view.width/2, @view.height/2), Color.new(0, 0, 1), Vector.new(30, 225))
155
+ @environment.objects << GameObject.new(
156
+ "Duey", #Name.
157
+ Location.new(@view.width/2, @view.height/2),
158
+ Color.new(0, 1, 0),
159
+ Vector.new(20, 135),
160
+ 0, #Age.
161
+ DEFAULT_OBJECT_SIZE * 2 #Size.
162
+ )
163
+ @environment.objects << GameObject.new(
164
+ "Louie", #Name.
165
+ Location.new(@view.width/2, @view.height/2),
166
+ Color.new(0, 0, 1),
167
+ Vector.new(30, 225),
168
+ 0, #Age.
169
+ DEFAULT_OBJECT_SIZE * 3 #Size.
170
+ )
147
171
  animate(FRAME_COUNT)
148
172
 
149
173
  say("The viewing area can be resized at any time via its width and height attributes.")
@@ -151,16 +175,22 @@ class Demo
151
175
  @view.height += 100
152
176
  animate(FRAME_COUNT)
153
177
 
154
- say("TrailsView lets you set the thickness and length of the trails as well.")
155
- @view.trail_width = 10
156
- @view.trail_length = 10
178
+ say("TrailsView lets you set the length of the trails as well.")
179
+ @view.trail_length = 50
157
180
  animate(FRAME_COUNT)
158
181
 
159
182
  end
160
-
183
+
161
184
  end
162
185
 
163
186
 
187
+ class Gravity < EnvironmentalFactor
188
+ #Accelerate object toward the 'ground'.
189
+ def act(target)
190
+ target.vector.y += 9.8
191
+ end
192
+ end
193
+
164
194
  #Demonstrates environmental factors by adding gravity to the environment.
165
195
  def test_environmental_factors
166
196
 
@@ -172,26 +202,19 @@ class Demo
172
202
  animate(FRAME_COUNT)
173
203
 
174
204
  say("Let's add a new EnvironmentalFactor to simulate gravity.")
175
- gravity = EnvironmentalFactor.new
205
+ gravity = Gravity.new
176
206
 
177
- say("We create a Behavior the EnvironmentalFactor will follow.")
178
- accelerate = Behavior.new
179
- say("The behavior will have a single action: to accelerate objects toward the 'ground' at 9.8 meters/second.")
180
- accelerate.actions << lambda {|gravity, target| target.vector.y += 9.8}
181
- say("We add the behavior to the EnvironmentalFactor.")
182
- gravity.behaviors << accelerate
183
- say("Then we add the EnvironmentalFactor to the Environment.")
207
+ say("We add gravity to the Environment.")
184
208
  @environment.environmental_factors << gravity
185
209
 
186
210
  say("Everything immediately drops.")
187
211
  animate(FRAME_COUNT)
188
212
 
189
213
  end
190
-
214
+
191
215
  end
192
216
 
193
217
 
194
-
195
218
  #Demonstrates creature behaviors.
196
219
  def test_behaviors
197
220
 
@@ -199,17 +222,17 @@ class Demo
199
222
 
200
223
  thread = Thread.new do
201
224
 
202
- say("Let's add a behavior to our creatures.")
225
+ say("Let's add a Behavior to our creatures.")
203
226
  chase = Behavior.new
204
227
 
205
- say("We'll have them head straight toward their target.")
206
- chase.actions << lambda do |creature, target|
207
- angle_to_target = Utility.find_angle(creature.location, target.location)
208
- creature.vector.pitch = angle_to_target
209
- end
228
+ say("A Behavior has one or more Action objects that define an action to take on the current target.")
229
+ say("We'll add an Action that makes the creatures head straight toward their target.")
230
+ chase.actions << FaceAction.new
210
231
 
232
+ say("A Behavior also has one or more Condition objects.")
233
+ say("Unless every Condition is true, the action(s) won't be carried out.")
211
234
  say("So that they don't target every creature on the screen, we'll add a condition to the behavior saying the target must have the label 'food'.")
212
- chase.conditions << lambda {|creature, target| target.tags.include?('food')}
235
+ chase.conditions << TagCondition.new("food")
213
236
 
214
237
  say("We'll apply this behavior to all creatures currently in the environment.")
215
238
  @environment.objects.each {|creature| creature.behaviors << chase}
@@ -217,11 +240,12 @@ class Demo
217
240
 
218
241
  say("Then we'll toss a piece of food (a GameObject with the label 'food') into the environment.")
219
242
  @environment.objects << GameObject.new(
220
- "target",
243
+ "target", #Name.
221
244
  Location.new(@view.width / 2, @view.height / 2),
222
245
  Color.new(1, 1, 1),
223
246
  Vector.new(50, 315),
224
247
  0, #Age.
248
+ DEFAULT_OBJECT_SIZE * 2, #Size.
225
249
  ["food"] #Tags.
226
250
  )
227
251
 
@@ -235,17 +259,14 @@ class Demo
235
259
 
236
260
  #A Creature that changes the colors of other objects.
237
261
  class Morpher < Creature
262
+ #Changes an object's color.
238
263
  def initialize(*arguments)
239
264
  super
240
265
  morph = Behavior.new
241
266
  #Shift the target's color to match the creature's.
242
- morph.actions << lambda do |creature, target|
243
- target.color.red += 0.1 if target.color.red < creature.color.red
244
- target.color.green += 0.1 if target.color.green < creature.color.green
245
- target.color.blue += 0.1 if target.color.blue < creature.color.blue
246
- end
267
+ morph.actions << BlendAction.new(self.color)
247
268
  #Act only on nearby targets.
248
- morph.conditions << lambda {|creature, target| Utility.find_distance(creature.location, target.location) < 50}
269
+ morph.conditions << ProximityCondition.new(50)
249
270
  @behaviors << morph
250
271
  end
251
272
  end
@@ -260,15 +281,15 @@ class Demo
260
281
  say("Morphers are created with a single behavior, which shifts the color of any nearby target to match the Morpher's color.")
261
282
 
262
283
  say("Let's place a red Morpher...")
263
- @environment.objects << Morpher.new(nil, Location.new(0, 100), Color.new(1, 0, 0), Vector.new(100, 0))
284
+ @environment.objects << Morpher.new(nil, Location.new(0, 100), Color.new(1, 0, 0), Vector.new(100, 0), 0, DEFAULT_OBJECT_SIZE)
264
285
  say("a green one...")
265
- @environment.objects << Morpher.new(nil, Location.new(0, 150), Color.new(0, 1, 0), Vector.new(200, 0))
286
+ @environment.objects << Morpher.new(nil, Location.new(0, 150), Color.new(0, 1, 0), Vector.new(200, 0), 0, DEFAULT_OBJECT_SIZE)
266
287
  say("and a blue one...")
267
- @environment.objects << Morpher.new(nil, Location.new(0, 200), Color.new(0, 0, 1), Vector.new(300, 0))
288
+ @environment.objects << Morpher.new(nil, Location.new(0, 200), Color.new(0, 0, 1), Vector.new(300, 0), 0, DEFAULT_OBJECT_SIZE)
268
289
 
269
290
  say("And see what they do.")
270
291
  thread = Thread.new {animate(FRAME_COUNT)}
271
-
292
+
272
293
  end
273
294
 
274
295
 
@@ -280,30 +301,17 @@ class Demo
280
301
  thread = Thread.new do
281
302
 
282
303
 
283
- say("Many actions need to happen smoothly over time, such as accelerating at a given rate.")
284
-
285
304
  say("Here are some Creatures, just plodding along.")
286
305
  animate(FRAME_COUNT / 4)
287
306
  say("We're going to have them pick up the pace.")
288
307
 
289
- say("A Clock helps track the passage of time (obviously).")
290
- say("We'll create a separate Clock object for each creature.")
291
- clocks = Hash.new {|h, k| h[k] = Clock.new}
292
-
293
- say("Our acceleration behavior is going to check to see how long it's been since it last took effect.")
294
- say("It will multiply that time by a given rate, say 100 meters/second.")
295
- say("Let's say it's been 0.1 seconds since this creature last accelerated.")
296
- say("100 times 0.1 is 10.")
297
- say("So we should add 10 meters/second to the creature's speed.")
298
- accelerate = Behavior.new
299
- accelerate.actions << lambda do |creature, target|
300
- #Accelerate the appropriate amount for the elapsed time.
301
- creature.vector.speed += 100 * clocks[creature].elapsed_time
308
+ say("We add a Behavior with an AccelerateAction to all the creatures, and specify they should increase their speed by 100 units/second...")
309
+ @environment.objects.each do |creature|
310
+ accelerate = Behavior.new
311
+ accelerate.actions << AccelerateAction.new(100)
312
+ creature.behaviors << accelerate
302
313
  end
303
314
 
304
- say("We add acceleration to all the creatures...")
305
- @environment.objects.each {|creature| creature.behaviors << accelerate}
306
-
307
315
  say("And watch them rocket away.")
308
316
  animate(FRAME_COUNT)
309
317
 
@@ -319,24 +327,16 @@ class Demo
319
327
 
320
328
  thread = Thread.new do
321
329
 
322
- say("Turning smoothly requires tracking the rate of the turn as well.")
330
+ say("This time we'll use the TurnAction class.")
323
331
  animate(FRAME_COUNT / 2)
324
332
 
325
- say("Again, we keep a separate Clock for each Creature.")
326
- clocks = Hash.new {|h, k| h[k] = Clock.new}
327
-
328
- say("Our turn behavior follows the same principle as accelerating.")
329
- say("We see how many seconds have elapsed, then multiply that by the turn rate.")
330
- say("We add the result to the Vector angle.")
331
- turn = Behavior.new
332
- turn.actions << lambda do |creature, target|
333
- #Turn the appropriate amount for the elapsed time.
334
- creature.vector.pitch += 100 * clocks[creature].elapsed_time
333
+ say("We tell each creature it should turn by 90 degrees/second...")
334
+ @environment.objects.each do |creature|
335
+ turn = Behavior.new
336
+ turn.actions << TurnAction.new(90)
337
+ creature.behaviors << turn
335
338
  end
336
339
 
337
- say("We add the behavior to each Creature...")
338
- @environment.objects.each {|creature| creature.behaviors << turn}
339
-
340
340
  say("And watch things spiral out of control.")
341
341
  animate(FRAME_COUNT)
342
342
 
@@ -351,55 +351,25 @@ class Demo
351
351
  populate(@environment, 50)
352
352
 
353
353
  say("When your car skids on ice, you might steer in a different direction, but you're going to keep following your original vector for a while.")
354
- say("Adding vectors together lets us simulate this.")
355
-
356
- say("We'll keep a separate Vector for each Creature to track the direction it's 'steering' in.")
357
- headings = Hash.new {|h, k| h[k] = Vector.new(k.vector.speed, k.vector.pitch)}
358
-
359
- say("We'll only allow them to turn a maximum of 20 degrees in either direction.")
360
- max_turn_angle = 20
361
-
362
- say("We create a Behavior which adds the Vector the creature WANTS to follow to the Vector it's ACTUALLY following.")
363
- approach = Behavior.new
364
- approach.actions << lambda do |creature, target|
365
-
366
- #Find the difference between the current heading and the angle to the target.
367
- turn_angle = Utility.find_angle(creature.location, target.location) - headings[creature].pitch
368
-
369
- #If the angle is the long way around from the current heading, change it to the smaller angle.
370
- if turn_angle > 180 then
371
- turn_angle -= 360.0
372
- elsif turn_angle < -180 then
373
- turn_angle += 360.0
374
- end
375
-
376
- #If turn angle is greater than allowed turn speed, reduce it.
377
- turn_angle = Utility.constrain_value(turn_angle, max_turn_angle)
378
-
379
- #Turn the appropriate amount.
380
- headings[creature].pitch += turn_angle
381
-
382
- #Apply the heading to the creature's movement vector.
383
- creature.vector += headings[creature]
384
-
385
- end
386
-
387
- say("We add a condition that it should only target its prey.")
388
- approach.conditions << lambda do |creature, target|
389
- target.tags.include?('prey')
354
+ say("Our ApproachAction adds the vector the creature WANTS to follow to the vector it's ACTUALLY following.")
355
+ say("We add a behavior with an ApproachAction to all creatures...")
356
+ say("We also add a condition that it should only target food.")
357
+ @environment.objects.each do |creature|
358
+ approach = Behavior.new
359
+ approach.actions << ApproachAction.new(creature.vector)
360
+ approach.conditions << TagCondition.new("food")
361
+ creature.behaviors << approach
390
362
  end
391
363
 
392
- say("We add the behavior to all creatures...")
393
- @environment.objects.each {|creature| creature.behaviors << approach}
394
-
395
364
  say("Add a target...")
396
365
  @environment.objects << Creature.new(
397
366
  "target",
398
- Location.new(@view.width / 2, @view.height / 2),
367
+ Location.new(@view.width / 2, @view.height / 3),
399
368
  Color.new(1, 1, 1),
400
369
  Vector.new(3, 0),
401
370
  0, #Age.
402
- ["prey"] #Tags.
371
+ DEFAULT_OBJECT_SIZE, #Size.
372
+ ["food"] #Tags.
403
373
  )
404
374
 
405
375
  say("And watch them all TRY to catch it.")
@@ -408,58 +378,27 @@ class Demo
408
378
  end
409
379
 
410
380
 
411
- #Demonstrates turning away from an object, instead of toward it.
381
+
382
+ #Demonstrates adding vectors.
412
383
  def test_flee
413
384
 
414
- populate(@environment, 20)
415
-
416
- say("Fleeing from something is just like approaching it, but we head in the OPPOSITE direction.")
417
- say("Just get the angle toward the object, then add 180 degrees.")
418
-
419
- #Keep a separate heading for each object.
420
- headings = Hash.new {|h, k| h[k] = Vector.new(k.vector.speed, k.vector.pitch)}
421
-
422
- #Create a behavior.
423
- max_turn_angle = 20
424
- flee = Behavior.new
425
- flee.actions << lambda do |creature, target|
426
-
427
- #Find the difference between the current heading and the angle AWAY from the target.
428
- turn_angle = Utility.find_angle(creature.location, target.location) - headings[creature].pitch + 180
429
-
430
- #If the angle is the long way around from the current heading, change it to the smaller angle.
431
- if turn_angle > 180 then
432
- turn_angle -= 360.0
433
- elsif turn_angle < -180 then
434
- turn_angle += 360.0
435
- end
436
-
437
- #If turn angle is greater than allowed turn speed, reduce it.
438
- turn_angle = Utility.constrain_value(turn_angle, max_turn_angle)
439
-
440
- #Turn the appropriate amount for the elapsed time.
441
- headings[creature].pitch += turn_angle
442
-
443
- #Apply the heading to the creature's movement vector.
444
- creature.vector += headings[creature]
445
-
446
- end
385
+ populate(@environment, 50)
447
386
 
448
- #Flee from only the creature's predator.
449
- flee.conditions << lambda do |creature, target|
450
- target.tags.include?('predator')
387
+ say("A FleeAction is just like an ApproachAction, but we head in the OPPOSITE direction.")
388
+ @environment.objects.each do |creature|
389
+ flee = Behavior.new
390
+ flee.actions << FleeAction.new(creature.vector)
391
+ flee.conditions << TagCondition.new("predator")
392
+ creature.behaviors << flee
451
393
  end
452
394
 
453
- #Add behavior to creatures.
454
- @environment.objects.each {|creature| creature.behaviors << flee}
455
-
456
- #Add a target.
457
395
  @environment.objects << Creature.new(
458
- "lion",
396
+ "target",
459
397
  Location.new(@view.width / 2, @view.height / 2),
460
398
  Color.new(1, 1, 1),
461
399
  Vector.new(3, 0),
462
400
  0, #Age.
401
+ DEFAULT_OBJECT_SIZE, #Size.
463
402
  ["predator"] #Tags.
464
403
  )
465
404
 
@@ -468,47 +407,32 @@ class Demo
468
407
  end
469
408
 
470
409
 
471
- #A Behavior that destroys the target.
472
- class Destroy < Behavior
473
- #Environment from which targets will be removed.
474
- attr_accessor :environment
475
- def initialize(actions = [], conditions = [], environment = Environment.new)
476
- super(actions, conditions)
477
- @environment = environment
478
- #"Kill" target.
479
- self.actions << lambda do |creature, target|
480
- @environment.objects.delete(target)
481
- end
482
- #Act only if target is close.
483
- self.conditions << lambda do |creature, target|
484
- Utility.find_distance(creature.location, target.location) < 25
485
- end
486
- end
487
- end
488
-
489
-
410
+
490
411
  #Demonstrates keeping a reference to an Environment so a Creature can alter it.
491
- def test_destroy
412
+ def test_eat
492
413
 
493
414
  populate(@environment)
494
415
 
495
416
  say("Most games are all about destruction, but there hasn't been much so far.")
496
417
  say("Let's create a creature that causes some havoc.")
497
- delinquent = Creature.new(nil, Location.new(0, 150), Color.new(0, 1, 0), Vector.new(200, 0))
498
-
499
- say("This demo code includes a subclass of Behavior, called Destroy.")
500
- destroy = Destroy.new
501
-
502
- say("We'll destroy our targets by removing them from their environment.")
503
- say("Creatures and their Behaviors normally know nothing about the Environment they belong to, so we added an environment attribute to Destroy.")
504
- say("Destroy finds the target in Environment.objects and removes it.")
505
- destroy.environment = @environment
506
-
507
- say("Add the Destroy instance to the creature's behaviors...")
508
- delinquent.behaviors << destroy
418
+ predator = Creature.new(nil, Location.new(0, 150), Color.new(0, 1, 0), Vector.new(200, 0), 0, DEFAULT_OBJECT_SIZE * 2)
419
+
420
+ say("The EatAction eats targets by removing them from their environment.")
421
+ say("Creatures and their Actions normally know nothing about the Environment they belong to, so EatAction takes an Environment in its constructor.")
422
+ say("EatAction finds the target in Environment.objects and removes it.")
423
+ action = EatAction.new(@environment)
424
+
425
+ say("Create a behavior...")
426
+ behavior = Behavior.new
427
+ say("Add the action to the behavior...")
428
+ behavior.actions << action
429
+ say("Add a condition that they must collide first...")
430
+ behavior.conditions << CollisionCondition.new
431
+ say("Add the behavior to the creature...")
432
+ predator.behaviors << behavior
509
433
 
510
434
  say("Drop the creature into the actual environment...")
511
- @environment.objects << delinquent
435
+ @environment.objects << predator
512
436
 
513
437
  say("And - chomp!")
514
438
  thread = Thread.new {animate(FRAME_COUNT)}
@@ -518,6 +442,7 @@ class Demo
518
442
 
519
443
  #Run all the demos.
520
444
  def main
445
+ say "This is a demonstration of the Zyps library."
521
446
  say "After each demo, close the window to proceed."
522
447
  say("-" * 30)
523
448
  demo {test_render}
@@ -528,7 +453,10 @@ class Demo
528
453
  demo {test_turn}
529
454
  demo {test_approach}
530
455
  demo {test_flee}
531
- demo {test_destroy}
456
+ demo {test_eat}
457
+ say "To learn more about how the library works, you can read the source code in the 'bin/zyps_demo' file in the Zyps distribution."
458
+ say "And if you want to code your own Actions, Conditions, EnvironmentalFactors, or Views, see the distribution's 'lib' folder for examples."
459
+ say "Thanks for watching!"
532
460
  end
533
461
 
534
462