I wrote a piece of code to move an object in a Random Direction in Java. There are two functions.
FindRandomDirection - Gets a random direction from 8 possible directions (Directions are represented by the numbers 1,2,3,4,6,7,8,9 in a numpad). Check is done to see if the object is near any of the borders. If so the object will move in a direction away from the border.
MoveObject - Changes the (X, Y) co-ordinates of the object by moving by a constant STEP.
But what ever values I give for X, Y; after repeating the process several times (700 or more), the values of X, Y becomes {X: 20-50} and {Y: 450-465}.
I.e.
Case 1: (x:35,y:65) becomes (x:35, y:465)
Case 2: (x:30, y:455) becomes (x:30, y:460)
Case 3: (x:435, y:65) becomes (x:25, y:460)
Case 4: (x:430, y:465) becomes (x:40, y:460)
I would like to why the after several iterations the x, y points converge towards these values even though I generate the numbers at random.
Below is the code for the same.
import java.io.*;
public class bug
{
//public static int x = 35;
//public static int y = 60;
//public static int x = 35;
//public static int y = 460;
//public static int x = 435;
//public static int y = 60;
public static int x = 435;
public static int y = 460;
public static final int NORTH = 8;
public static final int EAST = 6;
public static final int WEST = 4;
public static final int SOUTH = 2;
public static final int NORTHEAST = 9;
public static final int NORTHWEST = 7;
public static final int SOUTHWEST = 1;
public static final int SOUTHEAST = 3;
public static final int STEP = 5;
//Function to move the object in a specified direction.
public static void moveObject(int direction)
{
double nX = 0, nY=0;
switch(direction)
{
case NORTH:
nY = y- STEP;
nX = x;
break;
case SOUTH:
nY = y+ STEP;
nX = x;
break;
case EAST:
nY = y;
nX = x + STEP;
break;
case WEST:
nY = y;
nX = x- STEP;
break;
case NORTHEAST:
nX = x + STEP;
nY = y- STEP;
break;
case NORTHWEST:
nX = x- STEP;
nY = y- STEP;
break;
case SOUTHEAST:
nX = x + STEP;
nY = y+ STEP;
break;
case SOUTHWEST:
nX = x- STEP;
nY = y+ STEP;
break;
}
x = (int) nX;
y = (int) nY;
System.out.println("Direction: "+direction+"; X: "+x+"; Y: "+y);
}
//Function to move the object in a random direction
//Also if wall(Border) is present the object should move in proper direction
public static int findRandomDirection(int objObjectX, int objObjectY)
{
int[] move = {1,2,3,4,0,6,7,8,9};
int randDir=0;
//Generate a random direction to move. Generate new direction if the objected can not be moved in a direction
do
{
java.util.Random randomGenerator = new java.util.Random();
randDir = randomGenerator.nextInt(8);
//If the object lies near East Border, it can not move in that direction
if(objObjectX <= 25)
{
move[0] = 0;
move[3] = 0;
move[6] = 0;
}
//If the object lies near West Border, it can not move in that direction
if(objObjectX >= 465)
{
move[2] = 0;
move[5] = 0;
move[8] = 0;
}
//If the object lies near North Border, it can not move in that direction
if(objObjectY <= 25)
{
move[6] = 0;
move[7] = 0;
move[8] = 0;
}
//If the object lies near South Border, it can not move in that direction
if(objObjectY >= 465)
{
move[0] = 0;
move[1] = 0;
move[2] = 0;
}
} while(move[randDir]==0);
return move[randDir];
}
public static void main(String[] args)
{
for(int i = 0; i<1000;i++)
{
int dir=findRandomDirection(x,y);
moveObject(dir);
}
}
}
So over time, my object moves towards the lower left corner of the board. Kindly help me in finding the bug.
Since you are using nextInt(8), the value returned will always be between 0 and 7 (inclusive). Since 8 is never returned, movement is biased towards the opposite direction. You probably want to use nextInt(9), to return a value between 0 and 8 (inclusive).
edit: To clarify, since "8" is never chosen as your random direction, and moves[8]==9, the object never moves in the NORTHEAST direction, which means over time it will tend to travel SOUTHWEST.
Also, as #Joey said above, you shouldn't reinitialize the Random object every time, but that isn't what was causing the drifting behavior.
When you select a direction, you select a value between 0 and 7 inclusive. These values (in your mapping) correspond to:
public static final int SOUTHWEST = 1;
public static final int SOUTH = 2;
public static final int SOUTHEAST = 3;
public static final int WEST = 4;
public static final int EAST = 6;
public static final int NORTHWEST = 7;
public static final int NORTH = 8;
But note that this will never be chosen:
public static final int NORTHEAST = 9;
So, it's not surprising to me that your code seems biased toward the southeast...
The nextInt(n) method return numbers from zero, inclusive, to n, exclusive. Since you are passing the value 8, the results are from the set of 8 values, 0–7. Thus, the ninth element of the array, which represents the northeast direction, is never selected.
This bias against the northeast results in an eventual migration to the southwest.
Related
I'm brand new to game development and have chosen to start working on a 2D top-down scroller game. I'm using the Slick2D library for this game.
My question is about best practices for taking multiple direction input for sprite movement (UP + RIGHT = Diagonal)
Currently, I have a rather ugly looking if/elseif chain to read in the input from the keyboard that is then checked in the 'Mob' class to determine how the sprite will move. The current setup works just fine, but my question is if there is another, better way of going about taking multiple inputs for diagonals (or any combo of keys, for that matter)
Here is the main class's update method that reads the input (blooper is the instance of 'Mob'):
public void update(GameContainer container, StateBasedGame arg1, int delta) throws SlickException {
Input input = container.getInput();
if(input.isKeyDown(Input.KEY_RIGHT)) { //RIGHT
if(input.isKeyDown(Input.KEY_UP)){ //RIGHT + UP
blooper.direction = 2;
} else if(input.isKeyDown(Input.KEY_DOWN)){ //RIGHT + DOWN
blooper.direction = 3;
}
else {
blooper.direction = 1;
}
} else if(input.isKeyDown(Input.KEY_LEFT)){ //LEFT
if(input.isKeyDown(Input.KEY_UP)){ //LEFT + UP
blooper.direction = 5;
} else if(input.isKeyDown(Input.KEY_DOWN)){ //LEFT + DOWN
blooper.direction = 6;
} else{
blooper.direction = 4;
}
} else if(input.isKeyDown(Input.KEY_UP)){ //UP
if(input.isKeyDown(Input.KEY_RIGHT)){ //UP + RIGHT
blooper.direction = 8;
} else if(input.isKeyDown(Input.KEY_LEFT)){ //UP + LEFT
blooper.direction = 9;
} else{
blooper.direction = 7;
}
} else if(input.isKeyDown(Input.KEY_DOWN)){ //DOWN
if(input.isKeyDown(Input.KEY_RIGHT)){ //DOWN + RIGHT
blooper.direction = 11;
} else if(input.isKeyDown(Input.KEY_LEFT)){ //DOWN + LEFT
blooper.direction = 12;
} else{
blooper.direction = 10;
}
} else{
blooper.direction = -1;
}
blooper.update(delta);
}
And here is how that input is processed in the Mob class:
public class Mob {
private final int RIGHT = 1;
private final int RIGHTUP = 2;
private final int RIGHTDOWN = 3;
private final int LEFT = 4;
private final int LEFTUP = 5;
private final int LEFTDOWN = 6;
private final int UP = 7;
private final int UPRIGHT = 8;
private final int UPLEFT = 9;
private final int DOWN = 10;
private final int DOWNRIGHT = 11;
private final int DOWNLEFT = 12;
private final int IDLE = -1;
int direction = IDLE;
int x, y;
Image sprite;
public Mob() throws SlickException{
x = 20;
y = 20;
sprite = new Image("res/blooper.png");
}
public void update(int delta){
move();
}
public void draw(){
sprite.draw(x, y);
}
public void move(){
switch(direction){
case RIGHT:
x += 1;
break;
case RIGHTUP:
x += 1;
y -= 1;
break;
case RIGHTDOWN:
x += 1;
y += 1;
break;
case LEFT:
x -= 1;
break;
case LEFTUP:
x -= 1;
y -= 1;
break;
case LEFTDOWN:
x -= 1;
y += 1;
break;
case UP:
y -= 1;
break;
case UPRIGHT:
y -= 1;
x += 1;
break;
case UPLEFT:
y -= 1;
x -= 1;
break;
case DOWN:
y += 1;
break;
case DOWNRIGHT:
y += 1;
x += 1;
break;
case DOWNLEFT:
y += 1;
x -= 1;
break;
case IDLE:
//nothing
}
}
}
Like I said...this works, but doesn't seem like the best way to go about it. Any tips?
Make left/right and up/down movement independent. You (or the framework - I'm unfamiliar with it) seems to work with x/y coordinates, which are mathematically independent.
Thus make one function that treats the up/down movement, and one function that treats the left/right movement, and you can get rid of their 'combinations'. Is there a reason why the directions are positive integers? Try making left = -1, right = 1, and no x-direction movement 0, and then do the same for y. That should hopefully make it more intuitive.
Happy coding
Make a blooper.direction a bitwise, like:
blooper.direction=0;
if(input.isKeyDown(Input.KEY_RIGHT)) blooper.direction|=1;
if(input.isKeyDown(Input.KEY_LEFT)) blooper.direction|=2;
if(input.isKeyDown(Input.KEY_UP)) blooper.direction|=4;
if(input.isKeyDown(Input.KEY_DOWN)) blooper.direction|=8;
then in move do the move, like:
if ((blooper.direction&1)!=0) x+=1;
if ((blooper.direction&2)!=0) x-=1;
if ((blooper.direction&4)!=0) y-=1;
if ((blooper.direction&8)!=0) y+=1;
public class Pond {
public static void allcreationco(){
for (int i = 0; i < 100; i++){
int radius = 100;
int x = (int) (Math.random() * 2 * radius - radius);
int ylim = (int) Math.sqrt(radius * radius - x * x);
int y = (int) (Math.random() * 2 * ylim - ylim);
Fish.xfishc.add((int) x);
Fish.yfishc.add((int) y);
}
allcreationdir();
}
public static void allcreationdir(){
for (int i = 0; i < Fish.xfishc.size(); i++){
double radius = Math.random()*1;
double angle = Math.random()*2*Math.PI;
double x = Math.cos(angle)*radius + 0;
if (x > 0){
Fish.xfishcb1.add(true);
}
else {
Fish.xfishcb1.add(false);
}
}
for (int i = 0; i < Fish.yfishc.size(); i++){
double radius = Math.random()*1;
double angle = Math.random()*2*Math.PI;
double x = Math.cos(angle)*radius + 0;
if (x > 0){
Fish.yfishcb1.add(true);
}
else {
Fish.yfishcb1.add(false);
}
}
Hi, my objective is to create a simulation (no visual drawing needed, just something to easily print info about) of a circular pond with fish randomly swimming in it. The code above is a way of initiating 100 hypothetical fish into Arraylists in the form of x and y coordinates based on a hypothetical circle with a radius of 100 (there's gotta be a better way to do this). I would like to have each of the 100 fish be able to swim in random directions and change to new random directions every time they reach the end of the pond. Maybe I'd like them to reproduce after a certain time, or include another fish that moves in straight lines and can eat another fish.
public class Salmon extends Fish {
public static int salmonre = 0;
public static void salmonmove(){
for (int i = 0; i < xfishc.size(); i++){
if (xfishcb1.get(i) == true){
int d = xfishc.get(i);
int e = d + 1;
xfishc.set(i , e);
}
else{
int d = xfishc.get(i);
int e = d - 1;
xfishc.set(i , e);
}
}
for (int i = 0; i < yfishc.size(); i++){
if (yfishcb1.get(i) == true){
int d = yfishc.get(i);
int e = d + 1;
yfishc.set(i , e);
}
else{
int d = yfishc.get(i);
int e = d - 1;
yfishc.set(i , e);
}
}
salmonre++;
}
}
I also used Boolean arraylists to randomly determine what directions the fish are supposed move in. Please be gentle with me in your rhetoric because I'm well aware that my approaches are ridiculous. I know it's best to use trigonometry when simulating objects and their behaviors in a circle, but for some reason, I'm not able to wrap my head around this when looking on the internet (I keep finding things more complicated that involve visual illustrations). Could you "please" give me comprehensive answers with ideas? I'm frustrated.
I wasn't entirely able to figure out how you wanted your Fish class to work based on your code, but some tips:
In Object Oriented programming, you do not want to have a class Fish that has static methods for updating two lists containing X and Y coordinates for all the fish.
Instead, you want an object of class Fish to represent everything about a single fish. You can then have a list of Fish objects.
A pair of booleans is really too coarse for directions. Use a double instead, one for each fish (stored in the Fish instance).
To implement the direction changing behavior, just check whether the next move would move the fish out of the water, and if so, pick a different direction.
Here's a simple, self contained example of the above for two Fish. They start out together and in the same direction, but diverge when they hit the edge and swim in different, random directions:
class Fish {
private double x, y;
private double angle, speed;
public Fish() {
x = y = angle = 0;
speed = 5;
}
void move() {
// If we swim at this angle, this is where we'll end up
double newX = x + Math.cos(angle) * speed;
double newY = y + Math.sin(angle) * speed;
if (isInPond(newX, newY)) {
// That's still in the pond, go there
x = newX;
y = newY;
} else {
// That's outside the pond, change direction
angle = Math.random() * 2 * Math.PI;
}
}
public String toString() {
return String.format(
"Position: %.0f,%.0f. Angle: %.0f degrees.",
x, y, angle * 180/Math.PI);
}
// Check whether some coordinates are within a circular pond with radius 100
static boolean isInPond(double x, double y) {
return Math.sqrt(x*x+y*y) < 100;
}
public static void main(String[] args) throws Exception {
Fish nemo = new Fish();
Fish marlin = new Fish();
while(true) {
nemo.move();
marlin.move();
System.out.println("Nemo: " + nemo);
System.out.println("Marlin: " + marlin);
Thread.sleep(500);
}
}
}
This is about the third time I've asked a question like this. I've already read all the similar questions and the previous help was useful but this time I want to add a new feature to this app. I have an app that makes a ball move in a circle.
Now I want to place the ball at a random location on the screen and then move in a circle. I think I've got the logic mostly correct but the ball jumps around erratically - no matter how much I play with the math. Code is below.
Does anyone know what I'm doing wrong?
public class DrawingTheBall extends View {
Bitmap bball;
int randX, randY;
double theta;
public DrawingTheBall(Context context) {
super(context);
// TODO Auto-generated constructor stub
bball = BitmapFactory.decodeResource(getResources(), R.drawable.blueball);
randX = 1 + (int)(Math.random()*500);
randY = 1 + (int)(Math.random()*500);
theta = 45;
}
public void onDraw(Canvas canvas){
super.onDraw(canvas);
Rect ourRect = new Rect();
ourRect.set(0, 0, canvas.getWidth(), canvas.getHeight()/2);
float a = 50;
float b = 50;
float r = 50;
int x = 0;
int y = 0;
theta = theta + Math.toRadians(2);
Paint blue = new Paint();
blue.setColor(Color.BLUE);
blue.setStyle(Paint.Style.FILL);
canvas.drawRect(ourRect, blue);
if(x < canvas.getWidth()){
x = randX + (int) (a +r*Math.cos(theta));
}else{
x = 0;
}
if(y < canvas.getHeight()){
y = randY + (int) (b +r*Math.sin(theta));
}else{
y = 0;
}
Paint p = new Paint();
canvas.drawBitmap(bball, x, y, p);
invalidate();
}
}
Do you really mean to generate new random values for randX and randY on every pass trough onDraw()? If I understand you right, this bit should be moved in to the constructor:
int randX = 1 + (int)(Math.random()*500);
int randY = 1 + (int)(Math.random()*500);
edit: Also, remove the "int"s as so:
randX = 1 + (int)(Math.random()*500);
randY = 1 + (int)(Math.random()*500);
This way will assign values to your class-level variables instead of creating local variables (which never get read). If that doesn't make sense, here's a clearer explanation:
class foo {
int x = 1; // this is a class-level variable
public foo() {
bar1();
System.out.println(x); // result: 1
bar2();
System.out.println(x); // result: 2
}
public void bar1() {
int x = 2; // This instantiated a new
// local variable "x", it did not
// affect the global variable "x"
}
public void bar2() {
x = 2; // This changed the class var
}
}
The problem I'm trying to solve is building a pyramid centered in the window built with individual bricks. With the code blow I expect the there should be 1 brick on the 1st row, two bricks on the 2nd row, 3 on the 3rd row all the way up to 12 bricks on the base of the pyramid. Instead only 1 brick is appearing in the center of the screen. What could I do to correct my code?
import acm.program.*;
import acm.graphics.*;
public class BrickPyramid extends GraphicsProgram{
public void run() {
/** xBrick, yBrick trying to calculate for the center of the window **/
double xBrick = (getWidth() - BRICK_WIDTH) / 2 ;
double yBrick = (getHeight() - BRICK_HEIGHT) /2 ;
/** getting the size of the brick by multiplying the brick width by brick height **/
double buildingBrick = BRICK_WIDTH * BRICK_HEIGHT;
for (int i = 0 ; i <= 12; i ++ ){
for (int j = 0; j < BRICKS_IN_BASE; j++){
double x = i * buildingBrick;
double y = j * buildingBrick;
/* adding the brick with starting point xbrick, ybrick **/
GRect brick = new GRect (xBrick, yBrick, BRICK_WIDTH, BRICK_HEIGHT);
add(brick);
}
}
}
private static final int BRICK_WIDTH = 100;
private static final int BRICK_HEIGHT = 50;
private static final int BRICKS_IN_BASE = 12;
}
You construct all of your bricks with this line executed many times:
GRect brick = new GRect (xBrick, yBrick, BRICK_WIDTH, BRICK_HEIGHT);
but you never change the values of any of the variables passed to the constructor so the bricks are all drawn in the same place.
You need to change these values for each brick. Maybe use i and j to determine those values?
don't know if you are still looking for an answer but this code does the job:
public class Pyramid extends GraphicsProgram {
private static final int BRICK_WIDTH = 30;
private static final int BRICK_HEIGHT = 12;
public void run() {
double xBrick = (getWidth() - BRICK_WIDTH) / 2 ;
double yBrick = (getHeight() - BRICK_HEIGHT) /2 ;
for (int n=1;n<15;n++){
for (int m=0;m<=n-1;m++){
GRect Brick =new GRect(xBrick-(BRICK_WIDTH*(n-1))/2+m*BRICK_WIDTH,yBrick+BRICK_HEIGHT*(n-1),BRICK_WIDTH,BRICK_HEIGHT);
add(Brick);
}
}
}
}
I'm working on the physics for my GTA2-like game so I can learn more about game physics.
The collision detection and resolution are working great.
I'm now just unsure how to compute the point of contact when I hit a wall.
Here is my OBB class:
public class OBB2D
{
private Vector2D projVec = new Vector2D();
private static Vector2D projAVec = new Vector2D();
private static Vector2D projBVec = new Vector2D();
private static Vector2D tempNormal = new Vector2D();
private Vector2D deltaVec = new Vector2D();
// Corners of the box, where 0 is the lower left.
private Vector2D corner[] = new Vector2D[4];
private Vector2D center = new Vector2D();
private Vector2D extents = new Vector2D();
private RectF boundingRect = new RectF();
private float angle;
//Two edges of the box extended away from corner[0].
private Vector2D axis[] = new Vector2D[2];
private double origin[] = new double[2];
public OBB2D(float centerx, float centery, float w, float h, float angle)
{
for(int i = 0; i < corner.length; ++i)
{
corner[i] = new Vector2D();
}
for(int i = 0; i < axis.length; ++i)
{
axis[i] = new Vector2D();
}
set(centerx,centery,w,h,angle);
}
public OBB2D(float left, float top, float width, float height)
{
for(int i = 0; i < corner.length; ++i)
{
corner[i] = new Vector2D();
}
for(int i = 0; i < axis.length; ++i)
{
axis[i] = new Vector2D();
}
set(left + (width / 2), top + (height / 2),width,height,0.0f);
}
public void set(float centerx,float centery,float w, float h,float angle)
{
float vxx = (float)Math.cos(angle);
float vxy = (float)Math.sin(angle);
float vyx = (float)-Math.sin(angle);
float vyy = (float)Math.cos(angle);
vxx *= w / 2;
vxy *= (w / 2);
vyx *= (h / 2);
vyy *= (h / 2);
corner[0].x = centerx - vxx - vyx;
corner[0].y = centery - vxy - vyy;
corner[1].x = centerx + vxx - vyx;
corner[1].y = centery + vxy - vyy;
corner[2].x = centerx + vxx + vyx;
corner[2].y = centery + vxy + vyy;
corner[3].x = centerx - vxx + vyx;
corner[3].y = centery - vxy + vyy;
this.center.x = centerx;
this.center.y = centery;
this.angle = angle;
computeAxes();
extents.x = w / 2;
extents.y = h / 2;
computeBoundingRect();
}
//Updates the axes after the corners move. Assumes the
//corners actually form a rectangle.
private void computeAxes()
{
axis[0].x = corner[1].x - corner[0].x;
axis[0].y = corner[1].y - corner[0].y;
axis[1].x = corner[3].x - corner[0].x;
axis[1].y = corner[3].y - corner[0].y;
// Make the length of each axis 1/edge length so we know any
// dot product must be less than 1 to fall within the edge.
for (int a = 0; a < axis.length; ++a)
{
float l = axis[a].length();
float ll = l * l;
axis[a].x = axis[a].x / ll;
axis[a].y = axis[a].y / ll;
origin[a] = corner[0].dot(axis[a]);
}
}
public void computeBoundingRect()
{
boundingRect.left = JMath.min(JMath.min(corner[0].x, corner[3].x), JMath.min(corner[1].x, corner[2].x));
boundingRect.top = JMath.min(JMath.min(corner[0].y, corner[1].y),JMath.min(corner[2].y, corner[3].y));
boundingRect.right = JMath.max(JMath.max(corner[1].x, corner[2].x), JMath.max(corner[0].x, corner[3].x));
boundingRect.bottom = JMath.max(JMath.max(corner[2].y, corner[3].y),JMath.max(corner[0].y, corner[1].y));
}
public void set(RectF rect)
{
set(rect.centerX(),rect.centerY(),rect.width(),rect.height(),0.0f);
}
// Returns true if other overlaps one dimension of this.
private boolean overlaps1Way(OBB2D other)
{
for (int a = 0; a < axis.length; ++a) {
double t = other.corner[0].dot(axis[a]);
// Find the extent of box 2 on axis a
double tMin = t;
double tMax = t;
for (int c = 1; c < corner.length; ++c) {
t = other.corner[c].dot(axis[a]);
if (t < tMin) {
tMin = t;
} else if (t > tMax) {
tMax = t;
}
}
// We have to subtract off the origin
// See if [tMin, tMax] intersects [0, 1]
if ((tMin > 1 + origin[a]) || (tMax < origin[a])) {
// There was no intersection along this dimension;
// the boxes cannot possibly overlap.
return false;
}
}
// There was no dimension along which there is no intersection.
// Therefore the boxes overlap.
return true;
}
public void moveTo(float centerx, float centery)
{
float cx,cy;
cx = center.x;
cy = center.y;
deltaVec.x = centerx - cx;
deltaVec.y = centery - cy;
for (int c = 0; c < 4; ++c)
{
corner[c].x += deltaVec.x;
corner[c].y += deltaVec.y;
}
boundingRect.left += deltaVec.x;
boundingRect.top += deltaVec.y;
boundingRect.right += deltaVec.x;
boundingRect.bottom += deltaVec.y;
this.center.x = centerx;
this.center.y = centery;
computeAxes();
}
// Returns true if the intersection of the boxes is non-empty.
public boolean overlaps(OBB2D other)
{
if(right() < other.left())
{
return false;
}
if(bottom() < other.top())
{
return false;
}
if(left() > other.right())
{
return false;
}
if(top() > other.bottom())
{
return false;
}
if(other.getAngle() == 0.0f && getAngle() == 0.0f)
{
return true;
}
return overlaps1Way(other) && other.overlaps1Way(this);
}
public Vector2D getCenter()
{
return center;
}
public float getWidth()
{
return extents.x * 2;
}
public float getHeight()
{
return extents.y * 2;
}
public void setAngle(float angle)
{
set(center.x,center.y,getWidth(),getHeight(),angle);
}
public float getAngle()
{
return angle;
}
public void setSize(float w,float h)
{
set(center.x,center.y,w,h,angle);
}
public float left()
{
return boundingRect.left;
}
public float right()
{
return boundingRect.right;
}
public float bottom()
{
return boundingRect.bottom;
}
public float top()
{
return boundingRect.top;
}
public RectF getBoundingRect()
{
return boundingRect;
}
public boolean overlaps(float left, float top, float right, float bottom)
{
if(right() < left)
{
return false;
}
if(bottom() < top)
{
return false;
}
if(left() > right)
{
return false;
}
if(top() > bottom)
{
return false;
}
return true;
}
public static float distance(float ax, float ay,float bx, float by)
{
if (ax < bx)
return bx - ay;
else
return ax - by;
}
public Vector2D project(float ax, float ay)
{
projVec.x = Float.MAX_VALUE;
projVec.y = Float.MIN_VALUE;
for (int i = 0; i < corner.length; ++i)
{
float dot = Vector2D.dot(corner[i].x,corner[i].y,ax,ay);
projVec.x = JMath.min(dot, projVec.x);
projVec.y = JMath.max(dot, projVec.y);
}
return projVec;
}
public Vector2D getCorner(int c)
{
return corner[c];
}
public int getNumCorners()
{
return corner.length;
}
public static float collisionResponse(OBB2D a, OBB2D b, Vector2D outNormal)
{
float depth = Float.MAX_VALUE;
for (int i = 0; i < a.getNumCorners() + b.getNumCorners(); ++i)
{
Vector2D edgeA;
Vector2D edgeB;
if(i >= a.getNumCorners())
{
edgeA = b.getCorner((i + b.getNumCorners() - 1) % b.getNumCorners());
edgeB = b.getCorner(i % b.getNumCorners());
}
else
{
edgeA = a.getCorner((i + a.getNumCorners() - 1) % a.getNumCorners());
edgeB = a.getCorner(i % a.getNumCorners());
}
tempNormal.x = edgeB.x -edgeA.x;
tempNormal.y = edgeB.y - edgeA.y;
tempNormal.normalize();
projAVec.equals(a.project(tempNormal.x,tempNormal.y));
projBVec.equals(b.project(tempNormal.x,tempNormal.y));
float distance = OBB2D.distance(projAVec.x, projAVec.y,projBVec.x,projBVec.y);
if (distance > 0.0f)
{
return 0.0f;
}
else
{
float d = Math.abs(distance);
if (d < depth)
{
depth = d;
outNormal.equals(tempNormal);
}
}
}
float dx,dy;
dx = b.getCenter().x - a.getCenter().x;
dy = b.getCenter().y - a.getCenter().y;
float dot = Vector2D.dot(dx,dy,outNormal.x,outNormal.y);
if(dot > 0)
{
outNormal.x = -outNormal.x;
outNormal.y = -outNormal.y;
}
return depth;
}
public Vector2D getMoveDeltaVec()
{
return deltaVec;
}
};
I'm now just unsure how to compute the point of contact when I hit a
wall.
You can represent a wall with a simple plane.
The OBB-vs-plane intersection test is the simplest separating axis test of them all:
If two convex objects don't intersect, then there is a plane where
the projection of these two objects will not intersect.
A box intersects plane only if the plane normal forms a separating axis. Compute the projection of the box center and the projected radius (4 dot products and a few adds) and you're good to go (you also get penetration depth for free).
The condition looks as follows:
|d| <= a1|n*A1| + a2|n*A2| + a3|n*A3|
Here:
d distance from the center of the box to the plane.
a1...a3 the extents of the box from the center.
n normal of the plane
A1...A3 the x,y,z-axis of the box
Some pseudocode:
//Test if OBB b intersects plane p
int TestOBBPlane(OBB b, Plane p)
{
// Compute the projection interval radius of b onto L(t) = b.c + t * p.n
float r = b.e[0]*Abs(Dot(p.n, b.u[0])) +
b.e[1]*Abs(Dot(p.n, b.u[1])) +
b.e[2]*Abs(Dot(p.n, b.u[2]));
// Compute distance of box center from plane
float s = Dot(p.n, b.c) – p.d;
// Intersection occurs when distance s falls within [-r,+r] interval
return Abs(s) <= r;
}
The OBB-vs-OBB intersection test is more complicated.
Let us refer to this great tutorial:
In this case we no longer have corresponding separating lines that are
perpendicular to the separating axes. Instead, we have separating
planes that separate the bounding volumes (and they are perpendicular
to their corresponding separating axes).
In 3D space, each OBB only has 3 unique planes extended by its faces,
and the separating planes are parallel to these faces. We are
interested in the separating planes parallel to the faces, but in 3D
space, the faces are not the only concern. We are also interested in
the edges. The separating planes of interest are parallel to the faces
of the boxes, and the separating axes of interest are perpendicular to
the separating planes. Hence the separating axes of interest are
perpendicular to the 3 unique faces of each box. Notice these 6
separating axes of interest correspond to the 6 local (XYZ) axes of
the two boxes.
So there are 9 separating axes to consider for edges collision in
addition to the 6 separating axes we already have found for the faces
collision. This makes the total number of possible separating axes to
consider at 15.
Here are the 15 possible separating axes (L) you will need to test:
CASE 1: L = Ax
CASE 2: L = Ay
CASE 3: L = Az
CASE 4: L = Bx
CASE 5: L = By
CASE 6: L = Bz
CASE 7: L = Ax x Bx
CASE 8: L = Ax x By
CASE 9: L = Ax x Bz
CASE 10: L = Ay x Bx
CASE 11: L = Ay x By
CASE 12: L = Ay x Bz
CASE 13: L = Az x Bx
CASE 14: L = Az x By
CASE 15: L = Az x Bz
Here:
Ax unit vector representing the x-axis of A
Ay unit vector representing the y-axis of A
Az unit vector representing the z-axis of A
Bx unit vector representing the x-axis of B
By unit vector representing the y-axis of B
Bz unit vector representing the z-axis of B
Now you can see the algorithm behind the OBB-OBB intersection test.
Let's jump to the source code:
2D OBB-OBB: http://www.flipcode.com/archives/2D_OBB_Intersection.shtml
3D OBB-OBB: http://www.geometrictools.com/LibMathematics/Intersection/Intersection.html
P.S: This link http://www.realtimerendering.com/intersections.html will be useful to those, who wish to go beyond planes and boxes.