I'm making a tron game and have everything except for the collision. Currently making it through MVC (required as it is an AP Compsci project) and I am having trouble with the collision between the trail and the bike. My idea is to have a point on the racer to which it collides with (the front point). The light trails are drawn with a list of points, the first point being the start point and each point after are added when the racer turns. The last point is the current location of the bike. It draws a line between each point in paintComponent, and also uses a method in the Line class (which contains the arraylist of points) to check collision. So far I have tried to see if the front point is on any of the axis (that is, if the x or y value is equal to any of the corresponding x or y pvalues on the line) and then check the opposite coordinate and check if the x or y is in between the x or y of two points on that axis, and return true or false if they are. Here is what I mean:
public boolean checkPoint(Point p)
{
for(int k = points.size() - 1; k > 0;k -= 2)
{
//if the x's are the same ( vertical ), check if within y's
if(p.getX() == points.get(k).getX() && p.getX() == points.get(k - 1).getX())
{
if(points.get(k).getY() > points.get(k - 1).getY()){
if(p.getY() < points.get(k).getY() && p.getY() > points.get(k-1).getY())
return true;
}
if(points.get(k).getY() < points.get(k - 1).getY()){
if(p.getY() > points.get(k).getY() && p.getY() < points.get(k-1).getY())
return true;
}
}
// if the y's are the same (horizontal), check if within x's
if(p.getY() == points.get(k).getY() && p.getY() == points.get(k - 1).getY())
{
if(points.get(k).getX() > points.get(k - 1).getX()){
if(p.getX() < points.get(k).getX() && p.getX() > points.get(k-1).getX())
return true;
}
if(points.get(k).getX() < points.get(k - 1).getX()){
if(p.getX() > points.get(k).getX() && p.getX() < points.get(k-1).getX())
return true;
}
}
}
}
The game either runs really slowly after a few seconds and works, or doesn't work at all. Am I going in the right direction, how should I go about this?
Related
I am working to solve a problem where I need to determine if a Point lies on a line connecting two other Points. For example, if I have Point a, b, c, I want to determine if c is on the line segment connecting a and b. In my code, I have a point, me (a in the example) and two lists of points, hits (b in the example) and reachable (c in the example). For each point in hits, I want to determine if there is any point in reachable that is on the line segment that connects me and the point in hits. If there is a point on that line segment, then numHits needs to be decremented. Here is my code:
Point me; //Point a from the example above
ArrayList<Point> hits = new ArrayList<>(); //a list of Point b's from the example above
ArrayList<Point> reachable = new ArrayList<>(); //a list of point c's from the example above
for(Point hit : hits) {
for(Point p : reachable) {
if(!hit.equals(p) && !me.equals(p)) {
//find the equation of a line from me to hit
if(hit.x - me.x == 0) { //if the line has an undefined slope... if the line is vertical
if( (((p.y <= hit.y) && (p.y >= me.y)) || ((p.y >= hit.y) && (p.y <= me.y))) && p.x - me.x == 0) { //if there is any occupied point on that line in between me and the hit, that point blocks the hit
numHits--;
break;
}
} else {
//create a line from me to the hit... if there is any occupied point on that line in between me and the hit, that point blocks the hit
double deltaY = hit.y - me.y;
double deltaX = hit.x - me.x;
double m = deltaY / deltaX; //slope
double b = me.y - (double)(m*me.x); //y intercept
if((double) p.y == ((double)(m * p.x) + b)) { //if this point is on the same line
if( ((p.x <= hit.x && p.x >= me.x) && (p.y <= hit.y && p.y >= me.y)) ||
((p.x <= hit.x && p.x >= me.x) && (p.y >= hit.y && p.y <= me.y)) ||
((p.x >= hit.x && p.x <= me.x) && (p.y >= hit.y && p.y <= me.y)) ||
((p.x >= hit.x && p.x <= me.x) && (p.y <= hit.y && p.y >= me.y))) { //if the point is in between me and the hit
numHits--;
break;
}
}
}
}
}
}
My code works to determine if there is any point in reachable between me and each point in hits, it just gets incredibly slow the larger hits and reachable get. For example, if hits has a size of 780,000 and reachable has a size of 1,500,000 the code takes a very long time to run. I was wondering how I may be able to optimize this to run more quickly. I'm not sure if the bottleneck issue lies in the loops themselves or in the code within the loops. Any help or optimization ideas are greatly appreciated. Thank you!
I recommend you look at Line2D.Double. It has many methods including ones to determine if a point or even a line is on a given line segment.
I would also suggest that you use a map to memoize existing points and other information that you may have already encountered. That way you won't keep repeating calculations. Because floating point operations can result in miniscule differences you will probably have to use some acceptable error to determine the validity of the result.
One way you can optimise the code is by shifting the point me to origin, after that divide all points to respective quadrants, so now you only have to compare the points which are in same quadrants.
I will write a rough algorithm below.
1. Shift the point me to origin
2. Shift all other points, ie hits and reachable to origin ( this can be done by subtracting X and Y of point me from all other X and Y )
3. Now divide hits and reachable to 4 quadrants based on their X and Y components, now you will have 4 arrays for hits and 4 arrays for reachable
4. You can also reduce the number of points in reachable by comparing them against the greatest point in hits like below, do this for all quadrants
a. find greatest |X| (mode(X)) and greatest |Y| of hits in each quadrant, lets call it me.|x| and me.|y|
b. for(Point p : reachable) {
if(p.|x| > me.|x| or p.|y| > me.|y|) then remove p from reachable }
since these points will be outside of the line segment formed by me and hits
5. Now you can compare the values, using nested for loops as you did, you will have to use different conditions for each quadrant
Don't forget to add edge conditions and to shift back from origin
Project each line ac to x = 0 or y = 0, whichever is of smaller magnitude (so as to minimize rounding errors). Sort or hash these intercepts, keeping a reference to each c, making sure to preserve duplicates.
Then for each line ab, project that also to x = 0 or y = 0, whichever is of smaller magnitude, and search a matching intercept in your sorted or hashed collection. This gives you a small number of candidate line segments which all lie on the same line through a, b and c; you only need to check that the point lies within the endpoints (a.x < c.x < b.x or a.x > c.x > b.x and again for y).
Be careful about rounding errors.
if(pl.y+pl.height >= a.y && pl.x+pl.width >= a.x+1 && pl.x <= a.x+a.width-1 && pl.y<=a.y) { //TOP
colUP=true;
}
else colUP=false;
if(pl.y <= a.y+a.height && pl.x+pl.width >= a.x+1 && pl.x <= a.x+a.width-1 && pl.y+pl.height>=a.y+a.height) { //BOTTOM
colDOWN=true;
}
else colDOWN=false;
if(pl.x <= a.x+a.width && pl.x+pl.width>a.x+a.width && pl.y+pl.height >= a.y && pl.y <= a.y+a.height){ //RIGHT
colRIGHT=true;
}
else colRIGHT=false;
if(pl.x+pl.width >= a.x && pl.x<a.x && pl.y+pl.height >= a.y && pl.y <= a.y+a.height){ //LEFT
colLEFT=true;
}
else colLEFT=false;
I setup a debug that will tell me which of the 4 Booleans is being set to true, and they don't show that when I put the box 'pl' on top of box 'a' colUP is not equal to true, and they will only come true in weird instances where box 'pl' is colliding with several box 'a's , and the collision for a certain side might be true when it isn't but if colUP is true then colRIGHT is true for some reason. (This code is inside a for loop that goes through an array list of Rectangles and sets the current Rectangle equal to the variable 'a' so that a.x is the box's x position)
You have right logic but you set false for each condition separately. In reality all conditions should be true. So, use one boolean variable - isInRectangle=true; then check all conditions - left,right,top,bottom. If any is not true then isInRectangle=false;
It is simple AND logical operation for all 4 conditions.
this is more of a logic question than syntax but I'm sure you guys have better ways to do it than me anyway. So I have some pieces (JLabels) on a board (11x11 Grid of JPanels on a JPanel) all in a frame. I'm trying to highlight the "possible moves" for these pieces (I.E. how many panels they can cross in a move) very similar to in many online chessgames. I have the logic that checks if movement follows the rules, and it works perfect, I just can't seem to figure out how to find all of those legal moves at once and use them.
This is the code that I use to check if movement is legal:
public void movePieces(MouseEvent me)
{
MouseEvent e = me;
if (turnCount%2 == 1 && pieceCheck[(e.getY()/yInc)][(e.getX()/xInc)])
{
if (playerOne && (logic[(e.getY()/yInc)][(e.getX()/xInc)] == 0 || logic[(e.getY()/yInc)][(e.getX()/xInc)] == 2 || logic[(e.getY()/yInc)][(e.getX()/xInc)] == 4))
{
otherCount = logic[(e.getY()/yInc)][(e.getX()/xInc)];
tempX = e.getX();
tempY = e.getY();
paintPossible(tempX, tempY);
turnCount++;
}
if (!playerOne && (logic[(e.getY()/yInc)][(e.getX()/xInc)] == 1 || logic[(e.getY()/yInc)][(e.getX()/xInc)] == 3 || logic[(e.getY()/yInc)][(e.getX()/xInc)] == 5))
{
otherCount = logic[(e.getY()/yInc)][(e.getX()/xInc)];
tempX = e.getX();
tempY = e.getY();
turnCount++;
}
}
else if ((turnCount%2 == 0 && logic[(e.getY()/yInc)][(e.getX()/xInc)] == -1 && !pieceCheck[(e.getY()/yInc)][(e.getX()/xInc)]) && (Math.abs(tempX - e.getX()) <= xInc && (Math.abs(tempY - e.getY()) <= yInc) || ((Math.abs(tempX - e.getX()) <= 2*xInc) && (Math.abs(tempY - e.getY()) < yInc/4)) || ((Math.abs(tempX - e.getX()) < xInc/4) && (Math.abs(tempY - e.getY()) <= 2*yInc))))
{
panels[(e.getY()/yInc)][(e.getX()/xInc)].add(pieces[otherCount]);
logic[(e.getY()/yInc)][(e.getX()/xInc)] = otherCount;
pieceCheck[(e.getY()/yInc)][(e.getX()/xInc)] = true;
pieceCheck[tempY/yInc][tempX/xInc] = false;
panels[tempY/yInc][tempX/xInc].setBackground(veryDarkGray);
panels[tempY/yInc][tempX/xInc].setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
playerOne = !playerOne;
turnCount++;
}
....
}
Basically the first if-statement and what's inside is for when you first click a piece, and then if it's your turn then it will set that piece as the next in line to put down, and grab the X-Y coordinates of where it is.
The else-if then activates the next time you click due to the rotation of my turnCount, and makes sure there's no piece where you're clicking and that the space is within a certain# of pixels to where your piece is before you move.
I need to take that same checking of validity in where you can move, and apply it in a separate method that will loop through the 2d array of JPanels that is my board and color the valid moves. The method would be called where "paintPossible(tempX, tempY)" is, and in the same spot in the "if" below.
I am currently working on a relatively simple platform game that has an odd bug. You start the game by falling onto the ground (you spawn a few blocks above the ground), but when you land your feet get stuck INSIDE the world and you can't move until you jump. Here's what I mean:
http://i.imgur.com/IKLZY.png
The player's feet are a few pixels below the ground level. However, this problem only occurs in 3 places throughout the map and only in those 3 select places. I'm assuming that the problem lies within my collision detection code but I'm not entirely sure, as I don't get an error when it happens.
public boolean isCollidingWithBlock(Point pt1, Point pt2) {
//Checks x
for(int x = (int) (this.x / Tile.tileSize); x < (int) (this.x / Tile.tileSize + 4); x++) {
//Checks y
for(int y = (int) (this.y / Tile.tileSize); y < (int) (this.y / Tile.tileSize + 4); y++) {
if(x >= 0 && y >= 0 && x < Component.dungeon.block.length && y < Component.dungeon.block[0].length) {
//If the block is not air
if(Component.dungeon.block[x][y].id != Tile.air) {
//If the player is in contact with point one or two on the block
if(Component.dungeon.block[x][y].contains(pt1) || Component.dungeon.block[x][y].contains(pt2)) {
//Checks for specific blocks
if(Component.dungeon.block[x][y].id == Tile.portalBlock) {
Component.isLevelDone = true;
}
if(Component.dungeon.block[x][y].id == Tile.spike) {
Health.health -= 1;
Component.isJumping = true;
if(Health.health == 0) {
Component.isDead = true;
}
}
return true;
}
}
}
}
}
return false;
}
What I'm asking is how I would fix the problem. I've looked over my code for quite a while and I'm not sure what's wrong with it. Also, if there's a more efficient way to do my collision checking then please let me know!
I hope that is enough information, if it's not just tell me what you need and I'll be sure to add it.
Thank you!
The problem probably isn't your collision check, but your logic of what to do on collision. Your character is falling into the block which once in there is always colliding with the block. So it won't be able to jump (since you check for collision when jumping I guess). When you check for collision you have to make sure your character doesn't fall into the block by pre-checking and adjusting.
if (will collide) {
put beside block
}
You're probably doing something like
if (colliding) {
stop moving
}
When putting beside though, you have to check which way you're moving and that you don't move into blocks.
Would like to know which direction player hits terrain tile from (just a simple up/down, left/right). Everything I find is either too simple, or is much more complex and seemingly way too much for what I need, like with AABB (granted it's hard to tell, my brain has trouble digesting what amounts to really long equations). What I've got so far is the result of spending better part of today reading and experimenting:
public int move(double toX, double toY) {
int col = COLLISION_NONE; //these are bit flags, in case I collide with a block to my right as well as below me
double nextX = mX+(toX*main.getDelta()); //delta regulates speed
double nextY = mY+(toY*main.getDelta());
if(mTerrainCollision){
int w = GameView.GameLoop.TILE_WIDTH;
int h = GameView.GameLoop.TILE_HEIGHT;
for(int i = -2; i <= 2; i++) //broad tile picking will be optimized later, better trace around players path
for(int j = -2; j <= 2; j++) {
GameTerrain.Block block = main.mTerrain.get(((int)Math.round(mX)/w)+i,((int)Math.round(mY)/h)+j);
if(block.type != GameTerrain.BLOCK_TYPE_NONE) {
if(nextX+w >= block.x() && mX+w <= block.x()){ //COLLISION ON THE RIGHT?
if(mY+h > block.y() && mY < block.y()+h) { //<THIS is a problem line, see below
nextX = block.x() - w;
xMomentum = 0;
col |= COLLISION_RIGHT;
}
}
else if(nextX < block.x()+w && mX >= block.x()+w){ //COLLISION ON THE LEFT?
if(mY+h > block.y() && mY < block.y()+h) { //same as above, make sure were on the same plane
nextX = block.x() + w;
xMomentum = 0;
col |= COLLISION_LEFT;
}
}
if(nextY+h >= block.y() && mY+h <= block.y()){ //COLLISION ON THE BOTTOM?
if(mX+w > block.x() && mX < block.x()+w) { //make sure were on the same plane
nextY = block.y() - h;
yMomentum = 0;
col |= COLLISION_DOWN;
}
}
else if(nextY < block.y()+h && mY >= block.y()+h){ //COLLISION ON THE TOP?
if(mX+w > block.x() && mX < block.x()+w) { //make sure were on the same plane
nextY = block.y() + h;
yMomentum = 0;
col |= COLLISION_UP;
}
}
}
}
}
mX = nextX;
mY = nextY;
return col;
}
It works... mostly. Player won't phase through blocks even after long sleeps making the delta skyrocket. The collision detection itself works unless the player's previous position (mX/mY) are not on the same plane as the block we're checking (see commented line with "THIS..."). Say we're perfectly diagonal to a block and moving straight for it, player will zip right through. I've been scratching my head for a while now trying to figure out how to go about solving this last issue, preferably without a major rehaul of everything, but if it had to come to that oh well! I'm only interested in simple collision data for things like "Did I touch a floor this frame? Ok I can jump", "Am I touching a wall right now? Ok I can wall jump, but not if I also touched a floor", etc.
Grow the wall's AABB by the size of the object's AABB (keeping the center of the wall's AABB fixed), construct a line segment from the object's before and after positions (use the center of the object's AABB), then do a segment-AABB intersection test.