rgame 0.4.0-x86_64-linux-gnu
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/CHANGELOG.md +251 -0
- data/LICENSE +26 -0
- data/README.md +325 -0
- data/docs/api/README.md +224 -0
- data/docs/api/app.md +249 -0
- data/docs/api/assets.md +463 -0
- data/docs/api/audio.md +249 -0
- data/docs/api/cli.md +259 -0
- data/docs/api/components.md +1112 -0
- data/docs/api/drawing.md +398 -0
- data/docs/api/examples.md +263 -0
- data/docs/api/game.md +219 -0
- data/docs/api/images.md +118 -0
- data/docs/api/input.md +476 -0
- data/docs/api/internals.md +289 -0
- data/docs/api/localization.md +285 -0
- data/docs/api/scene_graph.md +539 -0
- data/docs/api/signals.md +147 -0
- data/docs/api/systems.md +231 -0
- data/docs/api/text.md +133 -0
- data/docs/api/tile_maps.md +237 -0
- data/docs/api/toolbox.md +493 -0
- data/docs/api/ui.md +927 -0
- data/docs/api/values.md +316 -0
- data/examples/assets/README.md +322 -0
- data/examples/assets/blip.ogg +0 -0
- data/examples/assets/glyphs.json +5 -0
- data/examples/assets/glyphs.png +0 -0
- data/examples/assets/hero.json +12 -0
- data/examples/assets/hero.png +0 -0
- data/examples/assets/icons.json +13 -0
- data/examples/assets/icons.png +0 -0
- data/examples/assets/music.ogg +0 -0
- data/examples/assets/skills.json +10 -0
- data/examples/assets/skills.png +0 -0
- data/examples/assets/tileset.png +0 -0
- data/examples/assets/tileset.tsx +65 -0
- data/examples/assets/town.tmx +26 -0
- data/examples/assets/ui.json +11 -0
- data/examples/assets/ui.png +0 -0
- data/examples/collision/locales/en.yml +8 -0
- data/examples/collision/main.rb +316 -0
- data/examples/collision_tiles/locales/en.yml +9 -0
- data/examples/collision_tiles/main.rb +274 -0
- data/examples/fullscreen/locales/en.yml +10 -0
- data/examples/fullscreen/main.rb +216 -0
- data/examples/game_menu/locales/en.yml +8 -0
- data/examples/game_menu/main.rb +170 -0
- data/examples/input_glyphs/locales/en.yml +14 -0
- data/examples/input_glyphs/main.rb +213 -0
- data/examples/jump_topdown/locales/en.yml +9 -0
- data/examples/jump_topdown/main.rb +178 -0
- data/examples/localization/locales/de.yml +12 -0
- data/examples/localization/locales/en.yml +13 -0
- data/examples/localization/main.rb +158 -0
- data/examples/menu_navigation/locales/en.yml +23 -0
- data/examples/menu_navigation/main.rb +365 -0
- data/examples/music/locales/en.yml +7 -0
- data/examples/music/main.rb +134 -0
- data/examples/pathfinding/locales/en.yml +17 -0
- data/examples/pathfinding/main.rb +298 -0
- data/examples/pooling/locales/en.yml +7 -0
- data/examples/pooling/main.rb +259 -0
- data/examples/quick_wheel/locales/en.yml +16 -0
- data/examples/quick_wheel/main.rb +184 -0
- data/examples/radial_menu/locales/en.yml +16 -0
- data/examples/radial_menu/main.rb +184 -0
- data/examples/save_load/locales/en.yml +11 -0
- data/examples/save_load/main.rb +207 -0
- data/examples/save_load_ids/locales/en.yml +11 -0
- data/examples/save_load_ids/main.rb +322 -0
- data/examples/scroll_map/locales/en.yml +4 -0
- data/examples/scroll_map/main.rb +140 -0
- data/examples/signals/locales/en.yml +6 -0
- data/examples/signals/main.rb +278 -0
- data/examples/skill_bar/locales/en.yml +14 -0
- data/examples/skill_bar/main.rb +159 -0
- data/examples/sound/locales/en.yml +6 -0
- data/examples/sound/main.rb +122 -0
- data/examples/split_screen/locales/en.yml +9 -0
- data/examples/split_screen/main.rb +304 -0
- data/examples/sprite/locales/en.yml +8 -0
- data/examples/sprite/main.rb +180 -0
- data/examples/timer/locales/en.yml +12 -0
- data/examples/timer/main.rb +273 -0
- data/examples/velocity/locales/en.yml +6 -0
- data/examples/velocity/main.rb +196 -0
- data/examples/walk/locales/en.yml +4 -0
- data/examples/walk/main.rb +99 -0
- data/exe/rgame +9 -0
- data/lib/rgame/boot.rb +3 -0
- data/lib/rgame/cli/new_project.rb +139 -0
- data/lib/rgame/cli/templates/Gemfile.tt +23 -0
- data/lib/rgame/cli/templates/README.md.tt +93 -0
- data/lib/rgame/cli/templates/Rakefile.tt +9 -0
- data/lib/rgame/cli/templates/assets/locales/en.yml.tt +10 -0
- data/lib/rgame/cli/templates/game.rb.tt +23 -0
- data/lib/rgame/cli/templates/gitignore.tt +12 -0
- data/lib/rgame/cli/templates/main.rb.tt +11 -0
- data/lib/rgame/cli/templates/nodes/root.rb.tt +24 -0
- data/lib/rgame/cli/templates/rspec.tt +2 -0
- data/lib/rgame/cli/templates/rubocop.yml.tt +75 -0
- data/lib/rgame/cli/templates/ruby-version.tt +1 -0
- data/lib/rgame/cli/templates/spec/locales_spec.rb.tt +18 -0
- data/lib/rgame/cli/templates/spec/nodes/root_spec.rb.tt +18 -0
- data/lib/rgame/cli/templates/spec/spec_helper.rb.tt +37 -0
- data/lib/rgame/cli.rb +66 -0
- data/lib/rgame/core/app.rb +46 -0
- data/lib/rgame/core/asset_manager.rb +206 -0
- data/lib/rgame/core/audio.rb +145 -0
- data/lib/rgame/core/font.rb +46 -0
- data/lib/rgame/core/gamepad.rb +55 -0
- data/lib/rgame/core/image.rb +55 -0
- data/lib/rgame/core/input.rb +71 -0
- data/lib/rgame/core/locale.rb +22 -0
- data/lib/rgame/core/nine_slice.rb +142 -0
- data/lib/rgame/core/recording.rb +54 -0
- data/lib/rgame/core/renderer.rb +354 -0
- data/lib/rgame/core/sprite_sheet.rb +105 -0
- data/lib/rgame/core/tile_map_renderer.rb +172 -0
- data/lib/rgame/core/ui_atlas.rb +101 -0
- data/lib/rgame/core/virtual_gamepad.rb +26 -0
- data/lib/rgame/core.rb +18 -0
- data/lib/rgame/core_ext.so +0 -0
- data/lib/rgame/engine/actor_blockers.rb +131 -0
- data/lib/rgame/engine/animation_set.rb +50 -0
- data/lib/rgame/engine/animator.rb +44 -0
- data/lib/rgame/engine/audio_bus.rb +24 -0
- data/lib/rgame/engine/audio_director.rb +59 -0
- data/lib/rgame/engine/bounds_blockers.rb +74 -0
- data/lib/rgame/engine/camera.rb +78 -0
- data/lib/rgame/engine/circle_collider.rb +34 -0
- data/lib/rgame/engine/collision_box.rb +59 -0
- data/lib/rgame/engine/collision_system.rb +132 -0
- data/lib/rgame/engine/component.rb +64 -0
- data/lib/rgame/engine/components/action_trigger.rb +40 -0
- data/lib/rgame/engine/components/animated_sprite.rb +71 -0
- data/lib/rgame/engine/components/box_collider.rb +99 -0
- data/lib/rgame/engine/components/camera_follow.rb +45 -0
- data/lib/rgame/engine/components/character_body.rb +50 -0
- data/lib/rgame/engine/components/circle_collider.rb +80 -0
- data/lib/rgame/engine/components/collision_world.rb +231 -0
- data/lib/rgame/engine/components/despawn_offscreen.rb +42 -0
- data/lib/rgame/engine/components/feet_collider.rb +61 -0
- data/lib/rgame/engine/components/hop.rb +76 -0
- data/lib/rgame/engine/components/identity.rb +73 -0
- data/lib/rgame/engine/components/mover.rb +285 -0
- data/lib/rgame/engine/components/navigator.rb +145 -0
- data/lib/rgame/engine/components/path_follow.rb +176 -0
- data/lib/rgame/engine/components/player_controller.rb +27 -0
- data/lib/rgame/engine/components/pool.rb +53 -0
- data/lib/rgame/engine/components/screen_wrap.rb +49 -0
- data/lib/rgame/engine/components/sprite.rb +47 -0
- data/lib/rgame/engine/components/targeting.rb +52 -0
- data/lib/rgame/engine/components/thrust_controller.rb +65 -0
- data/lib/rgame/engine/components/tile_world.rb +109 -0
- data/lib/rgame/engine/components/timer.rb +75 -0
- data/lib/rgame/engine/components/velocity.rb +43 -0
- data/lib/rgame/engine/components/wander_controller.rb +64 -0
- data/lib/rgame/engine/components/world.rb +133 -0
- data/lib/rgame/engine/contact_set.rb +74 -0
- data/lib/rgame/engine/culling.rb +45 -0
- data/lib/rgame/engine/debug_overlay.rb +111 -0
- data/lib/rgame/engine/i18n/plural.rb +45 -0
- data/lib/rgame/engine/i18n/plural_rules.rb +82 -0
- data/lib/rgame/engine/i18n/template.rb +59 -0
- data/lib/rgame/engine/i18n.rb +322 -0
- data/lib/rgame/engine/input/action_mapper.rb +100 -0
- data/lib/rgame/engine/input/actions.rb +92 -0
- data/lib/rgame/engine/input/input_map.rb +196 -0
- data/lib/rgame/engine/layout.rb +82 -0
- data/lib/rgame/engine/nav_grid.rb +87 -0
- data/lib/rgame/engine/node2d.rb +555 -0
- data/lib/rgame/engine/path.rb +76 -0
- data/lib/rgame/engine/player.rb +69 -0
- data/lib/rgame/engine/player_layer.rb +70 -0
- data/lib/rgame/engine/players.rb +205 -0
- data/lib/rgame/engine/pool.rb +51 -0
- data/lib/rgame/engine/presentation.rb +171 -0
- data/lib/rgame/engine/scene/scene_stack.rb +87 -0
- data/lib/rgame/engine/sealed_privates.rb +54 -0
- data/lib/rgame/engine/signal.rb +75 -0
- data/lib/rgame/engine/spatial_hash.rb +116 -0
- data/lib/rgame/engine/text.rb +194 -0
- data/lib/rgame/engine/tile_blockers.rb +63 -0
- data/lib/rgame/engine/tile_map.rb +147 -0
- data/lib/rgame/engine/tile_map_layer.rb +82 -0
- data/lib/rgame/engine/tileset.rb +99 -0
- data/lib/rgame/engine/timer.rb +51 -0
- data/lib/rgame/engine/ui/button.rb +248 -0
- data/lib/rgame/engine/ui/column.rb +20 -0
- data/lib/rgame/engine/ui/icon_button.rb +93 -0
- data/lib/rgame/engine/ui/menu.rb +290 -0
- data/lib/rgame/engine/ui/navigation.rb +57 -0
- data/lib/rgame/engine/ui/nine_slice_style.rb +50 -0
- data/lib/rgame/engine/ui/option_button.rb +163 -0
- data/lib/rgame/engine/ui/panel_button.rb +32 -0
- data/lib/rgame/engine/ui/panel_menu.rb +36 -0
- data/lib/rgame/engine/ui/pointing.rb +146 -0
- data/lib/rgame/engine/ui/radial_menu.rb +85 -0
- data/lib/rgame/engine/ui/ring.rb +55 -0
- data/lib/rgame/engine/ui/row.rb +21 -0
- data/lib/rgame/engine/ui/shape_style.rb +102 -0
- data/lib/rgame/engine/ui/stack.rb +58 -0
- data/lib/rgame/engine/ui/stepping.rb +93 -0
- data/lib/rgame/engine/ui/text_button.rb +59 -0
- data/lib/rgame/engine/view.rb +76 -0
- data/lib/rgame/engine/viewports.rb +171 -0
- data/lib/rgame/engine/world_view.rb +71 -0
- data/lib/rgame/engine.rb +86 -0
- data/lib/rgame/fonts/LiberationSans-Regular.ttf +0 -0
- data/lib/rgame/fonts/OFL.txt +102 -0
- data/lib/rgame/game.rb +271 -0
- data/lib/rgame/rubocop/cop/game/draw_in_local_space.rb +103 -0
- data/lib/rgame/rubocop/cop/game/hot_path.rb +36 -0
- data/lib/rgame/rubocop/cop/game/layer_boundary.rb +43 -0
- data/lib/rgame/rubocop/cop/game/no_core_in_engine_layer.rb +100 -0
- data/lib/rgame/rubocop/cop/game/no_engine_in_core_layer.rb +84 -0
- data/lib/rgame/rubocop/cop/game/no_interpolation_in_hot_path.rb +50 -0
- data/lib/rgame/rubocop/cop/game/no_literal_text.rb +41 -0
- data/lib/rgame/rubocop/cop/game/no_needless_allocation.rb +112 -0
- data/lib/rgame/rubocop/default.yml +39 -0
- data/lib/rgame/rubocop/plugin.rb +45 -0
- data/lib/rgame/rubocop.rb +11 -0
- data/lib/rgame/util/color.rb +23 -0
- data/lib/rgame/util/controls.rb +169 -0
- data/lib/rgame/util/route_search.rb +27 -0
- data/lib/rgame/util/save_file.rb +107 -0
- data/lib/rgame/util/solid_grid.rb +37 -0
- data/lib/rgame/util/tensor.rb +3 -0
- data/lib/rgame/util/tile_sweep.rb +36 -0
- data/lib/rgame/util/z.rb +123 -0
- data/lib/rgame/util.rb +10 -0
- data/lib/rgame/util_ext.so +0 -0
- data/lib/rgame/version.rb +12 -0
- data/lib/rgame.rb +5 -0
- data/licenses/SDL2/LICENSE.txt +18 -0
- metadata +309 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RGame
|
|
4
|
+
module Engine
|
|
5
|
+
# One viewport being drawn: a rectangle of the screen, and (for a world
|
|
6
|
+
# view) the camera to look through it with.
|
|
7
|
+
#
|
|
8
|
+
# view.x, view.y, view.width, view.height # the screen rect
|
|
9
|
+
# view.camera # nil in screen space
|
|
10
|
+
# view.player # whose view this is, or nil
|
|
11
|
+
# view.visible?(x, y, w, h) # is this worth drawing
|
|
12
|
+
#
|
|
13
|
+
# ## What a view is for
|
|
14
|
+
#
|
|
15
|
+
# `draw(renderer, view)` hands every node the view it is being drawn into,
|
|
16
|
+
# which answers two questions nothing else can. **Where the edges are**: a
|
|
17
|
+
# HUD laying itself out against the whole window is wrong the moment the
|
|
18
|
+
# window is a player's half of one, and until this existed there was nothing
|
|
19
|
+
# else to ask. And **what is worth drawing**: with the world drawn once per
|
|
20
|
+
# player, culling stops being an optimisation and starts being the
|
|
21
|
+
# difference between one frame's work and four.
|
|
22
|
+
#
|
|
23
|
+
# ## It is reused, not rebuilt
|
|
24
|
+
#
|
|
25
|
+
# Viewports owns one of these per viewport and mutates it in place each
|
|
26
|
+
# frame, the way ActionMapper reuses its Actions. Building fresh ones would
|
|
27
|
+
# allocate a handful of objects every frame — invisible by every measure
|
|
28
|
+
# except the frame that stutters. **Hold the player or the viewports, never
|
|
29
|
+
# this**: the object a node was handed last frame is the same one, with
|
|
30
|
+
# different numbers in it.
|
|
31
|
+
class View
|
|
32
|
+
attr_reader :x, :y, :width, :height, :camera, :player
|
|
33
|
+
|
|
34
|
+
def initialize(x: 0, y: 0, width: 0, height: 0, camera: nil, player: nil)
|
|
35
|
+
set(x, y, width, height, camera: camera, player: player)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Mutated in place by Viewports once per frame. Not for game code.
|
|
39
|
+
def set(x, y, width, height, camera: nil, player: nil)
|
|
40
|
+
@x = x
|
|
41
|
+
@y = y
|
|
42
|
+
@width = width
|
|
43
|
+
@height = height
|
|
44
|
+
@camera = camera
|
|
45
|
+
@player = player
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Where this view's contents start, in the space its nodes draw in: the
|
|
50
|
+
# camera's offset for a world view, and the origin for a screen-space one,
|
|
51
|
+
# whose nodes draw relative to the view's own corner.
|
|
52
|
+
# hot-path
|
|
53
|
+
def origin_x = @camera ? @camera.x : 0
|
|
54
|
+
# hot-path
|
|
55
|
+
def origin_y = @camera ? @camera.y : 0
|
|
56
|
+
|
|
57
|
+
# Does a rectangle overlap what this view shows? Coordinates are in the
|
|
58
|
+
# space the caller draws in — world coordinates under a camera, view-local
|
|
59
|
+
# ones in screen space — which is the same space `origin_x` is in.
|
|
60
|
+
# hot-path
|
|
61
|
+
def visible?(x, y, width, height)
|
|
62
|
+
left = origin_x
|
|
63
|
+
top = origin_y
|
|
64
|
+
x + width > left && x < left + @width &&
|
|
65
|
+
y + height > top && y < top + @height
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The offset to translate by so that content at `origin` lands at this
|
|
69
|
+
# view's corner on screen. The whole of split-screen, in two numbers.
|
|
70
|
+
# hot-path
|
|
71
|
+
def offset_x = @x - origin_x
|
|
72
|
+
# hot-path
|
|
73
|
+
def offset_y = @y - origin_y
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RGame
|
|
4
|
+
module Engine
|
|
5
|
+
# How the screen is divided, as a root-scoped system.
|
|
6
|
+
#
|
|
7
|
+
# node.system(Viewports).views # one View per active player
|
|
8
|
+
# node.system(Viewports).screen # the whole window, no camera
|
|
9
|
+
# node.system(Viewports).solo!(cam) # collapse to one view — cutscene
|
|
10
|
+
#
|
|
11
|
+
# It holds the mutable half of the question — which mode is current, who is
|
|
12
|
+
# playing, how big the window is — while Layout holds the arithmetic. That
|
|
13
|
+
# split is deliberate: the rect maths is pure and gets specced with no tree
|
|
14
|
+
# and no window, and only the state that genuinely changes lives in a
|
|
15
|
+
# component.
|
|
16
|
+
#
|
|
17
|
+
# It is a Component on the root, so any node reaches it by walking the tree
|
|
18
|
+
# rather than having it threaded through a constructor — the same shape
|
|
19
|
+
# CollisionWorld and Players use.
|
|
20
|
+
#
|
|
21
|
+
# ## Mode changes are deferred
|
|
22
|
+
#
|
|
23
|
+
# `solo!` and `split!` record a request; it is applied in `update`, which
|
|
24
|
+
# runs in the root's component phase. That matters because this is reachable
|
|
25
|
+
# from anywhere, including from a `draw` — and a `draw` now runs once per
|
|
26
|
+
# view, so a mode change made there would fire several times and tear the
|
|
27
|
+
# frame it was made in. Deferring is the same shape `queue_free` uses, and it
|
|
28
|
+
# means a change takes effect on the next tick.
|
|
29
|
+
class Viewports < Component
|
|
30
|
+
# `views` is the list drawn through this frame — one per active player
|
|
31
|
+
# while split, exactly one while solo. Reused, like the Views in it.
|
|
32
|
+
attr_reader :screen, :width, :height, :views
|
|
33
|
+
|
|
34
|
+
def initialize(players, width: 0, height: 0)
|
|
35
|
+
super()
|
|
36
|
+
@players = players
|
|
37
|
+
@width = width
|
|
38
|
+
@height = height
|
|
39
|
+
@solo_camera = nil
|
|
40
|
+
@pending = nil
|
|
41
|
+
@pool = []
|
|
42
|
+
@screen_pool = []
|
|
43
|
+
@views = []
|
|
44
|
+
@screen = View.new
|
|
45
|
+
refresh
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The window changed size. Rects are recomputed from it on the next
|
|
49
|
+
# refresh, and every camera reclamps against its new rect — which is why
|
|
50
|
+
# a camera does not carry one.
|
|
51
|
+
def resize(width, height)
|
|
52
|
+
@width = width
|
|
53
|
+
@height = height
|
|
54
|
+
refresh
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def solo? = !@solo_camera.nil?
|
|
58
|
+
|
|
59
|
+
# The screen-space region belonging to `player`: the same rectangle their
|
|
60
|
+
# world view is drawn into, with **no camera**, so its contents are laid
|
|
61
|
+
# out against their own corner rather than the window's. Their HUD and
|
|
62
|
+
# their menus live here.
|
|
63
|
+
#
|
|
64
|
+
# `nil` when they have no region to draw into, which is two cases and one
|
|
65
|
+
# answer. An **empty seat** has no viewport at all. And while the split is
|
|
66
|
+
# **collapsed**, nobody has a half of the screen to own: a cutscene is
|
|
67
|
+
# everyone looking at one thing, so per-player UI has no place to be, and
|
|
68
|
+
# a game wanting something on screen through it draws in the global
|
|
69
|
+
# overlay band instead.
|
|
70
|
+
#
|
|
71
|
+
# Returning nil rather than an empty rectangle is deliberate: one check at
|
|
72
|
+
# the one caller that needs it beats every caller relying on a zero-sized
|
|
73
|
+
# clip happening to draw nothing.
|
|
74
|
+
def screen_for(player)
|
|
75
|
+
return nil if player.nil? || @solo_camera
|
|
76
|
+
|
|
77
|
+
world = @views.find { |view| view.player.equal?(player) }
|
|
78
|
+
return nil if world.nil?
|
|
79
|
+
|
|
80
|
+
pooled_screen(@players.list.index(player))
|
|
81
|
+
.set(world.x, world.y, world.width, world.height, player: player)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Collapse to a single screen-wide view through `camera`.
|
|
85
|
+
#
|
|
86
|
+
# The camera is **required**: promoting one player's would silently give
|
|
87
|
+
# everyone else their view, and deciding what is on screen is what a
|
|
88
|
+
# cutscene is for. A game points an ordinary Camera wherever it likes —
|
|
89
|
+
# with a CameraFollow on a cutscene actor, or its own component framing
|
|
90
|
+
# every player at once — and hands it here.
|
|
91
|
+
def solo!(camera)
|
|
92
|
+
raise ArgumentError, 'solo! needs a camera to look through' if camera.nil?
|
|
93
|
+
|
|
94
|
+
@pending = camera
|
|
95
|
+
self
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Back to one view per player.
|
|
99
|
+
def split! = @pending = :split
|
|
100
|
+
|
|
101
|
+
# Applies a pending mode change, then rebuilds the rects. Runs in the
|
|
102
|
+
# root's component phase, so a change requested during a tick lands on the
|
|
103
|
+
# next one.
|
|
104
|
+
def update(_dt)
|
|
105
|
+
apply_pending
|
|
106
|
+
refresh
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Recompute every rect from the current mode and window, and reclamp each
|
|
110
|
+
# camera against the rect it is about to be drawn into.
|
|
111
|
+
#
|
|
112
|
+
# Allocation-free once the pool has grown: Layout yields its rects rather
|
|
113
|
+
# than building them, and the Views are mutated in place.
|
|
114
|
+
#
|
|
115
|
+
# @api private
|
|
116
|
+
def refresh
|
|
117
|
+
@screen.set(0, 0, @width, @height)
|
|
118
|
+
@solo_camera ? refresh_solo : refresh_split
|
|
119
|
+
@views.each { |view| view.camera&.resolve(view.width, view.height) }
|
|
120
|
+
self
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
|
|
125
|
+
def apply_pending
|
|
126
|
+
return if @pending.nil?
|
|
127
|
+
|
|
128
|
+
@solo_camera = @pending == :split ? nil : @pending
|
|
129
|
+
@pending = nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def refresh_solo
|
|
133
|
+
@views.clear
|
|
134
|
+
@views << pooled(0).set(0, 0, @width, @height, camera: @solo_camera)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def refresh_split
|
|
138
|
+
@views.clear
|
|
139
|
+
count = @players.active_count
|
|
140
|
+
return if count.zero?
|
|
141
|
+
|
|
142
|
+
index = 0
|
|
143
|
+
Layout.each_rect(count, @width, @height) do |i, x, y, w, h|
|
|
144
|
+
player = active_at(i)
|
|
145
|
+
@views << pooled(index).set(x, y, w, h, camera: player.camera, player: player)
|
|
146
|
+
index += 1
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def active_at(index)
|
|
151
|
+
found = nil
|
|
152
|
+
i = 0
|
|
153
|
+
@players.each do |player|
|
|
154
|
+
next unless player.active?
|
|
155
|
+
|
|
156
|
+
found = player if i == index
|
|
157
|
+
i += 1
|
|
158
|
+
end
|
|
159
|
+
found
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def pooled(index)
|
|
163
|
+
@pool[index] ||= View.new
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def pooled_screen(index)
|
|
167
|
+
@screen_pool[index] ||= View.new
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RGame
|
|
4
|
+
module Engine
|
|
5
|
+
# The node that marks where world space begins.
|
|
6
|
+
#
|
|
7
|
+
# view = scene.add_node(WorldView.new)
|
|
8
|
+
# view.add_node(player) # draws at its own world coordinates
|
|
9
|
+
#
|
|
10
|
+
# Its children live in **world** coordinates and are drawn once per active
|
|
11
|
+
# viewport: for each one, clip to that viewport's rectangle and translate by
|
|
12
|
+
# its camera, then run the same subtree again. Everything outside a
|
|
13
|
+
# WorldView is screen space and draws once.
|
|
14
|
+
#
|
|
15
|
+
# That is the whole of split-screen, and it is why the transform and clip
|
|
16
|
+
# stacks in Core are proper push/pop stacks — a clip always narrows, so a
|
|
17
|
+
# child can never draw outside the region its parent allowed.
|
|
18
|
+
#
|
|
19
|
+
# ## The subtree does not know how many times it is drawn
|
|
20
|
+
#
|
|
21
|
+
# Children draw in their own local space and never see a camera. This node
|
|
22
|
+
# supplies the view — one clip and one translate around the whole subtree —
|
|
23
|
+
# so the same world serves one player or four with nothing below it changing,
|
|
24
|
+
# which is exactly what a camera owned by a node *inside* the world could not
|
|
25
|
+
# do, and why cameras belong to players.
|
|
26
|
+
#
|
|
27
|
+
# ## Only `draw` multiplies, and deliberately
|
|
28
|
+
#
|
|
29
|
+
# `control` and `update` still run **once** for this subtree, however many
|
|
30
|
+
# viewports draw it. That is what keeps simulation cost independent of player
|
|
31
|
+
# count, and it is also a safety property: a world node's `on_update` is
|
|
32
|
+
# where mutation belongs, so per-view updating would move an actor at twice
|
|
33
|
+
# the speed with two players — correctly in single player, silently wrong the
|
|
34
|
+
# moment somebody joins.
|
|
35
|
+
#
|
|
36
|
+
# It does make the standing "draw renders state" rule load-bearing rather
|
|
37
|
+
# than stylistic: a `draw` with a side effect now happens once per player,
|
|
38
|
+
# and an allocation in one costs that many times.
|
|
39
|
+
#
|
|
40
|
+
# A node that genuinely needs per-view work at draw time can read
|
|
41
|
+
# `system(Viewports).views` itself. Anything needing per-view state *over
|
|
42
|
+
# time* — a screen shake, a hit flash — is per-*player* rather than per-view,
|
|
43
|
+
# and belongs on that player's camera or their own subtree, which tick once.
|
|
44
|
+
class WorldView < Node2D
|
|
45
|
+
# World content, which is the default anyway — stated because this is the
|
|
46
|
+
# node that marks where world space begins, and a reader looking for
|
|
47
|
+
# "which band is the world in" should find the answer here.
|
|
48
|
+
def initialize(**)
|
|
49
|
+
super(band: :world, **)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Drawn once per viewport, so this overrides the whole of `draw` rather
|
|
53
|
+
# than just `draw_children`: the node's own visuals belong inside the
|
|
54
|
+
# viewport too, not once outside all of them.
|
|
55
|
+
#
|
|
56
|
+
# `view` is the screen-space view this node was reached with, and is
|
|
57
|
+
# deliberately ignored — a WorldView asks the system which viewports exist
|
|
58
|
+
# rather than being told, so a game can place one anywhere in the tree.
|
|
59
|
+
def draw(renderer, _view = nil)
|
|
60
|
+
viewports = system(Viewports)
|
|
61
|
+
viewports.views.each do |world_view|
|
|
62
|
+
renderer.clipped(world_view.x, world_view.y, world_view.width, world_view.height) do
|
|
63
|
+
renderer.translated(world_view.offset_x, world_view.offset_y) do
|
|
64
|
+
super(renderer, world_view)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/rgame/engine.rb
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'util'
|
|
4
|
+
|
|
5
|
+
require_relative 'engine/culling'
|
|
6
|
+
require_relative 'engine/signal'
|
|
7
|
+
require_relative 'engine/sealed_privates'
|
|
8
|
+
require_relative 'engine/node2d'
|
|
9
|
+
require_relative 'engine/component'
|
|
10
|
+
require_relative 'engine/animation_set'
|
|
11
|
+
require_relative 'engine/animator'
|
|
12
|
+
require_relative 'engine/camera'
|
|
13
|
+
require_relative 'engine/layout'
|
|
14
|
+
require_relative 'engine/presentation'
|
|
15
|
+
require_relative 'engine/view'
|
|
16
|
+
require_relative 'engine/world_view'
|
|
17
|
+
require_relative 'engine/player_layer'
|
|
18
|
+
require_relative 'engine/ui/button'
|
|
19
|
+
require_relative 'engine/ui/nine_slice_style'
|
|
20
|
+
require_relative 'engine/ui/shape_style'
|
|
21
|
+
require_relative 'engine/ui/text_button'
|
|
22
|
+
require_relative 'engine/ui/panel_button'
|
|
23
|
+
require_relative 'engine/ui/option_button'
|
|
24
|
+
require_relative 'engine/ui/icon_button'
|
|
25
|
+
require_relative 'engine/ui/stack'
|
|
26
|
+
require_relative 'engine/ui/column'
|
|
27
|
+
require_relative 'engine/ui/row'
|
|
28
|
+
require_relative 'engine/ui/ring'
|
|
29
|
+
require_relative 'engine/ui/navigation'
|
|
30
|
+
require_relative 'engine/ui/stepping'
|
|
31
|
+
require_relative 'engine/ui/pointing'
|
|
32
|
+
require_relative 'engine/ui/menu'
|
|
33
|
+
require_relative 'engine/ui/panel_menu'
|
|
34
|
+
require_relative 'engine/ui/radial_menu'
|
|
35
|
+
require_relative 'engine/path'
|
|
36
|
+
require_relative 'engine/timer'
|
|
37
|
+
require_relative 'engine/tileset'
|
|
38
|
+
require_relative 'engine/tile_map'
|
|
39
|
+
require_relative 'engine/tile_blockers'
|
|
40
|
+
require_relative 'engine/nav_grid'
|
|
41
|
+
require_relative 'engine/actor_blockers'
|
|
42
|
+
require_relative 'engine/bounds_blockers'
|
|
43
|
+
require_relative 'engine/collision_box'
|
|
44
|
+
require_relative 'engine/collision_system'
|
|
45
|
+
require_relative 'engine/circle_collider'
|
|
46
|
+
require_relative 'engine/spatial_hash'
|
|
47
|
+
require_relative 'engine/contact_set'
|
|
48
|
+
require_relative 'engine/pool'
|
|
49
|
+
require_relative 'engine/audio_bus'
|
|
50
|
+
require_relative 'engine/audio_director'
|
|
51
|
+
require_relative 'engine/i18n'
|
|
52
|
+
require_relative 'engine/text'
|
|
53
|
+
require_relative 'engine/debug_overlay'
|
|
54
|
+
require_relative 'engine/input/actions'
|
|
55
|
+
require_relative 'engine/input/input_map'
|
|
56
|
+
require_relative 'engine/input/action_mapper'
|
|
57
|
+
require_relative 'engine/player'
|
|
58
|
+
require_relative 'engine/players'
|
|
59
|
+
require_relative 'engine/viewports'
|
|
60
|
+
require_relative 'engine/scene/scene_stack'
|
|
61
|
+
require_relative 'engine/components/identity'
|
|
62
|
+
require_relative 'engine/components/mover'
|
|
63
|
+
require_relative 'engine/components/velocity'
|
|
64
|
+
require_relative 'engine/components/pool'
|
|
65
|
+
require_relative 'engine/components/path_follow'
|
|
66
|
+
require_relative 'engine/components/navigator'
|
|
67
|
+
require_relative 'engine/components/timer'
|
|
68
|
+
require_relative 'engine/components/world'
|
|
69
|
+
require_relative 'engine/components/screen_wrap'
|
|
70
|
+
require_relative 'engine/components/despawn_offscreen'
|
|
71
|
+
require_relative 'engine/components/circle_collider'
|
|
72
|
+
require_relative 'engine/components/box_collider'
|
|
73
|
+
require_relative 'engine/components/feet_collider'
|
|
74
|
+
require_relative 'engine/components/collision_world'
|
|
75
|
+
require_relative 'engine/components/targeting'
|
|
76
|
+
require_relative 'engine/components/sprite'
|
|
77
|
+
require_relative 'engine/components/thrust_controller'
|
|
78
|
+
require_relative 'engine/components/action_trigger'
|
|
79
|
+
require_relative 'engine/components/tile_world'
|
|
80
|
+
require_relative 'engine/components/character_body'
|
|
81
|
+
require_relative 'engine/components/player_controller'
|
|
82
|
+
require_relative 'engine/components/hop'
|
|
83
|
+
require_relative 'engine/components/wander_controller'
|
|
84
|
+
require_relative 'engine/components/animated_sprite'
|
|
85
|
+
require_relative 'engine/components/camera_follow'
|
|
86
|
+
require_relative 'engine/tile_map_layer'
|
|
Binary file
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Digitized data copyright (c) 2010 Google Corporation
|
|
2
|
+
with Reserved Font Arimo, Tinos and Cousine.
|
|
3
|
+
Copyright (c) 2012 Red Hat, Inc.
|
|
4
|
+
with Reserved Font Name Liberation.
|
|
5
|
+
|
|
6
|
+
This Font Software is licensed under the SIL Open Font License,
|
|
7
|
+
Version 1.1.
|
|
8
|
+
|
|
9
|
+
This license is copied below, and is also available with a FAQ at:
|
|
10
|
+
http://scripts.sil.org/OFL
|
|
11
|
+
|
|
12
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
13
|
+
|
|
14
|
+
PREAMBLE The goals of the Open Font License (OFL) are to stimulate
|
|
15
|
+
worldwide development of collaborative font projects, to support the font
|
|
16
|
+
creation efforts of academic and linguistic communities, and to provide
|
|
17
|
+
a free and open framework in which fonts may be shared and improved in
|
|
18
|
+
partnership with others.
|
|
19
|
+
|
|
20
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
21
|
+
redistributed freely as long as they are not sold by themselves.
|
|
22
|
+
The fonts, including any derivative works, can be bundled, embedded,
|
|
23
|
+
redistributed and/or sold with any software provided that any reserved
|
|
24
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
25
|
+
however, cannot be released under any other type of license. The
|
|
26
|
+
requirement for fonts to remain under this license does not apply to
|
|
27
|
+
any document created using the fonts or their derivatives.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
DEFINITIONS
|
|
32
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
33
|
+
Holder(s) under this license and clearly marked as such.
|
|
34
|
+
This may include source files, build scripts and documentation.
|
|
35
|
+
|
|
36
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
37
|
+
copyright statement(s).
|
|
38
|
+
|
|
39
|
+
"Original Version" refers to the collection of Font Software components
|
|
40
|
+
as distributed by the Copyright Holder(s).
|
|
41
|
+
|
|
42
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
43
|
+
or substituting ? in part or in whole ?
|
|
44
|
+
any of the components of the Original Version, by changing formats or
|
|
45
|
+
by porting the Font Software to a new environment.
|
|
46
|
+
|
|
47
|
+
"Author" refers to any designer, engineer, programmer, technical writer
|
|
48
|
+
or other person who contributed to the Font Software.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
PERMISSION & CONDITIONS
|
|
52
|
+
|
|
53
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
|
54
|
+
copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
55
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
56
|
+
Software, subject to the following conditions:
|
|
57
|
+
|
|
58
|
+
1) Neither the Font Software nor any of its individual components,in
|
|
59
|
+
Original or Modified Versions, may be sold by itself.
|
|
60
|
+
|
|
61
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
62
|
+
redistributed and/or sold with any software, provided that each copy
|
|
63
|
+
contains the above copyright notice and this license. These can be
|
|
64
|
+
included either as stand-alone text files, human-readable headers or
|
|
65
|
+
in the appropriate machine-readable metadata fields within text or
|
|
66
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
67
|
+
|
|
68
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
69
|
+
Name(s) unless explicit written permission is granted by the
|
|
70
|
+
corresponding Copyright Holder. This restriction only applies to the
|
|
71
|
+
primary font name as presented to the users.
|
|
72
|
+
|
|
73
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
74
|
+
Software shall not be used to promote, endorse or advertise any
|
|
75
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
76
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
77
|
+
permission.
|
|
78
|
+
|
|
79
|
+
5) The Font Software, modified or unmodified, in part or in whole, must
|
|
80
|
+
be distributed entirely under this license, and must not be distributed
|
|
81
|
+
under any other license. The requirement for fonts to remain under
|
|
82
|
+
this license does not apply to any document created using the Font
|
|
83
|
+
Software.
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
TERMINATION
|
|
88
|
+
This license becomes null and void if any of the above conditions are not met.
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
DISCLAIMER
|
|
93
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
94
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
95
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
96
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
97
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
98
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
99
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
100
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
|
|
101
|
+
DEALINGS IN THE FONT SOFTWARE.
|
|
102
|
+
|