From The Mana World
Revision as of 16:02, 30 August 2006 by Bjørn (talk | contribs) (Two proposals based on current practices →‎sequences based on default sequences)

This proposal has been accepted

The development team has discussed the contents of this article and has decided that it should be implemented as described. But the implementation is not finished yet. You can help to bring the features described here into the game.

Each animation will be defined by an xml document, here follows an example:

<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="50" />
			<frame index="2" delay="50" />
			<frame index="3" delay="50" />
			<frame index="4" delay="50" />
			<frame index="5" delay="50" />
			<frame index="6" delay="50" />
		</animation>
		<animation direction="left">
			<sequence start="19" end="24" delay="50" />
		</animation>
		<animation direction="up">
			<sequence start="37" end="42" delay="50" />
		</animation>
		<animation direction="right">
			<sequence start="55" end="60" delay="50" />
		</animation>
	</action>

	<action name="sit" imageset="base">
		<animation direction="down">
		 <frame index="7" />
		</animation>
		<animation direction="left">
		 <frame index="25" />
		</animation>
		<animation direction="up">
		 <frame index="43" />
		</animation>
		<animation direction="right">
		 <frame index="61" />
		</animation>
	</action>

	<action name="dead" imageset="base">
		<animation direction="down">
		  <frame index="8" />
		</animation>
		<animation direction="left">
		  <frame index="26" />
		</animation>
		<animation direction="up">
		  <frame index="44" />
		</animation>
		<animation direction="right">
		  <frame index="62" />
		</animation>
	</action>

	<action name="attack" imageset="base">
		<animation direction="down">
			<sequence start="9" end="12" delay="100" />
		</animation>
		<animation direction="left">
			<sequence start="27" end="30" delay="100" />
		</animation>
		<animation direction="up">
			<sequence start="45" end="48" delay="100" />
		</animation>
		<animation direction="right">
			<sequence start="63" end="66" delay="100" />
		</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 got 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. variants defines the number of variants in the spriteset and variant_offset 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 wants anotherone.

<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.

possible names:

               use-item
                   |
  attack-bow  cast-magic
       |           |
 attack-stab  attack-throw
         \     /       
run   attack-swing   sleeping   dead
 |          |           |        | 
walk     attack        sit      hurt
   \        \          /         /
               stand
                 |
        (no name attribute)

this table also specifies the substitution precedence. when a specific name is requested but it is not defined the next lower name is used instead.

<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

when a specific direction isn't provided, the first defined direction is used instead. this can be overridden by defining an animation with the direction "default" for this action.

Every <animation> has one or more frame or sequence child elements.

<frame>

each of them defines one frame of the animation. the required properties are index and optional offsetX, offsetY and delay.

index defines the index of the graphic on the spriteset.

an optional property is delay. every frame is displayed for delay millisecounds and then replaced by the next frame in sequence. when the last frame is reached the sequence starts with the first frame again. when no delay is specified (or specified as "0") the animation doesn't continue when this frame is reached.

Each frame element can optionally have the 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>

sequence tag does not only define one animation phase but multiple phases. Its properties are delay, start and end which are all required. The delay is the delay in ms between each phase. the properties start and end define the first and the last index of the animation sequence on the spritesheet.

Suggested additions

multiple delays in a <frame> with start and end attribute

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 frame elements in such cases.

<random> childtag for <animation>

Also, it includes an experimental suggestion on specifying that a random frame should be chosen. This should also have an exclude_current_frame thingy, to make sure a frame isn't shown twice or more in a row. Sometimes this may be wanted though.

sequences based on default sequences

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:

<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>

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

I'm not convinced yet that the name attribute of the sprite 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.

Since we're currently not naming sprites and defining each in its own file, the following variations on this approach could be implemented:

Since inheritance

Ability to extend one spriteset from another, while overriding imagesets and possibly also adding new ones and redefining specific animations:

<sprite extends="other-sprite-file.xml">
 <imageset name="base" ... />
</sprite>

Including other spritesets

Mentioned by Matt, we could have a generic import/include element. This would even allow you to combine several spritesets together. Below the assumption is that you can only define an imageset once while animations can be redefined (actually the same as with the inheritance approach above). This prevents us from loading imagesets unnecessarily and allows us to verify the validity of animation definitions as we encounter them.

<sprite>
 <imageset name="base" .../>
 <include file="other-sprite-file.xml">
</sprite>

Implementation proposal of hairstyles by Modanung

I think the current system with an xml file for every hairstyle with an index and offset defined for every frame is a bit too much. It also takes a lot of work to correct small errors and to add new hairstyles that may have a different frame size and with it a different offset. I suggest we split hairstyles up in three catagories:

  • Static hairstyles
  • Standard animated hairstyles
  • Special case hairstyles

On top of the current system we need a variable that defines which gender can use which hairstyles, in every hairstyle's xml-file.

Static hairstyles

These are hairstyles which only have one frame for each direction and death, so five. All current hairstyles would be static hairstyles. Every individual static hairstyle needs nothing more to be defined then their bitmap, their size and their offset for each direction. The offset for each seperate frame would be defined in a shared file (say hairoffsets.xml), this summed with the individual hairstyle's offset is the offset for that single frame with that hairstyle.

hairstyle1.xml
<offsetfile="hairoffsets.xml", framefile="static_hairstyles_frames.xml">
<sprite variants="10" variant_offset="5">
<gender="male">

	<imageset name="base" src="graphics/sprites/hairstyle1.png" width="40" height="40" />
	<offsetX="0" offsetY ="0" />

End of file
hairoffsets.xml

<action name="stand" imageset="base">
		<animation direction="down">
		  <offsetX="0" offsetY ="-29" />
		</animation>
		<animation direction="left">
		  <offsetX="-1" offsetY ="-29"/>
		</animation>
		<animation direction="up">
		  <offsetX="-1" offsetY ="-25"/>
		</animation>
		<animation direction="right">
		  <offsetX="1" offsetY ="-29"/>
		</animation>
	</action>

	<action name="walk" imageset="base">
		<animation direction="down">
		  <offsetX="0" offsetY ="-28" delay="75"/>
		  <offsetX="0" offsetY ="-29" delay="75" />
		  <offsetX="0" offsetY ="-29" delay="75" />
		  <offsetX="0" offsetY ="-28" delay="75" />
		  <offsetX="0" offsetY ="-29" delay="75" />
		  <offsetX="0" offsetY ="-29" delay="75" />
		</animation>
...etc...
static_hairstyles_frames.xml

	<animation direction="down">
	  <frame index="0" />
	</animation>
	<animation direction="left">
	  <frame index="1" />
	</animation>
	<animation direction="up">
	  <frame index="2" />
	</animation>
	<animation direction="right">
	  <frame index="3" />
	</animation>
	
<action name="dead" imageset="base">
	<animation direction="default">
		<frame index="4"/>
	</animation>
End of file

Standard animated hairstyles

These are hairstyles that can be compared with hair in SoM or Chrono Trigger, they move as you walk and attack. They have three frames for each direction and death, so thirteen frames all together. These three frames can be devided in a relaxed frame and two extremes. Those two extremes either being up and down or left and right, depending on the hair style. A heavily greased Evil look would wobble up and down as you walk and a pony talk would wave from left to right. Standard animated hairstyles hairstyles use the same offset as static ones only they have a different file for defining the frames.

hairstyle2.xml
<offsetfile="hairoffsets.xml", framefile="animated_hairstyles_frames.xml">
<sprite variants="10" variant_offset="13">
<gender="both">

	<imageset name="base" src="graphics/sprites/hairstyle2.png" width="40" height="40" />
	<offsetX="0" offsetY ="0" />

End of file
animated_hairstyles_frames.xml

<action name="stand" imageset="base">
		<animation direction="down">
		  <frame index="0" />
		</animation>
		<animation direction="left">
		  <frame index="3" />
		</animation>
		<animation direction="up">
		  <frame index="6" />
		</animation>
		<animation direction="right">
		  <frame index="9" />
		</animation>
	</action>

	<action name="walk" imageset="base">
		<animation direction="down">
		  <frame index="0" delay="75"/>
		  <frame index="1" delay="75" />
		  <frame index="1" delay="75" />
		  <frame index="0" delay="75" />
		  <frame index="2" delay="75" />
		  <frame index="2" delay="75" />
		</animation>
...etc...

Special case hairstyles

And then there are the odd ones, the hairstyles that need a little more freedom. Hairstyles that people want to make wave in the wind, even when the player stands still. Or a birdnest hairstyle with nestlings that constantly open and close their mouth in a random frequency nomatter what you do. I'll take as example a Medusa hairstyle.

hairstyle3.xml
<offsetfile="hairoffsets.xml", framefile=0>
<sprite variants="10" variant_offset="13">
<gender="female">
<imageset name="base" src="graphics/sprites/hairstyle3.png" width="50" height="60" />
<offsetX="-5" offsetY ="-10" />

	<animation direction="down">
	  <frame index="0" delay="100"/>
	  <frame index="1" delay="100"/>
	  <frame index="2" delay="100"/>
	</animation>
	<animation direction="left">
	  <frame index="3" delay="100"/>
	  <frame index="4" delay="100"/>
	  <frame index="5" delay="100"/>
	</animation>
	<animation direction="up">
	  <frame index="6" delay="100"/>
	  <frame index="7" delay="100"/>
	  <frame index="8" delay="100"/>
	</animation>
	<animation direction="right">
	  <frame index="9" delay="100"/>
	  <frame index="10" delay="100"/>
	  <frame index="11" delay="100"/>
	</animation>
	
<action name="dead" imageset="base">
	<animation direction="default">
		<frame index="12"/>
	</animation>

End of file

This hairstyle showes a bunch of snakes on your head that always wave 'n wobble unless you're dead. The animation rolls asychronusly with the attack and walking animations and it also moves when you stand still. But the offsets are in sync. Special hairstyles might also use their own offsets ofcourse I think this pretty much finished my proposal.