summaryrefslogtreecommitdiffhomepage
path: root/samples
diff options
context:
space:
mode:
authorAmir Rajan <[email protected]>2021-09-06 14:32:04 -0500
committerAmir Rajan <[email protected]>2021-09-06 14:34:34 -0500
commit2f5eb6ab368b062dbbde39b3cee6eae23c5452ff (patch)
treee2f0b5f4a1ab4919cf5669f6187994489411821a /samples
parentaa8d3ac4bdccf522b8082a7fa7d595be2bd54b7d (diff)
downloaddragonruby-game-toolkit-contrib-2f5eb6ab368b062dbbde39b3cee6eae23c5452ff.tar.gz
dragonruby-game-toolkit-contrib-2f5eb6ab368b062dbbde39b3cee6eae23c5452ff.zip
Synced with version 2.26
Diffstat (limited to 'samples')
-rw-r--r--samples/04_physics_and_collisions/06_box_collision_3/app/main.rb4
-rw-r--r--samples/04_physics_and_collisions/08_bouncing_on_collision/app/block.rb42
-rw-r--r--samples/07_advanced_rendering/11_render_target_noclear/app/main.rb47
-rw-r--r--samples/07_advanced_rendering/11_render_target_noclear/metadata/game_metadata.txt5
-rw-r--r--samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb18
-rw-r--r--samples/12_c_extensions/README.md28
-rw-r--r--samples/99_genre_teenytiny/app/main.rb162
-rw-r--r--samples/99_genre_teenytiny/license.txt9
-rw-r--r--samples/99_genre_teenytiny/metadata/game_metadata.txt6
-rw-r--r--samples/99_genre_teenytiny/metadata/icon.pngbin0 -> 157056 bytes
-rw-r--r--samples/99_genre_teenytiny/sprites/square-green.pngbin0 -> 283 bytes
-rw-r--r--samples/99_genre_teenytiny/sprites/square-red.pngbin0 -> 274 bytes
12 files changed, 296 insertions, 25 deletions
diff --git a/samples/04_physics_and_collisions/06_box_collision_3/app/main.rb b/samples/04_physics_and_collisions/06_box_collision_3/app/main.rb
index 4f5b483..ae447fd 100644
--- a/samples/04_physics_and_collisions/06_box_collision_3/app/main.rb
+++ b/samples/04_physics_and_collisions/06_box_collision_3/app/main.rb
@@ -112,7 +112,7 @@ class Game
def calc_below
return unless player.dy <= 0
- tiles_below = find_tiles { |t| t.rect.top <= player.y }
+ tiles_below = find_tiles { |t| t.rect.top <= player.prev_rect.y }
collision = find_colliding_tile tiles_below, (player.rect.merge y: player.next_rect.y)
return unless collision
if collision.neighbors.b == :none && player.jumped_down_at.elapsed_time < 10
@@ -143,7 +143,7 @@ class Game
def calc_above
return unless player.dy > 0
- tiles_above = find_tiles { |t| t.rect.y >= player.y }
+ tiles_above = find_tiles { |t| t.rect.y >= player.prev_rect.y }
collision = find_colliding_tile tiles_above, (player.rect.merge y: player.next_rect.y)
return unless collision
return if collision.neighbors.t == :none
diff --git a/samples/04_physics_and_collisions/08_bouncing_on_collision/app/block.rb b/samples/04_physics_and_collisions/08_bouncing_on_collision/app/block.rb
index fa2f3a5..ca0cb3a 100644
--- a/samples/04_physics_and_collisions/08_bouncing_on_collision/app/block.rb
+++ b/samples/04_physics_and_collisions/08_bouncing_on_collision/app/block.rb
@@ -1,22 +1,24 @@
-class Block
+DEGREES_TO_RADIANS = Math::PI / 180
+
+class Block
def initialize(x, y, block_size, rotation)
@x = x
- @y = y
+ @y = y
@block_size = block_size
@rotation = rotation
#The repel velocity?
- @velocity = {x: 2, y: 0}
+ @velocity = {x: 2, y: 0}
- horizontal_offset = (3 * block_size) * Math.cos(rotation.to_radians)
- vertical_offset = block_size * Math.sin(rotation.to_radians)
+ horizontal_offset = (3 * block_size) * Math.cos(rotation * DEGREES_TO_RADIANS)
+ vertical_offset = block_size * Math.sin(rotation * DEGREES_TO_RADIANS)
if rotation >= 0
theta = 90 - rotation
#The line doesn't visually line up exactly with the edge of the sprite, so artificially move it a bit
modifier = 5
- x_offset = modifier * Math.cos(theta.to_radians)
- y_offset = modifier * Math.sin(theta.to_radians)
+ x_offset = modifier * Math.cos(theta * DEGREES_TO_RADIANS)
+ y_offset = modifier * Math.sin(theta * DEGREES_TO_RADIANS)
@x1 = @x - x_offset
@y1 = @y + y_offset
@x2 = @x1 + horizontal_offset
@@ -25,8 +27,8 @@ class Block
@imaginary_line = [ @x1, @y1, @x2, @y2 ]
else
theta = 90 + rotation
- x_offset = @block_size * Math.cos(theta.to_radians)
- y_offset = @block_size * Math.sin(theta.to_radians)
+ x_offset = @block_size * Math.cos(theta * DEGREES_TO_RADIANS)
+ y_offset = @block_size * Math.sin(theta * DEGREES_TO_RADIANS)
@x1 = @x + x_offset
@y1 = @y + y_offset + 19
@x2 = @x1 + horizontal_offset
@@ -34,7 +36,7 @@ class Block
@imaginary_line = [ @x1, @y1, @x2, @y2 ]
end
-
+
end
def draw args
@@ -78,7 +80,7 @@ class Block
collision = false
if @rotation >= 0
- if (current_area < min_area &&
+ if (current_area < min_area &&
current_area > 0 &&
args.state.ball.center.y > @y1 &&
args.state.ball.center.x < @x2)
@@ -86,7 +88,7 @@ class Block
collision = true
end
else
- if (current_area < min_area &&
+ if (current_area < min_area &&
current_area > 0 &&
args.state.ball.center.y > @y2 &&
args.state.ball.center.x > @x1)
@@ -94,7 +96,7 @@ class Block
collision = true
end
end
-
+
return collision
end
@@ -103,8 +105,8 @@ class Block
slope = (@y2 - @y1) / (@x2 - @x1)
#Create a unit vector and tilt it (@rotation) number of degrees
- x = -Math.cos(@rotation.to_radians)
- y = Math.sin(@rotation.to_radians)
+ x = -Math.cos(@rotation * DEGREES_TO_RADIANS)
+ y = Math.sin(@rotation * DEGREES_TO_RADIANS)
#Find the vector that is perpendicular to the slope
perpVect = { x: x, y: y }
@@ -115,10 +117,10 @@ class Block
x:args.state.ball.center.x-args.state.ball.velocity.x,
y:args.state.ball.center.y-args.state.ball.velocity.y
}
-
+
velocityMag = (args.state.ball.velocity.x**2 + args.state.ball.velocity.y**2)**0.5 # the current velocity magnitude of the ball
theta_ball = Math.atan2(args.state.ball.velocity.y, args.state.ball.velocity.x) #the angle of the ball's velocity
- theta_repel = (180.to_radians) - theta_ball + (@rotation.to_radians)
+ theta_repel = (180 * DEGREES_TO_RADIANS) - theta_ball + (@rotation * DEGREES_TO_RADIANS)
fbx = velocityMag * Math.cos(theta_ball) #the x component of the ball's velocity
fby = velocityMag * Math.sin(theta_ball) #the y component of the ball's velocity
@@ -137,16 +139,16 @@ class Block
dampener = 0.3
ynew *= dampener * 0.5
-
+
#If the bounce is very low, that means the ball is rolling and we don't want to dampenen the X velocity
if ynew > -0.1
xnew *= dampener
end
#Add the sine component of gravity back in (X component)
- gravity_x = 4 * Math.sin(@rotation.to_radians)
+ gravity_x = 4 * Math.sin(@rotation * DEGREES_TO_RADIANS)
xnew += gravity_x
-
+
args.state.ball.velocity.x = -xnew
args.state.ball.velocity.y = -ynew
diff --git a/samples/07_advanced_rendering/11_render_target_noclear/app/main.rb b/samples/07_advanced_rendering/11_render_target_noclear/app/main.rb
new file mode 100644
index 0000000..9e5dffd
--- /dev/null
+++ b/samples/07_advanced_rendering/11_render_target_noclear/app/main.rb
@@ -0,0 +1,47 @@
+def tick args
+ args.state.x ||= 500
+ args.state.y ||= 350
+ args.state.xinc ||= 7
+ args.state.yinc ||= 7
+ args.state.bgcolor ||= 1
+ args.state.bginc ||= 1
+
+ # clear the render target on the first tick, and then never again. Draw
+ # another box to it every tick, accumulating over time.
+ clear_target = (args.state.tick_count == 0) || (args.inputs.keyboard.key_down.space)
+ args.render_target(:accumulation).background_color = [ 0, 0, 0, 0 ];
+ args.render_target(:accumulation).clear_before_render = clear_target
+ args.render_target(:accumulation).solids << [args.state.x, args.state.y, 25, 25, 255, 0, 0, 255];
+ args.state.x += args.state.xinc
+ args.state.y += args.state.yinc
+ args.state.bgcolor += args.state.bginc
+
+ # animation upkeep...change where we draw the next box and what color the
+ # window background will be.
+ if args.state.xinc > 0 && args.state.x >= 1280
+ args.state.xinc = -7
+ elsif args.state.xinc < 0 && args.state.x < 0
+ args.state.xinc = 7
+ end
+
+ if args.state.yinc > 0 && args.state.y >= 720
+ args.state.yinc = -7
+ elsif args.state.yinc < 0 && args.state.y < 0
+ args.state.yinc = 7
+ end
+
+ if args.state.bginc > 0 && args.state.bgcolor >= 255
+ args.state.bginc = -1
+ elsif args.state.bginc < 0 && args.state.bgcolor <= 0
+ args.state.bginc = 1
+ end
+
+ # clear the screen to a shade of blue and draw the render target, which
+ # is not clearing every frame, on top of it. Note that you can NOT opt to
+ # skip clearing the screen, only render targets. The screen clears every
+ # frame; double-buffering would prevent correct updates between frames.
+ args.outputs.background_color = [ 0, 0, args.state.bgcolor, 255 ]
+ args.outputs.sprites << [ 0, 0, 1280, 720, :accumulation ]
+end
+
+$gtk.reset
diff --git a/samples/07_advanced_rendering/11_render_target_noclear/metadata/game_metadata.txt b/samples/07_advanced_rendering/11_render_target_noclear/metadata/game_metadata.txt
new file mode 100644
index 0000000..3d0bd06
--- /dev/null
+++ b/samples/07_advanced_rendering/11_render_target_noclear/metadata/game_metadata.txt
@@ -0,0 +1,5 @@
+devid=dragonruby
+devtitle=DragonRuby LLC
+gameid=render_target_noclear
+gametitle=Render targets that don't clear every frame.
+version=1.0
diff --git a/samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb b/samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb
index 8505bdf..12c08f6 100644
--- a/samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb
+++ b/samples/10_advanced_debugging/03_unit_tests/serialize_deserialize_tests.rb
@@ -2,12 +2,12 @@ def test_serialize args, assert
GTK::Entity.__reset_id__!
args.state.player_one = "test"
result = args.gtk.serialize_state args.state
- assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}"
+ assert.equal! result, "{:entity_id=>4, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}"
GTK::Entity.__reset_id__!
args.gtk.write_file 'state.txt', ''
result = args.gtk.serialize_state 'state.txt', args.state
- assert.equal! result, "{:entity_id=>3, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}"
+ assert.equal! result, "{:entity_id=>4, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>\"test\"}"
end
def test_deserialize args, assert
@@ -56,7 +56,7 @@ def test_strict_entity_serialization_with_nil args, assert
args.state.player_two = args.state.new_entity_strict(:player_strict, name: "Ken", blood_type: nil)
serialized_state = args.gtk.serialize_state args.state
- assert.equal! serialized_state, '{:entity_id=>9, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>2, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken", :blood_type=>nil}}'
+ assert.equal! serialized_state, '{:entity_id=>7, :entity_keys_by_ref=>{}, :tick_count=>-1, :player_one=>{:entity_id=>1, :entity_name=>:player, :entity_keys_by_ref=>{}, :entity_type=>:player, :created_at=>-1, :global_created_at=>-1, :name=>"Ryu"}, :player_two=>{:entity_id=>2, :entity_name=>:player_strict, :entity_type=>:player_strict, :created_at=>-1, :global_created_at_elapsed=>-1, :entity_strict=>true, :entity_keys_by_ref=>{:entity_type=>:entity_name, :global_created_at_elapsed=>:created_at}, :name=>"Ken", :blood_type=>nil}}'
GTK::Entity.__reset_id__!
deserialized_state = args.gtk.deserialize_state serialized_state
@@ -113,3 +113,15 @@ def test_by_reference_state_strict_entities args, assert
deserialized_state = args.gtk.deserialize_state serialized_state
assert.equal! deserialized_state.strict_entity.one, deserialized_state.strict_entity.two
end
+
+def test_serialization_excludes_thrash_count args, assert
+ GTK::Entity.__reset_id__!
+ args.state.player.name = "Ryu"
+ # force a nil pun
+ if args.state.player.age > 30
+ end
+ assert.equal! args.state.player.as_hash[:__thrash_count__][:>], 1
+ result = args.gtk.serialize_state args.state
+ assert.false! (result.include? "__thrash_count__"),
+ "The __thrash_count__ key exists in state when it shouldn't have."
+end
diff --git a/samples/12_c_extensions/README.md b/samples/12_c_extensions/README.md
index 8950e53..aef3864 100644
--- a/samples/12_c_extensions/README.md
+++ b/samples/12_c_extensions/README.md
@@ -350,6 +350,34 @@ dragonruby-bind --ffi-module=CoolStuff bridge.c
Then one can use `include FFI::CoolStuff` instead.
+### Type checks
+
+C extensions expect the right types in the right place!
+
+Given the following C code:
+
+```c
+void take_int(int x) { ... }
+void take_struct(struct S) { ... }
+```
+
+the next calls from the Ruby side
+
+```ruby
+take_int(15.0)
+take_struct(42)
+```
+
+may not work as you would expect.
+In the case of `take_int`, you'll likely see some garbage instead of "expected" `15`.
+The call to `take_struct` will likely crash.
+
+To prevent this from happening, `dragonruby-bind` emits code that does type checking:
+if you use the wrong types DragonRuby will throw an exception.
+
+If the type checking takes CPU cycles out of your game (or if you feel brave) you can
+disable type checks via `--no-typecheck` CLI argument when emitting C bindings.
+
### Pitfalls
There is no so-called marshalling when it comes to structs. When you read or
diff --git a/samples/99_genre_teenytiny/app/main.rb b/samples/99_genre_teenytiny/app/main.rb
new file mode 100644
index 0000000..a542a93
--- /dev/null
+++ b/samples/99_genre_teenytiny/app/main.rb
@@ -0,0 +1,162 @@
+# full documenation is at http://docs.dragonruby.org
+# be sure to come to the discord if you hit any snags: http://discord.dragonruby.org
+def tick args
+ # ====================================================
+ # initialize default variables
+ # ====================================================
+
+ # ruby has an operator called ||= which means "only initialize this if it's nil"
+ args.state.count_down ||= 20 * 60 # set the count down to 20 seconds
+ # set the initial position of the target
+ args.state.target ||= { x: args.grid.w.half,
+ y: args.grid.h.half,
+ w: 20,
+ h: 20 }
+
+ # set the initial position of the player
+ args.state.player ||= { x: 50,
+ y: 50,
+ w: 20,
+ h: 20 }
+
+ # set the player movement speed
+ args.state.player_speed ||= 5
+
+ # set the score
+ args.state.score ||= 0
+ args.state.teleports ||= 3
+
+ # set the instructions
+ args.state.instructions ||= "Get to the red goal! Use arrow keys to move. Spacebar to teleport (use them carefully)!"
+
+ # ====================================================
+ # render the game
+ # ====================================================
+ args.outputs.labels << { x: args.grid.w.half, y: args.grid.h - 10,
+ text: args.state.instructions,
+ alignment_enum: 1 }
+
+ # check if it's game over. if so, then render game over
+ # otherwise render the current time left
+ if game_over? args
+ args.outputs.labels << { x: args.grid.w.half,
+ y: args.grid.h - 40,
+ text: "game over! (press r to start over)",
+ alignment_enum: 1 }
+ else
+ args.outputs.labels << { x: args.grid.w.half,
+ y: args.grid.h - 40,
+ text: "time left: #{(args.state.count_down.idiv 60) + 1}",
+ alignment_enum: 1 }
+ end
+
+ # render the score
+ args.outputs.labels << { x: args.grid.w.half,
+ y: args.grid.h - 70,
+ text: "score: #{args.state.score}",
+ alignment_enum: 1 }
+
+ # render the player with teleport count
+ args.outputs.sprites << { x: args.state.player.x,
+ y: args.state.player.y,
+ w: args.state.player.w,
+ h: args.state.player.h,
+ path: 'sprites/square-green.png' }
+
+ args.outputs.labels << { x: args.state.player.x + 10,
+ y: args.state.player.y + 40,
+ text: "teleports: #{args.state.teleports}",
+ alignment_enum: 1, size_enum: -2 }
+
+ # render the target
+ args.outputs.sprites << { x: args.state.target.x,
+ y: args.state.target.y,
+ w: args.state.target.w,
+ h: args.state.target.h,
+ path: 'sprites/square-red.png' }
+
+ # ====================================================
+ # run simulation
+ # ====================================================
+
+ # count down calculation
+ args.state.count_down -= 1
+ args.state.count_down = -1 if args.state.count_down < -1
+
+ # ====================================================
+ # process player input
+ # ====================================================
+ # if it isn't game over let them move
+ if !game_over? args
+ dir_y = 0
+ dir_x = 0
+
+ # determine the change horizontally
+ if args.inputs.keyboard.up
+ dir_y += args.state.player_speed
+ elsif args.inputs.keyboard.down
+ dir_y -= args.state.player_speed
+ end
+
+ # determine the change vertically
+ if args.inputs.keyboard.left
+ dir_x -= args.state.player_speed
+ elsif args.inputs.keyboard.right
+ dir_x += args.state.player_speed
+ end
+
+ # determine if teleport can be used
+ if args.inputs.keyboard.key_down.space && args.state.teleports > 0
+ args.state.teleports -= 1
+ dir_x *= 20
+ dir_y *= 20
+ end
+
+ # apply change to player
+ args.state.player.x += dir_x
+ args.state.player.y += dir_y
+ else
+ # if r is pressed, reset the game
+ if args.inputs.keyboard.key_down.r
+ $gtk.reset
+ return
+ end
+ end
+
+ # ====================================================
+ # determine score
+ # ====================================================
+
+ # calculate new score if the player is at goal
+ if !game_over? args
+
+ # if the player is at the goal, then move the goal
+ if args.state.player.intersect_rect? args.state.target
+ # increment the goal
+ args.state.score += 1
+
+ # move the goal to a random location
+ args.state.target = { x: (rand args.grid.w), y: (rand args.grid.h), w: 20, h: 20 }
+
+ # make sure the goal is inside the view area
+ if args.state.target.x < 0
+ args.state.target.x += 20
+ elsif args.state.target.x > 1280
+ args.state.target.x -= 20
+ end
+
+ # make sure the goal is inside the view area
+ if args.state.target.y < 0
+ args.state.target.y += 20
+ elsif args.state.target.y > 720
+ args.state.target.y -= 20
+ end
+ end
+ end
+end
+
+def game_over? args
+ args.state.count_down < 0
+end
+
+$gtk.reset
diff --git a/samples/99_genre_teenytiny/license.txt b/samples/99_genre_teenytiny/license.txt
new file mode 100644
index 0000000..100dcec
--- /dev/null
+++ b/samples/99_genre_teenytiny/license.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/99_genre_teenytiny/metadata/game_metadata.txt b/samples/99_genre_teenytiny/metadata/game_metadata.txt
new file mode 100644
index 0000000..1b03500
--- /dev/null
+++ b/samples/99_genre_teenytiny/metadata/game_metadata.txt
@@ -0,0 +1,6 @@
+#devid=myname
+#devtitle=My Name
+#gameid=mygame
+#gametitle=My Game
+#version=0.1
+#icon=metadata/icon.png
diff --git a/samples/99_genre_teenytiny/metadata/icon.png b/samples/99_genre_teenytiny/metadata/icon.png
new file mode 100644
index 0000000..e20e8c2
--- /dev/null
+++ b/samples/99_genre_teenytiny/metadata/icon.png
Binary files differ
diff --git a/samples/99_genre_teenytiny/sprites/square-green.png b/samples/99_genre_teenytiny/sprites/square-green.png
new file mode 100644
index 0000000..5ef7f75
--- /dev/null
+++ b/samples/99_genre_teenytiny/sprites/square-green.png
Binary files differ
diff --git a/samples/99_genre_teenytiny/sprites/square-red.png b/samples/99_genre_teenytiny/sprites/square-red.png
new file mode 100644
index 0000000..3ed5f13
--- /dev/null
+++ b/samples/99_genre_teenytiny/sprites/square-red.png
Binary files differ