Chaos Project

RPG Maker => RPG Maker Scripts => RMXP Script Database => Topic started by: orochii on May 15, 2015, 04:17:25 pm

Title: [XP] External Animation Editor
Post by: orochii on May 15, 2015, 04:17:25 pm
External Animation Editor
Authors: Orochii Zouveleki
Version: 0.2
Type: Game Utility
Key Term: Game Utility

Introduction
Due to life's unexpected happenings (?), I got fed up of certain limitations in battle animations, more specifically the X/Y coordinates. So I started doing this to break those limits. It's still unfinished, barely started maybe. There is a lot to be reimplemented from the default editor to this.
This is probably going to be either made slow, or could even never be completed. I have a lot of assignments in college and/or desires to continue my game (fun fact is, I started this to use it on my own game so... heck).


Features


Screenshots
(http://puu.sh/hOtv9/2634e057b5.png)


Demo
https://www.dropbox.com/s/gdulaha9fgkjiyv/AnimationEditor.rar?dl=0

Instructions

Copy the Animations.rxdata file from your project to the Data folder of this thing. Then copy the Graphics\Animations folder too.
I'm sorry, maybe one day it will support opening projects.
THEN open the program (Game.exe). Do stuff.
THEN, lastly, copy the Animations.rxdata to your project. Remember to make sure your project isn't open!

RPG::SPRITE ANIMATION MOD SCRIPTLET
This is necessary in case you want to use some extra functions from the editor. These extra functions include by now:
- More than 16 sprites per frame.
More extra functions will be added in the future.

RPG::Sprite Animation Mod
Spoiler: ShowHide

module RPG
  class Sprite < ::Sprite
   
    def animation(animation, hit)
      dispose_animation
      @_animation = animation
      return if @_animation == nil
      @_animation_hit = hit
      @_animation_duration = @_animation.frame_max
      animation_name = @_animation.animation_name
      animation_hue = @_animation.animation_hue
      bitmap = RPG::Cache.animation(animation_name, animation_hue)
      if @@_reference_count.include?(bitmap)
        @@_reference_count[bitmap] += 1
      else
        @@_reference_count[bitmap] = 1
      end
      max_sprite = get_max_sprites(@_animation)
      @_animation_sprites = []
      if @_animation.position != 3 or not @@_animations.include?(animation)
        max_sprite.times {
          sprite = ::Sprite.new(self.viewport)
          sprite.bitmap = bitmap
          sprite.visible = false
          @_animation_sprites.push(sprite)
        }
        unless @@_animations.include?(animation)
          @@_animations.push(animation)
        end
      end
      update_animation
    end
   
    def loop_animation(animation)
      return if animation == @_loop_animation
      dispose_loop_animation
      @_loop_animation = animation
      return if @_loop_animation == nil
      @_loop_animation_index = 0
      animation_name = @_loop_animation.animation_name
      animation_hue = @_loop_animation.animation_hue
      bitmap = RPG::Cache.animation(animation_name, animation_hue)
      if @@_reference_count.include?(bitmap)
        @@_reference_count[bitmap] += 1
      else
        @@_reference_count[bitmap] = 1
      end
      max_sprite = get_max_sprites(@_loop_animation)
      @_loop_animation_sprites = []
      max_sprite.times {
        sprite = ::Sprite.new(self.viewport)
        sprite.bitmap = bitmap
        sprite.visible = false
        @_loop_animation_sprites.push(sprite)
      }
      update_loop_animation
    end
   
    def get_max_sprites(animation)
      max = 0
      animation.frames.each {|frame|
        max = frame.cell_max if max<frame.cell_max
      }
      return max
    end
   
    def dispose_animation
      if @_animation_sprites != nil
        sprite = @_animation_sprites[0]
        if sprite != nil
          @@_reference_count[sprite.bitmap] -= 1
          if @@_reference_count[sprite.bitmap] == 0
            sprite.bitmap.dispose
          end
        end
        for sprite in @_animation_sprites
          sprite.dispose
        end
        @_animation_sprites = nil
        @_animation = nil
      end
    end
   
    def dispose_loop_animation
      if @_loop_animation_sprites != nil
        sprite = @_loop_animation_sprites[0]
        if sprite != nil
          @@_reference_count[sprite.bitmap] -= 1
          if @@_reference_count[sprite.bitmap] == 0
            sprite.bitmap.dispose
          end
        end
        for sprite in @_loop_animation_sprites
          sprite.dispose
        end
        @_loop_animation_sprites = nil
        @_loop_animation = nil
      end
    end
   
    def update_animation
      if @_animation_duration > 0
        frame_index = @_animation.frame_max - @_animation_duration
        cell_data = @_animation.frames[frame_index].cell_data
        position = @_animation.position
        animation_set_sprites(@_animation_sprites, cell_data, position)
        for timing in @_animation.timings
          if timing.frame == frame_index
            animation_process_timing(timing, @_animation_hit)
          end
        end
      else
        dispose_animation
      end
    end
   
    def update_loop_animation
      frame_index = @_loop_animation_index
      cell_data = @_loop_animation.frames[frame_index].cell_data
      position = @_loop_animation.position
      animation_set_sprites(@_loop_animation_sprites, cell_data, position)
      for timing in @_loop_animation.timings
        if timing.frame == frame_index
          animation_process_timing(timing, true)
        end
      end
    end
   
    def animation_set_sprites(sprites, cell_data, position)
      sprites.size.times{ |i|
        sprite = sprites[i]
        pattern = cell_data[i, 0]
        if sprite == nil or pattern == nil or pattern == -1
          sprite.visible = false if sprite != nil
          next
        end
        sprite.visible = true
        sprite.src_rect.set(pattern % 5 * 192, pattern / 5 * 192, 192, 192)
        if position == 3
          if self.viewport != nil
            sprite.x = self.viewport.rect.width / 2
            sprite.y = self.viewport.rect.height - 160
          else
            sprite.x = 320
            sprite.y = 240
          end
        else
          sprite.x = self.x - self.ox + self.src_rect.width / 2
          sprite.y = self.y - self.oy + self.src_rect.height / 2
          sprite.y -= self.src_rect.height / 4 if position == 0
          sprite.y += self.src_rect.height / 4 if position == 2
        end
        sprite.x += cell_data[i, 1]
        sprite.y += cell_data[i, 2]
        sprite.z = 2000
        sprite.ox = 96
        sprite.oy = 96
        sprite.zoom_x = cell_data[i, 3] / 100.0
        sprite.zoom_y = cell_data[i, 3] / 100.0
        sprite.angle = cell_data[i, 4]
        sprite.mirror = (cell_data[i, 5] == 1)
        sprite.opacity = cell_data[i, 6] * self.opacity / 255.0
        sprite.blend_type = cell_data[i, 7]
      }
    end
   
    def animation_process_timing(timing, hit)
      if (timing.condition == 0) or
        (timing.condition == 1 and hit == true) or
        (timing.condition == 2 and hit == false)
        if timing.se.name != ""
          se = timing.se
          Audio.se_play("Audio/SE/" + se.name, se.volume, se.pitch)
        end
        case timing.flash_scope
        when 1
          self.flash(timing.flash_color, timing.flash_duration * 2)
        when 2
          if self.viewport != nil
            self.viewport.flash(timing.flash_color, timing.flash_duration * 2)
          end
        when 3
          self.flash(nil, timing.flash_duration * 2)
        end
      end
    end
   
    def x=(x)
      sx = x - self.x
      if sx != 0
        if @_animation_sprites != nil
          @_animation_sprites.size.times {|i|
            @_animation_sprites[i].x += sx
          }
        end
        if @_loop_animation_sprites != nil
          @_loop_animation_sprites.size.times {|i|
            @_loop_animation_sprites[i].x += sx
          }
        end
      end
      super
    end
   
    def y=(y)
      sy = y - self.y
      if sy != 0
        if @_animation_sprites != nil
          @_animation_sprites.size.times {|i|
            @_animation_sprites[i].y += sy
          }
        end
        if @_loop_animation_sprites != nil
          @_loop_animation_sprites.size.times {|i|
            @_loop_animation_sprites[i].y += sy
          }
        end
      end
      super
    end
   
  end
end



Compatibility
It can be potentially uncompatible with anything that overrides the RPG::Sprite class. Because for several extra functions it needs a small scriptlet that modifies that.


Credits and Thanks


Author's Notes
Licence is CC0, so make anything you wish with it.
Title: Re: [XP] External Animation Editor
Post by: KK20 on May 15, 2015, 07:28:24 pm
Holy shit...I was working on something like this too. Granted all I got was like the first couple of windows and defining resolutions, but I did have plans to add a couple other things that the editor didn't have.

Well, seeing as you already have a framework to build upon, I can help out with this. :) Let me take a look at it.
Title: Re: [XP] External Animation Editor
Post by: G_G on May 15, 2015, 11:30:55 pm
How to make it so people can copy it to their projects.

1. Rename everything to AnimEditor. So the executable and the project file.
2. Open the ini and change the scripts file to Data/AnimEditor.rxdata
3. Rename Scripts.rxdata to AnimEditor.rxdata

Then they can just copy it into any project. But other than that, great idea and hella good job for doing this inside RGSS. I seriously would have just created something in C#. xD
Title: Re: [XP] External Animation Editor
Post by: KK20 on May 16, 2015, 12:01:41 am
Quote from: gameus on May 15, 2015, 11:30:55 pm
I seriously would have just created something in C#. xD

I would too if I knew how :P
Would've at least made the UI stuff easy.
Title: Re: [XP] External Animation Editor
Post by: finalholylight on May 16, 2015, 11:56:48 am
Nice!
Oh, I have some ideas, would you mind add these options to your editor ?
1. Zoom only X/ only Y
2. Animation below battlers.
Title: Re: [XP] External Animation Editor
Post by: orochii on May 17, 2015, 02:32:49 pm
@KK20: Before anything, excuse me for my horrible mess of code! "orz.
@Gameus: Maybe doing it in C# would had been better. But I... was eager to start, and RMXP was the first thing I had close. And I've never done anything in C#.

I can work on these things, zoom_y and zoom_x by separate, and making some sprites to be below the character is easy too. Only thing it would need is including a small scriptlet that would change the RPG::Sprite::animation_set_sprites method. I did the latter on my game, because I wanted to make certain sprites under the character, but the rest over it.

I want to preserve compatibility with default animations too. So... I guess make something like an AnimExtraData class. That way it will be readable by RMXP.
Only issue I have found regarding to opening the animations from the RMXP editor is that coordinates are automatically readjusted to their default range (-320...320 for X and -160...160 for Y), when you select them from the list.
Title: Re: [XP] External Animation Editor
Post by: KK20 on May 17, 2015, 03:37:42 pm
Yeah that's kind of a thing with the editor. I tried something like making item descriptions longer via script and went into the database. If I viewed an item that had too many characters for its description, it wouldn't let me close the window, move to another tab, or view a different item until I reduced the description.

So you will have to put some disclaimer that the user should NOT go back into the animation editor.

As for other things I wanted to be able to change with animations:
Give the cell animations the "hit" and "miss" properties, more than 16 sprites, modifying the battler graphic (moving, shaking, zooming, etc), Z-layering, and more than 200 frames.

Things in the editor I always wanted:
More hotkeys (number keys could refer to like what cell you want to modify), layers (like typical art programs), preview loop animations at the same time as playing a normal animation, grid lines toggle and changing the grid interval, and probably a couple other things I'm forgetting.
Title: Re: [XP] External Animation Editor
Post by: Ryex on May 17, 2015, 08:04:31 pm
I'm getting ideas for ARC...
Title: Re: [XP] External Animation Editor
Post by: orochii on May 19, 2015, 09:24:53 am
Quote from: Ryex on May 17, 2015, 08:04:31 pm
I'm getting ideas for ARC...
Yes, please! :^D.
Title: Re: [XP] External Animation Editor
Post by: orochii on May 19, 2015, 10:44:39 pm
UPDATE!

Here is how it looks now.
(http://puu.sh/hTHgX/3ab0970c46.png)

Right now, these are the most important things to add:
- Support for "timings" (SFX, screen/target flashes, etc)
- Tools (copy/paste frames, autocompletion, cell options, move frames...).
- More hotkeys!

Press F10 to open the help window. ESC closes that window.

Also I've updated the download. Now it is prepared to be copied inside your game folder, just like gameus suggested.

EDIT: Now this is getting interesting. I've added the option to have more than 16 sprites, and... the RMXP editor seems to support them! They don't have a number at the upper left corner but still... they can be moved and edited as any other sprite.
(http://puu.sh/hUo13/3d1a07f763.png)
Title: Re: [XP] External Animation Editor
Post by: KK20 on May 21, 2015, 12:04:30 am
Still looking good. It seems you're working pretty diligently on this; perhaps I'll wait until it's either finished or stopped before I work on it myself.

While that is interesting the built-in editor allows more than 16 sprites, it's kind of a fruitless discovery. The point is to not allow the user to ever have to rely on the built-in editor for making animations. Besides, most of the things you're changing break the RMXP standard and are not compatible anyways.

I'll also move this.
Title: Re: [XP] External Animation Editor
Post by: orochii on May 21, 2015, 09:08:26 pm
Thanks KK20 for moving the topic. By the way, I think I'll be doing the three necessary things I've mentioned earlier, then I will leave it be some time, because I need to continue with my game. I wanted to make a demo release this June!

Yeah, that discovery was more something I found interesting, I think I was more worried that it could explode or something (?).

And by the way, I had an idea for fixing the issue with the default editor overriding the X/Y coordinates out of range. What I was thinking is that the custom editor worked on a copy of the Animations.rxdata (I called it "Animations2.rxdata). The custom animation editor makes sure there is the same amount of animations on both files (because skills and other editor stuff need to be able to know there are more animations, and these only read from the default file).

It's time to make the timings!
Title: Re: [XP] External Animation Editor
Post by: ForeverZer0 on May 21, 2015, 09:36:58 pm
Could just rewrite how animations work with a script, but then you would still need to load from a separate file. It would allow for things out of range, but add some clunkiness to the whwhole thing. Depends on how important it is to use more than 16.
Title: Re: [XP] External Animation Editor
Post by: KK20 on May 21, 2015, 11:49:19 pm
Rewriting the animation methods in RPG::Sprite is going to be inevitable at this point--there are a lot of things I'd like to see done with animations that can't be done without doing so.

And yeah, making a second file might help prevent the overwriting, but it'll probably cause more complications depending on how much you want the default editor to be compatible with your custom editor (to which I say "who cares about compatibility?").
Title: Re: [XP] External Animation Editor
Post by: orochii on May 22, 2015, 12:57:05 pm
The only things I care in terms of compatibility are that the animations show up in RMXP for being selected on skills, items, Show animation command and that stuff. After that, my concerns are not making the editor explode, because that can cause potential data loss/corruption, and that, if someone opens the default animation editor by mistake, they don't lose anything either, because that can be frustrating too.

I've tweaked some of the RPG::Sprite animation-related methods. Others are there in case I need them or as references.
Actually, I need to append that to the post! It's a necessary scriptlet.

RPG::Sprite Animation Mod
Spoiler: ShowHide

module RPG
  class Sprite < ::Sprite
   
    def animation(animation, hit)
      dispose_animation
      @_animation = animation
      return if @_animation == nil
      @_animation_hit = hit
      @_animation_duration = @_animation.frame_max
      animation_name = @_animation.animation_name
      animation_hue = @_animation.animation_hue
      bitmap = RPG::Cache.animation(animation_name, animation_hue)
      if @@_reference_count.include?(bitmap)
        @@_reference_count[bitmap] += 1
      else
        @@_reference_count[bitmap] = 1
      end
      max_sprite = get_max_sprites(@_animation)
      @_animation_sprites = []
      if @_animation.position != 3 or not @@_animations.include?(animation)
        max_sprite.times {
          sprite = ::Sprite.new(self.viewport)
          sprite.bitmap = bitmap
          sprite.visible = false
          @_animation_sprites.push(sprite)
        }
        unless @@_animations.include?(animation)
          @@_animations.push(animation)
        end
      end
      update_animation
    end
   
    def loop_animation(animation)
      return if animation == @_loop_animation
      dispose_loop_animation
      @_loop_animation = animation
      return if @_loop_animation == nil
      @_loop_animation_index = 0
      animation_name = @_loop_animation.animation_name
      animation_hue = @_loop_animation.animation_hue
      bitmap = RPG::Cache.animation(animation_name, animation_hue)
      if @@_reference_count.include?(bitmap)
        @@_reference_count[bitmap] += 1
      else
        @@_reference_count[bitmap] = 1
      end
      max_sprite = get_max_sprites(@_loop_animation)
      @_loop_animation_sprites = []
      max_sprite.times {
        sprite = ::Sprite.new(self.viewport)
        sprite.bitmap = bitmap
        sprite.visible = false
        @_loop_animation_sprites.push(sprite)
      }
      update_loop_animation
    end
   
    def get_max_sprites(animation)
      max = 0
      animation.frames.each {|frame|
        max = frame.cell_max if max<frame.cell_max
      }
      return max
    end
   
    def dispose_animation
      if @_animation_sprites != nil
        sprite = @_animation_sprites[0]
        if sprite != nil
          @@_reference_count[sprite.bitmap] -= 1
          if @@_reference_count[sprite.bitmap] == 0
            sprite.bitmap.dispose
          end
        end
        for sprite in @_animation_sprites
          sprite.dispose
        end
        @_animation_sprites = nil
        @_animation = nil
      end
    end
   
    def dispose_loop_animation
      if @_loop_animation_sprites != nil
        sprite = @_loop_animation_sprites[0]
        if sprite != nil
          @@_reference_count[sprite.bitmap] -= 1
          if @@_reference_count[sprite.bitmap] == 0
            sprite.bitmap.dispose
          end
        end
        for sprite in @_loop_animation_sprites
          sprite.dispose
        end
        @_loop_animation_sprites = nil
        @_loop_animation = nil
      end
    end
   
    def update_animation
      if @_animation_duration > 0
        frame_index = @_animation.frame_max - @_animation_duration
        cell_data = @_animation.frames[frame_index].cell_data
        position = @_animation.position
        animation_set_sprites(@_animation_sprites, cell_data, position)
        for timing in @_animation.timings
          if timing.frame == frame_index
            animation_process_timing(timing, @_animation_hit)
          end
        end
      else
        dispose_animation
      end
    end
   
    def update_loop_animation
      frame_index = @_loop_animation_index
      cell_data = @_loop_animation.frames[frame_index].cell_data
      position = @_loop_animation.position
      animation_set_sprites(@_loop_animation_sprites, cell_data, position)
      for timing in @_loop_animation.timings
        if timing.frame == frame_index
          animation_process_timing(timing, true)
        end
      end
    end
   
    def animation_set_sprites(sprites, cell_data, position)
      sprites.size.times{ |i|
        sprite = sprites[i]
        pattern = cell_data[i, 0]
        if sprite == nil or pattern == nil or pattern == -1
          sprite.visible = false if sprite != nil
          next
        end
        sprite.visible = true
        sprite.src_rect.set(pattern % 5 * 192, pattern / 5 * 192, 192, 192)
        if position == 3
          if self.viewport != nil
            sprite.x = self.viewport.rect.width / 2
            sprite.y = self.viewport.rect.height - 160
          else
            sprite.x = 320
            sprite.y = 240
          end
        else
          sprite.x = self.x - self.ox + self.src_rect.width / 2
          sprite.y = self.y - self.oy + self.src_rect.height / 2
          sprite.y -= self.src_rect.height / 4 if position == 0
          sprite.y += self.src_rect.height / 4 if position == 2
        end
        sprite.x += cell_data[i, 1]
        sprite.y += cell_data[i, 2]
        sprite.z = 2000
        sprite.ox = 96
        sprite.oy = 96
        sprite.zoom_x = cell_data[i, 3] / 100.0
        sprite.zoom_y = cell_data[i, 3] / 100.0
        sprite.angle = cell_data[i, 4]
        sprite.mirror = (cell_data[i, 5] == 1)
        sprite.opacity = cell_data[i, 6] * self.opacity / 255.0
        sprite.blend_type = cell_data[i, 7]
      }
    end
   
    def animation_process_timing(timing, hit)
      if (timing.condition == 0) or
         (timing.condition == 1 and hit == true) or
         (timing.condition == 2 and hit == false)
        if timing.se.name != ""
          se = timing.se
          Audio.se_play("Audio/SE/" + se.name, se.volume, se.pitch)
        end
        case timing.flash_scope
        when 1
          self.flash(timing.flash_color, timing.flash_duration * 2)
        when 2
          if self.viewport != nil
            self.viewport.flash(timing.flash_color, timing.flash_duration * 2)
          end
        when 3
          self.flash(nil, timing.flash_duration * 2)
        end
      end
    end
   
    def x=(x)
      sx = x - self.x
      if sx != 0
        if @_animation_sprites != nil
          @_animation_sprites.size.times {|i|
            @_animation_sprites[i].x += sx
          }
        end
        if @_loop_animation_sprites != nil
          @_loop_animation_sprites.size.times {|i|
            @_loop_animation_sprites[i].x += sx
          }
        end
      end
      super
    end
   
    def y=(y)
      sy = y - self.y
      if sy != 0
        if @_animation_sprites != nil
          @_animation_sprites.size.times {|i|
            @_animation_sprites[i].y += sy
          }
        end
        if @_loop_animation_sprites != nil
          @_loop_animation_sprites.size.times {|i|
            @_loop_animation_sprites[i].y += sy
          }
        end
      end
      super
    end
   
  end
end


So yeah, that's going to the OP.
Title: Re: [XP] External Animation Editor
Post by: PrinceEndymion88 on June 02, 2015, 06:08:20 pm
I love it! I hope to see, one day, the feature to insert animation under the battler :D