I got the following problem with drawing an animation on a surfaceview in android:
I use a thread to make the onDraw()-method get called regulary, and i use this:
c.drawBitmap(pic, partOfTheImage, destination, null);
to draw the part of the spritesheet i want to draw.
The problem now is that altough i change "partOfTheImage", the only thing thats drawn is what was drawn first. It doesnt change, no animation.
Here is the code in the thread:
try {
c = getGw().getHolder().lockCanvas();
synchronized (getGw().getHolder()) {
getGw().onDraw(c);
}
} finally {
if (c != null) {
getGw().getHolder().unlockCanvasAndPost(c);
}
}
Does anyone know why my Bitmap isnt updating?
Are you sure it's actually drawing? What does getGw() do? I don't think just calling onDraw() will work. Instead, you should call invalidate() on the view. Invalidate will cause the view to be redrawn at some point in the future. Android has a rich set of animation tools. Have you taken a look at them?
Related
I am trying to draw a Sprite but it just appears for a frame when I first run my program, and then immediately disappears. I am working with a Stage, a Tiled map and two Batches, which I hope is not a problem.
It pretty much looked like the sprite was hiding behind something else, but I made completely sure that nothing was being drawn after it. So after a long time messing around, I found out that deleting the setProjectionMatrix() method "solved" my problem, since the sprite showed perfectly somehow.
I don't understand the reason why this happened at all, and I don't want to just delete the method and have sprites following the camera around, so:
Why or how would a setProjectionMatrix() method "hide" a Sprite? Is it altering the order in which my sprites are drawing? And most importantly, how do I fix it?
Here's my render method:
public void render(float delta) {
Render.cleanScreen(); //Render is a class i made with useful static stuff, like the Batch i am using.
//This method is pretty much just a Gdx.gl.glClearColor() method.
tmr.setView(camera); // tileMapRenderer
tmr.render();
b2dr.render(world, camera.combined); // Box2DDebugRenderer
stage.act();
stage.draw();
Render.batch.begin();
sprite2.draw(Render.batch); //The sprite i want to draw
Render.batch.end();
hudBatch.begin();
sprite1.draw(hudBatch); //This works fine
hudBatch.end();
Render.batch.setProjectionMatrix(camera.combined);
Gdx.input.setInputProcessor(stage);
}
Edit: Someone asked for extra info, so:
• This is what my game looks like with the setProjectionMatrix (after the first frame when the Sprite dissapears) and this is what it looks when i delete it. You can clearly see the red square(the Sprite).
• I am currently using a FitViewport.
Stage.draw() calls apply() on the Stage's Viewport, but never restores the OpenGL Viewport to what it was before. I'm guessing that your stage uses FitViewport, so it is cropping part of the screen. If you want different viewport behavior for stuff you draw outside your stage, you should create a separate viewport for that, and call viewport.apply() before you start rendering the other stuff.
Side note: if you are lazy loading anything that uses native memory (like SpriteBatch and Texture, things implementing Disposable) and storing a reference in a static variable (your Render class), make sure you dispose all of it and null it out in your game's destroy() method. Otherwise, on Android you will be leaking that stuff and it will fail to work when you reopen your game.
Another side note: In my opinion, the Sprite class should not be used unless you have hundreds of them and they aren't moving much (in which case it might have slightly better performance). Sprite is a weird conflation of an asset (a TextureRegion) and game state data. As a result, it can create design issues that you have to work around, such as when you want to animate it or flip it. It is better to create your own GameObject class that references a TextureRegion (a single instance of which can be shared by many elements), and has its own variables for position, color, rotation, etc. Then draw it with the appropriate SpriteBatch.draw method using those properties. In my game, I have a wrapper interface around TextureRegion that can alternately wrap Animation<TextureRegion> so my GameObject class can use either and it can be swapped easily. This makes the code more flexible to change.
So... I just realized my coworker accidentally deleted the viewport.update() method.
#Override
public void resize(int width, int height) {
viewport.update(width, height);
}
Currently I use this code:
public void mouseDragged(MouseEvent e) {
try {
repaint();
getGraphics().drawImage(TreeDrag.obj.getImg(), getMousePosition().x, getMousePosition().y, null);
} catch (HeadlessException | IOException e1) { e1.printStackTrace(); }
}
in my JPanel class. Anyhow the result is this:Gif1
if I remove repaint() method the result is: Gif2
The image to drag is a BufferedImage Object
My question is: How I can do the drag without flickering or snake effects?
This is not how the paint mechanism works in Java. To explain your problem, you need to understand these points:
When you want to paint something permanently, you should override the paintComponent() method of your component to draw the stuff you want. So for your purpose, you should store the new image location in the panel, and call drawImage(image, newLocation.x, newLocation.y) in the panel's paintComponent() method.
When you try to getGraphics() and paint something to it, the stuff you paint will appear immediately, but it will be cleared at next round paint occurs. It is because every round of paint will clear all old painted stuff and ask all component to paint new stuff again.
When you trigger repaint(), you request for a new round of paint, this request is scheduled and coalesce which does not happen immediately, but is guaranteed to happen sometime later. You should always call repaint() instead of calling getGraphics().doSomething(), not to mentioned that getGraphics() may return null if the component is not showing.
This explains why with repaint(), the image will flicker, because your call to getGraphics().drawImage() force it to draw something immediately, then you triggered a new round of paint(), which cleared old content, but you does not provide something new to draw. This caused the image shown then image cleared effect.
But if without repaint(), you forced image at new position to draw immediately, but old content never cleared, caused the snake effect.
I'm developing some kind of videogame, so I am not interested in calling paint, repaint or any sort of those methods on each updating from keylistening, since they call also the update(Graphics g) method which cleans the whole screen. That's why I do want to #Override the update method, not allowing it to cleaning the screen at first. Doing this I can update what I want when I want.
However, sometimes it goes in a loop auto painting the components (such as jButtons) cleaning the screen(I couldn't track anything special happening whenever this occurs, I have already tried overriding some methods in order to catch which one's the troublesome and I couldn't find it, I'm likely missing something). I do not want this happening, because this auto painting cleans the screen which makes me draw everything once again. Moreover I don't feel comfortable with something looping until the player press any key. Do you have any clue? One solution could simply be using a timer with a boolean that each time a screen is completed and the next one is being loaded it calls update(g) from my JFrame (which contains the jPanel). But I would like something better..
Maybe I am doing something wrong, even if I tried to improve my painting methods thousand times surfing throughout the net and netbeans's suggs.
This is how my painting methods looks with some flags written and the ones that are called after running:
#Override public void paint(java.awt.Graphics g){
paintComponents(g);
System.out.println("Flag");
update(g);
}
#Override
public synchronized void update(Graphics g)
{
g.setColor(java.awt.Color.GREEN);
w.getDrawer().draw(g);
g.drawRect (0, 0, w.getActive().getW(), w.getActive().getH());
}`
The overriding on paint(Graphics g) method is not needed at all, just did it in order to see what was going on. I never called repaint() but update(getGraphics()) and it just spam Flags all around. Also thought that maybe I was making it run in a loop with paint and paintComponents, but deleting paintComponents(g) line helps not at all.
Any help would be welcome, since I am trying to make this project "serious". Thanks in advance.
Sergi.
By the way, w.getDrawer().draw(g); is just calling some entities (like 100) with something like g.drawImage(image, locationX, locationY, null) inside. I don't think it has anything to do with my prob.
..this auto painting cleans the screen which makes me draw everything once again.
Draw it to a BufferedImage displayed in a JLabel. When the data (the image) updates call label.repaint().
E.G. as seen in:
This answer to How to draw an image over another image?
This answer to Dynamic Graphics Object Painting.
I'm drawing on a SurfaceView using a thread that has the following calssic run() method:
#Override
public void run() {
while (mRun) {
Canvas canvas = null;
synchronized (mSurfaceHolder) {
try {
canvas = mSurfaceHolder.lockCanvas();
update();
doDraw(canvas);
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
I wanted to save some work if the next frames that are about to be drawn from that moment on, are exactly the same as the previous one, so I skipped the onDraw, but than I got flickering issues due to the double buffering mechanism.
Then I went on and did something like this:
#Override
public void run() {
while (mRun) {
Canvas canvas = null;
synchronized (mSurfaceHolder) {
if (mIsAnimating) { // <---- Look ma! A flag!!!
try {
canvas = mSurfaceHolder.lockCanvas();
update();
doDraw(canvas);
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
}
If I turn off the mIsAnimating flag, thus stopping my run loop from locking the canvas and drawing on it, everything worked like a charm - I did some animation and after it was finished, I could freeze the drawing cycle and keep the animation's last frame on screen.
But then I had to go and see the following from Android docs:
The content of the Surface is never preserved between unlockCanvas()
and lockCanvas(), for this reason, every pixel within the Surface area
must be written. The only exception to this rule is when a dirty
rectangle is specified, in which case, non-dirty pixels will be
preserved.
So... how come my code works?
Your question is a good one. The SurfaceView is a strange animal.
First, Views, in general, don't use cached drawing by default (as far as I know, that is still true), and SurfaceView doesn't do anything to enable it. So I'm reasonably sure there's no caching going on.
What the SurfaceView is doing is cutting a hole in the view hierarchy's Window so that you can see another Window or something drawn directly on the frame buffer behind it. Android has added a non-existent Porter-Duff mode into their PorterDuff.Mode enum to accomplish this weird behavior. Because of this, the normal drawing rules don't apply to SurfaceView. Invalidating or requesting a layout simply cuts the hole again. The view hierarchy doesn't know or care anything about what's actually drawn in that area of the screen, so unless you specifically draw on it some other way (e.g., using the SurfaceHolder to procure a Canvas for that same area on a separate Window), there's a reasonable chance it just sits there untouched.
So what I really think is happening is that Android simply isn't redrawing that portion of the frame buffer. What was drawn there from the Surface stays there while the rest of the view hierarchy is drawn around it. The resources of the Surface very well could be deallocated and you would never know.
I'm curious what happens if you switch to a different Activity or the home screen and come back? My guess is that your last frame will no longer be there. If it is, then SurfaceView is even stranger than I am aware...
Edit: Just a brief note... a Window is basically a Surface with a little extra functionality. My habit of using the term Window interchangeably with Surface is because a Surface is basically ANativeWindow on the native side of things.
I think that one of two things are happening, first (very unlikely) you might be accidentally drawing the last frame of the animation repeatedly, IF NOT, then it means that your SurfaceView is not being invalidated by any change on the view hence keeping cached the last buffer of pixels showed, in order to test it, set a button on the screen and when the SurfaceView gets "frozen" tap on button and make the button itself gone, so you will force a redraw over all the elements on the screen, if the animation dissapears mean that it was actually being cached...
Regards!
You redraw only when your application surface changes. That works well. But - and that's what the Android document is refering to - any other App, Service, Toast or whatever can overwrite parts of your surface for a while. Take a Toast from a Service as example. Your App and your SurfaceView must update that part of the screen when the Toast disappears. But in your code you do not update as long as your flag is not set.
I have searched for quite a few hours and have not been able to find a concise definite andswer to my question. I have an application where I need to draw a sports field (including all pitch lines) to the screen. So far, I have extended the SurfaceView and pretty much copied the rest of the LunarLander demo as well.
All the data the application requires to draw the pitch to the correct dimensions is being received from a socket which works fine too. However, at the minute in the onDraw() function, I am drawing all lines each frame which is causing a fairly slow framerate in the emulator (e.g. ~10fps). Here is my onDraw() function:
#Override
public void onDraw(Canvas canvas) {
canvas.drawARGB(255,0,144,0);
canvas.drawLine(canvas, getFirstLine(), mPaint);
canvas.drawRect(canvas, getFirstRect(), mPaint);
canvas.drawRect(canvas, getSecondRect(), mPaint);
...
canvas.drawRect(canvas, getSecondRect(), mPaint);
drawAnimatedObjects();
}
I then draw circles and different positions over this background. My question is how do I make this more efficient? Is there a way that I can draw the lines at the application initialisation and not have to redraw them every frame?
Thanks for any help.
You should definitely be caching any canvas drawing which will not change to a bitmap at initialization time and then draw that bitmap in onDraw(). That will help render times a lot. Something like:
Bitmap mField = null;
void init()
{
mField = new Bitmap(...dimensions...);
Canvas c = new Canvas(mField);
c.drawRect(...);
...
}
void onDraw(Canvas c)
{
c.drawBitmap(mField);
}
Is your sports field static or does is scroll over the screen?
If it is static, you should consider to build it once and save it as an image which than will be redrawn each time.
Another thing: The emulator is very slow in comparison with modern devices. You should check your performance at least on a G1 or later to verify the real performance.