getting more FPS from a SurfaceView, can i do something better? - java

I'm playing around with drawing jbox2d objects onto a surfaceview, yet I'm not really satisfied with the framerate I'm getting ( 10-13, when there are multiple objects on screen / in debug more I'm getting about 26-30 ).
while (isRun)
{
Canvas canvas = holder.lockCanvas();
_update(canvas); /// <- call to update
holder.unlockCanvasAndPost(canvas);
}
...
canvas.drawColor(0xFF6699FF);
for ( Body b = world.getBodyList(); b!=null; b = b.getNext() ) // <- cycle through all the world bodies
{
rlBodyImage bi = new rlBodyImage();
bi = (rlBodyImage) b.getUserData();
float x = b.getPosition().x*scale + wOffset + camera_x + camera_x_temp;
float y = b.getPosition().y*-scale + hOffset + camera_y + camera_y_temp;
canvas.save();
canvas.translate( x - (bi.getImage().getWidth()*bi.getCoof()*scale)/2 , y - (bi.getImage().getHeight()*bi.getCoof()*scale)/2 );
canvas.scale( bi.getCoof()*scale , bi.getCoof()*scale );
canvas.rotate( (float) -(b.getAngle() * (180/Math.PI)) , bi.getImage().getWidth() /2 , bi.getImage().getHeight() /2 );
canvas.drawPicture(bi.getImage()); // <- draw the image assossiated with current body on canvas
canvas.restore(); // images are stroed as "Pictures" , extracted from SVGs.
}
Is there a way to speed things up, other then of course using more simple SVGs? :)
Thanks!
EDIT :
Yep, will have to switch to PNGs, they give way better FPS rate.
vector Pictures = 10...13...16 FPS
PNG only = 35...40+ FPS
PNG with scaling = 32...37+ FPS
PNG with scaling & rotation = 27+ FPS

You should probably use a rasterized image instead of SVGs. You can either save them to pngs or similar before/during compile, or the phone can convert them to fitting images on (first) startup.
And it seems like you are swapping texture to be drawn multiple times per frame. That is extremely expensive for the GPU. You should create one big sprite/imageatlas containing all your images, load it onto the GPU and then draw different regions of it to the screen.
And do not allocate many new objects each frame if you are not going to use them for a longer time. The GC will freeze your game for a short period of time every now and then, dropping your fps.
Edit: you should also achieve much more than ~20 fps in debug mode (unless your phone is really old). Consider optimizing your box2d world. You should maybe consider using the Libgdx framework. It provides a JNI wrapper for Box2d, greatly improving performance.
Edit2:
the problem with PNGs start when I have to rotate and scale them (
when the user zooms in or out )
This is not an issue. Save the PNGs as the maximum size they will be shown on screen, and then just scale and rotate them to fit. If you use proper minification filter, and maybe add some extra space around (see this post for an example), you should see no to minimal loss in quality. If needed, you can create one large version and a smaller version of each sprite, and draw the one fitting best. Or just use mipmapping in OpenGL. And there is no way you should need to save a version for every size/angle of each sprite.
Libgdx Box2D
I don't know how much of a performance gain there is.

Immediately, this line strikes me as odd :
rlBodyImage bi = new rlBodyImage();
Every single frame you're making a new rlBodyImage, given it's name I suspect that's not a simple thing to do.
However, the next line ignores that image :
bi = (rlBodyImage) b.getUserData();
Try this inside your loop instead of those two lines :
rlBodyImage bi = (rlBodyImage) b.getUserData();
Let us know if that helps :)

Related

Set offset for BitmapDrawable while repeating in the x and y direction?

I'm making a 2D platformer game. I have created a texture for the platform, that is meant to be repeated over and over to fill the entire platform, without going over. My first attempt was to draw all the pixels from the bitmap manually, but this caused the background to flicker through while moving the platform (the movement and drawing threads are seperate, so the movement can run at a specific speed, while the FPS doesn't need to suffer). I found this technique worked better:
// Init
bitmap = new BitmapDrawable(res, Texture.PLATFORM.getBitmap());
bitmap.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
// Drawing loop
int x = getX() + (isStill() ? 0 : (int)MainActivity.offsetX);
int y = getY() + (isStill() ? 0 : (int)MainActivity.offsetY);
bitmap.setBounds(x, y, x + getWidth(), y + getHeight());
bitmap.draw(canvas);
However, the bitmap appears to be staying static while the platform is acting as a "view hole" to see through to the bitmap. The only work around I can think of is to somehow "offset" the static bitmap:
bitmap.offset(x, y);
Obviously, that isn't a function. I couldn't find one that would do what I want when looking through the docs.
To summon things up, the BitmapDrawable is causing the background to not move with the platform, making it look super weird.
Thanks in advance!
Try these tips in your code:(I assumed the game moves forward in the horizontal direction)
The GUY should only move up and down(with the appropriate touch input) and not forward and backward as you want the focus(or camera alternatively) solely on the GUY.I noticed that the WALL was moving up in your video when the GUY moved from initial higher position of the wall to little bit lower position later, rectify this because the GUY should move down(try to implement Gravity effect).
The WALL should only move forward(mostly) and backward(less often I guess).The WALL shouldn't move up and down normally. Do not apply Gravity effect to it. You can create at least 2 BitmapDrawable instance of WALL for a screen. They are going to be reused sequencially(for eg: If the 1st one goes totally outside of the screen, reshow it in the desired position using setBounds() method) and continue same for others the whole game.
The currently BLUE BACKGROUND, if it is a part of a larger map, then it needs to be appropriately offsetted.
One of the obstacles that I can think of at the time of writing this is to move the WALL down until it goes out of the screen which results in the death of the GUY.
At those places, where I have used the word move, you need to use the setBounds(a, b, c, d) method to make necessary position based changes as I didn't find other way to update the position of a BitmapDrawable instance. I think, you need to use game framework like libGdx to get method of luxury like setOffset(x, y) or of similar sort.
Sorry that I could only present you the ideas without specific code as I do not have past experience working in a project like this. Hope, it helps you in anyway possible.

Java Images - "1 to 1" image scaling

I want to have a pixel-styled look for my test game, but when I scale them to the size I want them to be displayed at (50x50; the actual size of the tiles is 16x16), it gets that ugly smoothed out look I dont want to have.
img = new Image("test.png").getScaledCopy(50,50);
Size of test image is 16x16 and I want it to be displayed at a size of 50x50, but like 1:1 scaled, not the ugly smoothed out way.
A bit like in the sandbox game "Minecraft", the block tiles and such are much smaller than the displayed ones, yet they are not "smoothed".
Thank you a lot for any help :)
In your case, the anti aliasing filters look crummy because you have a very small image (16x16) and are upscaling it only a small bit (to 50x50). In your case, you likely want a sharper image, so you'd likely want to go with nearest neighbor interpolation, rather than the built-in default (either bi-linear or bi-cubic, not entirely sure which is the default).
Image original = …;
original.setFilter(Image.FILTER_NEAREST);
Image scaled = original.getScaledCopy();
So in your case, it would be something like:
Image original;
Image scaled;
original = new Image("test.png");
original.setFilter(Image.FILTER_NEAREST);
scaled = original.getScaledCopy(50, 50);
Please note that this won't be a true "1:1" scaling, since the scaling factor is not an integer ratio (ie: 50/16 isn't a whole non-decimal number). If you were to upscale to 64x64, you could have a "1:1" rescaling, since 16*k=64, where k is an integer.

How to remove gaps between tiled textures?

I'm using LibGDX to make a platformer. I'm using square tiles for the platforms but when they are drawn some of them have gaps between them. When I zoom in/out or move the camera around the gaps move position.
More details:
Tiles are 32x32 and I have tried both 32x32 and 64x64.
Tiles are lined up 32 pixels apart (e.g. first tile would be x=0 y=0, second x=32 y=0, and so on in both x and y directions).
The gaps are not texture artifacts as I have checked this.
I use the TexturePacker with padding.
My best guess is that it is a problem when converting the textures to screen coords but have no idea how to fix this and I couldn't find any solution. I have checked and double-checked my precision with tile sizes and lining them up.
Has anyone had the same problem or know how it fix it?
I got it fixed by setting the duplicatePadding field of the TexturePacker.Settings class to true.
Example code:
import com.badlogic.gdx.tools.texturepacker.TexturePacker;
import com.badlogic.gdx.tools.texturepacker.TexturePacker.Settings;
Settings settings = new Settings();
settings.maxWidth = 1024;
settings.maxHeight = 1024;
settings.duplicatePadding = true;
TexturePacker.process(settings, "source", "destination", "name");
Well, I'm here to save your day!
The solution is called "Edge padding". Now if you are working with tilesets, I can assure you that this will work.
Personally I'm using Tiled which allows me to adjust margin and spacing in my tilesets. The only downside by this is that you'll have to use GIMP with this plugin: http://registry.gimp.org/node/26044
This plugin will let you apply edge padding to your tileset and voila! No more ugly artifacts.
Bleeding
Gaps
The short answer is that it may be your filter, which likely needs to be set to NEAREST.
Might also want to check out the working tutorials at Libgdx.
It's called "texture bleeding". You need to add padding to your tiles so that when the texture bleeds, it can collect the correct pixel data to fill the gap.
I know it's a bit late to answer on this post, but when I was looking for a solution I came here.
However, for me I found a much easier way to get rid of the flickering or gaps that appear randomly between the tiles.
I simply added a cast to the very long decimals I got for the player's position:
camera.position.set((int)robot.position.x, (int)robot.position.y, 0);
That made the player move very weirdly and to make him move smoothly again I added a cast to its render method aswell:
batcher.draw(robotSprite, (int)robot.position.x, (int)robot.position.y, 16, 16);
Et voilà! Working perfectly for me.
I would post my solution here and what I had tried for Libgdx about this problem.
--
T1. Make original spritesheet (no atlas file) that downloaded from somewhere to padding 2.
A1. This would be impossible for repacking the spritesheet that have no atlas, even if you find a slice/splitter tool, it should be a bunch of images that need to be repack properly for TiledMap(.tmx)
A1(Updated). Script that provided by #Nine Magics would be the best way to do this! (I use this as my final solution)
--
T2. Use TiledMapPacker that provided by libgdx-nighty or gdx-toolg, The batch code should be:
java -classpath "gdx.jar";"gdx-natives.jar";"gdx-backend-lwjgl.jar";"gdx-backend-lwjgl-natives.jar";"gdx-tiled-preprocessor.jar";"extensions/gdx-tools/gdx-tools.jar" com.badlogic.gdx.tiledmappacker.TiledMapPacker "PathToYourProject\android\assets\RawMap" "PathToYourProject\android\assets\Map" --strip-unused
A2. The output .tmx that could not be readable by Tiled If you are using complex folder path to category your .png files. And the output file could be possibly failed to load by AtlasTmxMapLoader.
--
T3. Camera position correction, make camera position to integer. The code liked #Julian or #strangecat from libgdx tiledmap flicker with Nearest filtering
A3. I use this solution for no problem, and also post my code that different from them.
float cameraX = (int)(mainCamera.position.x * Game.PPM_X) / Game.PPM_X;
float cameraY = (int)(mainCamera.position.y * Game.PPM_X) / Game.PPM_X;
float cameraZ = mainCamera.position.z;
mainCamera.position.set(cameraX, cameraY, cameraZ);
And also load it with TmxMapLoader.Parameters
TmxMapLoader.Parameters params = new TmxMapLoader.Parameters();
params.textureMinFilter = Texture.TextureFilter.Linear;
params.textureMagFilter = Texture.TextureFilter.Nearest;
params.generateMipMaps = true;
assetManager.load(TILED_MAP_SETS.FIRST_MAP, TiledMap.class, params);
If you used PPM and want to move pixel by pixel, you could use this integer correction for your game, If not you could just convert the position to integer.
I almost wasted whole day to slove this, hope these investigation could help every game developers :)
Edit(2018/04/21)
I found out that Unity is also having the same problem, but I haven't tested If Libgdx have 2x Anti-Alias setting by default. Libgdx might fix the issue as Unity by turning off the Anti-Alias.

How to rotate a bufferedimage, then copy the bufferedImage into a pixel array

I am trying to rotate a bufferedImage of a missile turret so that it looks like it's following a target. Basically, I can do it easily with the AffineTransform/ affinetransform
my current code in a nutshell is:
public BufferedImage tower = null;
try
{
tower = ImageIO.read(SpriteSheet.class.getResource("/spriteSheet/testTower.png"));
}
catch(IOException e)
{
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp = op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
//then I draw it using
g.drawImage(op.filter(tower, null), towerLocationX, towerLocationY, null);
this works, but what I want to do is transform(rotate) the bufferedImage, then copy the newly rotated pixel data into a pixel array and then draw it onto the screen because I believe this is how most games draw rotating images as opposed to drawing a png directly to the screen.
But what do I know. How exactly do 2D games draw rotating images? Am I doing it correctly, or is there a better/ more memory efficient way of doing this?
There are a lot of ways to tackle image manipulation in 2D games. Before optimizing though, you should ask yourself if there's a real need for it to begin with. Moreover, memory optimization usually comes at the cost of CPU performance and vice verse.
If CPU time is the problem, a common approach is to keep an array of images already rotated to certain angles (precalculated).
If memory is the problem, keep a single image and calculate the rotated form each time it's displayed. An even more memory efficient yet CPU consuming approach, is to draw vector shapes rather than images. This also leads to better looking results than the interpolation of the smoothing algorithm used for images when transformed. Java supports SVG, and there are several good packages available (e.g. http://xmlgraphics.apache.org/batik/).
Finally, Java can be connected to graphic libraries in order to perform the rendering, thus improving performance. Such libraries (OpenGL, etc.) use the memory of the graphic cards to store images in order to improve CPU usage (http://jogamp.org/jogl/www/).

slicing up a very big jpg map image , 49000* 34300 pixel

i want to write a mapviewer, i must to work small tile of big map image file and there is need to tiling the big image, the problem now is to tiling big image to small tiles (250 * 250 pixel or like this size)
so on, i used ImageMagic program to do it but there was problem
now is any other programing method or application that do tiling?
can i do it with JAI in java? how?
Have you tried doing it in java yourself? I tried this with (WARNING, big image, can crash your browser, use "save as...") this image. Needed to run with extra memory though (-Xmx400M).
public class ImageTile {
public static void main(String[] args) throws IOException {
Dimension tileDim = new Dimension(250, 250);
BufferedImage image = ImageIO.read(new File(args[0]));
Dimension imageDim = new Dimension(image.getWidth(), image.getHeight());
for(int y = 0; y < imageDim.height; y += tileDim.height) {
for(int x = 0; x < imageDim.width; x += tileDim.width) {
int w = Math.min(x + tileDim.width, imageDim.width) - x;
int h = Math.min(y + tileDim.height, imageDim.height) - y;
BufferedImage tile = image.getSubimage(x, y, w, h);
ImageIO.write(tile, "JPG", new File("tile-"+x+"-"+y+".jpg"));
}
}
}
}
For the large images sizes like you have, you will be best served with lossless editing of the JPEG files. Not only is this faster, since the image doesn't need to be rendered, but it also preserves quality, since the image is not recompressed.
Lossless editing works on blocks, typically 16px square. While restrictive for some applications, this seems a good fit for mapping. You could implement tiling at different zoom levels by first losslessly cropping the image to sized pieces. (This is quick an efficient since the image is not rendered.) This gives you tiles for full-zoom. To create lower-levels of zoom, combine 2x2 tiles and scale these down to the size of 1 tile. The next level uses 4x4 tiles, and 8x8 and so on, each time scaling down to one tile. At some point when the number of tiles beecomes too large, you can choose to use zoomed tiles as the base resource. For example, at zoom level 8, that would require 256x256 tiles. This might be too much to handle, so you could use 16x16 tiles from zoom level 4.
Wikipedia has more on lossless editing, and links to some implementing libraries.
imagemagick does tiling using -tile. It's more of a repitition of an image, but might be useful esp. since youre already using it. However If you mean generated seamless tiling I'm not sure if imagemagick can do that or not.
GDAL comes with a script called gdal2tiles.py that does exactly what you want, including formatting the tiles for use with Google Maps, OpenLayers, etc.
There seems to be an newer version of GDAL2Tiles as well.
How about a megatexture with an r-tree for efficient access? Apparently it can use images 128000x128000 pixels.
JAI is platform dependent and seems like a dead project today.
I advise using the open-source program imagemagick. Although it is platform dependent, it is available for the same platforms as JAI, but with full community support.
The trick about large images about imagemagick is using its "stream"-command instead of the convert command. Stream only reads the relevant portion of the image and saves the extracted part as raw data. You then need "convert" to save the small raw data as jpeg.
Example to save a tile from large.jpeg of size 800x600 from position 0x0 to tile.jpeg:
stream -extract 800x600+0+0 large.jpeg tile.rgb
convert -depth 8 -size 800x600 rgb:tile.rgb tile.jpeg
(When running on windows, be sure to use ImageMagick's convert.exe, as there is a windows command named "convert".)
When working with TIFF-images only, apache Sanselan could be the right choice - it is a pure-java imaging lib. Also, JAI seems to contain a platform independent codec for TIFF.

Categories