shattered_pack 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.
- data/lib/shattered_controller.rb +3 -0
- data/lib/shattered_controller/actor/actor.rb +107 -0
- data/lib/shattered_controller/base.rb +108 -0
- data/lib/shattered_controller/keyboard_input/key_converter.rb +43 -0
- data/lib/shattered_controller/keyboard_input/keyboard_input.rb +106 -0
- data/lib/shattered_controller/mock_camera.rb +9 -0
- data/lib/shattered_controller/runner.rb +33 -0
- data/lib/shattered_controller/state.rb +59 -0
- data/lib/shattered_model.rb +3 -0
- data/lib/shattered_model/base.rb +24 -0
- data/lib/shattered_model/fuzzy_logic.rb +188 -0
- data/lib/shattered_model/linear_interpolator.rb +29 -0
- data/lib/shattered_pack.rb +11 -0
- data/lib/shattered_pack/base.rb +127 -0
- data/lib/shattered_pack/pre_initialize/pre_initialize.rb +105 -0
- data/lib/shattered_pack/runner.rb +11 -0
- data/lib/shattered_pack/timer/timed_event.rb +77 -0
- data/lib/shattered_pack/timer/timer.rb +75 -0
- data/lib/shattered_view.rb +7 -0
- data/lib/shattered_view/base.rb +151 -0
- data/lib/shattered_view/camera.rb +7 -0
- data/lib/shattered_view/extensions.rb +10 -0
- data/lib/shattered_view/light.rb +28 -0
- data/lib/shattered_view/mesh/animation.rb +20 -0
- data/lib/shattered_view/mesh/mesh.rb +135 -0
- data/lib/shattered_view/node.rb +113 -0
- data/lib/shattered_view/resources.rb +47 -0
- data/lib/shattered_view/rmaterial.rb +43 -0
- data/lib/shattered_view/runner.rb +48 -0
- data/lib/shattered_view/utilities.rb +7 -0
- data/lib/shattered_view/vector.rb +242 -0
- metadata +75 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
module ShatteredController
|
2
|
+
module Actor #:nodoc:
|
3
|
+
def self.append_features(base)
|
4
|
+
super
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
end
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
# An actor is the MVC encapsulated into one object.
|
11
|
+
#
|
12
|
+
# Here is how it works: You specify a actor in a game state with some starting
|
13
|
+
# attributes:
|
14
|
+
# class BattleState
|
15
|
+
# actor :player
|
16
|
+
# actor :bullet, :target => :player
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# With a model BulletModel defined as:
|
20
|
+
# class BulletModel
|
21
|
+
# attr_writer :target
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# And our BulletModel will be created, with the target set to the player object.
|
25
|
+
#
|
26
|
+
# Setting values for the actors is done prior to initialization, so:
|
27
|
+
#
|
28
|
+
# class BulletModel
|
29
|
+
# attr_writer :target
|
30
|
+
# def initialize
|
31
|
+
# target.run_away_from(self)
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# Works fine.
|
36
|
+
#
|
37
|
+
# Actors delegate to ShatteredController::Base objects, and controllers delegate to Model and View.
|
38
|
+
#
|
39
|
+
# See ShatteredController::Base for more detail.
|
40
|
+
def actor( name, options = {} )
|
41
|
+
before_init_call( :actor, name, options )
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
module InstanceMethods
|
46
|
+
|
47
|
+
# This returns all of the actors created by this controller.
|
48
|
+
def actors
|
49
|
+
return @actors || []
|
50
|
+
end
|
51
|
+
|
52
|
+
# Same as ShatteredController::Actor::ClassMethods#actor
|
53
|
+
#
|
54
|
+
# Returns a newly created actor
|
55
|
+
def actor(name=nil, options={})
|
56
|
+
raise Error, "actor needs a name" if name.nil?
|
57
|
+
actor = Actor.new(name,load_actor_classes(name))
|
58
|
+
|
59
|
+
attr_reader(name.to_sym, actor)
|
60
|
+
call_object_function_for_each_key( name.to_sym, options )
|
61
|
+
|
62
|
+
@actors ||= []
|
63
|
+
@actors << actor
|
64
|
+
return actor
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def load_actor_classes(name)
|
70
|
+
actor_options={}
|
71
|
+
['model', 'view', 'controller'].each do |type|
|
72
|
+
evaled_class = nil
|
73
|
+
begin
|
74
|
+
evaled_class = eval("#{name.to_s.camelize}#{type.camelize}")
|
75
|
+
rescue NameError
|
76
|
+
next
|
77
|
+
end
|
78
|
+
actor_options[type.to_sym] = evaled_class.new
|
79
|
+
end
|
80
|
+
raise Error, "No Model, View, or Controller defined for Actor #{name}." if actor_options.empty?
|
81
|
+
return actor_options
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
# An actor is a delegator to a controller game object.
|
87
|
+
class Actor #:nodoc:all
|
88
|
+
attr_reader :controller
|
89
|
+
def initialize( name, options = {} )
|
90
|
+
@actor_name = name
|
91
|
+
@controller = (options[:controller] || ShatteredController::Base.new)
|
92
|
+
@controller.model = options[:model]
|
93
|
+
@controller.view = options[:view]
|
94
|
+
@controller.view.model = options[:model] unless @controller.view.nil?
|
95
|
+
|
96
|
+
if options[:view].nil? && options[:model].nil? && @controller.nil? && !options[:testing]
|
97
|
+
raise NameError, "No model view or controller found for actor #{name}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Delegate to controller
|
102
|
+
def method_missing(name, *args, &block)
|
103
|
+
@controller.send(name, *args, &block)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
3
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
4
|
+
|
5
|
+
require 'keyboard_input/keyboard_input'
|
6
|
+
require 'actor/actor'
|
7
|
+
|
8
|
+
module ShatteredController #:nodoc:
|
9
|
+
|
10
|
+
# Controllers tie together the Model and View. They are the only objects that know
|
11
|
+
# about both the model and the view, and as such have the responsibility of events.
|
12
|
+
#
|
13
|
+
# By default, when an unknown method is invoked on a controller object, it is delegated
|
14
|
+
# to the model and the view. IE:
|
15
|
+
#
|
16
|
+
# class BulletController < ...
|
17
|
+
# def known_method
|
18
|
+
# puts "This will not be delegated to the " + model.inspect + " or the " + view.inspect
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# class BattleState < ...
|
23
|
+
# actor :bullet
|
24
|
+
# def initialize
|
25
|
+
# bullet.known_method # This is not delegated
|
26
|
+
# bullet.unknown_method # This is delegated (and will throw an exception if unknown among the view/model)
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# When obtaining a result from a delegated method, the result will be the a response in the following order
|
31
|
+
# - Model (first)
|
32
|
+
# - View (last)
|
33
|
+
#
|
34
|
+
# They are sorted so that you get the most useful response first.
|
35
|
+
class Base < ShatteredPack::Base
|
36
|
+
attr_accessor :model, :view
|
37
|
+
|
38
|
+
before_init_call :disable_event
|
39
|
+
|
40
|
+
def quit
|
41
|
+
end
|
42
|
+
|
43
|
+
# Controller delegates all unknown methods to the view and model.
|
44
|
+
def method_missing(method_name, *args, &block)
|
45
|
+
return if disabled?
|
46
|
+
raise_no_method_error = true
|
47
|
+
result = nil
|
48
|
+
[view, model].each do |delegate|
|
49
|
+
if delegate.class.method_defined?(method_name)
|
50
|
+
result = delegate.send(method_name, *args, &block)
|
51
|
+
raise_no_method_error = false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
raise NoMethodError, "Model, View, and Controller have no method defined named '#{method_name}'" if raise_no_method_error
|
56
|
+
return result
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def update_timer(time_elapsed)#:nodoc:
|
61
|
+
return if @timer == :unloaded
|
62
|
+
[self, model, view].each do |base|
|
63
|
+
next if base.nil?
|
64
|
+
base.time_elapsed = time_elapsed
|
65
|
+
base.timer.update(time_elapsed)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# This is called to the State every frame by the main game loop. All update events are now
|
70
|
+
# run through Timers.
|
71
|
+
def update_timers(time_elapsed) #:nodoc:
|
72
|
+
update_timer(time_elapsed)
|
73
|
+
actors.each { |actor| actor.update_timers(time_elapsed) }
|
74
|
+
end
|
75
|
+
|
76
|
+
# Unloading a controller will completely unload an object from the scene.
|
77
|
+
def unload!
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
# An object is disabled when it has been unloaded.
|
82
|
+
def disabled?
|
83
|
+
super
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def disable_event
|
89
|
+
when_unloaded do
|
90
|
+
@disabled = true
|
91
|
+
@timer=:unloaded
|
92
|
+
@model.send(:unload!) if @model
|
93
|
+
@model = nil
|
94
|
+
@view.send(:unload!) if @view
|
95
|
+
@view = nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Error < StandardError #:nodoc:
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
ShatteredController::Base.class_eval do
|
105
|
+
include ShatteredController::KeyboardInput
|
106
|
+
include ShatteredController::Actor
|
107
|
+
end
|
108
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ShatteredController
|
2
|
+
# This is a wrapper around Ogre's input listener.
|
3
|
+
# It allows us to query the status of each device every frame.
|
4
|
+
class KeyConverter #:nodoc:
|
5
|
+
|
6
|
+
def initialize( ogre_input )
|
7
|
+
@ogre_input = ogre_input
|
8
|
+
end
|
9
|
+
def key_event?( type, key_symbol )
|
10
|
+
return @ogre_input.send(:"isKey#{type.to_s.capitalize}", self.class.convert(key_symbol))
|
11
|
+
end
|
12
|
+
def flush
|
13
|
+
@ogre_input.flush
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.back_conversions
|
17
|
+
@@back_conversions ||= conversions.invert
|
18
|
+
end
|
19
|
+
def self.conversions
|
20
|
+
begin
|
21
|
+
return @@conversions
|
22
|
+
rescue NameError
|
23
|
+
end
|
24
|
+
@@conversions = {}
|
25
|
+
ShatteredOgre.constants.each do |constant|
|
26
|
+
kc_evaled = eval("ShatteredOgre::#{constant}")
|
27
|
+
@@conversions[kc_evaled] = symbolize(constant[3..-1].downcase) if constant.to_s =~ /^KC_/
|
28
|
+
end
|
29
|
+
return @@conversions
|
30
|
+
end
|
31
|
+
def self.convert( kc_character )
|
32
|
+
return self.conversions[kc_character] if kc_character.is_a? Fixnum
|
33
|
+
return self.back_conversions[kc_character]
|
34
|
+
end
|
35
|
+
private
|
36
|
+
def self.symbolize( name )
|
37
|
+
exceptions = { 'pgdown' => :page_down,
|
38
|
+
'pgup' => :page_up }
|
39
|
+
return name.to_sym if exceptions[name] == nil
|
40
|
+
return exceptions[name]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'key_converter'
|
5
|
+
|
6
|
+
module ShatteredController
|
7
|
+
module KeyboardInput #:nodoc:
|
8
|
+
def self.append_features(base)
|
9
|
+
super
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
base.send(:include, InstanceMethods)
|
12
|
+
end
|
13
|
+
module ClassMethods
|
14
|
+
# Key pressing can take any of the following forms:
|
15
|
+
# key :released => :up, :action => :clicked_up
|
16
|
+
# key :pressed => :left, :action => :left_pressed
|
17
|
+
# key :held => :down, :action => :move_down_fluidly
|
18
|
+
# where each of the actions correspond to an actor function:
|
19
|
+
#
|
20
|
+
# class ExampleView < ShatteredView::Base
|
21
|
+
# def clicked_up
|
22
|
+
# #called when up is released
|
23
|
+
# end
|
24
|
+
# def left_pressed
|
25
|
+
# #called when left is released
|
26
|
+
# end
|
27
|
+
# def move_down_fluidly(time_elapsed)
|
28
|
+
# #called when down is held
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# Only key :pressed recieves the time_elapsed variable (as it is updated per frame)
|
33
|
+
def key(options = {})
|
34
|
+
before_init_call(:key, options)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module InstanceMethods
|
39
|
+
def pre_initialize
|
40
|
+
# update our keyboard input
|
41
|
+
timer.every(:frame) do |time_elapsed|
|
42
|
+
update_input(time_elapsed, ShatteredController::Runner.environment[:input])
|
43
|
+
end
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
def reaction_types
|
48
|
+
@reaction_types ||= [:pressed, :released, :held]
|
49
|
+
end
|
50
|
+
# Given an input of:
|
51
|
+
# key :held=>:up, :action => :key_action
|
52
|
+
# This will return [:held, [:up]]
|
53
|
+
def reaction_from(options) #:nodoc:
|
54
|
+
# Define the method to send the actions when the keys are pressed/clicked/released.
|
55
|
+
reaction_types.each do |reaction_type|
|
56
|
+
if options.has_key?(reaction_type)
|
57
|
+
return [reaction_type, options[reaction_type]] if options[reaction_type].is_a? Array
|
58
|
+
return [reaction_type, [options[reaction_type]]]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
raise Error, "Reaction to key input must be #{reaction_types.join(',')}. #{options.inspect}" if options[:reaction].nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Given an input of:
|
65
|
+
# 1) key :pressed => :up, :action => {:jump => :up}
|
66
|
+
# 2) key :pressed => :down, :action => :fall
|
67
|
+
# returns
|
68
|
+
# 1) [:jump, [:up]]
|
69
|
+
# 2) [:fall, []]
|
70
|
+
def action_from(params)
|
71
|
+
action = params[:action]
|
72
|
+
return [action, []] unless action.is_a? Hash
|
73
|
+
return [action.keys[0], action.values[0]]
|
74
|
+
end
|
75
|
+
# See KeyboardInput::key
|
76
|
+
def key(actions={})
|
77
|
+
reaction, keys = reaction_from(actions)
|
78
|
+
action, params = action_from(actions)
|
79
|
+
keys.each { |key| key_actions << [reaction, key.to_sym, action, params ] }
|
80
|
+
end
|
81
|
+
# Update_key will send each of the events to the actor object for whatever key options
|
82
|
+
# are defined.
|
83
|
+
def key_action(reaction, action, time_elapsed, params) #:nodoc:
|
84
|
+
send(action, *params)
|
85
|
+
end
|
86
|
+
def key_actions #:nodoc:
|
87
|
+
@key_actions ||= []
|
88
|
+
end
|
89
|
+
# update_input takes the input object, and sends the actor every key event this frame.
|
90
|
+
def update_input(time_elapsed, input) #:nodoc:
|
91
|
+
key_actions.each do |reaction, key, action, params|
|
92
|
+
key_action( reaction, action, time_elapsed, params) if input.key_event?(reaction,key)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Simulate a key press, release or hold event
|
97
|
+
def key_event(params = {})
|
98
|
+
lookup_reaction, keys = reaction_from(params)
|
99
|
+
lookup_key = keys[0]
|
100
|
+
key_actions.each do |reaction, key, action, params|
|
101
|
+
key_action(reaction, action, 0, params) if key == lookup_key && lookup_reaction == reaction
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module ShatteredController
|
3
|
+
#The class Runner is present in model, view, and controller, and signifies the
|
4
|
+
#main entry point into the program. This is called by script/runner.rb
|
5
|
+
class Runner #:nodoc:
|
6
|
+
def initialize( options = {} )
|
7
|
+
@@environment ||= options
|
8
|
+
@@environment[:input] = ShatteredController::KeyConverter.new(@@environment[:input])
|
9
|
+
end
|
10
|
+
#Every time this exits, a game dies.
|
11
|
+
def start_game
|
12
|
+
each_frame do |time_elapsed|
|
13
|
+
@@environment[:state].update_timers(time_elapsed)
|
14
|
+
@@environment[:input].flush
|
15
|
+
end
|
16
|
+
end
|
17
|
+
def each_frame
|
18
|
+
yield @@environment[:renderer].timeSinceLastFrame while @@environment[:renderer].nextFrame
|
19
|
+
end
|
20
|
+
def self.environment
|
21
|
+
begin
|
22
|
+
return @@environment
|
23
|
+
rescue NameError
|
24
|
+
return mock_environment
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def self.mock_environment
|
28
|
+
retv = {}
|
29
|
+
retv[:camera] = MockCamera.new
|
30
|
+
return retv
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ShatteredController
|
2
|
+
def self.append_features(base)
|
3
|
+
super
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
module ClassMethods
|
7
|
+
# In order to set the initial position of the camera, and any other attributes you would like,
|
8
|
+
# use the following format.
|
9
|
+
# class CameraTestState < ShatteredController::Base
|
10
|
+
# camera :position => v(10,0,0), :orientation => [0.5, 0.5, 0.5, 0.5]
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# If you pass a symbol as a value, the symbol will be evaluated.
|
14
|
+
#
|
15
|
+
# This allows for things such as:
|
16
|
+
#
|
17
|
+
# class CameraTestState < ShatteredController::Base
|
18
|
+
# camera :position => :camera_position
|
19
|
+
# def camera_position
|
20
|
+
# return [rand*10,0,0]
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# The valid options are any public functions within camera that ends in =
|
25
|
+
# (such as position=, looking_at=) See ShatteredView::Camera for more.
|
26
|
+
def camera(options = {})
|
27
|
+
before_init_call( 'camera', options )
|
28
|
+
end
|
29
|
+
|
30
|
+
# This blurs the lines between view and controller.
|
31
|
+
# Valid types are [:box]
|
32
|
+
def sky(type, options = {})
|
33
|
+
before_init_set( "self", { :"sky" => [type, options[:material]] })
|
34
|
+
end
|
35
|
+
end
|
36
|
+
# State is the entry point for your shattered game.
|
37
|
+
#
|
38
|
+
# States define the actors, communications between the actors, and control the camera.
|
39
|
+
# You can choose your starting state in config/environment.rb
|
40
|
+
class State < ShatteredController::Base
|
41
|
+
before_init_call :activate_state
|
42
|
+
def activate_state #:nodoc:
|
43
|
+
ShatteredPack::Configuration.environment[:state] = self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the camera used in the state. See ShatteredController::ClassMethods#camera
|
47
|
+
def camera(*args)
|
48
|
+
if args.length == 0
|
49
|
+
return @camera ||= Runner.environment[:camera]
|
50
|
+
end
|
51
|
+
call_object_function_for_each_key(:camera, args[0])
|
52
|
+
end
|
53
|
+
|
54
|
+
def sky=(type, material) #:nodoc:
|
55
|
+
Runner.environment[:scene].send("setSky#{type.to_s.capitalize}",material)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|