Bounce ball back android

3.1k views Asked by At

I have take Example of Accelerator with SensorManager, in which canvas(ball) are get update its position as per device Accelerator are rotated. Here is image :

enter image description here

As shown in the image there is a ball and one line. The ball's position is frequently updated, while the line's position is static.

I would like to have the ball bounce back when it touches the line. I have tried since from 3 day, but don't understand how I can do this.

here is my code:

public class ballsensor extends Activity implements SensorEventListener {

    // sensor-related
    private SensorManager mSensorManager;
    private Sensor mAccelerometer;

    // animated view
    private ShapeView mShapeView;

    // screen size
    private int mWidthScreen;
    private int mHeightScreen;

    // motion parameters
    private final float FACTOR_FRICTION = 0.5f; // imaginary friction on the
                                                // screen
    private final float GRAVITY = 9.8f; // acceleration of gravity
    private float mAx; // acceleration along x axis
    private float mAy; // acceleration along y axis
    private final float mDeltaT = 0.5f; // imaginary time interval between each
                                        // acceleration updates

    // timer
    private Timer mTimer;
    private Handler mHandler;
    private boolean isTimerStarted = false;
    private long mStart;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // set the screen always portait
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        // initializing sensors
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        mAccelerometer = mSensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        // obtain screen width and height
        Display display = ((WindowManager) this
                .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        mWidthScreen = display.getWidth();
        mHeightScreen = display.getHeight() - 35;

        // initializing the view that renders the ball
        mShapeView = new ShapeView(this);
        mShapeView.setOvalCenter((int) (mWidthScreen * 0.6),
                (int) (mHeightScreen * 0.6));

        setContentView(mShapeView);

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // obtain the three accelerations from sensors
        mAx = event.values[0];
        mAy = event.values[1];

        float mAz = event.values[2];

        // taking into account the frictions
        mAx = Math.signum(mAx) * Math.abs(mAx)
                * (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
        mAy = Math.signum(mAy) * Math.abs(mAy)
                * (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
    }

    @Override
    protected void onResume() {
        super.onResume();
        // start sensor sensing
        mSensorManager.registerListener(this, mAccelerometer,
                SensorManager.SENSOR_DELAY_NORMAL);

    }

    @Override
    protected void onPause() {
        super.onPause();
        // stop senser sensing
        mSensorManager.unregisterListener(this);
    }

    // the view that renders the ball
    private class ShapeView extends SurfaceView implements
            SurfaceHolder.Callback {

        private final int RADIUS = 30;
        private final float FACTOR_BOUNCEBACK = 0.50f;

        private int mXCenter;
        private int mYCenter;
        private RectF mRectF;
        private final Paint mPaint;
        private ShapeThread mThread;

        private float mVx;
        private float mVy;

        public ShapeView(Context context) {
            super(context);

            getHolder().addCallback(this);
            mThread = new ShapeThread(getHolder(), this);
            setFocusable(true);

            mPaint = new Paint();
            mPaint.setColor(0xFFFFFFFF);
            mPaint.setAlpha(192);
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            mPaint.setAntiAlias(true);

            mRectF = new RectF();
        }

        // set the position of the ball
        public boolean setOvalCenter(int x, int y) {
            mXCenter = x;
            mYCenter = y;
            return true;
        }

        // calculate and update the ball's position
        public boolean updateOvalCenter() {
            mVx -= mAx * mDeltaT;
            mVy += mAy * mDeltaT;

            System.out.println("mVx is ::" + mVx);
            System.out.println("mVy is ::" + mVy);

            mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT));
            mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT));

            if (mXCenter < RADIUS) {
                mXCenter = RADIUS;
                mVx = -mVx * FACTOR_BOUNCEBACK;
            }

            if (mYCenter < RADIUS) {
                mYCenter = RADIUS;
                mVy = -mVy * FACTOR_BOUNCEBACK;
            }
            if (mXCenter > mWidthScreen - RADIUS) {
                mXCenter = mWidthScreen - RADIUS;
                mVx = -mVx * FACTOR_BOUNCEBACK;
            }

            if (mYCenter > mHeightScreen - 2 * RADIUS) {
                mYCenter = mHeightScreen - 2 * RADIUS;
                mVy = -mVy * FACTOR_BOUNCEBACK;
            }

            return true;
        }

        // update the canvas.
        @Override
        protected void onDraw(Canvas canvas) {
            if (mRectF != null) {
                mRectF.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter
                        + RADIUS, mYCenter + RADIUS);
                canvas.drawColor(0XFF000000);
                // canvas.drawOval(mRectF, mPaint);

                Bitmap kangoo = BitmapFactory.decodeResource(getResources(),
                        R.drawable.stripe1);

                Bitmap ball = BitmapFactory.decodeResource(getResources(),
                        R.drawable.blackwhiteball);

                canvas.drawBitmap(ball, mXCenter - RADIUS, mYCenter - RADIUS,
                        mPaint);
                canvas.drawBitmap(kangoo, 130, 10, null);

            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            mThread.setRunning(true);
            mThread.start();
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            boolean retry = true;
            mThread.setRunning(false);
            while (retry) {
                try {
                    mThread.join();
                    retry = false;
                } catch (InterruptedException e) {

                }
            }
        }
    }

    class ShapeThread extends Thread {
        private SurfaceHolder mSurfaceHolder;
        private ShapeView mShapeView;
        private boolean mRun = false;

        public ShapeThread(SurfaceHolder surfaceHolder, ShapeView shapeView) {
            mSurfaceHolder = surfaceHolder;
            mShapeView = shapeView;
        }

        public void setRunning(boolean run) {
            mRun = run;
        }

        public SurfaceHolder getSurfaceHolder() {
            return mSurfaceHolder;
        }

        @Override
        public void run() {
            Canvas c;
            while (mRun) {
                mShapeView.updateOvalCenter();
                c = null;
                try {
                    c = mSurfaceHolder.lockCanvas(null);
                    synchronized (mSurfaceHolder) {
                        mShapeView.onDraw(c);
                    }
                } finally {
                    if (c != null) {
                        mSurfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }
    }
}
3

There are 3 answers

0
Richard Povinelli On

Rather than try to fix your code, work at the design level by developing a software architecture that has two components: physics model and display. The key is to separate the physics of the problem from the display. Modelling the physics becomes much easier when done separately from the display. Likewise the display also becomes easier. Have two separate packages - one for the physics and one for the display.

Start with a simpler version of the problem where the physics world just has a point and a line. Model the point reflecting off the line. You have some code that does this. Just rip it out of the current code. Make sure the physics does what you expect it to without worrying about the display.

Design a class for the ball. The ball has velocity and position properties. It has a move method that updates the position based on the velocity for one time click. The move method checks to see if it has interacted (collided) with the wall and changes the velocity according the physics you want your world to have. The collision detection is done by asking the wall were it is. The physics could be angle of incidence equals angle of reflection, or you could have a spin property on the ball that changes how the ball bounces. The key is that all of the physics modelling is done separately from the display. Similarly, you create a class for the wall. Initially the wall is fixed, but you could add movement to it. The nice thing is that if you've designed the ball class correctly changing the wall to make it move doesn't effect the design of the ball class. Also, none of these changes to the physics effect how the display is done.

Make a display that simply translates the physics into a presentation on the screen.

From there you can add complexity to your model. Make the point a circle. Redo the physics to make it work with this new complexity. The display won't change much, but keep them separate.

I have my CS1 class do versions of this same problem. Two years ago, I had them make a pong game. Last year a version of Centipede. This coming semester they'll have Breakout as a project. When they model the physics separately from the display, they get it working. When they don't, it is usually a muddled mess.

2
Konstantin Pribluda On

Physics modyle shall run in separate thread, and use best available time resolution for position updates. ( milliseconds should be enough ) This is how I design gameloop:

    lastFrameTime = System.currentTimeMillis();

    // as long as we run we move
    while (state == GameState.RUNNING) {
        currentFrame++;
        timeNow = System.currentTimeMillis();

        // sleep until this frame is scheduled
        long l = lastFrameTime + FRAME_DELAY - timeNow;
        updatePositions();
        redraw();       
        if (l > 0L) {
            try {
                Thread.sleep(l);
            }
            catch (Exception exception) {
            }
        } else {
            // something long kept us from updating, reset delays
            lastFrameTime = timeNow;
            l = FRAME_DELAY;
        }

        lastFrameTime = timeNow + l;
        // be polite, let others play
        Thread.yield();
    }

It is important to give up control of the thread, for UI tasks which will be processing events and hive commands to your phyiscs engine.

As for collision detection - it is pretty simple math. Your line is vertical, and zou must just check whether difference in x-coord of line and center is got less than radius - then reverse x-componen of velocity

0
theJosh On

You can use Rect.intersects(Rect, Rect) to detect collisions. Use your bitmap width and height to set up the new Rects.

Here is a dirty example:

import java.util.Timer;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.Display;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

public class ballsensor extends Activity implements SensorEventListener {

// sensor-related
private SensorManager mSensorManager;
private Sensor mAccelerometer;

// animated view
private ShapeView mShapeView;

// screen size
private int mWidthScreen;
private int mHeightScreen;

// motion parameters
private final float FACTOR_FRICTION = 0.5f; // imaginary friction on the
                                            // screen
private final float GRAVITY = 9.8f; // acceleration of gravity
private float mAx; // acceleration along x axis
private float mAy; // acceleration along y axis
private final float mDeltaT = 0.5f; // imaginary time interval between each
                                    // acceleration updates

// timer
private Timer mTimer;
private Handler mHandler;
private final boolean isTimerStarted = false;
private long mStart;

@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // set the screen always portait
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

    // initializing sensors
    mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    mAccelerometer = mSensorManager
            .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

    // obtain screen width and height
    final Display display = ((WindowManager) this
            .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    mWidthScreen = display.getWidth();
    mHeightScreen = display.getHeight() - 35;

    // initializing the view that renders the ball
    mShapeView = new ShapeView(this);
    mShapeView.setOvalCenter((int) (mWidthScreen * 0.6),
            (int) (mHeightScreen * 0.6));

    setContentView(mShapeView);

}

@Override
public void onAccuracyChanged(final Sensor sensor, final int accuracy) {

}

@Override
public void onSensorChanged(final SensorEvent event) {
    // obtain the three accelerations from sensors
    mAx = event.values[0];
    mAy = event.values[1];

    final float mAz = event.values[2];

    // taking into account the frictions
    mAx = Math.signum(mAx) * Math.abs(mAx)
            * (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
    mAy = Math.signum(mAy) * Math.abs(mAy)
            * (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
}

@Override
protected void onResume() {
    super.onResume();
    // start sensor sensing
    mSensorManager.registerListener(this, mAccelerometer,
            SensorManager.SENSOR_DELAY_NORMAL);

}

@Override
protected void onPause() {
    super.onPause();
    // stop senser sensing
    mSensorManager.unregisterListener(this);
}

// the view that renders the ball
private class ShapeView extends SurfaceView implements
        SurfaceHolder.Callback {

    private final int RADIUS = 30;
    private final float FACTOR_BOUNCEBACK = 0.50f;

    private int mXCenter;
    private int mYCenter;
    private final RectF mRectF;
    private final Paint mPaint;
    private final ShapeThread mThread;

    private float mVx;
    private float mVy;
    private final Rect lineRect = new Rect();
    private final Rect ballRect = new Rect();

    public ShapeView(final Context context) {
        super(context);

        getHolder().addCallback(this);
        mThread = new ShapeThread(getHolder(), this);
        setFocusable(true);

        mPaint = new Paint();
        mPaint.setColor(0xFFFFFFFF);
        mPaint.setAlpha(192);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setAntiAlias(true);

        mRectF = new RectF();
    }

    // set the position of the ball
    public boolean setOvalCenter(final int x, final int y) {
        mXCenter = x;
        mYCenter = y;
        return true;
    }

    // calculate and update the ball's position
    public boolean updateOvalCenter() {
        mVx -= mAx * mDeltaT;
        mVy += mAy * mDeltaT;

        System.out.println("mVx is ::" + mVx);
        System.out.println("mVy is ::" + mVy);

        mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT));
        mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT));

        if (mXCenter < RADIUS) {
            mXCenter = RADIUS;
            mVx = -mVx * FACTOR_BOUNCEBACK;
        }

        if (mYCenter < RADIUS) {
            mYCenter = RADIUS;
            mVy = -mVy * FACTOR_BOUNCEBACK;
        }
        if (mXCenter > mWidthScreen - RADIUS) {
            mXCenter = mWidthScreen - RADIUS;
            mVx = -mVx * FACTOR_BOUNCEBACK;
        }

        if (mYCenter > mHeightScreen - 2 * RADIUS) {
            mYCenter = mHeightScreen - 2 * RADIUS;
            mVy = -mVy * FACTOR_BOUNCEBACK;
        }

        if(Rect.intersects(lineRect, ballRect)){
            mVx = -mVx * FACTOR_BOUNCEBACK;
            mVy = -mVy * FACTOR_BOUNCEBACK;
             mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT)) * 5;
             mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT)) * 5;
        }

        return true;
    }

    // update the canvas.
    @Override
    protected void onDraw(final Canvas canvas) {
        if (mRectF != null) {
            mRectF.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter
                    + RADIUS, mYCenter + RADIUS);
            canvas.drawColor(0XFF000000);
            // canvas.drawOval(mRectF, mPaint);

            final Bitmap kangoo = BitmapFactory.decodeResource(getResources(),
                    R.drawable.blankcard);
            lineRect.set(130, 10, 130 + kangoo.getWidth(), 10 + kangoo.getHeight());

            final Bitmap ball = BitmapFactory.decodeResource(getResources(),
                    R.drawable.blankcard);
            ballRect.set(mXCenter - RADIUS,  mYCenter - RADIUS, mXCenter - RADIUS + ball.getWidth(),  mYCenter - RADIUS + ball.getHeight());

            canvas.drawBitmap(ball, mXCenter - RADIUS, mYCenter - RADIUS,
                    mPaint);
            canvas.drawBitmap(kangoo, 130, 10, null);

        }
    }

    @Override
    public void surfaceChanged(final SurfaceHolder holder, final int format, final int width,
            final int height) {
    }

    @Override
    public void surfaceCreated(final SurfaceHolder holder) {
        mThread.setRunning(true);
        mThread.start();
    }

    @Override
    public void surfaceDestroyed(final SurfaceHolder holder) {
        boolean retry = true;
        mThread.setRunning(false);
        while (retry) {
            try {
                mThread.join();
                retry = false;
            } catch (final InterruptedException e) {

            }
        }
    }
}

class ShapeThread extends Thread {
    private final SurfaceHolder mSurfaceHolder;
    private final ShapeView mShapeView;
    private boolean mRun = false;

    public ShapeThread(final SurfaceHolder surfaceHolder, final ShapeView shapeView) {
        mSurfaceHolder = surfaceHolder;
        mShapeView = shapeView;
    }

    public void setRunning(final boolean run) {
        mRun = run;
    }

    public SurfaceHolder getSurfaceHolder() {
        return mSurfaceHolder;
    }

    @Override
    public void run() {
        Canvas c;
        while (mRun) {
            mShapeView.updateOvalCenter();
            c = null;
            try {
                c = mSurfaceHolder.lockCanvas(null);
                synchronized (mSurfaceHolder) {
                    mShapeView.onDraw(c);
                }
            } finally {
                if (c != null) {
                    mSurfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }
}
}

Needs improvement but might get you on the right track.