summaryrefslogtreecommitdiffhomepage
path: root/samples/04_physics_and_collisions/03_entities/app/main.rb
diff options
context:
space:
mode:
Diffstat (limited to 'samples/04_physics_and_collisions/03_entities/app/main.rb')
-rw-r--r--samples/04_physics_and_collisions/03_entities/app/main.rb151
1 files changed, 151 insertions, 0 deletions
diff --git a/samples/04_physics_and_collisions/03_entities/app/main.rb b/samples/04_physics_and_collisions/03_entities/app/main.rb
new file mode 100644
index 0000000..afbc3df
--- /dev/null
+++ b/samples/04_physics_and_collisions/03_entities/app/main.rb
@@ -0,0 +1,151 @@
+=begin
+
+ Reminders:
+
+ - map: Ruby method used to transform data; used in arrays, hashes, and collections.
+ Can be used to perform an action on every element of a collection, such as multiplying
+ each element by 2 or declaring every element as a new entity.
+
+ - reject: Removes elements from a collection if they meet certain requirements.
+ For example, you can derive an array of odd numbers from an original array of
+ numbers 1 through 10 by rejecting all elements that are even (or divisible by 2).
+
+ - args.state.new_entity: Used when we want to create a new object, like a sprite or button.
+ In this sample app, new_entity is used to define the properties of enemies and bullets.
+ (Remember, you can use state to define ANY property and it will be retained across frames.)
+
+ - args.outputs.labels: An array. The values generate a label on the screen.
+ The parameters are [X, Y, TEXT, SIZE, ALIGN, RED, GREEN, BLUE, ALPHA, FONT STYLE]
+
+ - ARRAY#intersect_rect?: Returns true or false depending on if the two rectangles intersect.
+
+ - args.inputs.mouse.click.point.(x|y): The x and y location of the mouse.
+
+=end
+
+# This sample app shows enemies that contain an id value and the time they were created.
+# These enemies can be removed by shooting at them with bullets.
+
+# Calls all methods necessary for the game to function properly.
+def tick args
+ tick_instructions args, "Sample app shows how to use args.state.new_entity along with collisions. CLICK to shoot a bullet."
+ defaults args
+ render args
+ calc args
+ process_inputs args
+end
+
+# Sets default values
+# Enemies and bullets start off as empty collections
+def defaults args
+ args.state.enemies ||= []
+ args.state.bullets ||= []
+end
+
+# Provides each enemy in enemies collection with rectangular border,
+# as well as a label showing id and when they were created
+def render args
+ # When you're calling a method that takes no arguments, you can use this & syntax on map.
+ # Numbers are being added to x and y in order to keep the text within the enemy's borders.
+ args.outputs.borders << args.state.enemies.map(&:rect)
+ args.outputs.labels << args.state.enemies.flat_map do |enemy|
+ [
+ [enemy.x + 4, enemy.y + 29, "id: #{enemy.entity_id}", -3, 0],
+ [enemy.x + 4, enemy.y + 17, "created_at: #{enemy.created_at}", -3, 0] # frame enemy was created
+ ]
+ end
+
+ # Outputs bullets in bullets collection as rectangular solids
+ args.outputs.solids << args.state.bullets.map(&:rect)
+end
+
+# Calls all methods necessary for performing calculations
+def calc args
+ add_new_enemies_if_needed args
+ move_bullets args
+ calculate_collisions args
+ remove_bullets_of_screen args
+end
+
+# Adds enemies to the enemies collection and sets their values
+def add_new_enemies_if_needed args
+ return if args.state.enemies.length >= 10 # if 10 or more enemies, enemies are not added
+ return unless args.state.bullets.length == 0 # if user has not yet shot bullet, no enemies are added
+
+ args.state.enemies += (10 - args.state.enemies.length).map do # adds enemies so there are 10 total
+ args.state.new_entity(:enemy) do |e| # each enemy is declared as a new entity
+ e.x = 640 + 500 * rand # each enemy is given random position on screen
+ e.y = 600 * rand + 50
+ e.rect = [e.x, e.y, 130, 30] # sets definition for enemy's rect
+ end
+ end
+end
+
+# Moves bullets across screen
+# Sets definition of the bullets
+def move_bullets args
+ args.state.bullets.each do |bullet| # perform action on each bullet in collection
+ bullet.x += bullet.speed # increment x by speed (bullets fly horizontally across screen)
+
+ # By randomizing the value that increments bullet.y, the bullet does not fly straight up and out
+ # of the scope of the screen. Try removing what follows bullet.speed, or changing 0.25 to 1.25 to
+ # see what happens to the bullet's movement.
+ bullet.y += bullet.speed.*(0.25).randomize(:ratio, :sign)
+ bullet.rect = [bullet.x, bullet.y, bullet.size, bullet.size] # sets definition of bullet's rect
+ end
+end
+
+# Determines if a bullet hits an enemy
+def calculate_collisions args
+ args.state.bullets.each do |bullet| # perform action on every bullet and enemy in collections
+ args.state.enemies.each do |enemy|
+ # if bullet has not exploded yet and the bullet hits an enemy
+ if !bullet.exploded && bullet.rect.intersect_rect?(enemy.rect)
+ bullet.exploded = true # bullet explodes
+ enemy.dead = true # enemy is killed
+ end
+ end
+ end
+
+ # All exploded bullets are rejected or removed from the bullets collection
+ # and any dead enemy is rejected from the enemies collection.
+ args.state.bullets = args.state.bullets.reject(&:exploded)
+ args.state.enemies = args.state.enemies.reject(&:dead)
+end
+
+# Bullets are rejected from bullets collection once their position exceeds the width of screen
+def remove_bullets_of_screen args
+ args.state.bullets = args.state.bullets.reject { |bullet| bullet.x > 1280 } # screen width is 1280
+end
+
+# Calls fire_bullet method
+def process_inputs args
+ fire_bullet args
+end
+
+# Once mouse is clicked by the user to fire a bullet, a new bullet is added to bullets collection
+def fire_bullet args
+ return unless args.inputs.mouse.click # return unless mouse is clicked
+ args.state.bullets << args.state.new_entity(:bullet) do |bullet| # new bullet is declared a new entity
+ bullet.y = args.inputs.mouse.click.point.y # set to the y value of where the mouse was clicked
+ bullet.x = 0 # starts on the left side of the screen
+ bullet.size = 10
+ bullet.speed = 10 * rand + 2 # speed of a bullet is randomized
+ bullet.rect = [bullet.x, bullet.y, bullet.size, bullet.size] # definition is set
+ end
+end
+
+def tick_instructions args, text, y = 715
+ return if args.state.key_event_occurred
+ if args.inputs.mouse.click ||
+ args.inputs.keyboard.directional_vector ||
+ args.inputs.keyboard.key_down.enter ||
+ args.inputs.keyboard.key_down.space ||
+ args.inputs.keyboard.key_down.escape
+ args.state.key_event_occurred = true
+ end
+
+ args.outputs.debug << [0, y - 50, 1280, 60].solid
+ args.outputs.debug << [640, y, text, 1, 1, 255, 255, 255].label
+ args.outputs.debug << [640, y - 25, "(click to dismiss instructions)" , -2, 1, 255, 255, 255].label
+end