im new to android gaming and started andengine and facing problem while using createTiledFromAsset
the code where im getting problem is
#Override
public void onLoadResources() {
mBitmapTextureAtlas = new BitmapTextureAtlas(128, 128,
TextureOptions.BILINEAR);
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
mPlayerTextureRegion = BitmapTextureAtlasTextureRegionFactory
.createTiledFromAsset(this.mBitmapTextureAtlas, this,
"move.png", 0, 0, 10, 1);
mEngine.getTextureManager().loadTexture(mBitmapTextureAtlas);
}
#Override
public Scene onLoadScene() {
mEngine.registerUpdateHandler(new FPSLogger());
mMainScene = new Scene();
mMainScene
.setBackground(new ColorBackground(0.09804f, 0.6274f, 0.8784f));
player = new AnimatedSprite(0, 0, mPlayerTextureRegion);
mMainScene.attachChild(player);
return mMainScene;
}
im not getting the error as my BitmapTextureAtlas is of 128*128 and each tiled part coming from createTiledFromAsset should be of 78*85 as the passed arguments to it are 1 row and 10 columns and my source image is of 779*85 which means when the width is tiled to 10 parts then 779/10=78 approx which will be width of each tiled part and as row is 1 so 85/1=85 hence the width*height of each tiled part which is to be placed on the BitmapTextureAtlas is 78*85 and the BitmapTextureAtlas itself has size 128*128 then why the error saying Supplied pTextureAtlasSource must not exceed bounds of Texture
what is happening here ...? or im not understanding the actual functions ...? if im wrong then how the process of createTiledFromAsset is working........?
I found the same problem too. I am using a large sprite sheet, and try to use it. after searching, I found some clue in this tutorial: getting-started-working-with-images and i realize that the value of width and height used in:
BitmapTextureAtlas(WIDTH, HEIGHT ,TextureOptions.BILINEAR);
must be higher than the image size. for example if I use 1200*100 sprite sheet, I must use the width and height higher than 1200*100.
BitmapTextureAtlas(2047, 128, TextureOptions.BILINEAR);
If I understand BitmapTextureAtlas correctly, you are trying to put 779*85 image into the small space of 128*128. TextureAtlas is a large canvas on which you are supposed to place many images. These images are later accessed using object called TextureRegion, which basically specifies the size and coordinates of the smaller picture on the canvas. The method createTiledFromAsset probably copies the original image 1:1 onto the TextureAtlas and saves the coordinates of the tiles.
Please note that TextureRegion has nothing to do with the image itself, it is merely a "pointer" to the place on TextureAtlas where the image is stored.
To get the idea of what a TextureAtlas actually is, look at the awesome pictures at the bottom of this page:
http://www.blackpawn.com/texts/lightmaps/default.html
Related
I noticed that I can keep one atlas for one textureRegion although I can draw same region multiple times as a sprite. Is it possible to keep all textureRegions in one textureAtlas in a scene?
My special case is, I am generating images instead of using any image file. I do this with BaseBitmapTextureAtlasSourceDecorator and generate the region from IBitmapTextureAtlasSource.
Yes. Generally, though, you should only create atlases with a a max width/height of 1024 (these sizes must be powers of 2, by the way), to be efficient.
On another note, I've found it easier to use BuildableBitmapTextureAtlas. With this kind of atlas, you don't have to specify where in the atlas you are placing your textures. I think it also might take care of sprite-bleeding to some degree (not sure, though). It's the same idea really... Here is an example from my project:
BuildableBitmapTextureAtlas buttonAtlas = new BuildableBitmapTextureAtlas(getTextureManager(), 512, 512, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
model.moveLeftButtonTR = BitmapTextureAtlasTextureRegionFactory.createFromAsset(buttonAtlas, this, "moveleft_button.png");
model.moveRightButtonTR = BitmapTextureAtlasTextureRegionFactory.createFromAsset(buttonAtlas, this, "moveright_button.png");
model.handleBlockButtonTR = BitmapTextureAtlasTextureRegionFactory.createFromAsset(buttonAtlas, this, "handleblock_button.png");
model.restartButtonTR = BitmapTextureAtlasTextureRegionFactory.createFromAsset(buttonAtlas, this, "restart_button.png");
try{ buttonAtlas.build(new BlackPawnTextureAtlasBuilder<IBitmapTextureAtlasSource, BitmapTextureAtlas>(0, 1, 1)); }
catch(Exception e){ e.printStackTrace(); }
buttonAtlas.load();
In your case, use the following method:
BitmapTextureAtlasTextureRegionFactory.createFromSource(BuildableBitmapTextureAtlas atlas, IBitmapTextureAtlasSource source)
In summary, the atlas just holds all the textures you add to it. Then you load this atlas into memory so that these textures can be retrieved quickly. You can then use a single instance of a texture to build as many independent sprites as you wish.
I want to do an "chain" or circular loop of animations as can be described below:
LABEL start
do Anim1->Anim2->Anim3->Anim4
GOTO start
The above will do a circular loop: Anim1->Anim2->Anim3->Anim4 and back to Anim1 and so on.
I am not able to merge all the PNGs in one Texture because Andengine/Android is limited in loading the resources. However, when I split my initial large tile into 4 smaller tiles, everything works fine.
I tried to use an AnimationListener inside Anim1. When onAnimationFinished() is called, I detach Anim1, and run Anim2 and do this in chain of inner functions. However, when I am in Anim4, I do not know how to go back to the start and attach Anim1.
Note: All this problem could be solved if you know how I can pack a set of 150 PNGs that individually quite large but fit in a tile of 4096x4096 px.
Thank you for your help!
EDIT (following JiMMaR's proposed solution):
I am using Texture Packer and the overall Texture exceeds 4096*4096, causing an OutOfMemory error on Android.
At the moment, I have split the Textures into four tiles and I four PNG tilemaps.
You can use 'Texture Packer' to see if all your images can fit in 4096 x 4096.
You can download Texture Packer from here. (NOTE: Texture Packer supports AndEngine data output too)
You can use "BuildableBitmapTextureAtlas" and "BlackPawnTextureAtlasBuilder" classes to pack your PNGs into one texture atlas.
You should post some code so we can see the implementation.
Try to use several AnimatedSprites with animation listeners in each one. This way you can start the animation of the next sprite in the onAnimationFinished() call.
private class AnimationLooperListener implements IAnimationListener{
private AnimatedSprite nextSpriteToAnimate;
public AnimationLooperListener(AnimatedSprite sprite){
nextSpriteToAnimate = sprite;
}
/** other methods are hidden */
public void onAnimationFinished(AnimatedSprite sprite){
sprite.setVisible(false);
nextSpriteToAnimate.setVisible(true);
nextSpriteToAnimate.animate(100, new AnimationLooperListener(sprite);
}
}
AnimatedSprite sprite1 = new AnimatedSprite(0, 0, tiledTextureRegion, vertex);
AnimatedSprite sprite2 = new AnimatedSprite(0, 0, tiledTextureRegion, vertex);
AnimatedSprite sprite2 = new AnimatedSprite(0, 0, tiledTextureRegion, vertex);
AnimationLooperListener listener1 = new AnimationLooperListener(sprite2);
AnimationLooperListener listener2 = new AnimationLooperListener(sprite3);
AnimationLooperListener listener3 = new AnimationLooperListener(sprite1);
sprite1.animate(100, listener1);
sprite2.animate(100, listener2);
sprite3.animate(100, listener3);
This way, you have an animation loop, between several sprites that can be created using several TiledTextureRegions.
I am trying to understand below code from andengine to load the texture, i would like to know what is 512 n 512(i know its height and width), but the image of size 480 * 320.
public void onLoadResources() {
this.mTexture = new Texture(512, 512,
TextureOptions.BILINEAR_PREMULTIPLYALPHA);
this.mSplashTextureRegion = TextureRegionFactory
.createFromAsset(this.mTexture,this, "image.png", 0, 0);
this.mEngine.getTextureManager().loadTexture(this.mTexture);
}
I searched on the net but no expected explanation.
I don't know well AndEngine but this might be because a lot of libs assume that images dimension are power of two.
Here you can find a better explanation:
About power of 2 rule.
I'm trying to port an emulator that i have written in java to android. Things have been going nicely, I was able to port most of my codes with minor changes however due to how emulation works, I need to render image at pixel level.
As for desktop java I use
int[] pixelsA = ((DataBufferInt) src.getRaster().getDataBuffer()).getData();
which allow me to get the reference to the pixel buffer and update it on the fly(minimize object creations)
Currently this is what my emulator for android does for every frame
#Override
public void onDraw(Canvas canvas)
{
buffer = Bitmap.createBitmap(pixelsA, 256, 192, Bitmap.Config.RGB_565);
canvas.drawBitmap(buffer, 0, 0, null);
}
pixelsA is an array int[], pixelsA contains all the colour informations, so every frame it will have to create a bitmap object by doing
buffer = Bitmap.createBitmap(pixelsA, 256, 192, Bitmap.Config.RGB_565);
which I believe is quite expensive and slow.
Is there any way to draw pixels efficiently with canvas?
One quite low-level method, but working fine for me (with native code):
Create Bitmap object, as big as your visible screen.
Also create a View object and implement onDraw method.
Then in native code you'd load libjnigraphics.so native library, lookup functions AndroidBitmap_lockPixels and AndroidBitmap_unlockPixels.
These functions are defined in Android source in bitmap.h.
Then you'd call lock/unlock on a bitmap, receiving address to raw pixels. You must interpret RGB format of pixels accordingly to what it really is (16-bit 565 or 32-bit 8888).
After changing content of the bitmap, you want to present this on screen.
Call View.invalidate() on your View. In its onDraw, blit your bitmap into given Canvas.
This method is very low level and dependent on actual implementation of Android, however it's very fast, you may get 60fps no problem.
bitmap.h is part of Android NDK since platform version 8, so this IS official way to do this from Android 2.2.
You can use the drawBitmap method that avoids creating a Bitmap each time, or even as a last resort, draw the pixels one by one with drawPoint.
Don't recreate the bitmap every single time. Try something like this:
Bitmap buffer = null;
#Override
public void onDraw(Canvas canvas)
{
if(buffer == null) buffer = Bitmap.createBitmap(256, 192, Bitmap.Config.RGB_565);
buffer.copyPixelsFromBuffer(pixelsA);
canvas.drawBitmap(buffer, 0, 0, null);
}
EDIT: as pointed out, you need to update the pixel buffer. And the bitmap must be mutable for that to happen.
if pixelsA is already an array of pixels (which is what I would infer from your statement about containing colors) then you can just render them directly without converting with:
canvas.drawBitmap(pixelsA, 0, 256, 0, 0, 256, 192, false, null);
I have a big spritesheet (3808x1632) composed by 42 frames.
I would present an animation with these frames and I use a thread to load a bitmap array with all the frames, with a splash screen waiting for its end.
I'm not using a SurfaceView (and a draw function of a canvas), I just load frame by frame in an ImageView in my main layout.
My approach is similar to Loading a large number of images from a spritesheet
The completion actually takes almost 15 seconds, not acceptable.
I use this kind of function:
for (int i=0; i<TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
mVectorTeapotBG.add(Bitmap.createBitmap(framesBitmapTeapotBG, xStartTeapotBG, yStartTeapotBG, frameWidthTeapotBG, frameHeightTeapotBG));
}
framesBitmapTeapotBG is the big spritesheet.
Looking more deeply, I've read in the logcat that the createBitmap function takes a lot of time, maybe because the spritesheet is too big.
I found somewhere that I could make a window on the big spritesheet, using the rect function and canvas, creating small bitmaps to be loaded in the array, but it was not really clear. I'm talking about that post: cut the portion of bitmap
My question is: how can I speed the spritesheet cut?
Edit:
I'm trying to use this approach but I cannot see the final animation:
for (int i=0; i<TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
Bitmap bmFrame = Bitmap.createBitmap(frameWidthTeapotBG, frameHeightTeapotBG, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmFrame);
Rect src = new Rect(xStartTeapotBG, yStartTeapotBG, frameWidthTeapotBG, frameHeightTeapotBG);
Rect dst = new Rect(0, 0, frameWidthTeapotBG, frameHeightTeapotBG);
c.drawBitmap(framesBitmapTeapotBG, src, dst, null);
mVectorTeapotBG.add(bmFrame);
}
Probably, the Bitmap bmFrame is not correctly managed.
The short answer is better memory management.
The sprite sheet you're loading is huge, and then you're making a copy of it into a bunch of little bitmaps. Supposing the sprite sheet can't be any smaller, I'd suggest taking one of two approaches:
Use individual bitmaps. This will reduce the memory copies as well as the number of times Dalvik will have to grow the heap. However, these benefits may be limited by the need to load many images off the filesystem instead of just one. This would be the case in a normal computer, but Android systems may get different results since they're run off flash memory.
Blit directly from your sprite sheet. When drawing, just draw straight from sprite sheet using something like Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint). This will reduce your file loads to one large allocation that probably only needs to happen once in the lifetime of your activity.
I think the second option is probably the better of the two since it will be easier on the memory system and be less work for the GC.
Thanks to stevehb for the suggestion, I finally got it:
for (int i = 0; i < TotalFramesTeapotBG; i++) {
xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG;
yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
Bitmap bmFrame = Bitmap.createBitmap(frameWidthTeapotBG, frameHeightTeapotBG, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmFrame);
Rect src = new Rect(xStartTeapotBG, yStartTeapotBG, xStartTeapotBG+frameWidthTeapotBG, yStartTeapotBG+frameHeightTeapotBG);
Rect dst = new Rect(0, 0, frameWidthTeapotBG, frameHeightTeapotBG);
c.drawBitmap(framesBitmapTeapotBG, src, dst, null);
mVectorTeapotBG.add(bmFrame);
}
The computation time falls incredibly! :)
Use a LevelListDrawable. Cut the sprites into individual frames and drop them in your drawable resource directory. Either programmatically or through an xml based level-list drawable create your drawable. Then use ImageView.setImageLevel() to pick your frame.
I use a method of slicing based on rows and columns. However your sprite sheet is rather huge. You have to think you are putting that whole sheet into memory. 3808x1632x4 is the size of the image in memory.
Anyway, what I do is I take an image (lets say a 128x128) and then tell it there are 4 columns and 2 rows in the Sprite(bitmap, 4, 2) constructor. Then you can slice and dice based on that. bitmap.getWidth() / 4 etc... pretty simple stuff. However if you want to do some real stuff use OpenGL and use textures.
Oh I also forgot to mention there are some onDraw stuff that needs to happen. Basically you keep an index counter and slice a rectangle from the bitmap and draw that from a source rectangle to a destination rectangle on the canvas.