yeah 0.2.2 → 0.3.3
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 +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +52 -20
- data/bin/yeah +30 -0
- data/lib/yeah.rb +6 -9
- data/lib/yeah/_platform/asset.rb +24 -0
- data/lib/yeah/_platform/display.rb +181 -0
- data/lib/yeah/_platform/image.rb +16 -0
- data/lib/yeah/_platform/keyboard.rb +37 -0
- data/lib/yeah/_platform/mouse.rb +30 -0
- data/lib/yeah/_platform/sound.rb +15 -0
- data/lib/yeah/_platform/ticker.rb +21 -0
- data/lib/yeah/_template/Gemfile +4 -0
- data/lib/yeah/_template/code/code.rb +4 -0
- data/lib/yeah/_template/code/game.rb +7 -0
- data/lib/yeah/_web.rb +9 -0
- data/lib/yeah/color.rb +45 -13
- data/lib/yeah/constants.rb +6 -0
- data/lib/yeah/game.rb +89 -41
- data/lib/yeah/vector.rb +122 -99
- data/lib/yeah/version.rb +3 -0
- data/lib/yeah/web/dependencies.rb +2 -0
- data/lib/yeah/web/runner.html.erb +61 -0
- data/lib/yeah/web/server.rb +60 -0
- data/lib/yeah/web/setup.rb +5 -0
- data/lib/yeah/web/start.rb +1 -0
- data/opal/yeah/web.rb +8 -0
- data/opal/yeah/web/asset.opal +38 -0
- data/opal/yeah/web/constants.opal +5 -0
- data/opal/yeah/web/display.opal +244 -0
- data/opal/yeah/web/image.opal +23 -0
- data/opal/yeah/web/keyboard.opal +139 -0
- data/opal/yeah/web/mouse.opal +58 -0
- data/opal/yeah/web/sound.opal +19 -0
- data/opal/yeah/web/ticker.opal +39 -0
- metadata +111 -19
- data/CHANGELOG.md +0 -28
- data/lib/monkey/numeric.rb +0 -6
- data/lib/yeah/basic_physics.rb +0 -11
- data/lib/yeah/desktop.rb +0 -72
- data/lib/yeah/entity.rb +0 -137
- data/lib/yeah/map.rb +0 -40
- data/lib/yeah/rectangle.rb +0 -21
- data/lib/yeah/surface.rb +0 -66
@@ -0,0 +1,21 @@
|
|
1
|
+
module Yeah
|
2
|
+
|
3
|
+
# The `Ticker` provides a way to start a game loop.
|
4
|
+
# @example Print elapsed time between each tick
|
5
|
+
# Ticker.new.on_tick { |elapsed| puts elapsed }
|
6
|
+
# @abstract Provided by a `Platform`.
|
7
|
+
class Ticker
|
8
|
+
# @param [Hash] options for new object
|
9
|
+
# @option options [Numeric] :rate (60) at ticks per second
|
10
|
+
def initialize(options = {})
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
# @!attribute [r] rate
|
15
|
+
# @return [Numeric] ticks per second intended
|
16
|
+
|
17
|
+
# @!method on_tick
|
18
|
+
# @yield [elapsed] block to execute per tick
|
19
|
+
# @yieldparam [Numeric] elapsed time since last tick
|
20
|
+
end
|
21
|
+
end
|
data/lib/yeah/_web.rb
ADDED
data/lib/yeah/color.rb
CHANGED
@@ -1,25 +1,57 @@
|
|
1
|
-
|
2
|
-
class Yeah::Color
|
3
|
-
# @!attribute rgba_bytes
|
4
|
-
# @return [Array<(Integer, Integer, Integer, Integer)>] red, green, blue,
|
5
|
-
# alpha bytes
|
6
|
-
attr_accessor :rgba_bytes
|
1
|
+
module Yeah
|
7
2
|
|
3
|
+
# A `Color` represents a color.
|
4
|
+
#
|
5
|
+
# `C` is an alias for `Color`.
|
6
|
+
# @example Comparing two colors
|
7
|
+
# C[100, 100, 100] == C['#646464']
|
8
|
+
# # => true
|
9
|
+
class Color
|
8
10
|
class << self
|
9
|
-
|
11
|
+
# @param arguments catch-all
|
12
|
+
# @return [Vector]
|
13
|
+
# Alias for ::new.
|
14
|
+
def [](*args)
|
15
|
+
new(*args)
|
16
|
+
end
|
10
17
|
end
|
11
18
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
19
|
+
# @return [Array] color value in RGB format
|
20
|
+
attr_reader :value
|
21
|
+
|
22
|
+
# @overload initialize(red, green, blue)
|
23
|
+
# @param [Numeric] red value, from 0 to 255
|
24
|
+
# @param [Numeric] green value, from 0 to 255
|
25
|
+
# @param [Numeric] blue value, from 0 to 255
|
26
|
+
# @overload initialize(hex_string)
|
27
|
+
# @param [String] hex string in '#dadada' or '#fff' format
|
28
|
+
def initialize(*args)
|
29
|
+
if args[0].respond_to?(:[]) && args[0][0] == '#' # hex string
|
30
|
+
if args[0].length == 4 # short hex
|
31
|
+
@value = args[0][1..3].chars.map { |h| "#{h}#{h}".to_i(16) }
|
32
|
+
else # normal hex
|
33
|
+
@value = args[0][1..6].scan(/../).map { |h| h.to_i(16) }
|
34
|
+
end
|
35
|
+
else
|
36
|
+
@value = args
|
37
|
+
end
|
16
38
|
end
|
17
39
|
|
40
|
+
# @return [String] readable representation
|
18
41
|
def inspect
|
19
|
-
"#{self.class.name}
|
42
|
+
"#{self.class.name}#{value.to_s}"
|
20
43
|
end
|
21
44
|
|
45
|
+
# @return [Boolean] whether self matches other color
|
22
46
|
def ==(other)
|
23
|
-
|
47
|
+
value == other.value
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] color value as a hex string
|
51
|
+
def to_hex
|
52
|
+
"##{value.map { |v| v.to_s(16).rjust(2, '0') }.join }"
|
24
53
|
end
|
25
54
|
end
|
55
|
+
end
|
56
|
+
|
57
|
+
Yeah::C = Yeah::Color
|
data/lib/yeah/game.rb
CHANGED
@@ -1,54 +1,102 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
|
19
|
-
|
1
|
+
module Yeah
|
2
|
+
|
3
|
+
# A `Game` is subclassed to create the core of a game project.
|
4
|
+
#
|
5
|
+
# A project's `Game` is instantiated at runtime. Its {#setup} is called one
|
6
|
+
# time, then {#update} is called repeatedly.
|
7
|
+
#
|
8
|
+
# I/O objects are provided and can be used to build a modern video game.
|
9
|
+
# @abstract Subclass this to make a game.
|
10
|
+
class Game
|
11
|
+
class << self
|
12
|
+
# @param [Hash] configuration used for new game instances
|
13
|
+
# @option configuration [Hash] :ticker options
|
14
|
+
# @option configuration [Hash] :display options
|
15
|
+
# @option configuration [Hash] :keyboard options
|
16
|
+
# @option configuration [Hash] :mouse options
|
17
|
+
# @return [Hash] configuration for new game instances
|
18
|
+
def config
|
19
|
+
@config ||= {
|
20
|
+
ticker: {},
|
21
|
+
display: {},
|
22
|
+
keyboard: {},
|
23
|
+
mouse: {}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Game] default subclass (i.e. project game)
|
28
|
+
def default
|
29
|
+
subclasses.last
|
30
|
+
end
|
20
31
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
32
|
+
private
|
33
|
+
|
34
|
+
def inherited(klass)
|
35
|
+
subclasses << klass
|
36
|
+
|
37
|
+
super
|
27
38
|
end
|
39
|
+
|
40
|
+
def subclasses
|
41
|
+
@@subclasses ||= []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Ticker] ticker used for game loop and one-shot input
|
46
|
+
attr_reader :ticker
|
47
|
+
|
48
|
+
# @return [Display] display for drawing
|
49
|
+
attr_reader :display
|
50
|
+
|
51
|
+
# @return [Keyboard] keyboard input
|
52
|
+
attr_reader :keyboard
|
53
|
+
|
54
|
+
# @return [Mouse] mouse input
|
55
|
+
attr_reader :mouse
|
56
|
+
|
57
|
+
# @param [Hash] options
|
58
|
+
# @option options [Ticker] :ticker
|
59
|
+
# @option options [Display] :display
|
60
|
+
# @option options [Keyboard] :keyboard
|
61
|
+
# @option options [Mouse] :mouse
|
62
|
+
def initialize(options = {})
|
63
|
+
options = defaults.merge(options)
|
64
|
+
@ticker = options[:ticker]
|
65
|
+
@display = options[:display]
|
66
|
+
@keyboard = options[:keyboard]
|
67
|
+
@mouse = options[:mouse]
|
68
|
+
|
69
|
+
setup
|
70
|
+
|
71
|
+
@ticker.on_tick { |e| update(e) }
|
28
72
|
end
|
29
73
|
|
30
|
-
#
|
31
|
-
|
32
|
-
|
74
|
+
# @abstract
|
75
|
+
# Setup game at start.
|
76
|
+
def setup
|
33
77
|
end
|
34
78
|
|
35
|
-
|
36
|
-
|
37
|
-
|
79
|
+
# @param [Numeric] elapsed time since last update
|
80
|
+
# @abstract
|
81
|
+
# Update game on tick.
|
82
|
+
def update(elapsed)
|
38
83
|
end
|
39
84
|
|
40
|
-
|
85
|
+
private
|
41
86
|
|
42
|
-
def
|
43
|
-
|
87
|
+
def config
|
88
|
+
self.class.config
|
44
89
|
end
|
45
90
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
91
|
+
def defaults
|
92
|
+
ticker = Ticker.new(config[:ticker])
|
93
|
+
|
94
|
+
{
|
95
|
+
ticker: ticker,
|
96
|
+
display: Display.new(config[:display]),
|
97
|
+
keyboard: Keyboard.new({ ticker: ticker }.merge(config[:keyboard])),
|
98
|
+
mouse: Mouse.new({ ticker: ticker }.merge(config[:mouse]))
|
99
|
+
}
|
53
100
|
end
|
54
101
|
end
|
102
|
+
end
|
data/lib/yeah/vector.rb
CHANGED
@@ -1,137 +1,160 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Yeah
|
4
|
+
|
5
|
+
# The `Vector` represents a mathematical vector. It can be used to describe
|
6
|
+
# a position, velocity or direction.
|
7
|
+
#
|
8
|
+
# `V` is an alias for `Vector`.
|
9
|
+
#
|
10
|
+
# @example Basic vector math
|
11
|
+
# V[5, 5] + V[10, 20]
|
12
|
+
# # => Yeah::Vector[15, 25, 0]
|
13
|
+
#
|
14
|
+
# V[3, 6, 9] / 3
|
15
|
+
# # => Yeah::Vector[1, 2, 3]
|
16
|
+
class Vector
|
17
|
+
extend Forwardable
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
19
|
+
class << self
|
20
|
+
# @param arguments catch-all
|
21
|
+
# @return [Vector]
|
22
|
+
# Alias for ::new.
|
23
|
+
def [](*args)
|
24
|
+
new(*args)
|
25
|
+
end
|
16
26
|
end
|
17
27
|
|
18
|
-
|
19
|
-
|
20
|
-
error_message = "too many arguments (#{components.size} for up to 3)"
|
21
|
-
raise ArgumentError, error_message
|
22
|
-
end
|
28
|
+
# @return [Array] vector components
|
29
|
+
attr_accessor :components
|
23
30
|
|
24
|
-
|
31
|
+
# @!attribute []
|
32
|
+
# @return [Numeric] *n*th component value
|
33
|
+
def_delegators :@components, :[]
|
34
|
+
|
35
|
+
# @param [Numeric] first component, defaults to 0
|
36
|
+
# @param [Numeric] second component, defaults to 0
|
37
|
+
# @param [Numeric] third component, defaults to 0
|
38
|
+
def initialize(*components)
|
39
|
+
@components = components + [0] * (3 - components.size)
|
25
40
|
end
|
26
41
|
|
42
|
+
# @return [String] readable representation
|
27
43
|
def inspect
|
28
|
-
"#{self.class.name}
|
44
|
+
"#{self.class.name}#{@components.to_s}"
|
29
45
|
end
|
30
46
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
raise ArgumentError, error_message
|
35
|
-
end
|
47
|
+
# @!attribute x
|
48
|
+
# @param [Numeric] first component value
|
49
|
+
# @return [Numeric] first component value
|
36
50
|
|
37
|
-
|
38
|
-
|
51
|
+
# @!attribute y
|
52
|
+
# @param [Numeric] second component value
|
53
|
+
# @return [Numeric] second component value
|
39
54
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
set.each do |name|
|
47
|
-
define_method(name) { @components[ci] }
|
48
|
-
define_method("#{name}=") { |val| @components[ci] = val }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
55
|
+
# @!attribute z
|
56
|
+
# @param [Numeric] third component value
|
57
|
+
# @return [Numeric] third component value
|
58
|
+
%i[x y z].each_with_index do |component, i|
|
59
|
+
define_method(component) { @components[i] }
|
60
|
+
define_method("#{component}=") { |v| @components[i] = v }
|
52
61
|
end
|
53
62
|
|
54
|
-
|
63
|
+
# @return [Numeric] vector's length
|
64
|
+
def length
|
65
|
+
Math.sqrt(@components[0] ** 2 + @components[1] ** 2 + @components[2] ** 2)
|
66
|
+
end
|
55
67
|
|
56
|
-
|
57
|
-
|
68
|
+
# @return [Vector] vector of same direction whose length is 1
|
69
|
+
def normalize
|
70
|
+
self / magnitude
|
58
71
|
end
|
59
72
|
|
60
|
-
|
61
|
-
|
73
|
+
# @param [Vector] vector to add
|
74
|
+
# @return [Vector] vector added into other vector
|
75
|
+
def +(vector)
|
76
|
+
self.class.new(*
|
77
|
+
[@components, vector.components].transpose.map { |cc|
|
78
|
+
cc.reduce(:+) })
|
62
79
|
end
|
63
80
|
|
64
|
-
|
65
|
-
|
81
|
+
# @param [Vector] vector to subtract
|
82
|
+
# @return [Vector] vector from which other vector was subtracted
|
83
|
+
def -(vector)
|
84
|
+
self.class.new(*
|
85
|
+
[@components, vector.components].transpose.map { |cc|
|
86
|
+
cc.reduce(:-) })
|
66
87
|
end
|
67
88
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
comp_addends = [addend] * 3
|
74
|
-
end
|
75
|
-
components = @components.zip(comp_addends).map { |c| c.reduce(:+) }
|
89
|
+
# @param [Numeric] numeric value to multiply vector
|
90
|
+
# @return [Vector] vector multiplied by a numeric value
|
91
|
+
def *(numeric)
|
92
|
+
self.class.new(*@components.map { |c| c * numeric })
|
93
|
+
end
|
76
94
|
|
77
|
-
|
95
|
+
# @param [Numeric] numeric value to divide vector
|
96
|
+
# @return [Vector] vector divided by a numeric value
|
97
|
+
def /(numeric)
|
98
|
+
self.class.new(*@components.map { |c| c / numeric })
|
78
99
|
end
|
79
100
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
when Numeric
|
85
|
-
comp_subtrahends = [subtrahend] * 3
|
86
|
-
end
|
87
|
-
components = @components.zip(comp_subtrahends).map { |c| c.reduce(:-)}
|
101
|
+
# @return [Vector] identical vector
|
102
|
+
def +@
|
103
|
+
self.class.new(*@components)
|
104
|
+
end
|
88
105
|
|
89
|
-
|
106
|
+
# @return [Vector] negative vector
|
107
|
+
def -@
|
108
|
+
self.class.new(*@components.map(&:-@))
|
90
109
|
end
|
91
110
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
111
|
+
# @param [Vector] position
|
112
|
+
# @return [Numeric] distance to a position
|
113
|
+
def distance_to(position)
|
114
|
+
Math.sqrt((x - position.x) ** 2 +
|
115
|
+
(y - position.y) ** 2 +
|
116
|
+
(z - position.z) ** 2)
|
117
|
+
end
|
99
118
|
|
100
|
-
|
101
|
-
|
119
|
+
# @param [Vector] position
|
120
|
+
# @return [Numeric] angle to 2D position, in radians
|
121
|
+
def angle_to(position)
|
122
|
+
diff = position - self
|
123
|
+
Math.atan2(diff.y, diff.x)
|
102
124
|
end
|
103
125
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
126
|
+
# @param [Numeric] angle to move along, in radians
|
127
|
+
# @param [Numeric] distance to move
|
128
|
+
# @return [Vector] position moved along an angle for a distance in 2D
|
129
|
+
def along(angle, distance)
|
130
|
+
self.class.new(x + Math.cos(angle) * distance,
|
131
|
+
y + Math.sin(angle) * distance)
|
132
|
+
end
|
111
133
|
|
112
|
-
|
113
|
-
|
134
|
+
# @param (see #along)
|
135
|
+
# @return [Vector] self after moving along an angle for a distance in 2D
|
136
|
+
def along!(angle, distance)
|
137
|
+
self.x += Math.cos(angle) * distance
|
138
|
+
self.y += Math.sin(angle) * distance
|
139
|
+
self
|
114
140
|
end
|
115
141
|
|
116
|
-
# @
|
117
|
-
|
118
|
-
|
142
|
+
# @param [Vector] position to move to
|
143
|
+
# @param [Vector] distance to move
|
144
|
+
# @return [Vector] position moved toward other position for a distance in 2D
|
145
|
+
# @todo Make work in 3D.
|
146
|
+
def toward(position, distance)
|
147
|
+
along angle_to(position), amount
|
119
148
|
end
|
120
|
-
# @!attribute length
|
121
|
-
# @see magnitude
|
122
|
-
# @!attribute distance
|
123
|
-
# @see magnitude
|
124
|
-
# @!attribute speed
|
125
|
-
# @see magnitude
|
126
|
-
alias_method :length, :magnitude
|
127
|
-
alias_method :distance, :magnitude
|
128
|
-
alias_method :speed, :magnitude
|
129
149
|
|
130
|
-
#
|
131
|
-
|
132
|
-
|
150
|
+
# @param (see #toward)
|
151
|
+
# @return [Vector] self after moving toward other position for a distance in
|
152
|
+
# 2D
|
153
|
+
# @todo Make work in 3D.
|
154
|
+
def toward!(position, amount)
|
155
|
+
along! angle_to(position), amount
|
133
156
|
end
|
134
157
|
end
|
158
|
+
end
|
135
159
|
|
136
|
-
# Shorthand for Vector.
|
137
160
|
Yeah::V = Yeah::Vector
|