Simple sprite system demo
Community Forums/Showcase/Simple sprite system demo
| ||
Really simple sprite system demo actually, but if you look closely, it's showing off something neat: http://www.seamlesstexturegenerator.com/systems/sprite_system_demo.zip This is the code that makes it run: Include "Swift Sprite System-014.bmx" Graphics 800, 600, 32, 80 AutoMidHandle True Marble:TImage = LoadImage("marble-001.png") S1:Sprite = Sprite.Create(Marble) S2:Sprite = Sprite.Create(Marble) S3:Sprite = Sprite.Create(Marble) S2.SetParent(S1) S3.SetParent(S2) S1.SetPosition(400, 300) S1.SetScale(2, 2) S2.SetPosition(64, 0) S2.SetScale(0.5, 0.5) S3.SetPosition(64, 0) S3.SetScale(0.5, 0.5) Current_Time = MilliSecs() Game_Start_Time = Current_Time Repeat SetClsColor 0, 0, 0 Cls Repeat Time_Old = Current_Time ' Store start time of last frame. Current_Time = MilliSecs() ' Get the current time. Use this instead of Millisecs() from here on if you need the time. Time_Delta = Current_Time - Time_Old ' Calculate how long last frame took to render, in milliseconds. Time_Delta_Sec# = Float(Time_Delta)/1000.0 ' Convert frame time to seconds. Until (Time_Delta > 0) And (Time_Delta < 250) ' Handle unnaturally long pauses between frames gracefully. FPS = 1000.0 / Float(Time_Delta) ' Sprites are animated seperately, so you can move them, then check for collisions, then change their properties/positions, then draw them. ' Sprite.AnimateAll() S1.Rotate(0.25) S2.Rotate(4) Sprite.DrawAll() DrawText FPS, 16, 16 Flip Until KeyHit(KEY_ESCAPE) Notice anything special? Parenting! And it works as better than the parent/child system in Blitz 3D! Now, when you hide a parent, the children are hidden as well. But their state is maintained, so when you make the parent visible again the children that were visible reappear, and those that weren't, don't. Alpha is tied to the parent as well. Fade out a parent, and the children fade with it. It will even handle cases where a child is at 50% alpha, and you can even set up a case where the parent is 50% transparent and the child is not, because you can set the alpha for the child to 200%. Just as in Blitz 3D when you parent a child, and move that child, you move it in parent space. When you scale the parent up, the child scales up, and the position of the child moves to maintain it's position in the now scaled parent space. Rotation works as well. Each function for getting a sprite's position, scale, alpha, etc, has a global parameter. Set it to false, and you get the sprite's local state, relative to any parent it has. Set it to True, and you get it's global state, or where it actually is, and how it actually looks on the screen. This is all handled through recursion. Here's a list of all the methods so far: Type Sprite Function Create:Sprite(Image:TImage, Order=0, Parent:Sprite=Null) Method Remove() Method SetParent(NewParent:Sprite, StayStill=False) Method SetOrder(NewOrder) Method SetImage(NewImage:TImage) Method SetFrame(NewFrame#) Method SetBlend(NewBlend) Method SetColor(NewR#, NewG#, NewB#) Method SetAlpha(NewAlpha#) Method SetPosition(NewX#, NewY#, GlobalSpace=False) Method SetRotation(NewRotation#) Method SetScale(NewScaleX#, NewScaleY#) Method SetViewport(NewX#, NewY#, NewWidth#, NewHeight#) Method GetParent:Sprite() Method GetOrder() Method GetImage:TImage() Method GetFrame#() Method GetBlend() Method GetR#(GlobalSpace=False) Method GetG#(GlobalSpace=False) Method GetB#(GlobalSpace=False) Method GetAlpha#(GlobalSpace=False) Method GetX#(GlobalSpace=False) Method GetY#(GlobalSpace=False) Method GetRotation#(GlobalSpace=False) Method GetScaleX#(GlobalSpace=False) Method GetScaleY#(GlobalSpace=False) Method GetViewportX#(GlobalSpace=False) Method GetViewportY#(GlobalSpace=False) Method GetViewportWidth#(GlobalSpace=False) Method GetViewportHeight#(GlobalSpace=False) Method GetWidth#(GlobalSpace=False) Method GetHeight#(GlobalSpace=False) Method GetHidden(GlobalSpace=False) Method Translate(OffsetX#, OffsetY#) Method Rotate(Adjustment#) Method Show() Method Hide() Method Draw() Method Overlaps(Other:Sprite) Function DrawAll() Method Compare(OtherObject:Object) End Type Why do you want parenting in a sprite system? Well, there's a lot of reasons, but one of the best examples would be where you have a menu. The menu background could be the parent of all the buttons and things inside it. When you want to move the whole thing on or off the screen, you need only animate the parent and all the children will follow along with it. Anyway, getting all that parenting stuff working was a huge pain. There's a lot of rotation math involved and it's hard to wrap your head around how to invert the transformations so you can parent/unparent a sprite without changing the sprite's position on the screen. Especially at 4am. :-) So that's what I'm most happy with here, and what I'm testing now to make sure it all works right. |
| ||
I like parenting. |
| ||
Are going single surface here sswift? Are you building your own meshes or relying on the BMAX methods which could potentially have a lot of texture swapping if you don't batch your render properly. Also are these real viewports or a BMAX Viewport which is just a clipping rect? Tim. |
| ||
Indie: Single surface? No. I tested with 5000 sprites using the same image and it showed an FPS of over 60. I will test with multiple images randomly selected though. Anyway, shouldn't BlitzMax be doing this batching itself? Why should I have to recode all the image handling functions (including collision between rotated and scaled images with alpha) myself? As for the viewports, they are just clipping rects. I don't see a compelling reason to implement anything more than that. 99% of people have no need for anything more sophisticated. The children will be clipped to the parent's rects though, so there's a bit more to it than just a normal viewport. You will also be able to make the viewports move with the sprites, so you can make a menu with a window inside it with text that is clipped and move the window and text together. |
| ||
Blitzmax discards state changes if you are drawing the same sprite over and over again. The moment you draw 2 seperate sprites, that goes out of the window. If you draw 2 sprites in intermmitant order, you will see terrible performance. |
| ||
Ok, thanks. Still, needs to be considered. Sswift, 'cmon you know better than that. Sorry if I ruffled a few feathers but if you release a system make it air-tight. Maybe you need one of my modules (JOKE). sswift, you know I love what you do, this is not a dig, I love your stuff but I urge your to dig beneaath the bones of BMAX, it'll be worth it. Tim |
| ||
nice features :] - one thing id like to see is a sequence based animation - system added, so frame management can be mostly - left to the sprite class... - method addseq(fstart,fend,animmode,speed) - mode = loop,singleshot,pingpong - method playseq(seq)...blah... - method isplaying().. started wrapping my own sprite class before i got sidetracked writing a collision system...but i may use your system as a base..its looking good :] - assuming the src will be public? |
| ||
You guys talk about making a single surface system like it's incredibly easy to do, (So WHY not implement it if you get 1 fps extra? You'd be CRAZY not to!) and/or will have major benefits. But I ask you: How would you make a single surface system work, when all your sprites have to be drawn in random orders, and you can't use the zbuffer because either it doesn't exist in Max, or will not work with anything that has transparency, like all your nice antialaised sprites done with alpha mapping? For general sprites, I just don't see the benefit. Take my Lego game for example. At MOST, there were like 100 sprites on the screen at once. And at MOST, there might have been 6 copies of a particular sprite. For a single surface system to be worthwhile, you'd have to have a game where you have LOTS of identical sprites on the screen at once, using the same order, and so drawn on the same layer. That sort of thing is the realm of particle effects, and you'd be much better suited to use a particle system for particle effects like that than a sprite system. Download this and run it. It draws 2000 sprites on the screen at once, each with a randomly selected image out of four different images. http://www.seamlesstexturegenerator.com/systems/sprite_system_demo_1.zip Press TAB to see the framerate. I get 60fps in this. Still think a single surface system is neccessary? What on earth are you planning to do that needs more than 2000 sprites onscreen at once? And how do you suggest you implement a single surface system which would tackle the issue of just not having that many of the same sprite onscreen at once that need to be rendered on the same layer in most applications, which is precisely what you need to see any real benefits? |
| ||
..hey Swift, I'm trying to contact you regardint to your shadow system, but without any success..most probably everything I wrote to you went to junk.. |
| ||
Defoc8: The sprite system will have a very full featured animation system with it. I am implementing all the animation stuff from my AniBOB system. I'm probably not going to have you create sequences, then get pointers to them, and then have you play them with said pointers though. I don't really see the point in that. In the Lego game I coded using my AniBOB system, I don't think I ever used the same animation sequence twice in the code. So the current plan is to simply have you specify what you want played, when you want it played. To give you an idea what the anibob system could do, and what the new system will be able to do... For each sprite: You can tell it to play a set of frames. You can tell it how long you want the animation to take. You can tell it how long you want it to delay before playing the animation. This means you can chain animations. Ie, specify a bunch of animations at once, and let them run in order. You can tell it what image to switch to when it starts playing the animation. Again, so you can chain animations. Each animation has a type. Frame, position, scale, rotation, color, alpha. And using these types, you can query the system about whether the sprite is playing any kind of animation, (or has one queued) or whether a specific type of animation is playing. So you can make a sprite's frames loop, and have a one shot animation of it moving from place to place, and find out when the one shot has completed, so you can stop animation on it and free it. Speaking of looping and one shot, you can play animations forwards or backwards, and one shot, looping, ping-pong, or one shot ping pong. You can also play them with linear time, or various sine-wave based times, to make the animation speed up, or slow down, or speed up then slow down. "assuming the src will be public?" It's not going to be free, but like all my other systems, you will get the source with it, so you can modify it however you like. |
| ||
Naughty: What email address are you using? It seems more likely that you filtered out my replies than I filtered out yours, since I don't have any email filters. :-) But I guess it's possible Comcast is filtering them out. Or that you're emailing my old adelphia address and they finally stopped letting me get email from that account. I kinda doubt it though. I have a gmail address that forwards to me that you could try: scswift@... Or you can just ask here. |
| ||
..excellent..I'll go trough gmail.. |
| ||
Naughty: I replied to your email just now. |
| ||
2 sidenotes on things mentioned above: BM does not batch what you send it so switching between textures over and over again will result in quite some speed lose compared to an ordered way BM initializes the depthbuffer even in its default window setup although it would not even need it (all is drawn in the same depth). But if you want you can even force it to create Stencil and Accumulation buffer (the later has already been used for some nice show offs of realtime light in 2D without hardware lights). Alpha would not be a that large problem with 2D plane sprites thought (compared to problems with objects that can go into depth), just needs to be added to your batching routine. One possibility would be to first draw all non alpha in batch and then do the alpha on layer per layer base (if this is even needed. Dont know if depth buffer fails with planar alpha stuff in the same way as with complex 3d alpha objects) |
| ||
Dream: Trust me, It'll fail. :-) For one thing, draw all non alpha in batch? Who's gonna use aprites without alpha maps in Max? Crazy people, that's who! Second, it doesn't matter if they're in a plane or not. If I am to use the zbuffer to do the sorting for me, then I am going to have to render them at different depths. And it does not matter how the zfail thing works. If you draw a sprite with alpha, and then draw a second on top of it, if the second is drawn behind the first because of it's Z value, then the alpha portions of the first will appear to have been drawn behind the second, instead of in front as they should be. Also, did you try my demo there? 2000 sprites with different images goes really fast. On may card at least. Nobody's said it fails to go fast so far though. |
| ||
with regards to the whole single surface nonesense... how would you specify your single surface storage?, perhaps you would store all data in a vertex array or you'd place them in a buffer on the card...either way you have to update the coordinates...so unless your using shaders, your going to have to upload all the verts to card every frame.. or alteast those verts that movings..apart from the management problems..your going to hit a stall point - on my system, i can upload about 2k quads to card before it stalls and fps drops suddenly. The only way your going to negate the stall is drive your single surface system with shaders..or demand that everyone buys better gfx cards. another thing - if you need more than 2k quads, your a nutta! ;) |
| ||
Actually texture changes are quite cheap on never cards. It was an issue back in the voodoo days but I would'nt worry about it for 2D sprite type games. |