I'm trying to get a ball to bounce and roll around inside another ball, ultimately based on the accelerometer. There are countless tutorials out there to detect circle collisions and such, and they are indeed marginally helpful. Unfortunately, none that I have found deal with circle-inside-of-a-circle collision only circles bouncing around in a rectangle view.
Several helpful URLs are, where I got most of this code: http://xiangchen.wordpress.com/2011/12/17/an-android-accelerometer-example/ circle-circle collision
..but again, this isn't quite what I'm after.
I want to get a circle to bounce and roll around inside of another circle. Then, after that, I will want the inner ball to roll down the inside of the outer circle at the right time as the velocity lessens, not simply bounce to the bottom. Am I articulating that clearly? And finally, the bounce angle will need to be adjusted I'm sure so I will ultimately need to figure out how to do that as well.
My code is a mess because I've tried so many things, so in particular, the commented block isn't even close to what it needs to be I don't think. It is just my latest attempt.
Anyone know a little something about this and willing to give me a hand? I would appreciate it.
Edit: This guy is very close to what I'm after, but I am having trouble making sense of it and converting the selected answer into Java. Help? 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);
}
}
}
}
}
}