External Animation Editor
Authors: Orochii Zouveleki
Version: 0.2
Type: Game Utility
Key Term: Game Utility
IntroductionDue 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- External animation editor without limitations imposed.
- Uh... can be expanded upon. :^D!
Screenshots(http://puu.sh/hOtv9/2634e057b5.png)
Demohttps://www.dropbox.com/s/gdulaha9fgkjiyv/AnimationEditor.rar?dl=0
InstructionsCopy 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 SCRIPTLETThis 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
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
CompatibilityIt 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 NotesLicence is CC0, so make anything you wish with it.
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.
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
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.
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.
@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.
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.
I'm getting ideas for ARC...
Quote from: Ryex on May 17, 2015, 08:04:31 pm
I'm getting ideas for ARC...
Yes, please! :^D.
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)
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.
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!
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.
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?").
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
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.
I love it! I hope to see, one day, the feature to insert animation under the battler :D