Ways of implementing sprite animations

A place for your artistic side. Discuss techniques and tools for pixel art on the NES, GBC, or similar platforms.

Moderator: Moderators

User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: Ways of implementing sprite animations

Post by Kasumi »

pwnskar wrote:I've seen some gifs of mind blowing tools by kasumi but I don't know if he/she has shared anything related to animation sequences of existing CHR and metasprite data.
Coming soon™ since 2018!
Image
Image
Import an image sequence, get CHR data and metasprites. No preparation needed in most cases, even if the image sequence is more than 4 colors (so long as it is less than 14 colors). (That said, you probably at least want to set up a palette file for it to use. It's capable of guessing, but if you make it guess for multiple characters, the guesses won't align which isn't good if you want them on screen together.)

You're welcome to PM me if you want to try it. It works, and is stable (albeit probably hard coded to something slightly less useful for the latest shareable version), but I've been getting the impression that what people want from it is even more impossible than what it already does. There's also a public version here: https://kasumi.itch.io/ichr But it only does backgrounds (which, yes, can be animated). It should be similarly no fuss. Just import the image and get data. No manual recoloring or anything, unless the image doesn't conform. (In which case it usually shows you where.)

Assuming your sprite were only 3 colors plus transparent, you could use the public version for that, but I wouldn't recommend it. (It has been used in this way, though.)

Edit: It exports NES Screen Tool data (provided the entire character uses less than 256 tiles), but can't import it (through the UI, anyway). I have some code for it, but there are reasons why it's not available. I've been trying to plan how best to make those >256 tile characters still be interoperable with NES Screen Tool. Re-reading your post, this is probably the main thing you want, and I slightly regret writing the rest! :lol:

Edit2: I've attached a file of what it currently exports for sprite data. I guess I'm this deep in, anyway...
Exported Character Files.txt
(3.23 KiB) Downloaded 558 times
Edit3: (Some things are explained elsewhere in the documentation. u is unsigned, s is signed. The number following is how many bits. All big endian.)
It can also save and load all data to a single custom file, but I feel like I'm going to regret that it exists.

As far as Aseprite being unintuitive, I really disagree. Whatever ways you find its region select features to be unlike other editors can probably be changed. (By default it's MS Paint-like, but it can be set like other things I've used.) (Unless you mean specifically for selecting/moving frames and or layers, rather than the canvas, in which case I agree.) Similarly, if you find the file oriented dialogs terrible, there's an option to use native OS file dialogs (which doesn't work on Linux Mint at least, but well... or maybe that's fixed, I haven't updated in a bit, because I haven't done pixel art in a bit.)

Granted, it has some defaults that are unlike other software (my least favorite being the canvas scaled to 200X by default, in such a way you can't see 1:1 pixels), but I'm not sure it's fair to expect it to behave like something else. I don't think it does anything egregious (like say, Pro Motion which ran counter to everything I've used in nearly every respect as opposed to a reasonable number of things.)

Extensions are for skins/palettes. It supports Lua scripting, which might be a better start for dealing with NES Graphics: https://github.com/aseprite/api Whether documentation is good depends on perspective. People seemed to take to it very quickly. Had it had that when I started my graphics tool, maybe I'd have used it.

I find Aseprite to be the least frustrating piece of software I've ever used (that wasn't... like a text editor), and it's getting better all the time. It getting better all the time, and getting better quickly is perhaps why I cut it slack. Tile support is being worked on: https://twitter.com/aseprite/status/109 ... 21?lang=en All kinds of other things are constantly being worked on, across all areas of it to give it better features, and make it more user friendly. Lua scripting seemed to come out of nowhere, and perhaps it has problems now (haven't done much with it), but I have confidence they'll be fixed. I have confidence most things in Aseprite will be fixed. Forgive me, I'm an Aseprite stan.

My on topic answer, that is neither about Aseprite, nor about my own graphics tools:

For Indivisible, animations were more or less tied to states. Each state had a duplicate of some animation playback code, but not code to check for when to advance individual frames.

Code: Select all

lda AJNAframe,x;AJNAframetimer
	clc
	adc #%00000100
	sta AJNAframe,x;AJNAframetimer
	cmp #5*16;*16 to shift to the high bits
	bcc ajna.standingaxeless2.continue
	jmp ajna.toidle
ajna.standingaxeless2.continue:
	
	lsr a
	lsr a
	lsr a
	lsr a
	tay
	lda ajna.standingaxeless2frames,y
	sta AJNAmetasprite,x

	jmp spriteend
Usually (because, again, each state technically had its own version of this) a byte was split in half. The low bits were the frame timer, and the high bits were the frame. By adding to the low bits, the high bits would eventually change, changing the frame. Then the low bits were divided out to get the actual graphic to display, loaded from a table.

This state actually seems old, I ended up thinking of lots of clever bit hacks beyond the above to get the most out of that one byte. If you set the high bytes to $F0 - framecount*16, you can detect the end of the animation by the carry being set alone (as opposed to a cmp) and you can do similar for the lowest bits to make them advance in X frames without changing the add value.

I can certainly think of ways to make that generic (usually a state will play an animation, and then when the animation is complete go to another state), but some of Indivisible's states were reasonably meaty, and I think making them generic wouldn't have been worth it with regards to the kinds of things that needed to happen on specific frames. (During crouching Axe, a hitbox appears in the middle of the animation. You can cancel the attack by jumping if and only if that hitbox hits something, which will take effect after Ajna exits hitstop. Other attacks that hit can't be jump-canceled. Most attacks can be turned around within 3 frames of starting.)
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Ways of implementing sprite animations

Post by pwnskar »

koitsu wrote:You might consider using NES Screen Tool by Shiru here on the forum instead. I haven't used it myself, but have seen infiniteneslives use it on his live streams.
FrankenGraphics wrote:I use NESST for pretty much everything except 1) sprite overlays on still pictures such as title screens and 2) animation previews. For the latter i just screen grab and paste my NESST-made metasprites in photoshop and make gif animations; 2 frames equals 1 NES frame (on PAL, NTSC would be slightly quicker). I can still step through metapsrites to kind of review animations in NESST.

You need to be a bit careful when saving, which has its quirks, and when editing metasprites, but otherwise it's really great.
I really enjoy NESST too, it's what I do all my metasprite work in, apart from sketching in Photoshop. My workflow so far is to make all my tiles and metasprites in NESST, then running a python script in my compile script (*.bat file). The python script I've done is passed some parameters for what *.msb file to read and what the output should be. It then spits out the data as labeled assembly in a tailored format that I include in my game engine.
Kasumi wrote:My on topic answer, that is neither about Aseprite, nor about my own graphics tools:

For Indivisible, animations were more or less tied to states. Each state had a duplicate of some animation playback code, but not code to check for when to advance individual frames.

Code: Select all

lda AJNAframe,x;AJNAframetimer
	clc
	adc #%00000100
	sta AJNAframe,x;AJNAframetimer
	cmp #5*16;*16 to shift to the high bits
	bcc ajna.standingaxeless2.continue
	jmp ajna.toidle
ajna.standingaxeless2.continue:
	
	lsr a
	lsr a
	lsr a
	lsr a
	tay
	lda ajna.standingaxeless2frames,y
	sta AJNAmetasprite,x

	jmp spriteend
Usually (because, again, each state technically had its own version of this) a byte was split in half. The low bits were the frame timer, and the high bits were the frame. By adding to the low bits, the high bits would eventually change, changing the frame. Then the low bits were divided out to get the actual graphic to display, loaded from a table.

This state actually seems old, I ended up thinking of lots of clever bit hacks beyond the above to get the most out of that one byte. If you set the high bytes to $F0 - framecount*16, you can detect the end of the animation by the carry being set alone (as opposed to a cmp) and you can do similar for the lowest bits to make them advance in X frames without changing the add value.

I can certainly think of ways to make that generic (usually a state will play an animation, and then when the animation is complete go to another state), but some of Indivisible's states were reasonably meaty, and I think making them generic wouldn't have been worth it with regards to the kinds of things that needed to happen on specific frames. (During crouching Axe, a hitbox appears in the middle of the animation. You can cancel the attack by jumping if and only if that hitbox hits something, which will take effect after Ajna exits hitstop. Other attacks that hit can't be jump-canceled. Most attacks can be turned around within 3 frames of starting.)
Thank you for taking part in the discussion, and in such detail too! :beer:

I would definitely like to try your tools and I'll PM you a request about that. From what I understand your tool set is an all-in-one conversion from a sequence of images to CHRs, palettes, metasprites and animations? It would be really interesting to try that workflow. I would imagine it can speed up iterations quite a lot.

I read through the exported data example you linked and you seem to also store a delay value for each frame. But when reading your code example, I can't see where that value is actually read? It can't be "AJNAframe, x", right? Because then you would have to be using indirect addressing with the y register. Unless you've already read the value before and stored it somewhere in RAM, which would be AJNAframe?

Back to the issue of tools; as cool as it is to create all graphical content from bitmap sequences, I still feel it could be good to have a sequencer tool that dealt with CHR data and msb's natively. The more I think about, I wish there was a tab for sequencing animations in NESST, just like there's a tab for nametables and metasprites. It's been many years since I touched C++ and I never fully got the hang of it, but maybe I should have a look at the source code for NESST to see if I can add something like that. Or perhaps I'd do better to just make something in C# or python first. But from what I understand you are already working on code for your tool to import data from NESST?
Kasumi wrote:Edit: It exports NES Screen Tool data (provided the entire character uses less than 256 tiles), but can't import it (through the UI, anyway). I have some code for it, but there are reasons why it's not available. I've been trying to plan how best to make those >256 tile characters still be interoperable with NES Screen Tool. Re-reading your post, this is probably the main thing you want, and I slightly regret writing the rest! :lol:
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: Ways of implementing sprite animations

Post by Kasumi »

pwnskar wrote: From what I understand your tool set is an all-in-one conversion from a sequence of images to CHRs, palettes, metasprites and animations?
Yes. You open an image sequence. You wait (sometimes an hour, if you're doing something insane like 255 frames of Street Fighter 3 Third Strike animation). You optionally open another image sequence for another animation. You press shift+E. You're done.
pwnskar wrote:I would imagine it can speed up iterations quite a lot.
Making changes free to try is probably the largest benefit. Even if you eventually want to do it manually, this can help get things in game quickly to see how they'll look before committing to doing it manually.

I avoided changing certain attack animations in Indivisible for a long time, because I dreaded the work. I-CHR can do all the frames (over 200) significantly faster than I can do a single frame manually. The result is slightly worse (as far as tile use), but at this scale that doesn't matter.
I read through the exported data example you linked and you seem to also store a delay value for each frame. But when reading your code example, I can't see where that value is actually read? It can't be "AJNAframe, x", right? Because then you would have to be using indirect addressing with the y register. Unless you've already read the value before and stored it somewhere in RAM, which would be AJNAframe?
This tool was made after Indivisible, in response to the problems I had with the process. Indivisible doesn't have different frame delays per frame, only per animation. If a frame in an animation needs to be displayed for twice as long as the other frames:

Code: Select all

frames:
.db 0, 1, 2, 2, 3
I'm not that sure I'd support different frame delays per frame even in a new engine I might write, I don't think it'd do anything but use more CPU. The tool supports it because it was easy to do.
Back to the issue of tools; as cool as it is to create all graphical content from bitmap sequences, I still feel it could be good to have a sequencer tool that dealt with CHR data and msb's natively.
...
But from what I understand you are already working on code for your tool to import data from NESST?
I had NESST import working so I could compare my manual Indivisible work to it, but it got broken somewhere along the way. I don't personally benefit much from fixing it. My biggest goal is to just release the update. I have lots of ideas for it, but right now I'm mostly just killed by how much better what I'm sitting on is than the public version. (The public version is still real good at what it does, though.)
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Ways of implementing sprite animations

Post by pwnskar »

Hey Kasumi and everybody else! I've had a look at I-CHR and I definitely see the power in it once one gets the workflow integrated into one's project.

As my own project is founded on a workflow using raw tile data assembled into metasprites in NESST, I decided it would be better for me to create my own custom tool.

So here it is: https://github.com/pwnskar/NESAC
NESAC.zip
(17.17 KiB) Downloaded 481 times
I call it "NESAC - The NES Animation Corner" and it handles animation data in a fashion that I thought seemed to be shared more or less by everyone in this thread.
nesac.gif
file.jpg
file.jpg (38.56 KiB) Viewed 14469 times
tools.jpg
tools.jpg (19.65 KiB) Viewed 14469 times
Animations can be saved in either binary format or exported to an assembly file (asm6 style). I would recommend starting out by using the assembly export and compare the results to the binary format to dissect how that is stored. Given everybody probably wants the ability to have full control of how the data is finally stored in ROM, creating a build script that parses the binary animations might be optimal. You could of course also just download the source of the tool and adjust the assembly export to fit your needs.

metasprites.jpg
metasprites.jpg (26 KiB) Viewed 14469 times
Animation frames are stored with indexes to loaded metasprites, so I've added the ability to store labels as a separate file that can be edited through the tool or a text editor. If no labels are supplied the tool will automatically label each metasprite by itself. The way I use labels myself is I do them in NESAC, then parse the resulting *.msl file in my metasprites build process that is a python script separate from NESAC.

Labels can be saved and loaded separately (*.msl) but are also stored in the binary along with the animation sequences, so theoretically the label files are not needed apart from providing the ability to easily edit and parse them as they are actually just text files.

Here's an example of exported assembly animation data:

Code: Select all

playersprite1_animation_data:
	@last_animation_index:	.db 5

	.dw @running
	.dw @dying
	.dw @idle
	.dw @hit
	.dw @jumping
	.dw @throwing

	@running:
		.db 12	; index of last frame starting from this byte.
		.db 0	; index of frame to loop to * 3. If playersprite1_run_01 is 0 and playersprite1_run_02 is 1, then those would be 0 and 3.

		.db 4	; time delay for this frame.
		.dw playersprite1_run_01	; metasprite data pointer for this frame.

		.db 4	; time delay for this frame.
		.dw playersprite1_run_02	; metasprite data pointer for this frame.

		.db 4	; time delay for this frame.
		.dw playersprite1_run_01	; metasprite data pointer for this frame.

		.db 4	; time delay for this frame.
		.dw playersprite1_run_03	; metasprite data pointer for this frame.


	@dying:
		.db 18
		.db 15

		.db 10
		.dw playersprite1_dying_01

		.db 10
		.dw playersprite1_dying_02

		.db 5
		.dw playersprite1_dying_01

		.db 10
		.dw playersprite1_dying_02

		.db 5
		.dw playersprite1_dying_03

		.db 35
		.dw playersprite1_dying_04


	@idle:
		.db 3
		.db 0

		.db 4
		.dw playersprite1_idle


	@hit:
		.db 3
		.db 0

		.db 4
		.dw playersprite1_hit_01


	@jumping:
		.db 3
		.db 0

		.db 4
		.dw playersprite1_run_03


	@throwing:
		.db 6
		.db 3

		.db 4
		.dw playersprite1_throw_01

		.db 4
		.dw playersprite1_throw_02


The exported data is read by my game engine in such a way that a value is used to index an animation from the top list. The proper frame pointer is then collected from the indexed animation using a variable storing the current frame.
Again, it is probably best to either edit the source of the assembly export or create yourself a script (python/whatever) to parse the binary data of the animations to fit the needs and style of your engine.

As you can probably tell, I've shamelessly copied layout aspects from NESST by Shiru. My goal was not to impress anyone with my own design skills, I just wanted something functional and since NESST is so widely used I figured I might just make something that looks familiar. At first I wanted to attempt adding this as a feature to NESST itself as a fork, but I do not have the required Borland compiler and my C++ skills are minimal anyway. I am much more familiar with C#.

I will be making updates later on to add the ability to reorganize the order of animations and animation cels/frames. Currently only 8x8 mode is supported as I have no experience using 8x16 on the NES yet, but from what I understand it should be a quick feature to add. No support for banking or CHR-RAM either, as that is not something I feel I grasp enough yet nor have a test case for.

Thank you all for the insight into your ways of handling animation data! I hope this tool can prove to be useful to someone more than just myself. :)

:beer:
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: Ways of implementing sprite animations

Post by Kasumi »

Add animation, add frame. Click play. Add animation, click the new animation in the list. Unhandled exception.

Open program, add animation, export asm. Type any filename. Unhandled exception.

I actually cannot find a way to export asm without crashing, but perhaps something much earlier I did breaks it in a way that survives boots? For example, even these steps create an unhandled exception for me:
1. Open Program
2. Metasprites, open .msp
3. Select a valid msp file
4. Tile, open CHR
5. Select a valid chr file.
6. Click Add (animation) twice.
7. Click Add (frame).
8. Click animate_1
9. Click Add (frame).
10. Tools, export animations as .asm.

I don't have a guess as to why. The program can save a labels file as the exact same name as I am trying to save the .asm as, so it's not that saving is generally broken for some reason.

Edit: I think I narrowed it down. If the first animation (animation_0) has exactly one frame, the error occurs for me. The second animation (animation_1) can have one frame, but not the first.

I request that adding a frame (or animation) selects that frame (or animation) in the list. Clicking add, then delete does not delete the newly added frame, which is probably the usual want.

I'd also request the keyboard way NESST uses to switch metasprites (The [ and ] keys.)
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Ways of implementing sprite animations

Post by pwnskar »

Thanks for the feedback! :) I obviously had not tested the software well enough...

I should be able to fix those issues soon and I'll try to test things a bit more thorough next time. I'll get those NESST shortcut keys in as well and the behavior you described of having new frames being selected by default.

Cheers!

EDIT: I forgot to mention the shortcut CTRL+SPACE to toggle playback. There will be more to come!
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Ways of implementing sprite animations

Post by pwnskar »

OK, here's an update with fixes for the bugs you experienced Kasumi.
NESAC.zip
(17.69 KiB) Downloaded 480 times
I've also added the shortcut keys you requested, as well as two new ones - CTRL+LEFT / CTRL+RIGHT to switch focus between animation and frame lists.
Another thing I did was change the default metasprite labels to be derived from the *.msb file name, rather than just "metasprite_X". I thought this would decrease the risk of having duplicate labels between metasprite banks.
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: Ways of implementing sprite animations

Post by Banshaku »

If exporting to C like Nesst is not on the list, to export to a generic format like Json so we can parse it to our own format would be nice. Will see if I can try it soon.
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Ways of implementing sprite animations

Post by pwnskar »

Both of those features sound like good additions Banshaku. I'll add them later tonight. :)
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Ways of implementing sprite animations

Post by pwnskar »

I think I need some help on the formatting for JSON and C export.

Would this be correct JSON?:

Code: Select all

var player1_metasprites = {[
	"playersprite1_run_01":[
		{ "x":17, "y":24, "tile":91, "attr":3 },
		{ "x":20, "y":24, "tile":92, "attr":3 },
		...
	],
	"playersprite1_run_02":[
		{ "x":17, "y":24, "tile":91, "attr":3 },
		{ "x":20, "y":24, "tile":92, "attr":3 },
		...
	],
	...
]}

var player1_animations = {[
	"running":{
		"loop_frame":0,
		"frames":[
			{ "delay":4, "frame":"playersprite1_run_01" },
			{ "delay":4, "frame":"playersprite1_run_02" },
			{ "delay":4, "frame":"playersprite1_run_01" },
			{ "delay":4, "frame":"playersprite1_run_03" }
		]	
	}
]}
As for C, which I am even less accustomed to, does this look somewhat right?:

Code: Select all

// First the metasprites:
const unsigned char playersprite1_run_01[]={

	 17, 24,0x5b,3,
	 20, 24,0x5c,3,
	 22, 32,0x6b,3,
	 16, 32,0x6a,3,
	 15, 48,0x40,3,
	 23, 48,0x41,3,
	 12, 23,0x3e,1,
	 20, 23,0x3f,1,
	 14, 32,0x01,2,
	 27, 32,0x02,2,
	 12, 40,0x10,2,
	 20, 40,0x11,2,
	 27, 40,0x12,2,
	128

// Followed by the rest of the metasprites...
// .......
// .......

// Then the animations:
const unsigned char @running[]={
	0,	// index of frame to loop to.
	4,	// time delay for this frame.
	playersprite1_run_01,	// metasprite data pointer for this frame.
	4,	// time delay for this frame.
	playersprite1_run_02,	// metasprite data pointer for this frame.
	4,	// time delay for this frame.
	playersprite1_run_01,	// metasprite data pointer for this frame.
	4,	// time delay for this frame.
	playersprite1_run_03	// metasprite data pointer for this frame.
}

const unsigned char @idle[]={
	0,
	4,
	playersprite1_idle
}


// Finally, the list of pointers to the animations:
const unsigned char* const player1_animations[]={
	@running,
	@idle
};
Apart from the "@" chars on the labels, I'm guessing I'm not storing the pointers to the metasprites correctly in the animations. They would have to be stored as int, right? Like &label_name? But then they would also not be able to be put in a char array. Is there any way to neatly cast the pointer of the metasprite to the upper and lower bytes of an int? So that they would be stored like this:

Code: Select all

const unsigned char @idle[]={
	0,
	4,
	<&playersprite1_idle,		// this would be the lower byte of the label.
	>&playersprite1_idle		// this would be the upper.
}
Any help is appreciated!
niconii
Posts: 219
Joined: Sun Mar 27, 2016 7:56 pm

Re: Ways of implementing sprite animations

Post by niconii »

pwnskar wrote:Would this be correct JSON?
Not quite, for a couple reasons.

The first is that {["foo": 1, "bar": 2]} is bad syntax. It should be {"foo": 1, "bar": 2}; square brackets are used for lists like [1, 2, 3].

The second is that JSON should just be a single expression. Stuff like var foo = ... isn't allowed; if you use it, you're writing JavaScript, not JSON.

So, this is bad:

Code: Select all

var player1_metasprites = {...}
var player1_animations = {...}
And this is good:

Code: Select all

{
    "player1_metasprites": {...},
    "player1_animations": {...}
}
For more information, click here.
pwnskar wrote:As for C, which I am even less accustomed to, does this look somewhat right?
Besides @ being invalid in variable names, and a few missing semicolons, it looks reasonable enough.
pwnskar wrote:Is there any way to neatly cast the pointer of the metasprite to the upper and lower bytes of an int?
Like this (assuming playersprite1_idle is a pointer):

Code: Select all

    ((unsigned int) playersprite1_idle) & 0xff,      // this would be the lower byte of the label.
    ((unsigned int) playersprite1_idle) >> 8         // this would be the upper.
You may want to define macros just for convenience's sake.
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Ways of implementing sprite animations

Post by pwnskar »

Nicole wrote:
pwnskar wrote:Would this be correct JSON?
Not quite, for a couple reasons.

The first is that {["foo": 1, "bar": 2]} is bad syntax. It should be {"foo": 1, "bar": 2}; square brackets are used for lists like [1, 2, 3].
Thanks for the reply! :) My reasoning for using [] was to contain a list of metasprites, each containing a list of OAM entries, so I'm guessing I did that part right? Or am I still missing something?
Nicole wrote:
pwnskar wrote:Is there any way to neatly cast the pointer of the metasprite to the upper and lower bytes of an int?
Like this (assuming playersprite1_idle is a pointer):

Code: Select all

    ((unsigned int) playersprite1_idle) & 0xff,      // this would be the lower byte of the label.
    ((unsigned int) playersprite1_idle) >> 8         // this would be the upper.
You may want to define macros just for convenience's sake.
Yes, they are supposed to be pointers to the previously declared metasprites. I'm not very familiar with using macros but would I be correct to assume that they cannot/should not be defined more than once? Would it then be unfavorable to put a macro together with each exported C animation list or would it rather be a good tradeoff to get a more human readable output?

Cheers!
niconii
Posts: 219
Joined: Sun Mar 27, 2016 7:56 pm

Re: Ways of implementing sprite animations

Post by niconii »

pwnskar wrote:Thanks for the reply! :) My reasoning for using [] was to contain a list of metasprites, each containing a list of OAM entries, so I'm guessing I did that part right? Or am I still missing something?
These are correct:

Code: Select all

[value1, value2, value3]
{"key1": value1, "key2": value2, "key3": value3}
And you can nest them freely:

Code: Select all

{"key1": [1, 2, 3], "key2": [4, 5]}
[{"foo": 10, "bar": 20}, {"foo": 15, "bar": 22}]
But you have something like this in your code, which isn't right:

Code: Select all

{["key1": value1, "key2": value2, "key3": value3]}
Nicole wrote:Yes, they are supposed to be pointers to the previously declared metasprites. I'm not very familiar with using macros but would I be correct to assume that they cannot/should not be defined more than once? Would it then be unfavorable to put a macro together with each exported C animation list or would it rather be a good tradeoff to get a more human readable output?
Well, you could put something like this near the top, and then the macros would only be defined once. Not sure if it's best practice though.

Code: Select all

/* FOO should be some prefix unlikely to conflict with anything else */
#ifndef FOO_MACROS
#define FOO_MACROS
#define FOO_LOBYTE(p) (((unsigned int) (p)) & 0xff)
#define FOO_HIBYTE(p) (((unsigned int) (p)) >> 8)
#endif
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Ways of implementing sprite animations

Post by pwnskar »

Thank you for your continued help, Nicole!

Here's a slimmed down version of what the JSON export looks like right now:

Code: Select all

{
	"metasprites": [
		{
			"label": "playersprite1_idle",
			"oam_entries": [
				{
					"x": "57",
					"y": "32",
					"tile": "91",
					"attributes": "3",
				},
				{
					"x": "60",
					"y": "32",
					"tile": "92",
					"attributes": "3",
				}
			]
		},
		{
			"label": "playersprite1_run_03",
			"oam_entries": [
				{
					"x": "57",
					"y": "30",
					"tile": "91",
					"attributes": "3",
				},
				{
					"x": "60",
					"y": "30",
					"tile": "92",
					"attributes": "3",
				}
			]
		},
		{
			"label": "playersprite1_run_01",
			"oam_entries": [
				{
					"x": "57",
					"y": "32",
					"tile": "91",
					"attributes": "3",
				},
				{
					"x": "60",
					"y": "32",
					"tile": "92",
					"attributes": "3",
				}
			]
		},
		{
			"label": "playersprite1_run_02",
			"oam_entries": [
				{
					"x": "56",
					"y": "38",
					"tile": "6",
					"attributes": "2",
				},
				{
					"x": "56",
					"y": "46",
					"tile": "22",
					"attributes": "2",
				}
			]
		}
	],
	"animations": [
		{
			"label": "@running",
			"loop_frame": "0",
			"frames": [
				{
					"delay": "4",
					"metasprite": "playersprite1_run_01"
				},
				{
					"delay": "4",
					"metasprite": "playersprite1_run_02"
				},
				{
					"delay": "4",
					"metasprite": "playersprite1_run_01"
				},
				{
					"delay": "4",
					"metasprite": "playersprite1_run_03"
				}
			]
		},
		{
			"label": "@idle",
			"loop_frame": "0",
			"frames": [
				{
					"delay": "4",
					"metasprite": "playersprite1_idle"
				}
			]
		}
	]
}
Does that look valid or am I still doing something wrong? I've run it through an online JSON to XML converter and it seems to pass without errors.

I've still not done any work on a C exporter but it will come. While I'm at it I'm adding support for 8x16 along with the ability to load in two pattern tables to support that. Another idea I have is to add the ability to store any data you wish on each animation frame as a string. So if you for example wish to be able to store opcodes for use with your own game engine you could store those there and have them converted to your own format by making your own script to handle that when parsing the exported animations.

Any feedback or ideas on more features are welcome.

Cheers! :beer:
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: Ways of implementing sprite animations

Post by Banshaku »

Since I'm the one that mentioned JSON, I should maybe give more feedback about it ^^;;;

Well, you can define OAM entries like you did, which is one way and there is no confusion about what is what but it's more verbose. Another way, like the nesst version of C data is to just define the order of the OAM data and put it in an array like this:

(oam entry part only)

Code: Select all

{
    oam_entries : [
     0x57, 0x32, 0x91, 0x03,
     0x60, 0x32, 0x92, 0x03
    ]
}
This one is less clear but once you know the order, is not harder to parse. There is no "perfect" way, it mostly a matter of preference actually. So for now you current format is verbose but won't be error prone ;)
Post Reply