Another Texture Atlas Loader
Monkey Forums/Monkey Code/Another Texture Atlas Loader
| ||
| Here's another texture atlas or sprite sheet loader, as with Android programming you can't have enough options for these atlas loaders. It is compatible with this texture packer (windows): http://www.texturepacker.com/ Export the atlas as "libGDX", which exports a plain text file. Also it's recommended that you uncheck TRIM and ROTATION, since offsets and rotations cannot be used. EXAMPLE OF USAGE:
Class Game extends App
Field unitAtlas:CTextureAtlas
Field imgunit:Image[10]
Method OnCreate()
unitAtlas = LoadAtlas("unit_atlas.png")
imgunit[0] = unitatlas.Grab("tank_bottom.png",1,Image.MidHandle)
imgunit[1] = unitatlas.Grab("tank_top.png",1,Image.MidHandle)
''..or..
imgunit[2] = unitatlas.Grab("tank_bullet")
''....etc...
Method OnRender()
DrawImage imgunit[0],x,y
Create a field for the atlas texture, load it, then Grab() the files. With my version, it's almost interchangeable with LoadImage(). This way you can develop with LoadImage() and swap it out when you're ready. It could load animations, if you use GrabSet() and return an array. - You don't need the ".png" at the end of each filename if you don't want to. - Don't use more than one "." in the filename or it will truncate it. - The index and flags are optional. - The index is used for multiple images when the filenames end with numbers (tree01, tree02, etc.). Then you would need to use the index=1, index=2, etc. (TexturePacker truncates numbers at the end of the file name.) UPDATE: - added GrabSet:Images[] which returns an array of images based on a width, height, and number of individual images you want returned from a specified file in the atlas. Example: say I added an already-made sprite sheet of an explosion, then I would use this to extract the explosion frames so I could animate it in the array. - could also be used for a font array - page fix by Raph - load atlas array into one atlas SOURCE: |
| ||
| Updated to pseudo-handle animations, fonts. |
| ||
| Oke, thanks for sharing. Could someone give an example how to use it with moving a player ? I don't understand the idea with multiply images, so it look that the player is moving. What I have is for example: this image map: ![]() then
unitAtlas = LoadAtlas("char1.txt")
imgunit[0] = unitAtlas.Grab("char__0",1,Image.MidHandle)
imgunit[1] = unitAtlas.Grab("char__1",1,Image.MidHandle)
imgunit[2] = unitAtlas.Grab("char__2",1,Image.MidHandle)
imgunit[3] = unitAtlas.Grab("char__3",1,Image.MidHandle)
...
If KeyDown( KEY_LEFT )
DrawImage imgunit[0],DrawAtX,DrawAtY
End
but where i do the animation , so the player image changing ? |
| ||
| to make an animation you need to make use of variables for the array index that change as time goes by. the steps required in pseudocode load all of the images in the OnCreate method. variables are needed for the first and last image for each direction. a variable that keep track which frame is being displayed all of the keyboard input and calculations for the animation should be performed on OnUpdate() method. finally the On Render will just be used to display the image.
Method OnCreate:int()
'set frame rate
'loadImages
End
Method OnUpdate:int()
'check for key presses and select correct animation frame
End
Method OnRender()
'draw the correct image based on calculations done on the OnUpdate() Method
End
|
| ||
| thank Jesse, but I already have that, I don't understand the animation frame key. the thing I have to do in the OnUpdate() and Onrender
Method OnCreate:int()
unitAtlas = LoadAtlas("char1.txt")
imgunit[0] = unitAtlas.Grab("char__0",1,Image.MidHandle) ' walk left step 1
imgunit[1] = unitAtlas.Grab("char__1",1,Image.MidHandle) ' walk left step 2
imgunit[2] = unitAtlas.Grab("char__2",1,Image.MidHandle) ' walk left step 3
imgunit[3] = unitAtlas.Grab("char__3",1,Image.MidHandle)
End
Method OnUpdate:int()
'check for key presses and select correct animation frame
If KeyDown( KEY_LEFT )
DrawImage imgunit[0],DrawAtX,DrawAtY
End
End
Method OnRender()
'draw the correct image based on calculations done on the OnUpdate() Method
End
Let keep it simple that moving left is 4 pictures, like in this example. What to do next ? |
| ||
| you say you understand but your example illustrates otherwise. the OnRender is where all of the drawing go. the OnUpdate will give you an error if you try to use any of the draw commands on. something like this at it's simplest form:
Field index:Int
Method OnCreate:Int()
SetFrameRate(60)
unitAtlas = LoadAtlas("char1.txt")
imgunit[0] = unitAtlas.Grab("char__0",1,Image.MidHandle) ' walk left step 1
imgunit[1] = unitAtlas.Grab("char__1",1,Image.MidHandle) ' walk left step 2
imgunit[2] = unitAtlas.Grab("char__2",1,Image.MidHandle) ' walk left step 3
imgunit[3] = unitAtlas.Grab("char__3",1,Image.MidHandle)
index = 0
End
Method OnUpdate:Int()
'check for key presses and select correct animation frame
If KeyDown( KEY_LEFT )
index += 1
If index > 3
index = 0
Endif
End
End
Method OnRender()
'draw the correct image based on calculations done on the OnUpdate() Method
DrawImage imgunit[index],DrawAtX,DrawAtY
End
note that the code doesn't take into consideration desired speed, it changes frames at game rate speed. have you ever programmed animations in any other programming language and have you ever done any type of object oriented programming? |
| ||
a bit more advance:
Strict
Import Mojo
Function Main:Int()
New Game
End Function
Class Game Extends App
Field walkingLeft:Animation
Field walkingRight:Animation
Field standingRight:Animation
Field standingLeft:Animation
Field currentAnimation:Animation
Field pinguin:Image
Method OnCreate:Int()
SetUpdateRate(60)
pinguin = LoadImage("walker.png",32,32,16)
walkingLeft = New Animation(0,7,5,pinguin)'first frame, last frame ,duration, animated image set
walkingRight = New Animation(8,15,5,pinguin)
standingLeft = New Animation(6,6,5,pinguin)
standingRight = New Animation(9,9,5,pinguin)
currentAnimation = standingRight
End
Method OnUpdate:Int()
'check for key presses and select correct animation frame
If KeyDown( KEY_LEFT )
currentAnimation = walkingLeft
Elseif KeyDown( KEY_RIGHT)
currentAnimation = walkingRight
Else
Select currentAnimation
Case walkingLeft
currentAnimation = standingLeft
Case walkingRight
currentAnimation = standingRight
End Select
Endif
currentAnimation.update()
End
Method OnRender:Int()
Cls()
'draw the correct image based on calculations done on the OnUpdate() Method
currentAnimation.display(100,100)
End
End Class
Class Animation
Field firstFrame:Int
Field lastFrame:Int
Field duration:Int
Field delay:Int
Field index:Int
Field images:Image
Method New(first:Int,last:Int,dur:Int,img:Image)
firstFrame = first
lastFrame = last
duration = dur
images = img
index = first
delay = 0
End Method
Method update:Int()
delay = delay + 1
If delay > duration
index = index + 1
If index > lastFrame
index = firstFrame
Endif
delay = 0
Endif
End Method
Method display:Int(x:Int,y:Int)
DrawImage images,x,y,index
End Method
End Class
|
| ||
| Heres a quick example using Adam's Another Texture Atlas Loader: BTW Adam I think there is a slight issue with your code, it doesnt like it if there is only one image in the data file ;) |
| ||
| @Jesse, sorry I copy/past to quick. But you saved my life with that advanced code ! thanks very much its realy works very good. I had to change the CLS() to SetColor(255,255,255) because everything was black except for a red penguin. @therevills, I will going to combine the code with @Jesse thanks ! |
| ||
| it's better if you use the millisecs setup that therevills has by replacing the delay and duration on my example. |
| ||
| BTW Adam I think there is a slight issue with your code, it doesnt like it if there is only one image in the data file ;) Whooops! Updated, fixed! Thx! (was one small edit) :D |
| ||
I'm getting a crash using this... Monkey Runtime Error : Array index out of range C:/Monkey/MonkeyModules/modules/textureatlas/textureatlas.monkey<85> C:/Monkey/MonkeyModules/modules/textureatlas/textureatlas.monkey<12> It is specifically happening with atlases that are multiple pages, and seems to always be on the last image on the last page. A single page atlas works fine in a different project. |
| ||
| So, handling of multiple texture pages is definitely broken. Each page is adding a new image index, which results in the array getting blown. It's doing a Continue when it hits the blank lines. Also means that even if you expand the array, the indices are wrong from that point forward: A brief sample: This gives the following output across a page break... I can take a whack at fixing it later, unless you happen to have a version that works already, Adam... |
| ||
| Here's a version that seems to work. I haven't tested it with single page atlases, but it should be fine. I added a page field to CSubTextureAtlas and changed CTextureAtlas's field "texture" into an image array. A blank line triggers loading a new file at the end of the array, and Grab and GrabSet now just pick from the right texture in the array. |
| ||
| Ok, cool, thanks for updating that! I also updated the code to check for a "." in the next line (indication of an actual filename and not just an inadvertent blank line) So with that in mind, I've added the capability to load an array of atlas txt files, so they can be combined into one atlas.
unitAtlas = LoadAtlas(["cards0.txt","cards1.txt"])
imgunit[0] = unitAtlas.Grab("queen_of_spades") ''in cards0.txt/cards0.png
imgunit[1] = unitAtlas.Grab("red_joker")
imgunit[2] = unitAtlas.Grab("queen_of_diamonds") ''in cards1.txt/cards1.png
imgunit[3] = unitAtlas.Grab("queen_of_hearts")
|
| ||
| Nice! I like this loader because it has virtually no assumptions about the images after, you really can just drop it in and find-and-replace LoadImage, except for animations. I dug into a GrabAnim that you could load animation frames without the GrabSet workaround, but it looks like there's some limitations in monkey.graphics which prevent it. It isn't that hard to hack it to work, but I tossed a feature request in the bug forum and left it at that. |
| ||
| I think there is a bug in GrabSet as well. newx and newy were being incremented by w+1 and h+1 respectively. That created a growing offset that corrupted the frames, and could go off the surface altogether. I put this in instead and it seems to work fine: |
| ||
| Thanks for the code. However, there's a bug (of sorts) if you have the png and txt file in the same folder (but not in the exe folder) For example: If you call LoadAtlas() with a filepath e.g. "graphics/myAtlast.png" it fails because this line: texture[textureCount] = LoadImage(all[0].Trim(), 1) Uses the texture filename from the top of the .txt file which doesn't have path info if it's in the same file as the texture. I made a workaround by passing in the original png filepath to ProcessAtlas (in LoadText()): Return ProcessAtlas(str, fn) And modified the Method declaration as follows: ProcessAtlas:Int(str:String, texturePath:String) Then changed the texture loading as follows: texture[textureCount] = LoadImage(texturePath, 1) However it should be noted that I had to comment out this line in the version of LoadAtlas that takes an array of texture filenames: If Not atlas.ProcessAtlas(fconcat) Then Return Null Really the code needs to get the path for each png (which may not be the same) and add it to the top of each text file as it joins them together and then use that to load the png. Alternatively you can click Advanced in the Data section of TexturePacker and fill out the Texture Path and it will add it to the filename at the top of the .txt file. But this is kinda lame really... |
| ||
| Quick heads up that I tried using the offset values but I've discovered that they are sometimes slightly wrong by a pixel or two! If I output the same data as xml the offset values are correct, so I don't know what is going on there... |
