When I draw more around 100-200 textures all in the same screen, the device becomes very slow and the app crashes without any exceptions. Could you please let me know any best way to have 100 textures without compromising the performance.
I am using the TextureRegion from TextureAtlas.
MainGame
public void render(SpriteBatch sb) {
// TODO Auto-generated method stub
// System.out.println("BallPoolGame Screen - render");
batch = sb;
sb.setProjectionMatrix(camera.combined);
sb.begin();
sb.draw(BACKGROUND_BALL_POOL, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
cellManager.draw(sb);
ballManager.draw(sb);
sb.end();
}
private void setGameTextures() {
gameScreenAtlas = new TextureAtlas("data/texturetutorialpack.pack");
RED_BALL = gameScreenAtlas.findRegion("redball");
// RED_BALL.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
BLUE_BALL = gameScreenAtlas.findRegion("blueball");
// BLUE_BALL.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
GREEN_BALL = gameScreenAtlas.findRegion("greenball");
// GREEN_BALL.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
}
CellManager
public void draw(SpriteBatch sb){
batch=sb;
showImageTexture(MODEL1,207,1);
if(showSelectedCell){
if(allPossiblePathSize>0)
setupBoardCellTexture();
showImage(CELL_SELECTED, rowCoordinate[cellRow], colCoordinate[cellCol]);
}
}
private void setupBoardCellTexture(){
for(CellGrid c : masterGrid){
if(cellTextureIndicator[c.getRow()][c.getCol()]==1){
showImage(CELL_ALL_PATH_TEXTURE,c.getRowCoordinate() ,c.getColCoordinate() );
}
}
}
private void showImage(TextureRegion tr, float rowCoordinate, float colCoordinate) {
batch.draw(tr, colCoordinate,rowCoordinate);
}
BallManager
public void draw(SpriteBatch sb) {
batch = sb;
setupBoardBallTexture();
if (moveTheBall) {
updateBallPosition();
showImage(ball.getTextureRegion(), moveRow + 6, moveCol + 6);
}
squeezeBalls.draw(sb);
}
You are missing some essential data about your app to answer than question:
How big is one texture on average (Size: widthxheight)
On which device is this error occuring (some devices might have less fillrate than others)
What texture filter does the TextureAtlas use (LINEAR, NEAREST, ...)
I guess that you are trying to draw many textures event if they are out of sight. If that is the case you have to implement a check if the cell is visible to the camera.
Another guess would be that you are trying to draw too many elements with the LINEAR TextureFilter. When using linear as a texture filter the gpu needs to sample way more points then with nearest (i think it was 4 times the samples; so in theory your gpu draws 400-800 textures; depending on images size that are too much for mobile gpu fillrates)
Try to describe more circumstances then i can give probably more insight in your problem.
Related
I am trying to draw an circle to Libgdx which is created from Image not ShapeRenderer. But when I try to draw image to SpriteBatch it does not draw smoothly.
I checked the image resolution and Image Dimension is 1673x1673 and Sprite Size is 80x80.
//This is my GameState code
#Override
public void render(float delta) {
update(delta);
SpriteBatch sb = game.batch;
Color bg = ThemeFactory.getInstance().getTheme().backgroundColor;
Gdx.gl.glClearColor(bg.r, bg.g, bg.b, bg.a);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
sb.setProjectionMatrix(camera.combined);
sb.begin();
for(int i =0;i<elements.size();i++){
if(!(elements.get(i) instanceof Arrow)){
elements.get(i).render(sb);//HERE IS DRAWING HAPPENING
}
}
sb.end();
barriers.render(sb);
renderHud();
}
My circle constructor and rendering code is below.
public Circle(Texture texture, Size size, Vector3 position){
mSprite = new Sprite(texture);
mSprite.setSize(size.width, size.height);//80x80
mSprite.setPosition(position.x, position.y);
mSprite.setOriginCenter();
}
#Override
public void render(SpriteBatch sb) {
mSprite.draw(sb);
}
2 tips:
1. Raise up sampling
2. use libgdx mipmap for your texture
I recommend using : filter 1: MipMapLinearNearest,Nearest or filter 2: Linear,Linear
filter 1 is fast, filter 2 is high quality
for more info read this
Filtering
The minification/magnification filters define how the image is handled upon scaling. For "pixel-art" style games, generally Filter.Nearest is suitable as it leads to hard-edge scaling without blurring. Specifying Filter.Linear will use bilinear scaling for smoother results, which is generally effective for 3D games (e.g. a 1024x1024 rock or grass texture) but not always so for a 2D game. In OpenGL, the terms used are GL_NEAREST and GL_LINEAR, respectively.
Use filter in your texture.
Eg.
texture.setFilter (TextureFilter.Nearest, TextureFilter.Nearest );
For more information about filters, refer this blog: http://www.badlogicgames.com/wordpress/?p=1403
I have been experimenting with squeezing as much performance out of SurfaceView as possible. Currently, I'm subclassing it and implementing a runnable interface on it instead of a callback. I understand there is no hardware acceleration on it.
Still, if I either draw a canvas primitive vertical line scrolling across the screen or a bitmap vertical line, both run slower and slower after each pass. This felt to me like a memory leak, or is it just Android itself? Is OpenGL or another library really my last resort?
I've drawn plenty of scrolling backgrounds before at decent speeds (I think around 5 pixels per tick, this I'm aiming around 20-50 pixels a tick which if anything would be less stops along the way to render).
EDIT: Here is the SurfaceView extended, the thread it makes, the drawing method, and the initialization of it. Basically, this is in a slightly bigger class that just holds this screen's data. The drawXYZ() methods simply use the canvas primitives or a bitmap to paint mainly as the background, which is a solid background color with some vertical and horizontal lines on it like a music staff, little calculating is involved.
The drawCursor is what makes the scrolling vertical line and when I just let it loop the scrolling from left to right, it eventually lags much slower than the first scroll.
public class MySurfaceView extends SurfaceView implements Runnable
{
Thread renderThread = null;
SurfaceHolder holder;
volatile boolean running = false;
public MySurfaceView() {
super(mainActivity);
this.holder = getHolder();
holder.setFixedSize(screenW, screenH);
}
public void resume() {
running = true;
renderThread = new Thread(this);
renderThread.start();
}
#Override
public void run() {
while (running) {
if (!holder.getSurface().isValid()) {
continue;
}
Canvas canvas = holder.lockCanvas();
if(canvas != null) {
doDraw(canvas);
holder.unlockCanvasAndPost(canvas);
}
}
}
public void pause() {
running = false;
while (true) {
try {
renderThread.join();
break;
} catch (InterruptedException e) {
// retry
}
}
}
protected void doDraw(Canvas canvas)
{
canvas.drawColor(Color.rgb(56, 56, 62));
lastNotePlayed = OptionsContainer.getNotePlaying();
//Draw contours (rows).
paint.setColor(Color.rgb(0, 255, 255));
paint.setStrokeWidth(3);
paint.setTextSize(35);
drawContours(canvas, paint);
//Beats per measure (BPM).
paint.setColor(Color.rgb(233, 232, 232));
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.STROKE);
paint.setPathEffect(bpmLines);
drawBPM(canvas, paint);
paint.setPathEffect(null);
//Draw measures.
paint.setStrokeWidth(5);
drawMeasures(canvas, paint);
//Draw note node inputs.
paint.setColor(Color.rgb(76, 255, 0));
for (int i = 0; i < OptionsContainer.noteList.length; i++) {
if (OptionsContainer.noteList[i].getContour() != 0) {
if (OptionsContainer.noteList[i].getContour() > (OptionsContainer.contour / 2)) {
//Staff on left side, below note.
canvas.drawBitmap(lowerStaffBmp, OptionsContainer.noteList[i].getX(), OptionsContainer.noteList[i].getY(), null);
} else {
canvas.drawBitmap(higherStaffBmp, OptionsContainer.noteList[i].getX(), OptionsContainer.noteList[i].getY() - 40, null);
}
}
}
//Draw cursor.
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
drawCursor(canvas, paint);
if (OptionsContainer.isRest)
canvas.drawBitmap(restBmp, (OptionsContainer.screenWidth / 2), (screenHeight - 100) / 2, null);
}
}
#Override
public void init() {
surfaceView = new MySurfaceView();
surfaceView.setLayoutParams(layoutParams);
surfaceView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// Normalize x,y between 0 and 1
float x = event.getX();
float y = event.getY();
if (x < (OptionsContainer.screenWidth) && y < screenH) {
NoteNode note = new NoteNode(x, y, MainActivity.options);
if (note.getContour() == OptionsContainer.noteList[note.getBeat() - 1].getContour()) {
OptionsContainer.noteList[note.getBeat() - 1] = new NoteNode(x, screenHeight + 200, MainActivity.options);
} else {
OptionsContainer.noteList[note.getBeat() - 1] = note;
}
}
}
return true;
}
});
mainActivity.addContentView(surfaceView, layoutParams);
surfaceView.resume();
}
EDIT #2: Final Answer
Add Path.reset() after the path is drawn in drawBPM(). I'd imagine that stops a memory leak of that path which is trying to keep track of ALL the paths it has been writing and overwriting, little to our knowledge just looking at the lines on the screen. There was a similar Stack Overflow question but fadden's debugging tips below were very helpful for initially trying to figure out what and where it was going wrong.
"Squeezing performance" and Canvas-rendering don't really go together on a SurfaceView, but you can do okay on many devices.
Grafika's "multi-surface test" Activity features a bouncing circle, rendered in software. I haven't noticed it get slower over time, so I suspect something is wrong in your code. Note Grafika does not subclass SurfaceView, and I generally recommend against doing so -- it's too easy to do the wrong thing. The only valid reason to subclass SurfaceView is if you want to draw on both the Surface and the View, e.g. for some sort of mask effect.
You didn't show any code, so there's not much more we can tell you.
I don't see anything blatantly wrong in the code; seems pretty straightforward. I'd check to make sure OptionsContainer.noteList.length isn't growing without bound. Next step would be to use traceview to figure out which part of the rendering is slow, or just spread System.nanoTime() calls around to identify which part is getting progressively slower. If everything in the method shown is executing at a consistent speed except drawCursor(), move the time-check calls into there, narrowing it down until you find what's draining your performance.
If something is consuming memory quickly enough to cause heap issues, you should see a great deal of GC activity in the logcat output. The DDMS allocation tracker tool can help with that.
Apparently this is a popular topic. Im a beginner, so my problem is probably something fairly trivial. This is a very simple game kind of like Pong. I am using this code to draw my game:
#Override
public void paint(float deltaTime) {
Graphics g = game.getGraphics();
// draw the game elements
if (state == GameState.Running){
g.drawImage(Assets.back, 0, 0);
//g.drawRect(0, 0, g.getWidth(), g.getHeight(), Color.BLACK);
g.saveCanvas();
g.drawTransRect(0, 0, scene.getLine(), g.getHeight());
g.drawImage(Assets.fore, 0, 0);
//g.drawRect(0, 0, g.getWidth(), g.getHeight(), Color.WHITE);
g.restoreCanvas();
for (Pieces p : pieces){
if (p.getType() == true)
g.drawImage(Assets.pos, p.getX(), p.getY());
if (p.getType() == false){
g.drawImage(Assets.neg, p.getX(), p.getY());
}
}
}
If I run the code as-is, the frame rate looks to be about 15FPS? However, use drawRect (commented out above) instead of the drawImage (background and foreground bmps as Assets), my FPS is at least 60. Im assuming this means that there is much more CPU power involved in displaying bmps vs Rects. How can I use my images and maintain a decent framerate?
Thanks.
EDIT:
My drawImage method looks like this, if it helps:
public void drawImage(Image Image, int x, int y) {
canvas.drawBitmap(((AndroidImage)Image).bitmap, x, y, null);
}
This time i'm having an issue with the actual rendering of my model. I can load it all through the Libgdx loadObj() function, and render it using GL10_Triangles, however i keep getting missing triangles in my model (it seems like only half the models are being rendered). I've tried the old ObjLoad function (commented out) and also the different render styles but nothing seems to work.
And yes I have checked the model in Blender and the model is complete without missing faces.
See the print screen below, and the code below that.
Any help would be fantastic, it's very frustrating as i'm so close to getting this to work.
And here's the code.
public class LifeCycle implements ApplicationListener {
Mesh model;
private PerspectiveCamera camera;
public void create() {
InputStream stream = null;
camera = new PerspectiveCamera(45, 4, 4);
try
{
stream = Gdx.files.internal("Hammer/test_hammer.obj").read();
//model = ModelLoaderOld.loadObj(stream);
model = ObjLoader.loadObj(stream,true);
stream.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Gdx.gl.glEnable(GL10.GL_DEPTH_TEST);
Gdx.gl10.glTranslatef(0.0f, 0.0f, -3.0f);
}
protected float rotateZ = 0.1f;
protected float increment = 0.1f;
public void render()
{
Gdx.app.log("LifeCycle", "render()");
Gdx.gl.glClearColor(0.0f, 0.0f, 0.5f, 1.0f);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
camera.update();
camera.apply(Gdx.gl10);
Gdx.gl10.glTranslatef(0.0f, 0.0f, -3.0f);
Gdx.gl10.glRotatef(rotateZ, rotateZ, 5.0f, rotateZ);
model.render(GL10.GL_TRIANGLES);
rotateZ += increment;
System.out.println(""+rotateZ);
}
}
This actually looks like the OBJ file stores quads instead of triangles, but your loading routine just reads them as triangles (just reads the first 3 index groups of a face). Whereas Blender might (and should) be smart enough to handle quads, your loader routine isn't. So either write a better OBJ loader (but I guess this isn't your class), configure your OBJ loader to treat quads correctly (if possible), or export the model as triangles instead of quads (if possible).
I had a small question.If i want to make a man run in android one way of doing this is to get images of the man in different position and display them at different positions.But often,this does not work very well and it appears as two different images are being drawn.Is there any other way through which i can implement custom animation.(Like create a custom image and telling one of the parts of this image to move).
The way i do it is to use sprite sheets for example (Not my graphics!):
You can then use a class like this to handle your animation:
public class AnimSpriteClass {
private Bitmap mAnimation;
private int mXPos;
private int mYPos;
private Rect mSRectangle;
private int mFPS;
private int mNoOfFrames;
private int mCurrentFrame;
private long mFrameTimer;
private int mSpriteHeight;
private int mSpriteWidth;
public AnimSpriteClass() {
mSRectangle = new Rect(0,0,0,0);
mFrameTimer =0;
mCurrentFrame =0;
mXPos = 80;
mYPos = 200;
}
public void Initalise(Bitmap theBitmap, int Height, int Width, int theFPS, int theFrameCount) {
mAnimation = theBitmap;
mSpriteHeight = Height;
mSpriteWidth = Width;
mSRectangle.top = 0;
mSRectangle.bottom = mSpriteHeight;
mSRectangle.left = 0;
mSRectangle.right = mSpriteWidth;
mFPS = 1000 /theFPS;
mNoOfFrames = theFrameCount;
}
public void Update(long GameTime) {
if(GameTime > mFrameTimer + mFPS ) {
mFrameTimer = GameTime;
mCurrentFrame +=1;
if(mCurrentFrame >= mNoOfFrames) {
mCurrentFrame = 0;
}
}
mSRectangle.left = mCurrentFrame * mSpriteWidth;
mSRectangle.right = mSRectangle.left + mSpriteWidth;
}
public void draw(Canvas canvas) {
Rect dest = new Rect(getXPos(), getYPos(), getXPos() + mSpriteWidth,
getYPos() + mSpriteHeight);
canvas.drawBitmap(mAnimation, mSRectangle, dest, null);
}
mAnimation - This is will hold the actual bitmap containing the animation.
mXPos/mYPos - These hold the X and Y screen coordinates for where we want the sprite to be on the screen. These refer to the top left hand corner of the image.
mSRectangle - This is the source rectangle variable and controls which part of the image we are rendering for each frame.
mFPS - This is the number of frames we wish to show per second. 15-20 FPS is enough to fool the human eye into thinking that a still image is moving. However on a mobile platform it’s unlikely you will have enough memory 3 – 10 FPS which is fine for most needs.
mNoOfFrames -This is simply the number of frames in the sprite sheet we are animating.
mCurrentFrame - We need to keep track of the current frame we are rendering so we can move to the next one in order.~
mFrameTimer - This controls how long between frames.
mSpriteHeight/mSpriteWidth -These contain the height and width of an Individual Frame not the entire bitmap and are used to calculate the size of the source rectangle.
Now in order to use this class you have to add a few things to your graphics thread. First declare a new variable of your class and then it can be initialised in the constructor as below.
Animation = new OurAnimatedSpriteClass();
Animation.Initalise(Bitmap.decodeResource(res, R.drawable.stick_man), 62, 39, 20, 20);
In order to pass the value of the bitmap you first have to use the Bitmap Factory class to decode the resource. It decodes a bitmap from your resources folder and allows it to be passed as a variable. The rest of the values depend on your bitmap image.
In order to be able to time the frames correctly you first need to add a Game timer to the game code. You do this by first adding a variable to store the time as show below.
private long mTimer;
We now need this timer to be updated with the correct time every frame so we need to add a line to the run function to do this.
public void run() {
while (mRun) {
Canvas c = null;
mTimer = System.currentTimeMillis(); /////This line updates timer
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
Animation.update(mTimer);
doDraw(c);
}....
then you just have to add Animation.draw(canvas); your Draw function and the animation will draw the current frame in the right place.
When you describe : " one way of doing this is to get images of the man in different position and display them at different positions", this is indeed not only a programming technique to render animation but a general principle that is applied in every form of animation : it applies to making movies, making comics, computer gaming, etc, etc.
Our eyes see at the frequency of 24 images per second. Above 12 frames per second, your brain gets the feeling of real, fluid, movement.
So, yes, this is the way, if you got the feeling movement is not fuild, then you have to increase frame rate. But that works.
Moving only one part of an image is not appropriate for a small sprite representing a man running. Nevertheless, keep this idea in mind for later, when you will be more at ease with animation programming, you will see that this applies to bigger areas that are not entirely drawn at every frame in order to decresase the number of computations needed to "make a frame". Some parts of a whole screen are not "recomputed" every time, this technique is called double buffer and you should soon be introduced to it when making games.
But for now, you should start by making your man run, replacing quickly one picture by another. If movement is not fuild either increase frame rate (optimize your program) or choose images that are closer to each other.
Regards,
Stéphane