zyps 0.3.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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