![]() |
Forum Index : Microcontroller and PC projects : CMM2 and Sprites. Newcomer Questions
Author | Message | ||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Hi, thanks for having me in this forum (I just received my invite today) and congratulations on launching the amazing platform that is the Colour Maximite 2! I'm new to the world of Sprite programming and games programming in general but I'm an experienced developer otherwise. I found the SPRITE commands a little bit confusing so the fact that I'm not new to programming might indicate there is a bit of a learning curve to using them. While it was easy to use the SPRITE SHOW, SPRITE MOVE and SPRITE NEXT to get a sprite to glide on the screen, trying to add different poses started to get confusing... Let me explain. When I talk about poses I'm referring to sprites doing body movements by switching their images at each step. Say, we want a sprite to move their arms and legs while changing their X and Y positions. I will call this behavior sprite animation as opposed to sprite movement. Since there are just 64 indexes available for sprites it seems that I can only have a fraction of that number of sprites on the screen if I want sprites to behave like they are animated rather than just be static images sliding around the screen. I tried to figure out an official way of doing sprite animation and the pattern that I saw in a couple of projects looked as follows (this is for a single animated sprite). Say. we want a sprite with 8 frames of animation: 1. sprite's frames are loaded into a few consecutive indexes (say 1-8) 2. a separate index is used to refer to an actual animated sprite in the game code (let's say, 9 for example) 3. SPRITE COPY is used to point sprite 9 at one of the 8 frames i.e SPRITE COPY 1,9... or SPRITE COPY 2,9... etc. 4. SPRITE SHOW is then used to display sprite 9 5. SPRITE MOVE is used on sprite 9 to reposition the sprite in X,Y plane 6. then SPRITE COPY is used to switch sprite 9's content to another frame 7. any collision detection is then performed on sprite 9 if required 8. loop back to step 3 My question is whether this is the generally accepted method of making animated sprites? Is this efficient in terms of rendering time? If not what is the proper method of doing it? My concern about the above pattern is that it might be rather easy to run out of sprite indexes. In my example above 9 slots are used up for what the gamer perceives as just one character on the screen. Or is it possible to SPRITE COPY into a higher number than 64? Is the 64 sprite limit only applied to the SPRITE LOAD command? If the limit is global and if every sprite in the game had 8 poses the sprite system would allow for mere 7 unique sprites to be loaded and shown at any one time. 56 index slots would get used up by sprite images and then the remaining 8 slots could be used to point at them via SPRITE COPY. That would then allow for 7 unique animated sprites to appear leaving just one remaining sprite index. This is bothering me as it seems like a very strict budget even for a remake of something like Jet Set Willy or Dynamite Dan if one wanted to have more fluidly animated characters and monsters than in the original. A game with lots of animated monsters attacking you all at the same time would force us into avoiding the sprite system altogether and coding this at a much lower level which would be unfortunate given how much coding tedium the sprite system seems to prevent. Edited 2020-06-18 03:57 by abraxas |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10068 |
Your question is very good and merits a much longer answer that I can give at the moment. However, have you looked at the demos from Mauro Xavier. These show what can be achieved. Mauro tends to build the pictures on one video page and then copy that page to the view page rather than using sprites. There is a level of sophistication you could add on top of his approach to use the sprite collision detection without ever displaying the actual sprites. SPRITE MOVE and SPRITE NEXT aren't really needed on the CMM2 as there are other ways of avoiding tearing. There is a tutorial thread on graphics on the CMM2 which I need to complete. When I do that should give you more ideas |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
I've read the tutorial thread on graphics back to back last night and while very informative I don't think it answers my question about SPRITE commands and the feature I'm after. Namely I'm talking about how to manage multiple sprite images for a single character sprite and how to efficiently switch between them. Now, I haven't looked at the demos from Mauro that you've linked. You mention that he's doing some clever memory page swapping to achieve impressive results? That's great but I'm kind of hung up on using sprites as I want to introduce my 8 year old to the wonders of programming in BASIC and want to give him a simple way to animate characters in a game. Seems like the SPRITE system was specifically made with newcomers in mind. I know that if I try to get him to understand BLIT or memory pages his eyes will glaze over and he'll lose interest. |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 4251 |
Hi Abraxas, I haven't tried this so I'm guessing from the manual but what I suspect you can do without "going the whole hog" is have all your frames for all your sprites loaded into one of the additional pages 's' in a collage/tile-map then each time you want to change the frame for a character do: ' Switch to page containing the sprite frames PAGE WRITE s ' Update graphic for sprite 'n' by reading a block from that page SPRITE READ #n, x, y, w, h ' Switch back to the display page PAGE WRITE 0 ' Not sure if that will update the displayed image automatically on the ' next screen redraw or whether you need to give the sprite a kick with ' MOVE or SHOW. Alternatively I might not know what I am taking about ![]() Regards, Tom MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1122 |
Welcome to a fellow Canadian, eh? I can see by your description that a sprite system that automatically stepped through a sequence of images would sure make life easy. Failing that, perhaps you could draw simple unchanging sprites on an unused page in order to get the benefit of collision detection etc and at the same time blit animated images to the buffer that will be displayed. ---- time passes ---- I just had a play and created the attached that (rather crudely) implements the above. I grabbed a set of images from the web and hacked together a test program. The animation is a sequence of 16 images but I use only one as a sprite. A piece of the image also provides a bouncing ball. This demo shows also that I have more to learn about transparency. ![]() SpriteTest.zip Visit Vegipete's *Mite Library for cool programs. |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Indeed Vegipete, Atlantic Canada checking in! So if I get your idea, Vegipete & thwill the general theme is to read in the sprite's pixel data from another (invisible) page of memory into the memory area currently occupied by the sprite being displayed. This sounds pretty promising and pretty simple to do. I'm going by the words in your post as I haven't yet had a chance to download your example, Vegipete but will do so as soon as I get back to the Maximite. Now is there some chance this could mess up collision detection routines? I'm not even 100% sure if MMBasic offers pixel perfect collsion detection or bounding box only? I imagine the suggested trick will work just fine with the latter but might not work with the former? Anyway, I think I have an evening of delightful experimentation ahead of me! Also thanks for the warm welcome here, guys. |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1122 |
West coast here, so pretty far away! This was my first try with collision detection. Seems to be bounding box based. Plus I notice occasional pixel artefacts when a major collision occurs. And when transparency gets thrown into the mix, all manner of fun can ensue. Twill's idea ought to work well too. 'Sprite read' can replace an existing sprite with a new one if the size matches exactly. Maybe I'll rewrite my demo to try that method too. I look forward to seeing the neat programs you create! Visit Vegipete's *Mite Library for cool programs. |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10068 |
This was my first try with collision detection. Nice demo and a perfect example of how to combine sprites and BLIT ![]() This is tricky to explain but I'll try and then you can fix the program to prove I'm right (or not) ![]() Consider a single sprite (S1) displayed against the background. Stored in the memory is the image of the background that was there before the sprite. Now move another sprite (S2) such that it overlaps the first sprite. As part of the move of S2 it stores the background in the location it is going to move to. However, that part of the image already includes S1 so S2 stores a background including part of S1. If then you move S2 again so it no longer overlaps there is no issue, the background including S1 will be re-drawn. However, if instead you move S1 it will overwrite part of S2 with its background. To get round this you must always deal with the sprites in a LIFO fashion. This is what the firmware does when you scroll the background. It removes all the sprites in reverse order of them being written, scrolls the background and the replaces them in the original order. If we consider the case where sprite S2 is going to "kill" S1 then the correct algorithm is as follows MOVE S2 'The move will create a collision 'collision code HIDE S2 'this restores S1 against the background as before the collision HIDE S1 ' this restores the clean background SHOW S2 ' now S1 is gone and S2 is showing against the clean background As long as you stick within the limitations of the maximum number of sprites you can animate as you want. However, if you need more sprites with collisions, then the approach demonstrated by vegipete is precisely the way to go. Without collisions you can use sprites or BLIT but animation is faster using BLIT as in vegipete's code. Sprites and animation are definitely at the hard end of the things you can do with the CMM2 and it does need some experience to understand what is happening and how best to use them. This isn't easy to document simply - take the issue above. Edited 2020-06-19 00:30 by matherp |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Thanks for the input guys. So far I collated the ideas and arrived at the following solution. Can those of you with expertise comment on whether this is on the right path? Especially in the context of its behavior once a non-black background is introduced and when I try to use collision detection: MODE 3, 8 PAGE WRITE 3 screenWidth = MM.HRES screenHeight = MM.VRES X = screenWidth * RND Y = screenHeight * RND SPRITE LOAD "test.spr", 1 SPRITE SHOW 1,0,0,1 SPRITE SHOW 2,50,50,1 curFrame = 1 SPRITE CLOSE ALL SPRITE READ curFrame,0,0,18,20 lineColor = RGB(234,222,4) animPeriod = 0 whichSprite = 0 sps = 1 DO PAGE WRITE 2 k = KeyDown(1) 'read keypress IF k = 128 then Y = Y - sps 'UP key IF k = 129 then Y = Y + sps 'DOWN key IF k = 130 then X = X - sps 'LEFT key IF k = 131 then X = X + sps 'RIGHT key 'make rollover happen X = X + screenWidth Y = Y + screenHeight X = X mod screenWidth Y = Y mod screenHeight box X-2,Y-2,2,2,2,lineColor SPRITE SHOW curFrame,X,Y,1 PAGE COPY 2 TO 0 PAUSE 5 SPRITE HIDE curFrame PAGE WRITE 3 animPeriod = animPeriod + 1 IF animPeriod >= 40 THEN animPeriod = 0 whichSprite = (whichSprite + 1) mod 2 ENDIF IF whichSprite = 0 THEN SPRITE READ curFrame,0,0,18,20 ELSE SPRITE READ curFrame,50,50,18,20 ENDIF LOOP And this is the corresponding sprite file that I used: 18,2,20 44444444444444 4422244444422244 444444444444444444 444 444444 444 444 444444 444 444 444444 444 444444444444444444 444444444444444444 444411111111114444 444411111111114444 444444444444444444 444444444444444444 44444444444444 44444444 4444444 4444444 4444444 4444444 4 4 4 4 4 4 4 4 2 2 2 2 2 2 1 1 1 1 1 1 2 2 2 2 2 2 44444444444444 4422244444422244 444444444444444444 444 444444 444 444 444444 444 444 444444 444 444444444444444444 444444444444444444 444411111111114444 444411111111114444 444444444444444444 444444444444444444 44444444444444 44444444 4444444 4444444 4444444 4444444 4 4 4 4 4 4 4 4 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 So the key idea here is first loading the sprites into a non-rendered page and then using SPRITE READ to copy frames from there to the sprites to be displayed. Can you comment if my implementation is sound and whether it will function when I add collisions? I totally get it matherp but I think CMM2 has such a huge potential to be an amazing educational tool. So with that in mind a simple, accessible SPRITE API is worth spending a lot of time to polish. Most kids when first attempting to program will want to try to write a game as a goal. The simpler and less restricting the sprite design the more likely is that they won't get overwhelmed. Which actually brings me to one more thing. I think it's well worth keeping the .spr format alive and even expanding on it to add more CMM2 colour capability. I'd love to see native support for a format that would give more color flexibility but still retained the simplicity of .spr files. For example, imagine if we could do something to that effect: 8,1,10 a=RGB(100,200,100),b=RGB(50,40,40),c=RGB(60,40,0) aaaaa aaaaa b bbbbbbb b bbb b b bbb b b c c b c c c c c c |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10068 |
There is a new capability which should do what you want. It will be included in the user manual when updated and allows you to load a sprite from an array. It is in the latest code releases and my simple example program can obviously be expanded for as many colours as you want. I'm afraid I have been snowed under with bug fixes as the SC publication date approaches but will get back to the graphics tutorials eventually SPRITE LOADARRAY [#]n, w, h, array%() Creates the sprite n with width w and height h by reading w*h RGB888 values from array%(). The RGB888 values must be stored in order of columns across and then rows down starting at the top left. This allows the programmer to create simple sprites in a program without needing to load them from disk or read them from the display. The firmware will generate an error if array%() is not big enough to hold the number of values required. MODE 3,8 Rem data statement for a 14x12 sprite data " 444 " data " 444 " data " 454 " data " 444444544444 " data "44444555554444" data " 444444544444 " data " 454 " data " 444 " data " 444 " data " 444 " data " 444 " data " 4 " dim a%(12*14-1) FOR I=0 TO 11 READ S$ FOR J=1 TO 14 K=ASC(MID$(S$,J,1)) L=I*14+J-1 IF K=32 THEN A%(L)=0 IF k=ASC("4") THEN A%(l)=RGB(RED) IF k=ASC("5") THEN A%(l)=RGB(GREEN) NEXT J NEXT I SPRITE LOADARRAY 1,14,12,A%() SPRITE SHOW 1,100,100,1 DO:LOOP |
||||
abraxas Regular Member ![]() Joined: 16/06/2020 Location: CanadaPosts: 99 |
Ah great! That'll do nicely. Thanks. I'm sorry to bother you again, matherp but if I can ask really quick about the example sprite code that I posted? Is that more or less in the right ballpark? |
||||
cTrix Newbie ![]() Joined: 04/05/2020 Location: AustraliaPosts: 16 |
With the 12-bit mode : I'm having trouble getting sprites to work with the 4-bit alpha channel (importing from PNG). I haven't yet figured out how to do this... it always just masks the shape like it would in 8-bit or 16-bit mode. I know there is some kind of interleaving page modes for RGB vs. Alpha... so perhaps I need to import a separate alpha channel image to do this? Say, if I wanted fluffy semi transparent cloud sprites (with varying alpha). Does the PNG import function even support this? (I know the PNG format does) Loving this new Maximite, still figuring the quirks! (more specifically, how 12 bit ticks) Link: YouTube Colour MaxiMite 2 Intro Video | ctrix.net |
||||
cTrix Newbie ![]() Joined: 04/05/2020 Location: AustraliaPosts: 16 |
In the 12-bit modes : I'm having trouble getting sprites to work with the 4-bit alpha channel (importing from PNG). I haven't yet figured out how to do this... it always just masks the shape like it would in 8-bit or 16-bit mode. I know there is some kind of interleaving page modes for RGB vs. Alpha... so perhaps I need to import a separate alpha channel image to do this? Say, if I wanted fluffy semi transparent cloud sprites (with varying alpha). Does the PNG import function even support this? (I know the PNG format does) Loving this new Maximite, still figuring the quirks! (more specifically, how 12 bit ticks) Meanwhile : I love page stitching! But is there a way to vertical page stitching? Or do I have to do it the manual BLIT way between screens? My first test code was a vertically scrolling background and I got stumped straight away because an offset more than 320 (in VGA) seemed to crash it. Edited 2020-06-19 23:26 by cTrix Link: YouTube Colour MaxiMite 2 Intro Video | ctrix.net |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10068 |
At the moment the firmware imports png pixels as either transparency 0 (transparent) or transparency 15 (solid) based on the value of the cutoff parameter in the import, assuming an ARGB8888 png file (see the manual and this thread for details. I was going to import with pixel specific transparency but couldn't find an algorithm that worked when mapping the 256 transparency levels in ARGB8888 to ARGB4444. Imported images just ended up a mess. So it is one or the other at the moment. You can then change the transparency of the complete sprite using the SPRITE TRANSPARENCY command. There isn't currently a way of doing pixel specific transparency in sprites. I'm open to re-visiting this at a later date but for the moment at least it works in a way that is understandable. PAGE STITCH only works for horizontal because this requires much more processing than vertical where there would be no useful performance gain over two BLITs Any bugs, please post the simplest code demonstrating the problem in the bug thread and I'll fix them ASAP PS Glad you are up and running - any issues with the build? Edited 2020-06-20 00:44 by matherp |
||||
Grogster![]() Admin Group ![]() Joined: 31/12/2012 Location: New ZealandPosts: 9492 |
@ vegipete: That demo is excellent! ![]() ![]() Smoke makes things work. When the smoke gets out, it stops! |
||||
cTrix Newbie ![]() Joined: 04/05/2020 Location: AustraliaPosts: 16 |
Mostly fine thanks! Easy on the electronics side :-) A few minimum-order hassles; Waveshare took eons to arrive (but did). Dremel-ing the case holes took a couple of beers before I got it right and I cut the SD hole a bit high. I had to improvise some spacers to lift the board so things lined up to the panel. Update on Vertical Stitch: I've worked on a subroutine where you load the graphics into empty video pages, and it just BLITS between them with two BLIT commands. 12-bit : Meanwhile, for things like car windows or glass panels, I'm looking at layered sprites. Are the vectors (like box command) able to have semi-transparency too? 16 bit mode works great! I can't believe how big you can make the sprites and still have things scroll well. Would be amazing if there was a bit more movement on the off-screen sprites before an error came up. If you have sprites with slightly varying widths it'd be nice to not have to cater for each one being out of range at different values. ![]() Edited 2020-06-21 00:43 by cTrix Link: YouTube Colour MaxiMite 2 Intro Video | ctrix.net |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10068 |
Yes, I demo that on the 4.00 posting on the 13th May You should be able to make the checking generic by using the SPRITE(W, n) and SPRITE(H, n) functions. Example below - not checked function sprite_check_move(new_x as integer, new_y as integer, sprite_no as integer) as integer sprite_check_move=0 if new_x+sprite(W, sprite_no)< 0 then sprite_check_move=1 if new_x>=MM.HRES then sprite_check_move=1 if new_y+sprite(H, sprite_no)< 0 then sprite_check_move=1 if new_y>=MM.VRES then sprite_check_move=1 end function |
||||
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |