shattered_pack 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|