让球在另一个圆圈里滚动

时间:2014-08-07 16:34:22

标签: android geometry collision-detection game-physics

我试图让一个球反弹并在另一个球内滚动,最终基于加速度计。有无数的教程可以检测出圆形碰撞等等,它们确实对它们有所帮助。不幸的是,我发现没有任何关于圆形内圈碰撞的圆圈只能在矩形视图中反复出现。

有几个有用的网址,我在这里获得了大部分代码: http://xiangchen.wordpress.com/2011/12/17/an-android-accelerometer-example/ circle-circle collision

..但同样,这不是我之后的事情。

我想让一个圆圈反弹并在另一个圆圈内滚动。然后,在那之后,当速度减小时,我将希望内球在正确的时间滚动到外圆的内部,而不是简单地反弹到底部。我清楚地阐明了这一点吗?最后,反弹角度需要调整我确定,所以我最终还需要弄清楚如何做到这一点。

我的代码很乱,因为我已经尝试了很多东西,所以特别是,评论的版块甚至还没有接近我想不到的东西。这只是我最近的尝试。

任何人都对此有所了解并愿意帮助我吗?我很感激。

编辑:这家伙非常接近我之后的情况,但我无法理解它并将选定的答案转换为Java。救命? https://gamedev.stackexchange.com/questions/29650/circle-inside-circle-collision

    import android.app.Activity;
    import android.content.Context;
    import android.content.pm.ActivityInfo;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.Point;
    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.util.DisplayMetrics;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;

    public class Main extends Activity implements SensorEventListener {

        private SensorManager mSensorManager;
        private Sensor mAccelerometer;

        private ShapeView mShapeView;

        private int mWidthScreen;
        private int mHeightScreen;

        private final float FACTOR_FRICTION = 0.2f; // 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

        private static final float OUTERSTROKE = 5;

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

            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

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

            DisplayMetrics displaymetrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);

            mWidthScreen = displaymetrics.widthPixels;
            mHeightScreen = displaymetrics.heightPixels;

            mShapeView = new ShapeView(this);
            mShapeView.initOvalCenter((int) (mWidthScreen * 0.6), (int) (mHeightScreen * 0.6));

            setContentView(mShapeView);
        }

        @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
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }

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

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

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

            private final int BALLRADIUS = 100;
            private final float FACTOR_BOUNCEBACK = 0.15f;

            private final int OUTERRADIUS = 300;

            private Point ballCenter = new Point();
            private RectF mRectF;
            private final Paint mPaint;
            private ShapeThread mThread;

            private float mVx;
            private float mVy;

            private final Paint outerPaint;
            private RectF outerBounds;
            private Point outerCenter;

            private final double outerDiagonal;

            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);
                mPaint.setAntiAlias(true);

                outerPaint= new Paint();
                outerPaint.setColor(0xFFFFFFFF);
                outerPaint.setAlpha(255);
                outerPaint.setStrokeWidth(OUTERSTROKE);
                outerPaint.setStyle(Paint.Style.STROKE);
                outerPaint.setAntiAlias(true);

                mRectF = new RectF();

                outerDiagonal= Math.pow(BALLRADIUS - OUTERRADIUS, 2);
            }

            public void initOvalCenter(int x, int y) {
                mShapeView.setOvalCenter(x, y);
                outerCenter= new Point(x, y);
                outerBounds = new RectF(x - OUTERRADIUS, y - OUTERRADIUS, x + OUTERRADIUS, y + OUTERRADIUS);
            }

            public boolean setOvalCenter(int x, int y) {
                ballCenter.set(x, y);
                return true;
            }

            public boolean updateOvalCenter() {

/*-------
 * This is where the trouble is, currently.  How do I "snap" the inner circle back into the
 * outer circle?  Or even better, how do I keep it from crossing the line to bring with?
 */
        mVx -= mAx * mDeltaT;
        mVy += mAy * mDeltaT;

        Point newBallCenter = new Point();
        newBallCenter.x = ballCenter.x + (int) (mDeltaT * (mVx + 0.5 * mAx * mDeltaT));
        newBallCenter.y = ballCenter.y + (int) (mDeltaT * (mVy + 0.5 * mAy * mDeltaT));

        double distance = Math.sqrt(Math.pow(newBallCenter.x - outerCenter.x, 2) + Math.pow(newBallCenter.y - outerCenter.y, 2));
        if(distance >= OUTERRADIUS - BALLRADIUS) {
            mVx = -mVx * FACTOR_BOUNCEBACK;
            mVy = -mVy * FACTOR_BOUNCEBACK;

            newBallCenter.x = ballCenter.x + (int) (mDeltaT * (mVx + 0.5 * mAx * mDeltaT));
            newBallCenter.y = ballCenter.y + (int) (mDeltaT * (mVy + 0.5 * mAy * mDeltaT));
        }

        ballCenter.x = newBallCenter.x;
        ballCenter.y = newBallCenter.y;

        return true;
            }

            protected void doDraw(Canvas canvas) {
                if (mRectF != null && canvas != null) {
                    mRectF.set(ballCenter.x - BALLRADIUS, ballCenter.y - BALLRADIUS, ballCenter.x + BALLRADIUS, ballCenter.y + BALLRADIUS);
                    canvas.drawColor(0XFF000000);
                    canvas.drawOval(mRectF, mPaint);

                    canvas.drawOval(outerBounds, outerPaint);
                }
            }

            @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 ignored) { }
                }
            }
        }

        class ShapeThread extends Thread {
            private final 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;
            }

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

1 个答案:

答案 0 :(得分:2)

我想我会回答这个问题至少可以帮助你,即使它没有完全回答你的问题。

以下是您的代码,其中包含一些内容的更改

我修正了你的问题,发生碰撞时将内圈扣回来。 基本上,一旦碰撞发生,你需要将内圈向后移动一帧。我想你试过这样做但是你在碰撞检查之前重置了这些值。我还加了一点检查速度,说它是否小于0.5然后只是将内圈移动到最后一帧没有弹跳,以便在它试图稳定时摆脱弹跳效果。

package com.test.circleincircle;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
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.util.DisplayMetrics;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class Main extends Activity implements SensorEventListener {

    private SensorManager mSensorManager;
    private Sensor mAccelerometer;

    private ShapeView mShapeView;

    private int mWidthScreen;
    private int mHeightScreen;

    private final float FACTOR_FRICTION = 0.2f; // 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
    private int previousInnerX, previousInnerY;
    private static final float OUTERSTROKE = 5;

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

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

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

        DisplayMetrics displaymetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);

        mWidthScreen = displaymetrics.widthPixels;
        mHeightScreen = displaymetrics.heightPixels;

        mShapeView = new ShapeView(this);
        mShapeView.initOvalCenter((int) (mWidthScreen * 0.6), (int) (mHeightScreen * 0.6));

        setContentView(mShapeView);
    }

    @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
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }

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

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

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

        private final int BALLRADIUS = 100;
        private final float FACTOR_BOUNCEBACK = 0.45f;

        private final int OUTERRADIUS = 300;

        private Point ballCenter = new Point();
        private RectF mRectF;
        private final Paint mPaint;
        private ShapeThread mThread;

        private float mVx;
        private float mVy;

        private final Paint outerPaint;
        private RectF outerBounds;
        private Point outerCenter;

        private final double outerDiagonal;

        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);
            mPaint.setAntiAlias(true);

            outerPaint= new Paint();
            outerPaint.setColor(0xFFFFFFFF);
            outerPaint.setAlpha(255);
            outerPaint.setStrokeWidth(OUTERSTROKE);
            outerPaint.setStyle(Paint.Style.STROKE);
            outerPaint.setAntiAlias(true);

            mRectF = new RectF();

            outerDiagonal= Math.pow(BALLRADIUS - OUTERRADIUS, 2);
        }

        public void initOvalCenter(int x, int y) {
            mShapeView.setOvalCenter(x, y);
            outerCenter= new Point(x, y);
            outerBounds = new RectF(x - OUTERRADIUS, y - OUTERRADIUS, x + OUTERRADIUS, y + OUTERRADIUS);
        }

        public boolean setOvalCenter(int x, int y) {
            ballCenter.set(x, y);
            return true;
        }

        public boolean updateOvalCenter() {    




    Point newBallCenter = new Point();
    newBallCenter.x = ballCenter.x + (int) (mDeltaT * (mVx + 0.5 * mAx * mDeltaT));
    newBallCenter.y = ballCenter.y + (int) (mDeltaT * (mVy + 0.5 * mAy * mDeltaT));

    double distance = Math.sqrt(Math.pow(newBallCenter.x - outerCenter.x, 2) + Math.pow(newBallCenter.y - outerCenter.y, 2));
    if(distance >= OUTERRADIUS - BALLRADIUS) {
        mVx = -mVx * FACTOR_BOUNCEBACK;
        mVy = -mVy * FACTOR_BOUNCEBACK;

        if(Math.abs(mVx) > 0.5)
        {
            newBallCenter.x = previousInnerX + (int) (mDeltaT * (mVx + 0.5 * mAx * mDeltaT));
            newBallCenter.y = previousInnerY + (int) (mDeltaT * (mVy + 0.5 * mAy * mDeltaT));
        }
        else
        {
            newBallCenter.x = previousInnerX;
            newBallCenter.y = previousInnerY;   
        }
    }
    else
    {
        mVx -= mAx * mDeltaT;
        mVy += mAy * mDeltaT;
    }
    previousInnerX = ballCenter.x;
    previousInnerY = ballCenter.y;
    ballCenter.x = newBallCenter.x;
    ballCenter.y = newBallCenter.y;

    return true;
        }

        protected void doDraw(Canvas canvas) {
            if (mRectF != null && canvas != null) {
                mRectF.set(ballCenter.x - BALLRADIUS, ballCenter.y - BALLRADIUS, ballCenter.x + BALLRADIUS, ballCenter.y + BALLRADIUS);
                canvas.drawColor(0XFF000000);
                canvas.drawOval(mRectF, mPaint);

                canvas.drawOval(outerBounds, outerPaint);
            }
        }

        @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 ignored) { }
            }
        }
    }

    class ShapeThread extends Thread {
        private final 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;
        }

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

添加在外部滚动的iner圆的平滑运动同时也弹跳将更难以实现。正确的方法是让内圈旋转并遵循你引用的问题的说明。

在对弹跳感到满意之后,也许你可以就该部分提出一个单独的问题。

如果有任何问题,这可能会帮助您完成旅程,希望您能够添加到此。