Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #==============================================================================
- # Iavra Animate Everything 1.04
- # -----------------------------------------------------------------------------
- # Description:
- # Allows animation of every numeric attribute on any object.
- # -----------------------------------------------------------------------------
- # Prerequisites:
- # (optional) "Iavra Weak Reference" - only if Weak References are used.
- # -----------------------------------------------------------------------------
- # How to Use:
- # Place this script below every class that you want to support animating.
- #
- # To register a class, add it to the CLASSES hash and define a generic getter
- # and setter for it. The configuration lists common patterns for this part.
- #
- # Afterwards, call the following method on an object of said class:
- #
- # IAVRA::ANIMATE.start(<object>, {:attr => 100}, 120, :linear, 60, false) { p self }
- #
- # This will first wait 60 frames before animating the attribute "attr" on
- # <object> from its current value to 100 over a duration of 120 frames using
- # linear easing. Since the last parameter is false, the animation will cancel
- # existing ones. After the animation is finished, the given block will be
- # executed in the object's context and prints it to the console.
- #
- # Animations can also be start from the object itself by calling:
- #
- # <object>.iavra_animate_start({:attr => 100}, 120, :linear, 60, false) { p self }
- # -----------------------------------------------------------------------------
- # Terms of Use:
- # Free to use for both commercial and non-commercial games. Please give credit.
- # -----------------------------------------------------------------------------
- # Credits:
- # Iavra
- # -----------------------------------------------------------------------------
- # Changelog:
- # - 0.01: First version.
- # - 0.02: Added support for different accessing methods, restructured the code.
- # - 0.03: Changed the update method to fix rounding error on animation end.
- # Also fixed a bug that caused the update method to skip frames for
- # every animated attribute after the first.
- # - 0.04: Different attributes on the same object can now be animated, paused,
- # resumed and stopped independently from each other.
- # - 0.05: Changed the easing functions from lambdas to methods to increase
- # performance. Added Bounce/Back/Elastic easing.
- # - 0.06: Moved the start, pause, resume and stop methods to IAVRA::ANIMATE to
- # lessen the amount of clutter on the extended classes.
- # - 0.07: Exported the easing functions to their own script, so it's easily
- # possible to switch them out, if needed. I left the default :linear in
- # here, so the script can still work on its own.
- # - 0.08: Removed pause and resume functions, since they would mess up the
- # animation if the attributes are changed by other means.
- # - 0.09: Added support for on complete callbacks. Be careful only to use them
- # on objects that are not included in the savefiles.
- # - 0.10: An optional offset can now be specified that will cause the animation
- # to start delayed.
- # - 0.11: Animations will no longer overwrite existing ones, but are pushed in
- # a queue and processed in order. "stop" will still remove all stored
- # animations, while the new method "skip" will only remove the current
- # one and proceed with the remaining, if any.
- # - 0.12: On complete callbacks are now executed using instance_eval, so self
- # will be set to the animated object.
- # - 0.13: Default easing function and chaining behaviour can now be configured.
- # Also added a new parameter to IAVRA::ANIMATE.start, that specifies if
- # the animation should be queued or overwrite older ones.
- # - 1.00: Release version.
- # - 1.01: Added the "step" method, that was previously removed. It returns the
- # current frame of the active animation for an attribute. Returns 0 for
- # delayed animations and nil if the attribute isn't animating.
- # - 1.02: Moved the update_animation method to occur before the normal update,
- # since the old version sometimes resulted in strange behaviour.
- # - 1.03: Added dependency to "Iavra Weak Reference". Objects are now updated
- # from outside and don't need their own update method anymore. They
- # still have their own animation data, though, since checking equality
- # with WeakRefs can be a nightmare.
- # - 1.04: Added a new configuration option to toggle between Weak and Strong
- # references. "Iavra Weak Reference" is only needed if Weak References
- # are used.
- #==============================================================================
- ($imported ||= {})[:iavra_animate_everything] = true
- #==============================================================================
- # ▼ IAVRA::ANIMATE
- #==============================================================================
- module IAVRA
- module ANIMATE
- #==========================================================================
- # ■ ■ ■ ■ ■ CONFIGURATION ■ ■ ■ ■ ■
- #==========================================================================
- #==========================================================================
- # The keys are classes, which should implement animating. The values are
- # arrays containing a generic getter and a generic setter. Usually one of
- # 2 patterns is needed:
- #
- # - Direct variable access:
- #
- # Game_Picture => [
- # lambda {|a| instance_variable_get(:"@#{a}")},
- # lambda {|a, v| instance_variable_set(:"@#{a}", v)}
- # ]
- #
- # - Getter and setter, either implicit via attr_accessor or explicit:
- #
- # Sprite_Base => [
- # lambda {|a| send(:"#{a}")},
- # lambda {|a, v| send(:"#{a}=", v)}
- # ]
- #==========================================================================
- CLASSES = {
- Game_Picture => [
- lambda {|a| instance_variable_get(:"@#{a}")},
- lambda {|a, v| instance_variable_set(:"@#{a}", v)}
- ],
- Game_Character => [
- lambda {|a| instance_variable_get(:"@#{a}")},
- lambda {|a, v| instance_variable_set(:"@#{a}", v)}
- ],
- Sprite_Base => [
- lambda {|a| send(:"#{a}")},
- lambda {|a, v| send(:"#{a}=", v)}
- ]
- }
- #==========================================================================
- # If set to true, animations will chain by default.
- #==========================================================================
- DEFAULT_CHAINING = false
- #==========================================================================
- # The default easing function to be used, if the parameter is omitted.
- #==========================================================================
- DEFAULT_EASING = :linear
- #==========================================================================
- # If set to true, the script will keep weak references to the animated
- # objects instead of referencing to the objects themselves. An object that
- # is only referenced by Weak References may be garbage collected at any
- # time.
- #
- # Using this option adds a dependency to "Iavra Weak References".
- #
- # If you only intend to animate disposable objects (Sprites, Windows) and
- # dispose them correctly, you won't need this.
- #==========================================================================
- WEAK_REFERENCES = false
- #==========================================================================
- # ■ ■ ■ ■ ■ CONFIGURATION ■ ■ ■ ■ ■
- #==========================================================================
- fail "'Iavra Weak Reference' not found" if WEAK_REFERENCES && $imported[:iavra_weakref].nil?
- #==========================================================================
- # Keeping a list of references to animating objects.
- #==========================================================================
- @refs = {}
- #==========================================================================
- # Starts an animation on the target object.
- #
- # Takes the following parameters:
- #
- # target: Object to animate.
- # attrs: Attributes to animate and their target values.
- # duration (default 1): Animation duration, in frames.
- # easing (default configurable): Easing function to use.
- # offset (default 0): Delay, before starting the animation.
- # chain (default configurable): If the animation should be queued.
- # proc: (default nil) On complete callback.
- #
- # Stores the following data as an instance variable:
- #
- # v(alue): The target value.
- # t(ime): Animation step, starting at 0. Gets increased every frame.
- # d(uration: Total animation duration, in frames.
- # e(asing): Symbol identifying the easing function to be used.
- # o(ffset): Delay, before the animation starts (since :d was already taken).
- # p(roc): An optional block to be called once the animation is finished.
- # Inside the block, self will be set to the animated object.
- #
- # WARNING: Procs can NOT be serialized. If you pass a callback to an object
- # that is included in the savefile and the animation is still running when
- # the game is saved, it will crash.
- #
- # The method will fail, if:
- # - The target's class or one of its superclasses isn't listed in CLASSES.
- # - A duration less than 1 is specified.
- # - The specified easing function can't be found.
- #==========================================================================
- def self.start(target, attrs, duration = 1, easing = DEFAULT_EASING,
- offset = 0, chain = DEFAULT_CHAINING, &proc)
- fail "target doesn't support animating" unless CLASSES.keys.map{|k| target.is_a?(k)}.any?
- fail "duration must be higher than 0" if duration < 1
- fail "easing '#{easing}' not found" unless EASING.respond_to?(easing)
- dur = duration.to_i.to_f
- attrs.each do |k, v|
- data = {:v => v, :t => 0.0, :d => dur, :e => easing, :o => offset, :p => proc}
- (target.iavra_animate_data[k] ||= []).clear unless chain
- (target.iavra_animate_data[k] ||= []) << data
- end
- @refs[(WEAK_REFERENCES ? IAVRA::WeakRef.new(target) : target)] = true
- end
- #==========================================================================
- # Stops the animation for either all or selected attributes on the target.
- #==========================================================================
- def self.stop(target, *attrs)
- if attrs.empty?
- target.iavra_animate_data.clear
- else
- attrs.each{|a| target.iavra_animate_data.delete(a)}
- end
- end
- #==========================================================================
- # Skips the current animation for the selected attributes.
- #==========================================================================
- def self.skip(target, *attrs)
- attrs.each{|a| (target.iavra_animate_data[a] || []).shift}
- end
- #==========================================================================
- # Returns the frame of the current animation for the selected attribute or
- # nil, if the attribute isn't animating. If the animation is delayed, this
- # will return 0.
- #==========================================================================
- def self.step(target, attr)
- target.iavra_animate_data[attr][0][:t].to_i rescue nil
- end
- #==========================================================================
- # Returns the current animation status. An object is considered animating,
- # if it has any currently active, non-delayed animations.
- #==========================================================================
- def self.animating?(target)
- target.iavra_animate_data.values.reject{|v| v.empty?}.map{|v| v[0][:o] <= 0}.any?
- end
- #==========================================================================
- # Called every frame. First, clears all references to objects that have
- # been garbage collected, disposed or that have no active animations. Then
- # updates the animations of those remaining.
- #==========================================================================
- def self.update
- @refs.reject!{|ref, v| clear_ref?(ref)}
- @refs.keys.each{|ref| update_ref(ref)}
- end
- #==========================================================================
- # Returns true if:
- # - The reference can't access the object anymore, or
- # - The referenced object is disposable and has been disposed, or
- # - There are no active animations left on the object.
- #==========================================================================
- def self.clear_ref?(ref)
- obj = get_object(ref)
- obj.nil? || (obj.respond_to?(:disposed?) && obj.disposed?) ||obj.iavra_animate_data.empty?
- end
- #========================================================================
- # Iterates over the animation data of an object and does the following:
- #
- # - Cleans up empty animation queues. This happens after an animation is
- # finished or a call to skip removed the last animation.
- #
- # - Takes the first animation in the queue.
- #
- # - If an animation delay was specified and is still active, decreases it
- # by 1 and does nothing.
- #
- # - If the animation just started, stores the current attribute value.
- # This will be used to determine the current values during the animation.
- #
- # - Increases the animation times by 1.
- #
- # - If this is the last step of the animation, sets the attribute to its
- # target value, calls the on complete callback, if any, and removes the
- # animation from the queue.
- #
- # - Otherwise invoke the easing method to determine the current value.
- #
- # - Deletes the data hash, if the animation is finished.
- #========================================================================
- def self.update_ref(ref)
- obj = get_object(ref)
- obj.iavra_animate_data.reject!{|k, v| v.empty?}
- obj.iavra_animate_data.each do |a, data_array|
- next if ((data = data_array[0])[:o] -= 1) >= 0
- data[:b] = obj.iavra_animate_get_attribute(a).to_f unless data[:b]
- if((data[:t] += 1) >= data[:d])
- obj.iavra_animate_set_attribute(a, data[:v])
- obj.instance_eval(&data[:p]) if data[:p]
- data_array.shift
- else
- args = [data[:e], data[:b], data[:v] - data[:b], data[:t], data[:d]]
- obj.iavra_animate_set_attribute(a, EASING.send(*args))
- end
- end
- end
- #==========================================================================
- # Returns the referenced object.
- #==========================================================================
- def self.get_object(ref)
- WEAK_REFERENCES ? ref.object : ref
- end
- #==========================================================================
- # ▼ IAVRA::ANIMATE::METHODS
- # -------------------------------------------------------------------------
- # Since the script is supposed to work on virtually any class, we define
- # our methods in a module that gets included in all declared classes.
- #==========================================================================
- module METHODS
- #========================================================================
- # We define a generic getter and setter for supported classes based on
- # the classes declared in the configuration.
- #========================================================================
- def self.included(cls)
- proc_get, proc_set = *IAVRA::ANIMATE::CLASSES[cls]
- cls.send(:define_method, :iavra_animate_get_attribute, proc_get)
- cls.send(:define_method, :iavra_animate_set_attribute, proc_set)
- end
- #========================================================================
- # Gets the animation data. Initializes it, if it doesn't already exist.
- #========================================================================
- def iavra_animate_data
- @iavra_animate_data ||= {}
- end
- #========================================================================
- # Redirects to the module methods. Some of those are extended to allow
- # chaining.
- #========================================================================
- def iavra_animate_start(*args, &proc); IAVRA::ANIMATE.start(self, *args, &proc); self; end
- def iavra_animate_stop(*args); IAVRA::ANIMATE.stop(self, *args); self; end
- def iavra_animate_skip(*args); IAVRA::ANIMATE.skip(self, *args); self; end
- def iavra_animate_step(*args); IAVRA::ANIMATE.step(self, *args); end
- def iavra_animate_animating?(*args); IAVRA::ANIMATE.animating?(self, *args); end
- end
- #==========================================================================
- # ▼ IAVRA::ANIMATE::EASING
- # -------------------------------------------------------------------------
- # Easing functions to be used in animations. :linear is no easing. Each
- # method needs to take 4 parameters, all of which are floats:
- #
- # b(ase): Base value of the attribute.
- # c(hange): Difference to the target value.
- # t(ime): Elapsed animation time.
- # d(uration): Total animation time.
- #==========================================================================
- module EASING
- #========================================================================
- # Linear
- #========================================================================
- def self.linear(b, c, t, d)
- c * t / d + b
- end
- end
- end
- end
- #==============================================================================
- # We let each supported class include our method module.
- #==============================================================================
- IAVRA::ANIMATE::CLASSES.each{|cls, p| cls.send(:include, IAVRA::ANIMATE::METHODS)}
- #==============================================================================
- # ▼ Scene_Base
- #==============================================================================
- class Scene_Base
- alias :iavra_animate_update_basic :update_basic
- #============================================================================
- # Update animations on every frame.
- #============================================================================
- def update_basic
- IAVRA::ANIMATE.update
- iavra_animate_update_basic
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement