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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +52 -20
  4. data/bin/yeah +30 -0
  5. data/lib/yeah.rb +6 -9
  6. data/lib/yeah/_platform/asset.rb +24 -0
  7. data/lib/yeah/_platform/display.rb +181 -0
  8. data/lib/yeah/_platform/image.rb +16 -0
  9. data/lib/yeah/_platform/keyboard.rb +37 -0
  10. data/lib/yeah/_platform/mouse.rb +30 -0
  11. data/lib/yeah/_platform/sound.rb +15 -0
  12. data/lib/yeah/_platform/ticker.rb +21 -0
  13. data/lib/yeah/_template/Gemfile +4 -0
  14. data/lib/yeah/_template/code/code.rb +4 -0
  15. data/lib/yeah/_template/code/game.rb +7 -0
  16. data/lib/yeah/_web.rb +9 -0
  17. data/lib/yeah/color.rb +45 -13
  18. data/lib/yeah/constants.rb +6 -0
  19. data/lib/yeah/game.rb +89 -41
  20. data/lib/yeah/vector.rb +122 -99
  21. data/lib/yeah/version.rb +3 -0
  22. data/lib/yeah/web/dependencies.rb +2 -0
  23. data/lib/yeah/web/runner.html.erb +61 -0
  24. data/lib/yeah/web/server.rb +60 -0
  25. data/lib/yeah/web/setup.rb +5 -0
  26. data/lib/yeah/web/start.rb +1 -0
  27. data/opal/yeah/web.rb +8 -0
  28. data/opal/yeah/web/asset.opal +38 -0
  29. data/opal/yeah/web/constants.opal +5 -0
  30. data/opal/yeah/web/display.opal +244 -0
  31. data/opal/yeah/web/image.opal +23 -0
  32. data/opal/yeah/web/keyboard.opal +139 -0
  33. data/opal/yeah/web/mouse.opal +58 -0
  34. data/opal/yeah/web/sound.opal +19 -0
  35. data/opal/yeah/web/ticker.opal +39 -0
  36. metadata +111 -19
  37. data/CHANGELOG.md +0 -28
  38. data/lib/monkey/numeric.rb +0 -6
  39. data/lib/yeah/basic_physics.rb +0 -11
  40. data/lib/yeah/desktop.rb +0 -72
  41. data/lib/yeah/entity.rb +0 -137
  42. data/lib/yeah/map.rb +0 -40
  43. data/lib/yeah/rectangle.rb +0 -21
  44. 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
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'yeah', '~> 0.3.3'
4
+ gem 'opal', github: 'opal/opal', ref: '7e843b0'
@@ -0,0 +1,4 @@
1
+ # Entry point
2
+ # Specify dependencies and game code require order here.
3
+
4
+ require 'game'
@@ -0,0 +1,7 @@
1
+ class NewGame < Game
2
+ def setup
3
+ end
4
+
5
+ def update(elapsed)
6
+ end
7
+ end
data/lib/yeah/_web.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Yeah
2
+
3
+ # `Web`, through [Opal](http://opalrb.org/), compiles and runs Yeah games on
4
+ # the web.
5
+ #
6
+ # The `Display` is powered by HTML Canvas.
7
+ module Web
8
+ end
9
+ end
data/lib/yeah/color.rb CHANGED
@@ -1,25 +1,57 @@
1
- # Color.
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
- alias_method :[], :new
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
- def initialize(*values)
13
- default_values = [0, 0, 0, 255]
14
- values += default_values[values.size..-1]
15
- @rgba_bytes = values
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}[#{rgba_bytes.join(', ')}]"
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
- self.class == other.class && @rgba_bytes == other.rgba_bytes
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
@@ -0,0 +1,6 @@
1
+ module Yeah
2
+ DEFAULT_DISPLAY_SIZE = V[1280, 720]
3
+ DEFAULT_DISPLAY_FONT_FAMILY = 'DejaVu Serif'
4
+ DEFAULT_DISPLAY_FONT_SIZE = 36
5
+ DEFAULT_TICKER_RATE = 60
6
+ end
data/lib/yeah/game.rb CHANGED
@@ -1,54 +1,102 @@
1
- # Manages entities.
2
- class Yeah::Game
3
- # @!attribute resolution
4
- # @return [Vector] size of screen
5
- # @!attribute screen
6
- # @return [Surface] visual render
7
- # @!attribute [r] platform
8
- # @return [Platform] underlying platform bindings
9
- # @!attribute entities
10
- # @return [Array] active entities
11
- attr_accessor :resolution, :screen
12
- attr_reader :entities, :platform
13
-
14
- def initialize
15
- @resolution = V[320, 180]
16
- @screen = Surface.new(@resolution)
17
- @platform = Desktop.new
18
- @entities = []
19
- end
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
- # Start the game loop.
22
- def start
23
- platform.each_tick do
24
- update
25
- draw
26
- break if @stopped
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
- # Stop the game loop.
31
- def stop
32
- @stopped = true
74
+ # @abstract
75
+ # Setup game at start.
76
+ def setup
33
77
  end
34
78
 
35
- def entities=(value)
36
- @entities = value
37
- @entities.each { |e| e.game = self }
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
- protected
85
+ private
41
86
 
42
- def update
43
- @entities.each { |e| e.update }
87
+ def config
88
+ self.class.config
44
89
  end
45
90
 
46
- def draw
47
- screen.fill(Color[0, 0, 0, 0])
48
- @entities.each do |entity|
49
- surface = entity.draw
50
- screen.draw(surface, entity.position) unless surface.nil?
51
- end
52
- platform.render(screen)
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
- # Three-dimensional geometric vector. Used as position or size.
2
- class Yeah::Vector
3
- # @!attribute components
4
- # @return [Array<(Numeric, Numeric, Numeric)>]
5
- # @!attribute [r] to_a
6
- # @see components
7
- # @!attribute []
8
- # @param [Integer] *n* of component
9
- # @return [Numeric] *n*th component
10
- attr_reader :components
11
- alias_method :to_a, :components
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
- def self.random(*component_maxes)
14
- components = component_maxes.map { |cm| Random.rand(cm) }
15
- self.new(*components)
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
- def initialize(*components)
19
- if components.size > 3
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
- self.components = components
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}[#{components.join(', ')}]"
44
+ "#{self.class.name}#{@components.to_s}"
29
45
  end
30
46
 
31
- def components=(values)
32
- if values.size > 3
33
- error_message = "too many elements (#{values.size} for up to 3)"
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
- @components = values + [0] * (3 - values.size)
38
- end
51
+ # @!attribute y
52
+ # @param [Numeric] second component value
53
+ # @return [Numeric] second component value
39
54
 
40
- class << self
41
- alias_method :[], :new
42
-
43
- def define_component_helpers
44
- component_name_sets = [[:x, :width], [:y, :height], [:z, :depth]]
45
- component_name_sets.each_with_index do |set, ci|
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
- define_component_helpers
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
- def ==(other)
57
- other.class == self.class && @components == other.components ? true : false
68
+ # @return [Vector] vector of same direction whose length is 1
69
+ def normalize
70
+ self / magnitude
58
71
  end
59
72
 
60
- def [](index)
61
- @components[index]
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
- def []=(index, value)
65
- @components[index] = value
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
- def +(addend)
69
- case addend
70
- when self.class
71
- comp_addends = addend.components
72
- when Numeric
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
- self.class[*components]
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
- def -(subtrahend)
81
- case subtrahend
82
- when self.class
83
- comp_subtrahends = subtrahend.components
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
- self.class[*components]
106
+ # @return [Vector] negative vector
107
+ def -@
108
+ self.class.new(*@components.map(&:-@))
90
109
  end
91
110
 
92
- def *(multiple)
93
- case multiple
94
- when self.class
95
- comp_multiples = multiple.components
96
- when Numeric
97
- comp_multiples = [multiple] * 3
98
- end
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
- components = @components.zip(comp_multiples).map { |c| c.reduce(:*) }
101
- self.class[*components]
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
- def /(divisor)
105
- case divisor
106
- when self.class
107
- comp_divisors = divisor.components
108
- when Numeric
109
- comp_divisors = [divisor] * 3
110
- end
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
- components = @components.zip(comp_divisors).map { |c| c.reduce(:/) }
113
- self.class[*components]
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
- # @return [Numeric]
117
- def magnitude
118
- Math.sqrt(@components.reduce(0) { |m, c| m + c*c })
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
- # Reset every component to 0.
131
- def reset
132
- @components = [0, 0, 0]
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