Need help to animate a moving line with onDraw() - JAVA - java

I made a line via the drawLine method that starts from the bottom and goes to half the screen size.
So far so good. Now I wanted to "tilt" the line to the left or right when the screen is pressed.
Something like this in the picture:
And when pressed again have it tilt back to the other side and so on.
The problem I have now is that when I tilt the line, its "trail" is visible, because I call the drawLine method multiple times.
This is my code:
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = MainGamePanel.class.getSimpleName();
private MainThread thread;
private Paint paint;
private GameLine gameLine;
public MainGamePanel(Context context){
super(context);
getHolder().addCallback(this);
setFocusable(true);
paint = new Paint();
paint.setStrokeWidth(50);
paint.setColor(Color.WHITE);
gameLine = new GameLine(getWidth()/2,getHeight(),getWidth()/2, getHeight()/2,paint);
thread = new MainThread(getHolder(), this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
gameLine = new GameLine(getWidth()/2,getHeight(),getWidth()/2, getHeight()/2,paint);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
...}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
gameLine.setTapped(true);
}
return super.onTouchEvent(event);
}
public void render(Canvas canvas) {
gameLine.drawTheLine(canvas);
}
public void update(){
gameLine.update();
}}
public class MainThread extends Thread {
private boolean running;
private MainGamePanel gamePanel;
private SurfaceHolder surfaceHolder;
public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel){
super();
this.gamePanel=gamePanel;
this.surfaceHolder = surfaceHolder;
}
public void setRunning(boolean running){
this.running = running;
}
#Override
public void run(){
Canvas canvas;
while (running){
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder){
this.gamePanel.update();
this.gamePanel.render(canvas);
}
}finally {
if(canvas != null){
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}}
public class GameLine {
private float startX;
private float startY;
private float stopX;
private float stopY;
private Paint paint;
private boolean tapped = false;
public GameLine(float startX, float startY, float stopX, float stopY, Paint paint) {
Normal Constructor
}
public boolean getTapped(){
return tapped;
}
public void setTapped(boolean tapped){
this.tapped = tapped;
}
public void update(){
if(tapped){
this.stopX -=1;
}
}
public void drawTheLine(Canvas canvas){
canvas.drawLine(startX, startY,stopX, stopY,paint);
}}
I'm looking for any ideas. Did I attempt this the wrong way?
How can I make the trail invisible?
I'm grateful for any help. Thank you.

In order to eliminate the trail, you need to throw away the old canvas (to be more precise the old bitmap) and create a new one.
In your example i would suggest:
#Override
public void run(){
Canvas canvas;
while (running){
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder){
this.gamePanel.update();
canvas = gameLine.drawTheLine();
}
}finally {
if(canvas != null){
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
and
public Canvas drawTheLine(){
Canvas canvas = new Canvas();
canvas.drawLine(startX, startY,stopX, stopY,paint);
return canvas;
}

Related

DrawBitmap in Canvas Is Slow

Here is my code Below
public class GameController extends SurfaceView implements SurfaceHolder.Callback {
public static int WIDTH =800;
public static int HEIGHT =399;
private GameThread gameThread;
private ArrayList<Background> backgrounds = new ArrayList<>();
private Bitmap[] bgsrc ;
public GameController(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
bgsrc = new Bitmap[5];
for (int i=0;i<bgsrc.length;i++){
bgsrc[i] = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.mountain_background_v2), i*800,0,800,399);
}
for (int i=bgsrc.length-1;i>=0;i--){
if (i==0 || i==2) continue;
backgrounds.add(new Background(bgsrc[i],-10+i*2 ));
}
//create GameThread Object and start Thread
gameThread = new GameThread(getHolder(),this);
gameThread.setRunning(true);
gameThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
int counter =0;
while(retry && counter<1000){
counter++;
try{
gameThread.setRunning(false);
gameThread.join();retry = false;
gameThread = null;
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public void update(){
for (Background background: backgrounds){
background.update();
}
}
#Override
public void draw(Canvas canvas){
super.draw(canvas);
final float scaleFactorX = getWidth()/(WIDTH*1.f);
final float scaleFactorY = getHeight()/(HEIGHT*1.f);
if (canvas!=null){
final int saveCount = canvas.save();
canvas.scale(scaleFactorX,scaleFactorY);
for (Background background: backgrounds){
background.draw(canvas);
}
canvas.restoreToCount(saveCount);
}
}
}
when I Draw bitmap on Canvas From Arraylist , my drawing very slow where on GameThread Class I initialized FPS(Frame per second) = 60, But After Scaling Canvas its become slow ,I already Tried scaling bitmap before drawing , but still same performance. without array i mean with single Image drawing good, How can i solve this and get exact speed what i want .. plz help me .. Thanks..
The extra calls and calculations in the draw() causes a significant frame rate reduction. I would prefer to do all required scaling of bitmaps once, at the initialization stage, and keep the higher frame rate.

Bitmap doesn't seem to update when I try to rotate it in my canvas

I have an object on my canvas that I need to rotate based on user input. When the user touches the screen, and then moves their finger, the app calculates the angle between the two touch events, and that is the angle I need my Bitmap to face.
Here is a screenshot with a test Bitmap, I need the nose of the plane to face the direction determined by the input, and then update constantly as the input changes.
This GameView class is set at the ContentView for my activity:
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
MainThread thread;
private Player player;
private Point playerPoint;
private float originX;
private float originY;
private float currX;
private float currY;
private float playerAngle;
public GameView(Context context){
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(),this);
setBackgroundColor(getResources().getColor(R.color.background));
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){}
#Override
public void surfaceCreated(SurfaceHolder holder) {
int width = this.getWidth();
int height = this.getHeight();
playerPoint = new Point(width/2, height*3/5);
player = new Player(this.getContext(), playerPoint,
getResources().getColor(R.color.colorAccent));
thread = new MainThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder){
boolean retry = true;
while(retry){
try{
thread.setRunning(false);
thread.join();
}catch(Exception e){
e.printStackTrace();
}
retry = false;
}
}
#Override
public boolean onTouchEvent(MotionEvent event){
if(event.getAction() == MotionEvent.ACTION_DOWN){
//System.out.println("Down");
originX = event.getX();
originY = event.getY();
}
currX = event.getX();
currY = event.getY();
//return super.onTouchEvent(event);
return true;
}
public float getTouchAngle(){
float dX = currX - originX;
float dY = currY - originY;
double angle = Math.atan2(dX,-dY);
angle *= 180;
angle /= Math.PI;
return (float) angle;
}
public void update(){
playerAngle = getTouchAngle();
//System.out.println(playerAngle);
player.update(playerAngle);
}
#Override
public void draw(Canvas canvas){
super.draw(canvas);
player.draw(canvas);
}
}
I have my MainThread which just calls the update and draw methods for the GameView class:
public class MainThread extends Thread {
public static final int MAX_FPS = 30;
private double averageFPS;
private SurfaceHolder holder;
private GameView gameView;
private boolean running;
public static Canvas canvas;
public void setRunning(boolean b){
this.running = b;
}
public MainThread(SurfaceHolder holder, GameView gameView){
super();
this.holder = holder;
this.gameView = gameView;
//canvas = null;
}
#Override
public void run(){
long startTime;
long timeMillis = 1000/MAX_FPS;
long waitTime;
int frameCount = 0;
long totalTime = 0;
long targetTime = 1000/MAX_FPS;
while(running){
startTime = System.nanoTime();
canvas = null;
try{
canvas = this.holder.lockCanvas();
synchronized (holder){
this.gameView.update();
this.gameView.draw(canvas);
}
}catch(Exception e){
e.printStackTrace();
} finally {
if(canvas !=null){
try{
holder.unlockCanvasAndPost(canvas);
}catch(Exception e){
e.printStackTrace();
}
}
}
timeMillis = (System.nanoTime() - startTime/1000000);
waitTime = targetTime - timeMillis;
try{
if(waitTime > 0){
this.sleep(waitTime);
}
}catch(Exception e){
e.printStackTrace();
}
totalTime += System.nanoTime() - startTime;
frameCount++;
if(frameCount == MAX_FPS){
averageFPS = 1000 / ((totalTime/frameCount)/1000000);
frameCount = 0;
totalTime = 0;
//System.out.println(averageFPS);
}
}
}
}
And then I have my Player class, which is where I have been trying to get the Bitmap rotation to update:
public class Player implements GameObject {
//private Rect rect;
private int color;
private Paint paint;
private float angle;
private Bitmap icon;
private Point pos;
//private Matrix matrix;
public Player(Context context, Point pos, int color){
//this.rect = rect;
this.pos = pos;
this.color = color;
paint = new Paint();
//paint.setColor(color);
//paint.setStyle(Paint.Style.FILL);
paint.setFilterBitmap(true);
icon = BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_action_name);
//matrix = new Matrix();
}
#Override
public void draw(Canvas canvas){
//Matrix matrix = new Matrix();
//matrix.setRotate(angle, 100, 100);
//canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.rotate(-angle,pos.x,pos.y);
System.out.println(angle);
canvas.drawBitmap(icon,pos.x-50,pos.y-50,paint);
//canvas.restore();
}
#Override
public void update(){
}
public void update(float angle){
this.angle = angle;
}
}
I originally tried to perform the rotation using a Matrix, and I had it working at first, but then I made changes and haven't been able to get it to rotate at all since.
I can tell that the angle is getting computed properly, and the angle variable in the Player class is getting updated, because after a touch event, if I switch to a different app then go back into this activity, the bitmap is facing the correct direction. It just isn't updating in realtime anymore. I have no clue what I did.
(all the commented lines are me trying to tweak things to fix it)
I found the issue. After changing up my code several times to try and get my canvas to update, I tried commenting out the setBackgroundColor() method in my GameView class, and it suddenly started working again. Now I just set the background color on the Canvas itself and it works as it should. If anyone could explain why, I'd love to hear more

Trying to create a moving sprite on a scrolling background

I got interested on Android, I'm new at it so I created a scrolling background and I want to put a sprite on it I already created a character class and the scroll background. I want to make the sprite move to the right but I'm getting error on my GamePanel.
The error is Non-static method(android.graphics.Canvas) cannot be referenced from a static context, what does it mean and How am I going to fix this and my make the sprite look like running to the right?
Here's my code:
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback {
public static final int WIDTH = 856;
public static final int HEIGHT = 480;
public static int Score =0;
public static int Highscore;
private MainThread thread;
private Background bg;
private Bitmap bmp;
public GamePanel(Context context) {
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while(retry) {
try {
thread.setRunning(false);
thread.join();
}catch(InterruptedException e) {
e.printStackTrace();
retry = false;
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
bg = new Background(BitmapFactory.decodeResource(getResources(), R.drawable.gamebg));
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.deer);
bg.setVector(-5);
thread.setRunning(true);
thread.start();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
public void update() {
Score += 2;
if (Score > Highscore) {
Highscore = Score;
}
bg.update();
}
#SuppressLint("MissingSuperCall")
#Override
public void draw(Canvas canvas) {
final float scaleFactorX = (float)getWidth()/WIDTH;
final float scaleFactorY = (float)getHeight()/HEIGHT;
if(canvas !=null) {
final int savedState = canvas.save();
canvas.scale(scaleFactorX, scaleFactorY);
bg.draw(canvas);
canvas.restoreToCount(savedState);
Paint textpaint = new Paint();
textpaint.setTextSize(30);
canvas.drawText("Score:" +String.valueOf(Score), 0, 32, textpaint);
canvas.drawText("High Score: "+String.valueOf(Highscore), 0, 64, textpaint);
}
}
#Override
protected void onDraw(Canvas canvas) {
Character.onDraw(canvas);
}
}
You cannot access non static methods from a static context. So if you want to access a function or an object of the GamePanel class, you need to call this member by using the object instance of GamePanel class.
Just an example:
wrong:
GamePanel.draw(canvas);
correct:
GamePanel gamePanel = new GamePanel(this);
gamePanel.draw(canvas);

Drawing shapes one after another on Android's Canvas

Sorry if this question was asked before but I looked around and couldn't really find an answer.
So I'm drawing things onto a SurfaceView using a thread to update any changes made to the screen, and I also have an array of rectangles. How can I draw the rectangles, one after another, and by the end of it, have all of them on the screen? I thought a for loop in the run() method where everything's drawn would've worked, but it just ends up drawing them all at once (which I can understand why). Then, I tried to put a Thread.sleep after every draw in the for loop, but it just waits until after all the rects are drawn before drawing to the canvas. (again, makes sense since the canvas isn't unlocked until after the for loop). If I wanted, say, a two second pause between drawing rects, how could I do that?
Thanks
Edit: Here is the SurfaceView. the pause() and resume() methods are called in the onPause and onResume methods of the activity for which it is the view.
class TestView extends SurfaceView implements Runnable{
SurfaceHolder holder;
Thread t;
Rect[] rectArray;
public TestView(Context context) {
super(context);
holder = getHolder();
rectArray = shapes.getRectArray();
}
#Override
public void run() {
while(true){
if(!holder.getSurface().isValid())
continue;
Paint p = new Paint();
p.setColor(Color.BLACK);
p.setStrokeWidth(10)
Canvas c = holder.lockCanvas(null);
c.drawARGB(255, 255, 255, 255);
for(int i = 0; i<rectArray.length; i++){
c.drawRect(i,p);
}
holder.unlockCanvasAndPost(c);
}
}
public void pause(){
}
public void resume(){
t = new Thread(this);
t.start();
}
}
I'd probably abstract the thread and SurfaceView and do a basic "game loop" so I have full control over what gets drawn when.
Below is the game loop part:
public class GameLoopThread extends Thread
{
private TestView view;
private boolean running = false;
public GameLoopThread(TestView view)
{
this.view = view;
}
public void setRunning(boolean run)
{
running = run;
}
#SuppressLint("WrongCall")
#Override
public void run()
{
long startTime = System.currentTimeMillis();
while (running)
{
Canvas c = null;
//call update with the number of milliseconds that have passed since the last loop iteration
view.update(System.currentTimeMillis() - startTime);
startTime = System.currentTimeMillis();
try
{
c = view.getHolder().lockCanvas();
synchronized (view.getHolder())
{
//call onDraw so my surface draws the rectangles
view.onDraw(c);
}
}
finally
{
if (c != null)
{
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
}
Here is my new surface view:
public class TestView extends SurfaceView
{
private long totalElapsedTime = 0;
private Rect[] rectArray;
private int rectCount = 0;
private Paint p;
private GameLoopThread gameLoopThread;
public TestView(Context context)
{
super(context);
rectArray = shapes.getRectArray();
p = new Paint();
p.setColor(Color.BLACK);
p.setStrokeWidth(10)
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback()
{
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry)
{
try
{
gameLoopThread.join();
retry = false;
}
catch (InterruptedException e)
{
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder)
{
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
}
public void update(long elapsedTime)
{
totalElapsedTime += elapsedTime;
//increase the rectCount every second
rectCount = totalElapsedTime / 1000;
if(rectCount > rectArray.length)
{
rectCount = rectArray.length;
}
}
#Override
public void onDraw(Canvas canvas)
{
canvas.drawARGB(255, 255, 255, 255);
//draw my rectangles here
for(int i = 0; i < rectCount; i++)
{
canvas.drawRect(i,p);
}
}

App crush while running a simple ball app [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am trying to launch a very basic app - a ball that should bounce on the screen but I am getting an error while trying to launch app on device.
Here's my code, I may have some errors but please explain to me what are they and how could I fix them, I'm new to android development..
public class Ball {
float x;
float y;
boolean movingRight;
boolean movingBottom;
float screenWidth;
float screenHeight;
public Ball(float x, float y, float screenWidth, float screenHeight) {
this.x = x;
this.y = y;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight - 150;
this.movingBottom = false;
this.movingRight = false;
}
public void move() {
if (movingRight)
this.x++;
else
this.x--;
if (movingBottom)
this.y++;
else
this.y--;
if (this.x >= this.screenWidth)
this.movingRight = false;
else if (this.x <= 1)
this.movingRight = true;
if (this.y >= this.screenHeight)
this.movingBottom = false;
else if (this.y <= 1)
this.movingBottom = true;
}
public float getX(){
return this.x;
}
public float getY() {
return this.y;
}
public class MySurfaceView extends SurfaceView{
private Ball ball;
private SurfaceHolder holder;
Paint paint;
public Paint getBallPaint()
{
if(paint == null)
{
paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(3);
}
return this.paint;
}
public MySurfaceView(Context context) {
super(context);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas c = holder.lockCanvas(null);
draw(c);
holder.unlockCanvasAndPost(c);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
}
#Override
public void draw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
canvas.drawCircle(ball.x,ball.y,15,getBallPaint());
}
}
public class GameLoopThread extends Thread {
private MySurfaceView view;
private boolean running = false;
public GameLoopThread(MySurfaceView view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
#Override
public void run() {
while (running) {
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.draw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
}
public class MainActivity extends ActionBarActivity {
Ball ball;
MySurfaceView mySurfaceView;
GameLoopThread gameLoopThread;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int screenWidth;
screenWidth = getWindowManager().getDefaultDisplay().getWidth();
int screenHeight;
screenHeight = getWindowManager().getDefaultDisplay().getHeight();
ball = new Ball(screenWidth / 2, screenHeight / 2, screenWidth,screenHeight);
gameLoopThread = new GameLoopThread(mySurfaceView);
gameLoopThread.start();
mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
I cannot tell if there are other errors but most obvious one is that you are using mySurfaceView before you have created it.
You should change initialization order to:
mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
gameLoopThread = new GameLoopThread(mySurfaceView);
gameLoopThread.start();

Categories