diff options
| author | Amir Rajan <[email protected]> | 2020-09-11 02:02:01 -0500 |
|---|---|---|
| committer | Amir Rajan <[email protected]> | 2020-09-11 02:02:57 -0500 |
| commit | 33ec37b141e896b47ed642923fd33b0c658ae9fb (patch) | |
| tree | a40d3e5d41beeb06508200078f6f26b0ee57d6a4 /samples/04_physics_and_collisions/02_moving_objects | |
| parent | 958cf43779d2bf528869e80511c4c4f2a433b2db (diff) | |
| download | dragonruby-game-toolkit-contrib-33ec37b141e896b47ed642923fd33b0c658ae9fb.tar.gz dragonruby-game-toolkit-contrib-33ec37b141e896b47ed642923fd33b0c658ae9fb.zip | |
synced samples
Diffstat (limited to 'samples/04_physics_and_collisions/02_moving_objects')
3 files changed, 399 insertions, 0 deletions
diff --git a/samples/04_physics_and_collisions/02_moving_objects/app/main.rb b/samples/04_physics_and_collisions/02_moving_objects/app/main.rb new file mode 100644 index 0000000..35eabfb --- /dev/null +++ b/samples/04_physics_and_collisions/02_moving_objects/app/main.rb @@ -0,0 +1,300 @@ +=begin + + APIs listing that haven't been encountered in previous sample apps: + + - Hashes: Collection of unique keys and their corresponding values. The value can be found + using their keys. + + For example, if we have a "numbers" hash that stores numbers in English as the + key and numbers in Spanish as the value, we'd have a hash that looks like this... + numbers = { "one" => "uno", "two" => "dos", "three" => "tres" } + and on it goes. + + Now if we wanted to find the corresponding value of the "one" key, we could say + puts numbers["one"] + which would print "uno" to the console. + + - num1.greater(num2): Returns the greater value. + For example, if we have the command + puts 4.greater(3) + the number 4 would be printed to the console since it has a greater value than 3. + Similar to lesser, which returns the lesser value. + + - num1.lesser(num2): Finds the lower value of the given options. + For example, in the statement + a = 4.lesser(3) + 3 has a lower value than 4, which means that the value of a would be set to 3, + but if the statement had been + a = 4.lesser(5) + 4 has a lower value than 5, which means that the value of a would be set to 4. + + - 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). + + - find_all: Finds all values that satisfy specific requirements. + For example, you can find all elements of a collection that are divisible by 2 + or find all objects that have intersected with another object. + + - abs: Returns the absolute value. + For example, the command + (-30).abs + would return 30 as a result. + + - 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. + + Reminders: + + - args.inputs.keyboard.KEY: Determines if a key has been pressed. + For more information about the keyboard, take a look at mygame/documentation/06-keyboard.md. + + - ARRAY#intersect_rect?: Returns true or false depending on if the two rectangles intersect. + + - args.outputs.solids: An array. The values generate a solid. + The parameters are [X, Y, WIDTH, HEIGHT, RED, GREEN, BLUE] + For more information about solids, go to mygame/documentation/03-solids-and-borders.md. + +=end + +# Calls methods needed for game to run properly +def tick args + tick_instructions args, "Use LEFT and RIGHT arrow keys to move and SPACE to jump." + defaults args + render args + calc args + input args +end + +# sets default values and creates empty collections +# initialization only happens in the first frame +def defaults args + fiddle args + args.state.enemy.hammers ||= [] + args.state.enemy.hammer_queue ||= [] + args.state.tick_count = args.state.tick_count + args.state.bridge_top = 128 + args.state.player.x ||= 0 # initializes player's properties + args.state.player.y ||= args.state.bridge_top + args.state.player.w ||= 64 + args.state.player.h ||= 64 + args.state.player.dy ||= 0 + args.state.player.dx ||= 0 + args.state.enemy.x ||= 800 # initializes enemy's properties + args.state.enemy.y ||= 0 + args.state.enemy.w ||= 128 + args.state.enemy.h ||= 128 + args.state.enemy.dy ||= 0 + args.state.enemy.dx ||= 0 + args.state.game_over_at ||= 0 +end + +# sets enemy, player, hammer values +def fiddle args + args.state.gravity = -0.3 + args.state.enemy_jump_power = 10 # sets enemy values + args.state.enemy_jump_interval = 60 + args.state.hammer_throw_interval = 40 # sets hammer values + args.state.hammer_launch_power_default = 5 + args.state.hammer_launch_power_near = 2 + args.state.hammer_launch_power_far = 7 + args.state.hammer_upward_launch_power = 15 + args.state.max_hammers_per_volley = 10 + args.state.gap_between_hammers = 10 + args.state.player_jump_power = 10 # sets player values + args.state.player_jump_power_duration = 10 + args.state.player_max_run_speed = 10 + args.state.player_speed_slowdown_rate = 0.9 + args.state.player_acceleration = 1 + args.state.hammer_size = 32 +end + +# outputs objects onto the screen +def render args + args.outputs.solids << 20.map_with_index do |i| # uses 20 squares to form bridge + # sets x by multiplying 64 to index to find pixel value (places all squares side by side) + # subtracts 64 from bridge_top because position is denoted by bottom left corner + [i * 64, args.state.bridge_top - 64, 64, 64] + end + + args.outputs.solids << [args.state.x, args.state.y, args.state.w, args.state.h, 255, 0, 0] + args.outputs.solids << [args.state.player.x, args.state.player.y, args.state.player.w, args.state.player.h, 255, 0, 0] # outputs player onto screen (red box) + args.outputs.solids << [args.state.enemy.x, args.state.enemy.y, args.state.enemy.w, args.state.enemy.h, 0, 255, 0] # outputs enemy onto screen (green box) + args.outputs.solids << args.state.enemy.hammers # outputs enemy's hammers onto screen +end + +# Performs calculations to move objects on the screen +def calc args + + # Since velocity is the change in position, the change in x increases by dx. Same with y and dy. + args.state.player.x += args.state.player.dx + args.state.player.y += args.state.player.dy + + # Since acceleration is the change in velocity, the change in y (dy) increases every frame + args.state.player.dy += args.state.gravity + + # player's y position is either current y position or y position of top of + # bridge, whichever has a greater value + # ensures that the player never goes below the bridge + args.state.player.y = args.state.player.y.greater(args.state.bridge_top) + + # player's x position is either the current x position or 0, whichever has a greater value + # ensures that the player doesn't go too far left (out of the screen's scope) + args.state.player.x = args.state.player.x.greater(0) + + # player is not falling if it is located on the top of the bridge + args.state.player.falling = false if args.state.player.y == args.state.bridge_top + args.state.player.rect = [args.state.player.x, args.state.player.y, args.state.player.h, args.state.player.w] # sets definition for player + + args.state.enemy.x += args.state.enemy.dx # velocity; change in x increases by dx + args.state.enemy.y += args.state.enemy.dy # same with y and dy + + # ensures that the enemy never goes below the bridge + args.state.enemy.y = args.state.enemy.y.greater(args.state.bridge_top) + + # ensures that the enemy never goes too far left (outside the screen's scope) + args.state.enemy.x = args.state.enemy.x.greater(0) + + # objects that go up must come down because of gravity + args.state.enemy.dy += args.state.gravity + + args.state.enemy.y = args.state.enemy.y.greater(args.state.bridge_top) + + #sets definition of enemy + args.state.enemy.rect = [args.state.enemy.x, args.state.enemy.y, args.state.enemy.h, args.state.enemy.w] + + if args.state.enemy.y == args.state.bridge_top # if enemy is located on the top of the bridge + args.state.enemy.dy = 0 # there is no change in y + end + + # if 60 frames have passed and the enemy is not moving vertically + if args.state.tick_count.mod_zero?(args.state.enemy_jump_interval) && args.state.enemy.dy == 0 + args.state.enemy.dy = args.state.enemy_jump_power # the enemy jumps up + end + + # if 40 frames have passed or 5 frames have passed since the game ended + if args.state.tick_count.mod_zero?(args.state.hammer_throw_interval) || args.state.game_over_at.elapsed_time == 5 + # rand will return a number greater than or equal to 0 and less than given variable's value (since max is excluded) + # that is why we're adding 1, to include the max possibility + volley_dx = (rand(args.state.hammer_launch_power_default) + 1) * -1 # horizontal movement (follow order of operations) + + # if the horizontal distance between the player and enemy is less than 128 pixels + if (args.state.player.x - args.state.enemy.x).abs < 128 + # the change in x won't be that great since the enemy and player are closer to each other + volley_dx = (rand(args.state.hammer_launch_power_near) + 1) * -1 + end + + # if the horizontal distance between the player and enemy is greater than 300 pixels + if (args.state.player.x - args.state.enemy.x).abs > 300 + # change in x will be more drastic since player and enemy are so far apart + volley_dx = (rand(args.state.hammer_launch_power_far) + 1) * -1 # more drastic change + end + + (rand(args.state.max_hammers_per_volley) + 1).map_with_index do |i| + args.state.enemy.hammer_queue << { # stores hammer values in a hash + x: args.state.enemy.x, + w: args.state.hammer_size, + h: args.state.hammer_size, + dx: volley_dx, # change in horizontal position + # multiplication operator takes precedence over addition operator + throw_at: args.state.tick_count + i * args.state.gap_between_hammers + } + end + end + + # add elements from hammer_queue collection to the hammers collection by + # finding all hammers that were thrown before the current frame (have already been thrown) + args.state.enemy.hammers += args.state.enemy.hammer_queue.find_all do |h| + h[:throw_at] < args.state.tick_count + end + + args.state.enemy.hammers.each do |h| # sets values for all hammers in collection + h[:y] ||= args.state.enemy.y + 130 + h[:dy] ||= args.state.hammer_upward_launch_power + h[:dy] += args.state.gravity # acceleration is change in gravity + h[:x] += h[:dx] # incremented by change in position + h[:y] += h[:dy] + h[:rect] = [h[:x], h[:y], h[:w], h[:h]] # sets definition of hammer's rect + end + + # reject hammers that have been thrown before current frame (have already been thrown) + args.state.enemy.hammer_queue = args.state.enemy.hammer_queue.reject do |h| + h[:throw_at] < args.state.tick_count + end + + # any hammers with a y position less than 0 are rejected from the hammers collection + # since they have gone too far down (outside the scope's screen) + args.state.enemy.hammers = args.state.enemy.hammers.reject { |h| h[:y] < 0 } + + # if there are any hammers that intersect with (or hit) the player, + # the reset_player method is called (so the game can start over) + if args.state.enemy.hammers.any? { |h| h[:rect].intersect_rect?(args.state.player.rect) } + reset_player args + end + + # if the enemy's rect intersects with (or hits) the player, + # the reset_player method is called (so the game can start over) + if args.state.enemy.rect.intersect_rect? args.state.player.rect + reset_player args + end +end + +# Resets the player by changing its properties back to the values they had at initialization +def reset_player args + args.state.player.x = 0 + args.state.player.y = args.state.bridge_top + args.state.player.dy = 0 + args.state.player.dx = 0 + args.state.enemy.hammers.clear # empties hammer collection + args.state.enemy.hammer_queue.clear # empties hammer_queue + args.state.game_over_at = args.state.tick_count # game_over_at set to current frame (or passage of time) +end + +# Processes input from the user to move the player +def input args + if args.inputs.keyboard.space # if the user presses the space bar + args.state.player.jumped_at ||= args.state.tick_count # jumped_at is set to current frame + + # if the time that has passed since the jump is less than the player's jump duration and + # the player is not falling + if args.state.player.jumped_at.elapsed_time < args.state.player_jump_power_duration && !args.state.player.falling + args.state.player.dy = args.state.player_jump_power # change in y is set to power of player's jump + end + end + + # if the space bar is in the "up" state (or not being pressed down) + if args.inputs.keyboard.key_up.space + args.state.player.jumped_at = nil # jumped_at is empty + args.state.player.falling = true # the player is falling + end + + if args.inputs.keyboard.left # if left key is pressed + args.state.player.dx -= args.state.player_acceleration # dx decreases by acceleration (player goes left) + # dx is either set to current dx or the negative max run speed (which would be -10), + # whichever has a greater value + args.state.player.dx = args.state.player.dx.greater(-args.state.player_max_run_speed) + elsif args.inputs.keyboard.right # if right key is pressed + args.state.player.dx += args.state.player_acceleration # dx increases by acceleration (player goes right) + # dx is either set to current dx or max run speed (which would be 10), + # whichever has a lesser value + args.state.player.dx = args.state.player.dx.lesser(args.state.player_max_run_speed) + else + args.state.player.dx *= args.state.player_speed_slowdown_rate # dx is scaled down + 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 diff --git a/samples/04_physics_and_collisions/02_moving_objects/license-for-sample.txt b/samples/04_physics_and_collisions/02_moving_objects/license-for-sample.txt new file mode 100644 index 0000000..100dcec --- /dev/null +++ b/samples/04_physics_and_collisions/02_moving_objects/license-for-sample.txt @@ -0,0 +1,9 @@ +Copyright 2019 DragonRuby LLC + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/samples/04_physics_and_collisions/02_moving_objects/replay.txt b/samples/04_physics_and_collisions/02_moving_objects/replay.txt new file mode 100644 index 0000000..f6ef481 --- /dev/null +++ b/samples/04_physics_and_collisions/02_moving_objects/replay.txt @@ -0,0 +1,90 @@ +replay_version 2.0 +stopped_at 820 +seed 100 +recorded_at Sun Sep 29 21:39:44 2019 +[:key_down_raw, 1073741903, 0, 2, 1, 142] +[:key_up_raw, 1073741903, 0, 2, 2, 155] +[:key_down_raw, 1073741904, 0, 2, 3, 172] +[:key_down_raw, 1073741903, 0, 2, 4, 187] +[:key_up_raw, 1073741904, 0, 2, 5, 188] +[:key_down_raw, 32, 0, 2, 6, 207] +[:key_down_raw, 1073741904, 0, 2, 7, 207] +[:key_up_raw, 1073741903, 0, 2, 8, 210] +[:key_down_raw, 1073741903, 0, 2, 9, 229] +[:key_up_raw, 1073741904, 0, 2, 10, 233] +[:key_up_raw, 32, 0, 2, 11, 250] +[:key_down_raw, 1073741904, 0, 2, 12, 251] +[:key_up_raw, 1073741903, 0, 2, 13, 251] +[:key_up_raw, 1073741904, 0, 2, 14, 264] +[:key_down_raw, 1073741903, 0, 2, 15, 277] +[:key_up_raw, 1073741903, 0, 2, 16, 284] +[:key_down_raw, 1073741903, 0, 2, 17, 318] +[:key_down_raw, 1073741903, 0, 2, 18, 343] +[:key_down_raw, 1073741903, 0, 2, 19, 345] +[:key_down_raw, 1073741903, 0, 2, 20, 347] +[:key_down_raw, 1073741903, 0, 2, 21, 349] +[:key_down_raw, 1073741903, 0, 2, 22, 351] +[:key_up_raw, 1073741903, 0, 2, 23, 351] +[:key_down_raw, 1073741904, 0, 2, 24, 373] +[:key_up_raw, 1073741904, 0, 2, 25, 381] +[:key_down_raw, 1073741904, 0, 2, 26, 400] +[:key_up_raw, 1073741904, 0, 2, 27, 404] +[:key_down_raw, 1073741904, 0, 2, 28, 412] +[:key_down_raw, 1073741904, 0, 2, 29, 438] +[:key_up_raw, 1073741904, 0, 2, 30, 439] +[:key_down_raw, 1073741903, 0, 2, 31, 467] +[:key_down_raw, 1073741903, 0, 2, 32, 492] +[:key_down_raw, 1073741903, 0, 2, 33, 494] +[:key_down_raw, 1073741903, 0, 2, 34, 496] +[:key_down_raw, 1073741903, 0, 2, 35, 498] +[:key_down_raw, 1073741903, 0, 2, 36, 500] +[:key_down_raw, 1073741903, 0, 2, 37, 502] +[:key_down_raw, 1073741903, 0, 2, 38, 504] +[:key_down_raw, 1073741903, 0, 2, 39, 506] +[:key_down_raw, 1073741903, 0, 2, 40, 509] +[:key_down_raw, 1073741903, 0, 2, 41, 510] +[:key_down_raw, 1073741903, 0, 2, 42, 513] +[:key_down_raw, 1073741903, 0, 2, 43, 515] +[:key_down_raw, 1073741903, 0, 2, 44, 517] +[:key_down_raw, 1073741903, 0, 2, 45, 519] +[:key_down_raw, 1073741903, 0, 2, 46, 521] +[:key_down_raw, 1073741903, 0, 2, 47, 523] +[:key_down_raw, 1073741903, 0, 2, 48, 525] +[:key_down_raw, 1073741903, 0, 2, 49, 527] +[:key_up_raw, 1073741903, 0, 2, 50, 527] +[:key_down_raw, 1073741903, 0, 2, 51, 559] +[:key_down_raw, 1073741903, 0, 2, 52, 584] +[:key_down_raw, 1073741903, 0, 2, 53, 586] +[:key_down_raw, 1073741903, 0, 2, 54, 588] +[:key_down_raw, 1073741903, 0, 2, 55, 590] +[:key_down_raw, 1073741903, 0, 2, 56, 592] +[:key_down_raw, 1073741903, 0, 2, 57, 595] +[:key_down_raw, 1073741903, 0, 2, 58, 597] +[:key_down_raw, 1073741903, 0, 2, 59, 599] +[:key_down_raw, 1073741903, 0, 2, 60, 601] +[:key_down_raw, 32, 0, 2, 61, 601] +[:key_down_raw, 1073741904, 0, 2, 62, 604] +[:key_up_raw, 1073741903, 0, 2, 63, 606] +[:key_down_raw, 1073741903, 0, 2, 64, 620] +[:key_up_raw, 1073741904, 0, 2, 65, 624] +[:key_down_raw, 1073741903, 0, 2, 66, 645] +[:key_down_raw, 1073741903, 0, 2, 67, 647] +[:key_down_raw, 1073741903, 0, 2, 68, 649] +[:key_down_raw, 1073741903, 0, 2, 69, 651] +[:key_down_raw, 1073741903, 0, 2, 70, 654] +[:key_down_raw, 1073741903, 0, 2, 71, 656] +[:key_down_raw, 1073741903, 0, 2, 72, 658] +[:key_up_raw, 32, 0, 2, 73, 658] +[:key_down_raw, 1073741903, 0, 2, 74, 660] +[:key_down_raw, 1073741903, 0, 2, 75, 662] +[:key_down_raw, 1073741903, 0, 2, 76, 664] +[:key_down_raw, 1073741903, 0, 2, 77, 666] +[:key_down_raw, 1073741903, 0, 2, 78, 668] +[:key_down_raw, 1073741903, 0, 2, 79, 670] +[:key_down_raw, 1073741903, 0, 2, 80, 672] +[:key_down_raw, 1073741903, 0, 2, 81, 674] +[:key_down_raw, 1073741903, 0, 2, 82, 676] +[:key_down_raw, 1073741903, 0, 2, 83, 678] +[:key_up_raw, 1073741903, 0, 2, 84, 678] +[:key_down_raw, 1073742051, 1024, 2, 85, 818] +[:key_down_raw, 113, 1024, 2, 86, 819] |
