Here’s a simple diagram of the point and a circle the title is describing about.
As far as I know, when the point hits the circle from the inside, as shown above, the point of collision is registered via a simple collision detection.
Something confuses me is the tangent of the collision point. When I obtain the point of collision’s Cartesian coordinates, use the center of the circle to obtain the vector from it, I only know that I am able to calculate the Theta angle (from X axis to the vector) and Phi angle (from Y axis to the vector) of the collision point to the center of the circle by using Math.atan2(), which I think is not able to help me in finding the normal of the point is moving at.
Here’s how I calculate the ball’s projectory:
- Via some specific way of obtaining values from an accelerometer, I set the values and load them in a Java float array. These values are the acceleration values for X, Y and Z axes of the point in a 3D world. For now, we will focus on a 2D top-down view, with Z positive is up, Y positive is front, and X positive is to the right.
- The acceleration values are then feed into the speed values.
- Then I continuously add the speed values to the position values. In between these, I do not rely on a Time variable.
I was wondering if anyone knows how I should calculate the actual normal vector of the point? Thanks in advance.
Here’s the source code for the point.
Initializations:
public class Cue extends Point {
protected Bitmap bitmap = null;
protected final Rect srcRect = new Rect();
protected RectF dstRect = new RectF();
private boolean jumping = false;
private double radians = 0;
private double radianSpeed = 1.0;
private double multiplier = 0;
private double oldXOffset = 0;
private float[] jumpPositionSpeed = new float[3];
private Paint paint;
private boolean goingOut = false;
public Cue(int w, int h) {
srcRect.set(0, 0, 16, 16);
dstRect.set(0, 0, 16, 16);
this.diameter = (srcRect.right - srcRect.left);
for (int i = 0; i < 2; i++)
jumpPositionSpeed[i] = 0f;
jumping = false;
this.setBoundary(w, h);
paint = new Paint();
paint.setARGB(255, 255, 255, 255);
goingOut = false;
}
Gameticks and rendering:
public void render(Canvas c) {
if (bitmap != null) {
c.drawBitmap(bitmap, srcRect, dstRect, paint);
}
}
public void tick(Level l) {
if (bitmap != null) {
if (!jumping) {
if (l.goalReached){
bind(l.hole);
}
calculate();
move();
}
else
jump();
if (acceleration[2] < 0.0) {
multiplier = 10;
jumping = true;
}
}
else
bitmap = Art.sprites;
}
Bind function: Used for calculating how I should keep the point inside the boundaries I set forth. Ignore the //FIXME comment. I placed it there to remind me in third person, so my PC is like a boss.
//FIXME: If you know the current position and velocity of the object, then you can figure out the coordinates where it would intersect with the circle. You can then obtain the tangent to the circle at that point, or more specifically the normal to the circle at that point. Once you know that, you can calculate the angle to the normal that the point is moving at, and use that to calculate the angle that it will reflect at.
public void bind(Hole h){
double distance = Math.hypot(this.position[0] - h.x + (16+this.diameter)/2, this.position[1] - h.y + (16+this.diameter)/2);
if (distance > (16+this.diameter)/2){
//Clueless from here on out.
}
}
Actions
private void jump() {
//position[2] is never used.
int xOffset = (int) (Math.cos(radians + Math.PI) * multiplier) + 8;
for (int i = 0; i < 2; i++) {
jumpPositionSpeed[i] += acceleration[i];
position[i] -= jumpPositionSpeed[i];
jumpPositionSpeed[i] *= 0.1f;
}
if (position[0] > screenWidth - this.diameter)
position[0] = screenWidth - this.diameter;
if (position[1] > screenHeight - this.diameter)
position[1] = screenHeight - this.diameter;
if (position[0] < 0)
position[0] = 0;
if (position[1] < 0)
position[1] = 0;
dstRect.set(position[0], position[1], position[0] + this.diameter, position[1] + this.diameter);
dstRect.left -= xOffset + oldXOffset;
dstRect.right += xOffset + oldXOffset;
dstRect.top -= xOffset + oldXOffset;
dstRect.bottom += xOffset + oldXOffset;
radians += 0.1 * radianSpeed;
if (radians > 2 * Math.PI) {
multiplier -= 4;
oldXOffset += xOffset - (int) ((Math.cos(radians + Math.PI) * multiplier) + 8);
radians = 0;
radianSpeed += 2;
if (multiplier < 2.0) {
jumping = false;
multiplier = 10;
oldXOffset = 0;
radianSpeed = 1.0;
for (int i = 0; i < 2; i++)
jumpPositionSpeed[i] = 0;
}
}
}
private void move() {
if (position[0] > screenWidth - this.diameter)
position[0] = screenWidth - this.diameter;
if (position[1] > screenHeight - this.diameter)
position[1] = screenHeight - this.diameter;
for (int i = 0; i<= 1; i++)
if (position[i] < 0)
position[i] = 0;
dstRect.set(position[0], position[1], position[0] + this.diameter, position[1] + this.diameter);
}
This function is made to calculate the values from the accelerometer to the screen.
public void calculate(){
for (int i = 0; i < 2; i++) {
speed[i] += acceleration[i];
position[i] -= speed[i];
speed[i] *= 0.1f;
}
}
Misc. functions
public boolean isAlive() {
// There's currently no conditions for the ball to die yet.
return true;
}
public boolean isJumping(){
return jumping;
}
public void stopJumping() {
jumping = false;
multiplier = 10;
oldXOffset = 0;
radians = 0;
radianSpeed = 1.0;
for (int i = 0; i < 2; i++)
jumpPositionSpeed[i] = 0;
}
public void reset(int x, int y){
position[0] = x - 8;
position[1] = y - 8;
speed[0] = 0f;
speed[1] = 0f;
//setGoalFlag(false);
stopJumping();
}
Collision helper functions for other collision detections/responses
public void resolveCollision(Hole h){
float distance = (16 + this.diameter) / 2;
float dx = this.position[0] - h.x + distance;
float dy = this.position[1] - h.y + distance;
double dist = Math.hypot(dx, dy);
double penetration = Math.max(0, (distance - dist));
if (distance > dist) {
this.position[0] += ((float) (penetration * dx)) / (float) (dist * 2);
this.position[1] += ((float) (penetration * dy)) / (float) (dist * 2);
}
}
public void collisionResponse(Hole h){
//Assumes that the cue ball is in the hole.
/*
double angle = Math.atan2(this.position[1] - h.y+(16+this.diameter/2), this.position[0] - h.x+(16+this.diameter/2));
angle = Math.toDegrees(angle);
angle += 90;
angle = Math.toRadians(angle);
double temp = this.speed[0];
this.speed[0] =(float)(this.speed[0] * Math.cos(angle) - this.speed[1] * Math.sin(angle));
this.speed[1] =(float)(this.speed[1] * Math.cos(angle) + temp * Math.sin(angle));
for (int i = 0; i<=1; i++){
position[i] -= speed[i];
}*/
double xVelocity = this.speed[0];
double yVelocity = this.speed[1];
double xDist = this.position[0] - h.x + (16 + this.diameter)/2;
double yDist = this.position[1] - h.y + (16+this.diameter)/2;
double distSquared = xDist * xDist + yDist * yDist;
double dotProduct = xDist * xVelocity + yDist * yVelocity;
if (dotProduct > 0) {
double collisionScale = dotProduct / distSquared;
double xCollision = xDist * collisionScale;
double yCollision = yDist * collisionScale;
//b.speed[0] += xCollision;
//b.speed[1] += yCollision;
this.speed[0] -= xCollision;
this.speed[1] -= yCollision;
}
}
public void calculateResponse(){
for (int i = 0; i<=1; i++){
position[i] -= speed[i];
//speed[i] *= 0.1f;
}
}
}
The code is probably one of the most ugliest ones you seen here, so I apologize. Sorry.