red_bird 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +23 -0
- data/LICENSE.txt +21 -0
- data/README.md +47 -0
- data/Rakefile +10 -0
- data/bin/setup +8 -0
- data/ext/red_bird/bird.c +15 -0
- data/ext/red_bird/bird.h +10 -0
- data/ext/red_bird/color.c +95 -0
- data/ext/red_bird/color.h +27 -0
- data/ext/red_bird/dynamic_sprite.c +163 -0
- data/ext/red_bird/dynamic_sprite.h +30 -0
- data/ext/red_bird/engine.c +354 -0
- data/ext/red_bird/engine.h +40 -0
- data/ext/red_bird/extconf.rb +9 -0
- data/ext/red_bird/font.c +94 -0
- data/ext/red_bird/font.h +26 -0
- data/ext/red_bird/input_device.c +100 -0
- data/ext/red_bird/input_device.h +15 -0
- data/ext/red_bird/keycode.c +42 -0
- data/ext/red_bird/keycode.h +12 -0
- data/ext/red_bird/loader.c +154 -0
- data/ext/red_bird/loader.h +54 -0
- data/ext/red_bird/main.c +38 -0
- data/ext/red_bird/main.h +12 -0
- data/ext/red_bird/palette.c +132 -0
- data/ext/red_bird/palette.h +23 -0
- data/ext/red_bird/rect.c +257 -0
- data/ext/red_bird/rect.h +20 -0
- data/ext/red_bird/render.c +130 -0
- data/ext/red_bird/render.h +25 -0
- data/ext/red_bird/sprite.c +130 -0
- data/ext/red_bird/sprite.h +27 -0
- data/ext/red_bird/text.c +212 -0
- data/ext/red_bird/text.h +31 -0
- data/ext/red_bird/texture.c +157 -0
- data/ext/red_bird/texture.h +33 -0
- data/ext/red_bird/texture_imp.cpp +49 -0
- data/ext/red_bird/texture_imp.hpp +29 -0
- data/ext/red_bird/timer.c +134 -0
- data/ext/red_bird/timer.h +25 -0
- data/lib/red_bird.rb +15 -0
- data/lib/red_bird/animation.rb +133 -0
- data/lib/red_bird/camera.rb +61 -0
- data/lib/red_bird/controller.rb +44 -0
- data/lib/red_bird/dynamic_sprite.rb +38 -0
- data/lib/red_bird/engine.rb +81 -0
- data/lib/red_bird/entity.rb +74 -0
- data/lib/red_bird/entity_collision.rb +31 -0
- data/lib/red_bird/input_device.rb +86 -0
- data/lib/red_bird/palette.rb +23 -0
- data/lib/red_bird/relative_entity.rb +95 -0
- data/lib/red_bird/sprite.rb +40 -0
- data/lib/red_bird/stage.rb +60 -0
- data/lib/red_bird/tile_map.rb +118 -0
- data/lib/red_bird/tile_set.rb +56 -0
- data/lib/red_bird/uibox.rb +143 -0
- data/lib/red_bird/version.rb +3 -0
- data/lib/red_bird/vertical_menu.rb +110 -0
- data/red_bird.gemspec +37 -0
- metadata +149 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
module RedBird
|
3
|
+
# A Controller receives commands from a {RedBird::InputDevice} and executes
|
4
|
+
# codes associated with those commands. Every {RedBird::InputDevice} instance
|
5
|
+
# has a variable called +@controller+ that must point to an instance of this
|
6
|
+
# class.
|
7
|
+
#
|
8
|
+
# The purpose of this class is to allow you to change the effect of user
|
9
|
+
# input by merely changing the currently active controller; you would do so,
|
10
|
+
# for example, when the user pauses the game, or when the user's character
|
11
|
+
# enters a vehicle.
|
12
|
+
#
|
13
|
+
# @see RedBird::InputDevice
|
14
|
+
# @author Frederico Linhares
|
15
|
+
class Controller
|
16
|
+
def initialize
|
17
|
+
@commands = {}
|
18
|
+
|
19
|
+
# By default, quit game when window is closed or another signal to end
|
20
|
+
# the game is given.
|
21
|
+
self.add_command(:quit_game) do
|
22
|
+
Engine::quit_game
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Use this method to create an association between a command and a code
|
27
|
+
# block.
|
28
|
+
#
|
29
|
+
# @param command [Symbol]
|
30
|
+
# @param function
|
31
|
+
# @author Frederico Linhares
|
32
|
+
def add_command(command, &function)
|
33
|
+
@commands[command] = function
|
34
|
+
end
|
35
|
+
|
36
|
+
# Execute a code block associated with a command.
|
37
|
+
#
|
38
|
+
# @param command [Symbol]
|
39
|
+
# @author Frederico Linhares
|
40
|
+
def call_command(command)
|
41
|
+
@commands[command].call unless @commands[command].nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
module RedBird
|
3
|
+
# A sprite is rectangular part of an texture.
|
4
|
+
class DynamicSprite
|
5
|
+
# Create an array of sprites and return it. The array has homogeneous
|
6
|
+
# sprites that do not overlap and have no gaps between them.
|
7
|
+
#
|
8
|
+
# To create a grid starting from the position x 20, y 20, containing three
|
9
|
+
# columns and two rows, each sprite, having 100x80 pixels:
|
10
|
+
#
|
11
|
+
# RedBird::Texture.grid sprite, 20, 20, 3, 2, 100, 80
|
12
|
+
#
|
13
|
+
# @param texture [RedBird::Texture] a texture that will be used as the
|
14
|
+
# source for all sprites.
|
15
|
+
# @param init_x [Integer] initial x coordinate for the grid.
|
16
|
+
# @param init_y [Integer] initial y coordinate for the grid.
|
17
|
+
# @param num_hor_sprites [Integer] number of columns the grid will have.
|
18
|
+
# @param num_ver_sprites [Integer] number of rows the grid will have.
|
19
|
+
# @param width [Integer] width of every sprite.
|
20
|
+
# @param height [Integer] height of every sprite.
|
21
|
+
# @param mods [Hash]
|
22
|
+
# @return [Array]
|
23
|
+
# @author Frederico Linhares
|
24
|
+
def self.grid(texture, init_x, init_y, num_hor_sprites, num_ver_sprites,
|
25
|
+
width, height, mods)
|
26
|
+
sprites = []
|
27
|
+
num_ver_sprites.times do |v|
|
28
|
+
num_hor_sprites.times do |h|
|
29
|
+
sprites << new(
|
30
|
+
texture, init_x + h * width, init_y + v * height, width, height,
|
31
|
+
mods)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
return sprites
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
module RedBird
|
3
|
+
module Engine
|
4
|
+
# Maximum frames per second. The Engine will try to maintain the game
|
5
|
+
# running at the frame rate defined by this variable.
|
6
|
+
@@max_fps = 30
|
7
|
+
|
8
|
+
# Set frames per second.
|
9
|
+
#
|
10
|
+
# @param fps [Integer]
|
11
|
+
# @author Frederico Linhares
|
12
|
+
def self.fps=(fps)
|
13
|
+
@@max_fps = fps
|
14
|
+
end
|
15
|
+
|
16
|
+
# Get frames per second.
|
17
|
+
#
|
18
|
+
# @return [Integer]frames per second.
|
19
|
+
def self.fps
|
20
|
+
return @@max_fps
|
21
|
+
end
|
22
|
+
|
23
|
+
# Exit the current stage and end the {Engine#run} loop.
|
24
|
+
#
|
25
|
+
# @author Frederico Linhares
|
26
|
+
def self.quit_game
|
27
|
+
@@quit_game = true
|
28
|
+
@@quit_stage = true
|
29
|
+
end
|
30
|
+
|
31
|
+
# Exit the current stage. The code block sent to {Engine#run} will be
|
32
|
+
# called again to get the next stage.
|
33
|
+
#
|
34
|
+
# @author Frederico Linhares
|
35
|
+
def self.quit_stage
|
36
|
+
@@quit_stage = true
|
37
|
+
end
|
38
|
+
|
39
|
+
# This method initializes the engine, executes the main loop, and returns
|
40
|
+
# when the game is over. This function calls the code block to get the
|
41
|
+
# first stage of the game and call it again every time a stage end to get
|
42
|
+
# the next one.
|
43
|
+
#
|
44
|
+
# @param global_data [Object] any object that you want to share between the
|
45
|
+
# game. This method sends this object back to the code block received.
|
46
|
+
# @yield [global_data] give +global_data+ to the block and expect it to
|
47
|
+
# return the next stage.
|
48
|
+
def self.run(global_data, &stage_manager)
|
49
|
+
@@quit_game = false
|
50
|
+
|
51
|
+
self.load do
|
52
|
+
while not @@quit_game do
|
53
|
+
@@quit_stage = false
|
54
|
+
|
55
|
+
timer = Timer.new(@@max_fps)
|
56
|
+
clear_color = Color.new(0, 0, 0, 0)
|
57
|
+
current_stage = stage_manager.call(global_data)
|
58
|
+
|
59
|
+
# The best moment to cleanup memory is before the engine loads a new
|
60
|
+
# stage.
|
61
|
+
GC.start
|
62
|
+
|
63
|
+
while not @@quit_stage do
|
64
|
+
timer.tick
|
65
|
+
|
66
|
+
current_stage.input_device.exec_events(current_stage.entities)
|
67
|
+
|
68
|
+
current_stage.pre_tick
|
69
|
+
current_stage.entities.each { |e| e.tick }
|
70
|
+
current_stage.post_tick
|
71
|
+
|
72
|
+
Render.set_color(clear_color)
|
73
|
+
Render.clear_screen
|
74
|
+
current_stage.entities.each { |e| e.render }
|
75
|
+
Render.update_screen
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
module RedBird
|
3
|
+
# An entity is an object that is managed by the Engine; it has a size, a
|
4
|
+
# position, ticks, and is renderable. {RedBird::Engine.run} expects every
|
5
|
+
# single entity that it receives from {RedBird::Stage} to have these methods
|
6
|
+
# and attributes.
|
7
|
+
#
|
8
|
+
# @author Frederico Linhares
|
9
|
+
class Entity
|
10
|
+
# Position
|
11
|
+
#
|
12
|
+
# @return [Integer]
|
13
|
+
attr_reader :pos_x, :pos_y
|
14
|
+
# Size
|
15
|
+
#
|
16
|
+
# @return [Integer]
|
17
|
+
attr_reader :width, :height
|
18
|
+
# Entities with higher priorit are called first when the
|
19
|
+
# {RedBird::Engine.run} calls {#tick} and {#render}.
|
20
|
+
#
|
21
|
+
# @return [Integer]
|
22
|
+
attr_reader :priority
|
23
|
+
|
24
|
+
# The {RedBird::Engine.run} calls this method every single frame. Overwrite
|
25
|
+
# it if you want your entity to do something every frame.
|
26
|
+
#
|
27
|
+
# @author Frederico Linhares
|
28
|
+
def tick
|
29
|
+
# By default do nothing
|
30
|
+
end
|
31
|
+
|
32
|
+
# The {RedBird::Engine.run} calls this method whenever the cursor is over
|
33
|
+
# this entity. Overwrite this function if you want your entity to react to a
|
34
|
+
# cursor hover.
|
35
|
+
#
|
36
|
+
# @param x [Integer] cursor position.
|
37
|
+
# @param y [Integer] cursor position.
|
38
|
+
# @author Frederico Linhares
|
39
|
+
def cursor_hover(x, y)
|
40
|
+
# By default do nothing
|
41
|
+
end
|
42
|
+
|
43
|
+
# The {RedBird::Engine.run} calls this method whenever there is a click over
|
44
|
+
# this entity. Overwrite this function if you want your entity to react to a
|
45
|
+
# click.
|
46
|
+
#
|
47
|
+
# @param x [Integer] cursor position.
|
48
|
+
# @param y [Integer] cursor position.
|
49
|
+
# @author Frederico Linhares
|
50
|
+
def cursor_down(x, y)
|
51
|
+
# By default do nothing
|
52
|
+
end
|
53
|
+
|
54
|
+
# The {RedBird::Engine.run} calls this method whenever there is a cursor
|
55
|
+
# release over this entity. Overwrite this function if you want your entity
|
56
|
+
# to react to a cursor release.
|
57
|
+
#
|
58
|
+
# @param x [Integer] cursor position.
|
59
|
+
# @param y [Integer] cursor position.
|
60
|
+
# @author Frederico Linhares
|
61
|
+
def cursor_up(x, y)
|
62
|
+
# By default do nothing
|
63
|
+
end
|
64
|
+
|
65
|
+
# The {RedBird::Engine.run} calls this method to render this entity into
|
66
|
+
# the scree. Overwrite this method if you want to change how the entity
|
67
|
+
# renders into the screen.
|
68
|
+
#
|
69
|
+
# @author Frederico Linhares
|
70
|
+
def render
|
71
|
+
self.current_sprite.render_to_screen(self.pos_x, self.pos_y)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
module RedBird
|
3
|
+
# This class does collision tests between entities.
|
4
|
+
class EntityCollision
|
5
|
+
# @param entities [Array<RedBird::Entity>] array containing the entities to
|
6
|
+
# test for collusion.
|
7
|
+
# @author Frederico Linhares
|
8
|
+
def initialize(entities)
|
9
|
+
@entities = entities
|
10
|
+
end
|
11
|
+
|
12
|
+
# Loop over entities in the array and test if they collide. In the case of
|
13
|
+
# collision, it calls the method +collide+ of the entity and sends the
|
14
|
+
# entity it collided to as a single argument.
|
15
|
+
#
|
16
|
+
# @author Frederico Linhares
|
17
|
+
def call
|
18
|
+
# Calculate entities rote
|
19
|
+
@entities.each_with_index do |e1, i|
|
20
|
+
@entities.drop(i + 1).each do |e2|
|
21
|
+
if e1.collision_box.collide? e2.collision_box then
|
22
|
+
e1.collide(e2)
|
23
|
+
e2.collide(e1)
|
24
|
+
# If an object responds to 'collision_box' then it is tested.
|
25
|
+
end if e1.respond_to?('collision_box')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
module RedBird
|
3
|
+
# The InputDevice receives input from the keyboard, joystick, and mouse;
|
4
|
+
# then, it translates the input into a meaningful command name and sends it
|
5
|
+
# to a Controller. For example, you can use it to translate the 'space' key
|
6
|
+
# from the keyboard and an 'x' button from a controller into a +:jump+
|
7
|
+
# command.
|
8
|
+
#
|
9
|
+
# This class has an internal Hash that translates inputs into commands. For
|
10
|
+
# every input, it looks if there is a key associated with that input; it
|
11
|
+
# sends a command if such key exists; otherwise, it does nothing.
|
12
|
+
#
|
13
|
+
# @see RedBird::Controller
|
14
|
+
# @author Frederico Linhares
|
15
|
+
class InputDevice
|
16
|
+
# Defines a {RedBird::Controller} to receives commands from the input.
|
17
|
+
attr_writer :controller
|
18
|
+
|
19
|
+
# @param controller [RedBird::Controller]
|
20
|
+
# @author Frederico Linhares
|
21
|
+
def initialize(controller)
|
22
|
+
@controller = controller
|
23
|
+
@keydown_inputs = {}
|
24
|
+
@keyup_inputs = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
# Associates a keydown even to a command_name.
|
28
|
+
#
|
29
|
+
# @param keycode [Integer] use constants under {RedBird::Keycode}.
|
30
|
+
# @param command_name [Symbol]
|
31
|
+
# @author Frederico Linhares
|
32
|
+
def add_keydown(keycode, command_name)
|
33
|
+
@keydown_inputs[keycode] = command_name
|
34
|
+
end
|
35
|
+
|
36
|
+
# Associates a keyup even to a command_name.
|
37
|
+
#
|
38
|
+
# @param keycode [Integer] use constants under {RedBird::Keycode}.
|
39
|
+
# @param command_name [Symbol]
|
40
|
+
# @author Frederico Linhares
|
41
|
+
def add_keyup(keycode, command_name)
|
42
|
+
@keyup_inputs[keycode] = command_name
|
43
|
+
end
|
44
|
+
|
45
|
+
# Gets all events that are in the queue, translate then into commands, and
|
46
|
+
# send then to the current controller.
|
47
|
+
#
|
48
|
+
# @param entities [Array<RedBird::Entity>] entities that can receive events
|
49
|
+
# from mouse.
|
50
|
+
# @author Frederico Linhares
|
51
|
+
def exec_events(entities)
|
52
|
+
get_events do |type, data|
|
53
|
+
case type
|
54
|
+
when :key_down then
|
55
|
+
@controller.call_command(@keydown_inputs[data])
|
56
|
+
when :key_up then
|
57
|
+
@controller.call_command(@keyup_inputs[data])
|
58
|
+
when :mouse_motion then
|
59
|
+
entity = cursor_hover_entity(entities, data[:x], data[:y])
|
60
|
+
entity.cursor_hover(data[:x], data[:y]) unless entity.nil?
|
61
|
+
when :mouse_button_down then
|
62
|
+
entity = cursor_hover_entity(entities, data[:x], data[:y])
|
63
|
+
entity.cursor_down(data[:x], data[:y]) unless entity.nil?
|
64
|
+
when :mouse_button_up then
|
65
|
+
entity = cursor_hover_entity(entities, data[:x], data[:y])
|
66
|
+
entity.cursor_up(data[:x], data[:y]) unless entity.nil?
|
67
|
+
when :quit_game then
|
68
|
+
Engine::quit_game
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
# This function is called when user move the mouse to se if mouse is hover
|
75
|
+
# an entity.
|
76
|
+
def cursor_hover_entity(entities, x, y)
|
77
|
+
entities.each do |e|
|
78
|
+
if(x >= e.pos_x and x <= e.pos_x + e.width and
|
79
|
+
y >= e.pos_y and y <= e.pos_y + e.height) then
|
80
|
+
return e
|
81
|
+
end
|
82
|
+
end
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module RedBird
|
5
|
+
class Palette
|
6
|
+
# Load information from a YAML file and uses it to create an instance of this
|
7
|
+
# class
|
8
|
+
#
|
9
|
+
# @param tile_file [String] path to the yaml file.
|
10
|
+
# @return [RedBird::Palette]
|
11
|
+
# @author Frederico Linhares
|
12
|
+
def self.from_yml(palette_file)
|
13
|
+
data = YAML.load_file(palette_file)
|
14
|
+
|
15
|
+
# Convert data from YAML into Color instances.
|
16
|
+
colors = data.map do |i|
|
17
|
+
Color.new(i[0], i[1], i[2], i[3])
|
18
|
+
end
|
19
|
+
|
20
|
+
return self.new(colors)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
require_relative 'entity'
|
3
|
+
|
4
|
+
module RedBird
|
5
|
+
# The {RedBird::Entity} has an absolute position on the screen. Use this
|
6
|
+
# class if you want an entity that has a position relative to a scenario
|
7
|
+
# ({RedBird::TileMap}).
|
8
|
+
#
|
9
|
+
# @author Frederico Linhares
|
10
|
+
class RelativeEntity < Entity
|
11
|
+
# @return [RedBird::Rect]
|
12
|
+
attr_reader :collision_box
|
13
|
+
|
14
|
+
# @param relative_pos_x [Integer] position in the scenario
|
15
|
+
# @param relative_pos_y [Integer] position in the scenario
|
16
|
+
# @param width [Integer]
|
17
|
+
# @param height [Integer]
|
18
|
+
# @author Frederico Linhares
|
19
|
+
def initialize(relative_pos_x, relative_pos_y, width, height)
|
20
|
+
super()
|
21
|
+
|
22
|
+
@priority = 1
|
23
|
+
|
24
|
+
@collision_box = RedBird::Rect.new(
|
25
|
+
relative_pos_x, relative_pos_y, width, height)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Define which scenario to use as a reference for every
|
29
|
+
# {RedBird::RelativeEntity}.
|
30
|
+
#
|
31
|
+
# @param scenario [RedBird::TileMap]
|
32
|
+
# @author Frederico Linhares
|
33
|
+
def self.scenario=(scenario)
|
34
|
+
@@scenario = scenario
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [RedBird::TileMap]
|
38
|
+
# @author Frederico Linhares
|
39
|
+
def self.scenario
|
40
|
+
return @@scenario
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Integer] position in the scenario
|
44
|
+
# @author Frederico Linhares
|
45
|
+
def relative_pos_x
|
46
|
+
return @collision_box.x
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Integer] position in the scenario
|
50
|
+
# @author Frederico Linhares
|
51
|
+
def relative_pos_y
|
52
|
+
return @collision_box.y
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param x [Integer] position in the scenario
|
56
|
+
# @author Frederico Linhares
|
57
|
+
def relative_pos_x=(x)
|
58
|
+
@collision_box.x = x
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param y [Integer] position in the scenario
|
62
|
+
# @author Frederico Linhares
|
63
|
+
def relative_pos_y=(y)
|
64
|
+
@collision_box.y = y
|
65
|
+
end
|
66
|
+
|
67
|
+
# Gets absolute position (position in the screen).
|
68
|
+
#
|
69
|
+
# @return [Float] position in the screen
|
70
|
+
# @author Frederico Linhares
|
71
|
+
def pos_x
|
72
|
+
@collision_box.x - @@scenario.hor_offset + @@scenario.pos_x
|
73
|
+
end
|
74
|
+
|
75
|
+
# Gets absolute position (position in the screen).
|
76
|
+
#
|
77
|
+
# @return [Float] position in the screen
|
78
|
+
# @author Frederico Linhares
|
79
|
+
def pos_y
|
80
|
+
@collision_box.y - @@scenario.ver_offset + @@scenario.pos_y
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Integer]
|
84
|
+
# @author Frederico Linhares
|
85
|
+
def width
|
86
|
+
@collision_box.width
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Integer]
|
90
|
+
# @author Frederico Linhares
|
91
|
+
def height
|
92
|
+
@collision_box.height
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|