From The Mana World
m (Move page script moved page Dev:Animations to Development:Animations: Rename Dev: to Development: so the miraheze import works)
 
(43 intermediate revisions by 8 users not shown)
Line 1: Line 1:
{{Status_red}}
{{Category_programming}}
After a mini-meeting between me and Doener we refined the model.
{{Category_art}}
Each animation will be defined by an xml document, here follows an example:
{{Status_green}}


<pre>
TMW follows the same conventions as laid out by the client [http://manaplus.org/ manaplus]
<?xml version="1.0"?>
<animation name="player">
<spritemap name="misc" src="player-male-misc.png" width="64" height="64" />
<spritemap name="stab" src="player-male-stab.png" width="64" height="64" />
<spritemap name="bow" src="player-male-bow.png" width="64" height="64" />
<action name="stand">
<frame spritemap="misc" index="0" delay="0" />
</action>
<action name="walk">
<frame spritemap="misc" index="0" delay="20" />
<frame spritemap="misc" index="1" delay="20" />
</action>
<action name="bow-attack">
<sequence spritemap="bow" start="0" end="5" delay="20" />
</action>
</animation>
</pre>


So if you want to load the playerset you just load player.xml and it takes care of loading all related images. Of course delays are defined in milliseconds.
== Example of a sprite definition ==


== Some attempt by Bjørn ==
The animations of all sprites are defined by xml documents. Here follows an example:


A sprite is an object which can carry several animations, hence I call the root element the <code>sprite</code>. Also, I think we should seperately define animations in several directions. For now this example is based on the current playerset image, but by using multiple imagesets this could of course be split up.
<syntaxhighlight lang="xml">
<?xml version="1.0"?>
<sprite name="player" action="stand">


Each <code>frame</code> and <code>frames</code> element can optionally have attributes <code>x</code> and <code>y</code> to specify an offset from the default drawing position for that frame. This will allow the animation of for example the hairset (or any equipment) to reuse the same frames with different offsets, and be defined exactly like below.
<imageset name="base" src="graphics/sprites/player_male_base.png" width="64" height="64" />


In the bow animation I've chosen a shortcut to specify different delays for different frames. I am undecided yet whether this is a nice feature or whether it'll be better to require multiple <code>frame</code> elements in such cases.
<action name="stand" imageset="base">
<animation direction="down">
<frame index="0" />
</animation>
<animation direction="left">
<frame index="18" />
</animation>
<animation direction="up">
<frame index="36" />
</animation>
<animation direction="right">
<frame index="54" />
</animation>
</action>


For the dead frame I have specified no direction attribute, which makes this animation independent of direction. Also, it includes an experimental suggestion on specifying that a random frame should be chosen.
<action name="walk" imageset="base">
<animation direction="down">
<frame index="1" delay="75" />
<frame index="2" delay="75" />
<frame index="3" delay="75" />
<frame index="4" delay="75" />
<frame index="5" delay="75" />
<frame index="6" delay="75" />
</animation>
<animation direction="left">
<sequence start="19" end="24" delay="75" />
</animation>
<animation direction="up">
<sequence start="37" end="42" delay="75" />
</animation>
<animation direction="right">
<sequence start="55" end="60" delay="75" />
</animation>
</action>


<pre>
<action name="attack" imageset="base">
<sprite name="player">
<animation direction="down">
<imageset name="base" src="player-male-base.png" width="64" height="64" />
<sequence start="9" end="12" delay="75" />
 
<end />
<animation name="stand" direction="down">
</animation>
  <frame imageset="base" index="0" />
<animation direction="left">
</animation>
<sequence start="27" end="30" delay="75" />
<animation name="stand" direction="left">
<end />
  <frame imageset="base" index="18" />
</animation>
</animation>
<animation direction="up">
<animation name="stand" direction="up">
<sequence start="45" end="48" delay="75" />
  <frame imageset="base" index="36" />
<end />
</animation>
</animation>
<animation name="stand" direction="right">
<animation direction="right">
  <frame imageset="base" index="54" />
<sequence start="63" end="66" delay="75" />
</animation>
<end />
 
</animation>
<animation name="walk" direction="down">
</action>
  <frames imageset="base" start="1" end="6" delay="10" />
</animation>
...
 
<animation name="sit" direction="down">
  <frame imageset="base" index="7" />
</animation>
...
 
<animation name="bow-attack" direction="down">
  <frames imageset="base" start="13" end="17" delays="5,5,5,10,50" />
</animation>
...


<animation name="dead">
  <random>
  <frame imageset="base" index="8" />
  <frame imageset="base" index="26" />
  <frame imageset="base" index="44" />
  <frame imageset="base" index="62" />
  </random>
</animation>
</sprite>
</sprite>
</pre>
</syntaxhighlight>


After reflecting on this idea (and the above suggestion) I think a first issue to solve is to reduce the amount of duplicated definitions. For example, at the moment every monster uses the exact same frame size and animation frames. Hence, it would be interesting if the defined sprite could take the actual imageset it uses as a parameter instead of specifying this on its own. This way we can define a monster like:
So if you want to load the playerset you just load player.xml and it takes care of loading all related images. Of course delays are defined in milliseconds.
 
<pre>
<sprite name="monster-walking-default">
<imageset name="base" />
...
</sprite>
 
<being name="Scorpion">
<sprite ref="monster-walking-default">
  <with-imageset name="base" src="scorpion-base.png" width="60" height="60" />
</sprite>
</being>
 
<being name="Red Scorpion">
<sprite ref="monster-walking-default">
  <with-imageset name="base" src="scorpion-red-base.png" width="60" height="60" />
</sprite>
</being>
</pre>


Defining an empty imageset (without <code>src</code> attribute) will make the engine require this imageset to be passed as a parameter whenever the spriteset is referenced.
== Specifications ==


I'm not convinced yet that the <code>name</code> attribute of the <code>sprite</code> element is really necessary. I think I would prefer each sprite to be defined in its own file. However it could be attractive to define multiple things in the same file, but in that case I think we will have to read everything when the client is launched, and only graphics and imagesets can be created lazily (only when necessary). Because the other data consists of just numbers and strings, this should not be really a problem.
=== &lt;sprite&gt; ===
A sprite is an object which can carry several animations, hence we call the root element the <code>sprite</code>. The sprite tag has two optional properties <code>variants</code> and <code>variant_offset</code>. These are required when there are multiple very similar versions of a sprite in one spriteset. One example for this are the hairset spritesets. The <code>variants</code> property defines the number of variants in the spriteset and the <code>variant_offset</code> property how many sprites are between the first sprites of the variants. When defining multiple variants you only define the animation sequences of the first variant. the engine then shifts the index parameters when it needs another.


== An implementation idea by Peoro ==
=== &lt;action&gt; ===
collection of the animation in different directions that belong to an action the character can perform. the properties are the imageset the animation phases are taken from and the name of the action.


An idea to manage the animations should be a class based on a timer which choose automatically the current frame.
Current actions as defined in the base sprite 1.0:
Who needs animations (players, monsters and npcs) has only to start the animanion and to request the frame to draw it.
Default
Each instance of Animation class keep its own start-time (or a personal timer), its total time, number of frames, and the time of every frame.
Walk
The Animation class has to provide at least a function to start the animation and another one to get the current frame; other usefull functions should pause and restart the animation and allow to jump to a random frame.
sit
dead
attack
attack_bow
Directions: NSEW


Here a simple example of a prototype of an Animation class:
New actions/directions will be added as the sprite progresses.
(I mutilated my original Animation class to simplify and summarize it cutting a lot of other functions and vars and using the most possible of standard items)
Sprite 1.5 work can be seen here [https://trello.com/b/rNnx513z/art-development art trello]


The action type matches the item.xml:
<pre>
<pre>
class Animation
     <item id="522"
{
         image="equipment/weapon/dagger-sharpknife.png"
     int nframes;
         name="Sharp Knife"
    Img **frames;
         description="A really sharp knife. Don't hurt yourself!"
    int *times;
         effect="Damage +10"
    int totalTime;
        type="equip-1hand"
    int startTime;
         weapon-type="knife"
   
         attack-action="attack"
public:
         weight="150">
    Animation ( int n, Img **i, int *t )
         <sprite>weapon-dagger.xml</sprite>
    {
         <sound event="strike">weapons/knives/sharpknife-miss1.ogg</sound>
        int c;
     </item>
   
        nframes = n;
         frames = new Img* [n];
         times = new int [n];
         totalTime = 0;
       
         for ( c = 0; c < n; c ++ ) {
            frames[c] = i[c];
            times[c] = t[c];
            totalTime += t[c];
         }
    }
   
    inline void start ( int t = clock() )
    {
         startTime = t;
    }
   
    Img *getFrame ( int t = clock() )
    {
        int i;
        int it
       
         if ( ! totalTime ) {
            t = 0;
         } else {
            t = (t-startTime) % totalTime;
        }
           
         // searches the current frame
        for ( i = 0, it = 0; (it + times[i]) < t; i ++, it += times[i] );
       
        return frames[i];
     }
};
</pre>
</pre>
Would use attack from the sprite sheets or default if attack is not declared.


And here how to use this class:
=== &lt;animation&gt; ===
Defines an animation sequence that should be displayed when the sprite object faces in a specific direction (attribute "direction").


the possible directions are:
<pre>
<pre>
Img * imgs [] = { img1, img2, img3 };
up
int times [] = { 20, 20, 30 };
down
 
left
Animation a ( 3, imgs, times );
right
 
default
a.start ( );
 
while ( 1 ) {
    draw ( a.getFrame() );
}
</pre>
</pre>


An advantage of this class is that the animation speed is indipendent to FPS and it's very easy to use.
when a specific direction isn't provided, the default direction is used instead. When no default direction is defined, the first defined direction is used.


This is a simple way to manage a lot of animations for who needs more than one:
Every <code><animation></code> has one or more <code>frame</code> or <code>sequence</code> child elements and an optional <code><end></code> element.


<pre>
=== &lt;frame&gt; ===
class AnimationsList
Defines one frame of the animation. The only required property is <code>index</code>.  Optional properties include <code>offsetX</code>, <code>offsetY</code> and <code>delay</code>.
{
    // ltstr is only a const char* comparison function
    map<const char*, Animation*, ltstr> animations;
    int startTime;      // now Animation::startTime is no more usefull
    Animation *currentAnimation;
   
public:
    AnimationsList ( ) : currentAnimation(0) { }
   
    inline void addAnimation ( const char *n, Animation *a )
    {
        animations[n] = a;
    }
   
    int setAnimation ( const char *n, int t = clock() )
    {
map<const char*, Animation*, ltstr>::iterator ai;
        Animation *a;
       
ai = animations.find ( n );
if ( ai == animations.end() ) {
                // error... animation not found
return 0;
}
a = ai->second;
       
        if ( currentAnimation != a ) {
            a->start ( );
            currentAnimation = a;
            startTime = t;
}
       
        return 1;
    }
   
    Img * getFrame ( )
    {
        if ( currentAnimation ) {
            return currentAnimation->getFrame ( );
        }
        return 0;
    }
};
</pre>


Using a multimap instead the map it's possible to keep more than an animation bind to the same string. This would be a good way to make AnimationsList choose a random animation.
<code>index</code> defines the index of the graphic on the spriteset.  


The <code>delay</code> property specifies the number of milliseconds the frame is displayed before it is replaced by the next frame in the sequence. The sequence restarts when the last frame is finished. When no delay is specified (or specified as "0") the animation doesn't continue when this <code>frame</code> is reached.


And here how to use AnimationsList class:
The <code>frame</code> element has the optional properties <code>offsetX</code> and <code>offsetY</code> to specify an offset from the default drawing position for that frame. This allows the animation of for example the hairset (or any equipment) to reuse the same frames with different offsets.


<pre>
=== &lt;sequence&gt; ===
AnimationsList al;
The <code>sequence</code> tag defines one or more phases of animation. It has the required properties <code>delay</code>, <code>start</code>, and <code>end</code>. The <code>delay</code> property specifies the delay in milliseconds between each phase. The <code>start</code> and <code>end</code> properties define the first and the last indexes of the animation sequence on the spritesheet.


al.addAnimation ( "walking" "south", anim1 );
=== &lt;end&gt; ===
al.addAnimation ( "walking" "north", anim2 );
When the animation sequence reaches an <code>end</code> tag, the animation stops and the sprite animation is returned to the "stand" state. This can be used for one time action sequences like attacking. This tag has no properties.
al.addAnimation ( "anim3", anim3 );


al.setAnimation ( "walking" "south" );
=== &lt;include&gt; ===


while ( 1 ) {
Can be used to include another sprite definition file. If you want to override the images, you need to specify them ''before'' the include element. If you want to override any animations, you have to do so ''after'' the include element. Example:
    draw ( al.getFrame() );
}
</pre>


I hope not to had make a mistake writing... Anyway these are only theorical bases.
<syntaxhighlight lang="xml">
<sprite>
<imageset name="base" .../>
<include file="other-sprite-file.xml"/>
<!-- possibly override or introduce new actions -->
</sprite>
</syntaxhighlight>


== Implementation proposal by Doener ==
=== hairstyles ===


This proposal is incomplete regarding how new sequences are added to the
Most hairstyles which only have one frame for each direction and death, so five frames.
Animation (which is not the right name for this type of class *g*)... Anyway,
The offset for the hairstyles is standard to either haristlye01.xml or hairstyle13.xml
you should get the idea...
The Frame class encapsulates images and are used to create a single linked list that
creates an animation. The idea behind the linked list is that you can easily create loops,
by having the last element of the animation pointing to the first one. Also, you can easily
prepend a "starting animation" before the loop, like in an animation of a running player.
He starts out with one animation and then loops the real running... The Animation class is
responsible for mapping animation names to their starting frame and for disposing the encapsulating
Frame objects, thus a std::map and a std::list is used to keep track of those objects.
The next frame is simply calculated from the time that has passed since the last run of the
logic method. The case that a whole animation loop can be skipped is not taken into consideration,
because a) we cannot do this with frames linked together arbitrarily and b) animations will probably
take around 100ms or longer, ie. we'd have less than 10fps for that case to happen and then
there's nothing left to gain by that simple optimization anymore anyway ;)


For the linked list features, of course the xml format needs to be adjusted to support that.
Currently the hairsprites follow the [http://manaplus.org/items.xml hairsprite convetion]


<pre>
{{Template:Content XML}}
/*
[[Category:Development]]
* Frame class, stored information about frames in an animation
*/
struct Frame
{
Image *mImg;  // Image for this frame
int mDelay;  // Time in ms for how long this frame is shown
Frame *mNext; // Next frame in sequence
};
 
class Animation
{
public:
Animation():mCurrentFrame(0);
~Animation();
 
void setSequence(const std::string &);
 
void logic(int timePassed);
 
Image* getCurrentImage()
{ return mCurrentFrame ? mCurrentFrame->mImg : 0; }
 
private:
Frame *mCurrentFrame;
typedef std::map<std::string,Frame*> FrameMap;
typedef FrameMap::iterator FrameMapIterator;
FrameMap mStartFrames;
std::list<Frame*> mFrames;
int mRemain;
}
 
Animation::~Animation()
{
for_each(mFrames.begin(), mFrames.end(), make_dtor(mFrames));
}
 
void Animation::setSequence(const std::string &name)
{
FrameMapIterator i = mStartFrames.find(name);
mCurrentFrame = (i == mStartFrames.end()) ? 0 : i->second;
mRemain = 0;
}
 
void Animation::logic(int timePassed)
{
mRemain += timePassed;
while (mRemain >= mCurrentFrame->mDelay) {
mRemain -= mCurrentFrame->mDelay;
mCurrentFrame = mCurrentFrame->mNext;
}
};
</pre>

Latest revision as of 03:56, 27 March 2024

This article contains information for Programmers working or interested in working for The Mana World

This article contains information for Artists working or interested in working for The Mana World

This article is for reference purpose

The features described in this article are already implemented in the game. The article should describe how a certain aspect of the game currently works. You may of course edit this article to improve the description of the circumstances. Your opinions or improvement suggestions about the described aspects themself are of course appreciated, too. But please put these on the discussion page of this article to keep facts and fiction separated.


TMW follows the same conventions as laid out by the client manaplus

Example of a sprite definition

The animations of all sprites are defined by xml documents. Here follows an example:

<?xml version="1.0"?>
<sprite name="player" action="stand">

	<imageset name="base" src="graphics/sprites/player_male_base.png" width="64" height="64" />

	<action name="stand" imageset="base">
		<animation direction="down">
			<frame index="0" />
		</animation>
		<animation direction="left">
			<frame index="18" />
		</animation>
		<animation direction="up">
			<frame index="36" />
		</animation>
		<animation direction="right">
			<frame index="54" />
		</animation>
	</action>

	<action name="walk" imageset="base">
		<animation direction="down">
			<frame index="1" delay="75" />
			<frame index="2" delay="75" />
			<frame index="3" delay="75" />
			<frame index="4" delay="75" />
			<frame index="5" delay="75" />
			<frame index="6" delay="75" />
		</animation>
		<animation direction="left">
			<sequence start="19" end="24" delay="75" />
		</animation>
		<animation direction="up">
			<sequence start="37" end="42" delay="75" />
		</animation>
		<animation direction="right">
			<sequence start="55" end="60" delay="75" />
		</animation>
	</action>

	<action name="attack" imageset="base">
		<animation direction="down">
			<sequence start="9" end="12" delay="75" />
			<end />
		</animation>
		<animation direction="left">
			<sequence start="27" end="30" delay="75" />
			<end />
		</animation>
		<animation direction="up">
			<sequence start="45" end="48" delay="75" />
			<end />
		</animation>
		<animation direction="right">
			<sequence start="63" end="66" delay="75" />
			<end />
		</animation>
	</action>

</sprite>

So if you want to load the playerset you just load player.xml and it takes care of loading all related images. Of course delays are defined in milliseconds.

Specifications

<sprite>

A sprite is an object which can carry several animations, hence we call the root element the sprite. The sprite tag has two optional properties variants and variant_offset. These are required when there are multiple very similar versions of a sprite in one spriteset. One example for this are the hairset spritesets. The variants property defines the number of variants in the spriteset and the variant_offset property how many sprites are between the first sprites of the variants. When defining multiple variants you only define the animation sequences of the first variant. the engine then shifts the index parameters when it needs another.

<action>

collection of the animation in different directions that belong to an action the character can perform. the properties are the imageset the animation phases are taken from and the name of the action.

Current actions as defined in the base sprite 1.0: Default Walk sit dead attack attack_bow Directions: NSEW

New actions/directions will be added as the sprite progresses. Sprite 1.5 work can be seen here art trello

The action type matches the item.xml:

    <item id="522"
        image="equipment/weapon/dagger-sharpknife.png"
        name="Sharp Knife"
        description="A really sharp knife. Don't hurt yourself!"
        effect="Damage +10"
        type="equip-1hand"
        weapon-type="knife"
        attack-action="attack"
        weight="150">
        <sprite>weapon-dagger.xml</sprite>
        <sound event="strike">weapons/knives/sharpknife-miss1.ogg</sound>
    </item>

Would use attack from the sprite sheets or default if attack is not declared.

<animation>

Defines an animation sequence that should be displayed when the sprite object faces in a specific direction (attribute "direction").

the possible directions are:

up
down
left
right
default

when a specific direction isn't provided, the default direction is used instead. When no default direction is defined, the first defined direction is used.

Every <animation> has one or more frame or sequence child elements and an optional <end> element.

<frame>

Defines one frame of the animation. The only required property is index. Optional properties include offsetX, offsetY and delay.

index defines the index of the graphic on the spriteset.

The delay property specifies the number of milliseconds the frame is displayed before it is replaced by the next frame in the sequence. The sequence restarts when the last frame is finished. When no delay is specified (or specified as "0") the animation doesn't continue when this frame is reached.

The frame element has the optional properties offsetX and offsetY to specify an offset from the default drawing position for that frame. This allows the animation of for example the hairset (or any equipment) to reuse the same frames with different offsets.

<sequence>

The sequence tag defines one or more phases of animation. It has the required properties delay, start, and end. The delay property specifies the delay in milliseconds between each phase. The start and end properties define the first and the last indexes of the animation sequence on the spritesheet.

<end>

When the animation sequence reaches an end tag, the animation stops and the sprite animation is returned to the "stand" state. This can be used for one time action sequences like attacking. This tag has no properties.

<include>

Can be used to include another sprite definition file. If you want to override the images, you need to specify them before the include element. If you want to override any animations, you have to do so after the include element. Example:

<sprite>
	<imageset name="base" .../>
	<include file="other-sprite-file.xml"/>
	<!-- possibly override or introduce new actions -->
</sprite>

hairstyles

Most hairstyles which only have one frame for each direction and death, so five frames. The offset for the hairstyles is standard to either haristlye01.xml or hairstyle13.xml

Currently the hairsprites follow the hairsprite convetion

Content XML files

Databases

effects.xml | emotes.xml | hair.xml | items.xml | monsters.xml | maps.xml | npcs.xml | runes.xml | skills.xml | status-effects.xml

Filetypes

Particle XML files | Animation XML files | GUI window skin XML files