【问题标题】:Get a ball to roll around inside another circle让球在另一个圆圈内滚动
【发布时间】:2015-03-18 11:04:30
【问题描述】:

我试图让一个球在另一个球内反弹和滚动,最终基于加速度计。那里有无数的教程来检测圆碰撞等,它们确实有点帮助。不幸的是,我发现没有一个可以处理圆内圆碰撞,只有在矩形视图中弹跳的圆。

几个有用的 URL 是我获得大部分代码的地方: 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);
                        }
                    }
                }
            }
        }
    }

【问题讨论】:

    标签: android geometry collision-detection game-physics


    【解决方案1】:

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

    以下是您的代码,对一些内容进行了更改

    我解决了您在发生碰撞时将内圈折回的问题。 基本上,一旦发生碰撞,您需要将内圈向后移动一帧。我认为您尝试过这样做,但是您在碰撞检查之前重置了这些值。我还对速度进行了一点检查,如果它小于 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);
                        }
                    }
                }
            }
        }
    }
    

    添加内圈在外圈内滚动的平滑运动,同时还会弹跳,实现起来会困难得多。正确的方法是让内圈旋转并按照您引用的问题中的说明进行操作。

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

    如果这对您的旅程有所帮助,希望您能对此有所补充。

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2021-06-24
      • 1970-01-01
      • 2019-09-04
      • 2011-03-10
      • 2012-11-12
      • 2013-09-20
      相关资源
      最近更新 更多