terrain collision system returning small height values - java

I'm trying to make a simple terrain collision system for my game, but
it's just returning small values ranging from 0.3 - 1, nothing higher or lower, and my character just stays at the same height.
I've been trying to fix this for hours, but I haven't found the solution yet.
Here is the code for collisions:
package terrain;
public float getHeightOfTerrain(float worldx,float worldz)
{
float terrainX = worldx - this.x;
float terrainZ = worldz - this.z;
float gridSquareSize = size - (float)(heights.length -1f);
int gridx = (int)Math.floor(terrainX/gridSquareSize);
int gridz = (int)Math.floor(terrainZ/gridSquareSize);
if(gridx >= heights.length - 1 || gridz >= heights.length -1 || gridx<0||gridz<0)
{
return 0;
}
float xCoord = (terrainX % gridSquareSize)/gridSquareSize;
float zCoord = (terrainZ % gridSquareSize)/gridSquareSize;
float answer;
if (xCoord <= (1-zCoord))
{
answer = Mathc
.barryCentric(new Vector3f(0, heights[gridx][gridz], 0), new Vector3f(1,
heights[gridx + 1][gridz], 0), new Vector3f(0,
heights[gridx][gridz + 1], 1), new Vector2f(xCoord, zCoord));
} else
{
answer = Mathc
.barryCentric(new Vector3f(1, heights[gridx + 1][gridz], 0), new Vector3f(1,
heights[gridx + 1][gridz + 1], 1), new Vector3f(0,
heights[gridx][gridz + 1], 1), new Vector2f(xCoord, zCoord));
}
return answer;
}
private RawModel generateTerrain(Loader loader,String heightMap)
{
BufferedImage image = null;
try
{
image = ImageIO.read(new File("res/"+heightMap+".png"));
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
int vertex_count = image.getHeight();
heights = new float[vertex_count][vertex_count];
int count = vertex_count * vertex_count;
float[] vertices = new float[count * 3];
float[] normals = new float[count * 3];
float[] textureCoords = new float[count*2];
int[] indices = new int[6*(vertex_count-1)*(vertex_count-1)];
int vertexPointer = 0;
for(int i=0;i<vertex_count;i++){
for(int j=0;j<vertex_count;j++){
vertices[vertexPointer*3] = (float)j/((float)vertex_count - 1) * size;
float height = getheight(j,i,image);
heights[j][i] = height;
vertices[vertexPointer*3+1] = height;
vertices[vertexPointer*3+2] = (float)i/((float)vertex_count - 1) * size;
Vector3f normal = calculateNormal(j,i,image);
normals[vertexPointer*3] = normal.x;
normals[vertexPointer*3+1] = normal.y;
normals[vertexPointer*3+2] = normal.z;
textureCoords[vertexPointer*2] = (float)j/((float)vertex_count - 1);
textureCoords[vertexPointer*2+1] = (float)i/((float)vertex_count - 1);
vertexPointer++;
}
}
int pointer = 0;
for(int gz=0;gz<vertex_count-1;gz++){
for(int gx=0;gx<vertex_count-1;gx++){
int topLeft = (gz*vertex_count)+gx;
int topRight = topLeft + 1;
int bottomLeft = ((gz+1)*vertex_count)+gx;
int bottomRight = bottomLeft + 1;
indices[pointer++] = topLeft;
indices[pointer++] = bottomLeft;
indices[pointer++] = topRight;
indices[pointer++] = topRight;
indices[pointer++] = bottomLeft;
indices[pointer++] = bottomRight;
}
}
return loader.loadToVAO(vertices, textureCoords, normals, indices);
}
private float getheight(int x,int z,BufferedImage image)
{
if(x<=0 || x>= image.getHeight()||z<=0||z>=image.getHeight())
{
return 0;
}
float height = image.getRGB(x,z);
height += maxPixelColor/2f;
height /= maxPixelColor/2f;
height *= maxHeight;
return height;
}
}

Related

Android canvas. How to set circles at random position?

Below code generated and save position x y to HashMap and check collision two circles;
HashMap<Integer, Float> posX = new HashMap<>();
HashMap<Integer, Float> posY = new HashMap<>();
int numberOfCircle = 8;
for(int i=0; i < numberOfCircle; i ++){
// boolean flag = false;
while (true){
float x =random.nextInt(width - raduis/2) + raduis/2f;
float y =random.nextInt(height - raduis/2) + raduis/2f;
if(!posX.containsValue(x) && !posY.containsValue(y)){
if(i == 0){
posX.put(i, x);
posY.put(i, y);
break;
}
if(i > 0){
double distance = Math.sqrt(((posX.get(i - 1) - x) * (posX.get(i - 1) - x)) + ((posY.get(i - 1) - y) * ( posY.get(i - 1) - y)));
if (distance > raduis+raduis) {
posX.put(i, x);
posY.put(i, y);
Log.d(TAG, i + " xPos=" + posX.get(i) + " yPos=" + posY.get(i) + " distance=" + distance);
break;
}
if(numberOfCircle == posX.size()) break;
}
}
}
}
This code work only if circle count=2; But when circle count > 2 i have collision; How to check current generated position for each in HashMap?
For example:
xPos = {5, 10, 3}
yPos = {10, 33, 5}
generated position x=6, y=10;
calculate distance between x=6, y=10 with all positions in Map. If distance < radius+radius generate new position while distance > radius+radius;
Update ========================>
My code work like
I want like this
output: distance equal between current generated position(X, Y) and previous position(X, Y). I want to check between current generated x, y with all added positons in HashMap.
D/DEBUG DATA ===>: 1 xPos=432.0 yPos=411.0 distance=390.6430595825299
D/DEBUG DATA ===>: 2 xPos=316.0 yPos=666.0 distance=280.1446055165082
D/DEBUG DATA ===>: 3 xPos=244.0 yPos=83.0 distance=587.4291446634223
D/DEBUG DATA ===>: 4 xPos=214.0 yPos=551.0 distance=468.96055271205915
D/DEBUG DATA ===>: 5 xPos=76.0 yPos=1011.0 distance=480.2540994098853
D/DEBUG DATA ===>: 6 xPos=289.0 yPos=868.0 distance=256.55019002136794
D/DEBUG DATA ===>: 7 xPos=494.0 yPos=988.0 distance=237.53947040439405
P.s Sorry so poor English.
Maybe something closer to this will work? I'm not entirely sure if it's what you want or if it will help, but I did a quick rewrite for clarity.
HashMap<Integer, Integer> posX = new HashMap<>();
HashMap<Integer, Integer> posY = new HashMap<>();
final int circlesToPlace = 8;
for(int i = 0 ; i < circlesToPlace ; i++){
// boolean flag = false;
while (true){
final int x = ThreadLocalRandom.current().nextInt((radius/2f), width + 1);
final int y = ThreadLocalRandom.current().nextInt((radius/2f), height + 1);
// Iterate over all other positions to ensure no circle intersects with
// the new circle.
for (int index = 0 ; index < posX.size() ; index++) {
// Calculate distance where d = sqrt((x2 - x1)^2 + (y2 - y1)^2)
final int otherX = posX.get(index);
final int otherY = posY.get(index);
int differenceX = otherX - x;
differenceX *= differenceX;
int differenceY = otherY - y;
differenceY *= differenceY;
final double distance = Math.sqrt(differenceX + differenceY);
if (distance > (radius * 2)) {
posX.put(i, x);
posY.put(i, y);
Log.d(TAG, i + " xPos=" + posX.get(i) + " yPos=" + posY.get(i) + " distance=" + distance);
break;
}
}
}
}
My variant. You don't need to do sqrt to compare distance, you can instead square the constant (d2 in my case).
Random random = new Random();
int numberOfCircle = 8, width = 400, height = 300;
int diameter = 51;
final float radius = diameter * 0.5f;
final float d2 = diameter * diameter;
List<Float> posX = new ArrayList<>(numberOfCircle);
List<Float> posY = new ArrayList<>(numberOfCircle);
while (posX.size() < numberOfCircle) { // till enough generated
// generate new coordinates
float x = random.nextInt(width - diameter) + radius;
float y = random.nextInt(height - diameter) + radius;
System.out.printf("Generated [%3.3f, %3.3f] ... ", x, y);
// verify it does not overlap/touch with previous circles
int j = 0;
while (j < posX.size()) {
float dx = posX.get(j) - x, dy = posY.get(j) - y;
float diffSquare = (dx * dx) + (dy * dy);
if (diffSquare <= d2) break;
++j;
}
// generate another pair of coordinates, if it does touch previous
if (j != posX.size()) {
System.out.println("collided.");
continue;
}
System.out.println("added.");
// not overlapping/touch, add as new circle
posX.add(x);
posY.add(y);
} // while (posX.size() < numberOfCircle)
I resolve like this
private class MyView extends View implements View.OnTouchListener{
List<Circle> randomCircles = new ArrayList<>();
int radius = new Circle().getRadius();
int randomCircleCount = 3;
Random random = new Random();
public MyView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Integer width = canvas.getWidth();
Integer width = canvas.getWidth();
Integer height = canvas.getHeight() - (radius);
// Integer height = canvas.getHeight()/2 + canvas.getHeight();
///randomCircles.clear();
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLACK);
canvas.drawPaint(paint);
Paint paintT = new Paint();
paintT.setTextSize(18f);
paintT.setAntiAlias(true);
paintT.setTextAlign(Paint.Align.CENTER);
while(randomCircles.size() < randomCircleCount){
randomCircle(width, height);
}
for(int i=0; i < randomCircleCount; i ++){
Circle circle = randomCircles.get(i);
float curPosX = randomCircles.get(i).getCx().floatValue();
float curPosY = randomCircles.get(i).getCy().floatValue();
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
paint.setARGB(175, 77, 2, 200);
//if(r != 0 && g != 0 && b != 0) paint.setARGB(255, r, g, b);
canvas.drawCircle(curPosX, curPosY, radius, paint);
Rect bounds = new Rect();
String text = "" +i;
paintT.getTextBounds(text, 0, text.length(), bounds);
paint.setAntiAlias(true);
canvas.drawText(text, curPosX, curPosY, paintT);
circle.update(width, height);
//Log.d(TAG, "REDRAW");
}
///invalidate();
postInvalidateDelayed(100);
}
private void randomCircle(int width, int height) {
double x = getPosX(width, radius);
double y = getPosY(height,radius);
boolean hit = false;
for (int i = 0; i < randomCircles.size(); i++) {
Circle circle = randomCircles.get(i);
double dx = circle.getCx() - x;
double dy = circle.getCy() - y;
int r = circle.getRadius() + radius;
if (dx * dx + dy * dy <= r * r) {
Log.d(TAG, "dx=" + dx + " dy=" + dy);
hit = true;
}
}
if (!hit) {
Log.d(TAG, "Here!!!!!");
randomCircles.add(new Circle(x, y));
}
}
private Float getPosX(Integer width, Integer radius){
return random.nextInt(width - radius/2) + radius/2f;
}
private Float getPosY(Integer height, Integer radius){
return random.nextInt(height - radius/2) + radius/2f;
}

Dynamic drawing on canvas taking too long

I wanted to draw a grid of quads with an animated colour property but ended up with code that is too slow just to create a basic structure of an image, not to mention to animate it. Also, the image is drawing, not as it finished to compute, but in the process by parts. How can I fix it? Here's onDraw(Canvas canvas) method:
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
final int height = canvas.getHeight();
final int width = canvas.getWidth();
Bitmap.Config conf = Bitmap.Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(width, height, conf);
new Thread(() -> {
int cells_amount = 0;
float cells_vertical = 0;
float cells_horizontal = 0;
cells_vertical = (float) height / CELL_SIZE;
int y = 0;
if (cells_vertical % 1 > 0) {
y = Math.round(CELL_SIZE * -(cells_vertical % 1));
cells_vertical = (int) (cells_vertical + 1);
}
cells_horizontal = (float) width / CELL_SIZE;
int x = 0;
if (cells_horizontal % 1 > 0) {
x = Math.round(CELL_SIZE * -(cells_horizontal % 1));
cells_horizontal = (int) (cells_horizontal + 1);
}
cells_amount = (int) (cells_horizontal * cells_vertical);
Canvas c = new Canvas(bitmap);
c.drawColor(Color.BLACK);
int x_preserved = x;
Paint textPaint = new Paint();
textPaint.setColor(Color.parseColor("#EEEEEE"));
for (int i = 0; i < cells_amount; i++) {
Rect rect = new Rect(x, y, x + CELL_SIZE, y + CELL_SIZE);
Paint framePaint = new Paint();
framePaint.setColor(Color.parseColor("#1F1F1F"));
framePaint.setStyle(Paint.Style.STROKE);
framePaint.setStrokeWidth(1);
c.drawRect(rect, framePaint);
if (random.nextBoolean()) {
String output = String.format(Locale.getDefault(), "%d", "+");
int cx = (int) (x + (CELL_SIZE / 2) - (textPaint.measureText(output) / 2));
int cy = (int) ((y + (CELL_SIZE / 2)) - ((textPaint.descent() + textPaint.ascent()) / 2));
c.drawText(output, cx, cy, textPaint);
}
if (i % cells_horizontal == 0 && i >= cells_horizontal) {
y += CELL_SIZE;
x = x_preserved;
} else {
x += CELL_SIZE;
}
}
}).start();
canvas.drawBitmap(bitmap, 0, 0, null);
}

libGDX - My script takes a really long time to process my copy method

So right now I am copying tiles that are within the camera's viewport, and pasting them to the top of the TiledMap using the following function:
public TiledMapTile[][] copyRegion(TiledMapTileLayer layer, int x, int y, int width, int height) {
TiledMapTile[][] region = new TiledMapTile[width][height];
for (int ix = x; ix < x + width; ix++)
for (int iy = y; iy < y + height; iy++) {
Cell cell = layer.getCell(ix, iy);
if(cell == null)
continue;
region[ix - x][iy - y] = cell.getTile();
}
return region;
}
My problem is with this double for loop, its searching through each x and y value of the viewport, which is thousands of loops which is obviously my problem. Before this I was setting the width and height to the TiledMapTileLayer's layer.getTileHeight and layer.getTileWidth and it ran smoothly. This is because the layers x and y values go by the Tiles, so it was only really checking about 100 tiles, which is much faster. How would I go about "merging" the 2 together to only copy the tiles within the viewport, instead of the x and y values within the viewport?
I am calling this function using the following:
clipboard = TileMapCopier.copyRegion(layer, 0, 0, (int) camera.viewportWidth, (int) camera.viewportHeight);
TileMapCopier.pasteRegion(layer, clipboard, 0, layer.getHeight() / 2);
Thanks!
EDIT: I was told to look at how the orthogonal tiled map renderer renders based on viewport. I dont really know how to do this :P Im still new to this.
#Override
public void renderTileLayer (TiledMapTileLayer layer) {
final Color batchColor = spriteBatch.getColor();
final float color = Color.toFloatBits(batchColor.r, batchColor.g, batchColor.b, batchColor.a * layer.getOpacity());
final int layerWidth = layer.getWidth();
final int layerHeight = layer.getHeight();
final float layerTileWidth = layer.getTileWidth() * unitScale;
final float layerTileHeight = layer.getTileHeight() * unitScale;
final int col1 = Math.max(0, (int)(viewBounds.x / layerTileWidth));
final int col2 = Math.min(layerWidth, (int)((viewBounds.x + viewBounds.width + layerTileWidth) / layerTileWidth));
final int row1 = Math.max(0, (int)(viewBounds.y / layerTileHeight));
final int row2 = Math.min(layerHeight, (int)((viewBounds.y + viewBounds.height + layerTileHeight) / layerTileHeight));
float y = row1 * layerTileHeight;
float xStart = col1 * layerTileWidth;
final float[] vertices = this.vertices;
for (int row = row1; row < row2; row++) {
float x = xStart;
for (int col = col1; col < col2; col++) {
final TiledMapTileLayer.Cell cell = layer.getCell(col, row);
if (cell == null) {
x += layerTileWidth;
continue;
}
final TiledMapTile tile = cell.getTile();
if (tile != null) {
final boolean flipX = cell.getFlipHorizontally();
final boolean flipY = cell.getFlipVertically();
final int rotations = cell.getRotation();
TextureRegion region = tile.getTextureRegion();
float x1 = x;
float y1 = y;
float x2 = x1 + region.getRegionWidth() * unitScale;
float y2 = y1 + region.getRegionHeight() * unitScale;
float u1 = region.getU();
float v1 = region.getV2();
float u2 = region.getU2();
float v2 = region.getV();
vertices[X1] = x1;
vertices[Y1] = y1;
vertices[C1] = color;
vertices[U1] = u1;
vertices[V1] = v1;
vertices[X2] = x1;
vertices[Y2] = y2;
vertices[C2] = color;
vertices[U2] = u1;
vertices[V2] = v2;
vertices[X3] = x2;
vertices[Y3] = y2;
vertices[C3] = color;
vertices[U3] = u2;
vertices[V3] = v2;
vertices[X4] = x2;
vertices[Y4] = y1;
vertices[C4] = color;
vertices[U4] = u2;
vertices[V4] = v1;
if (flipX) {
float temp = vertices[U1];
vertices[U1] = vertices[U3];
vertices[U3] = temp;
temp = vertices[U2];
vertices[U2] = vertices[U4];
vertices[U4] = temp;
}
if (flipY) {
float temp = vertices[V1];
vertices[V1] = vertices[V3];
vertices[V3] = temp;
temp = vertices[V2];
vertices[V2] = vertices[V4];
vertices[V4] = temp;
}
if (rotations != 0) {
switch (rotations) {
case Cell.ROTATE_90: {
float tempV = vertices[V1];
vertices[V1] = vertices[V2];
vertices[V2] = vertices[V3];
vertices[V3] = vertices[V4];
vertices[V4] = tempV;
float tempU = vertices[U1];
vertices[U1] = vertices[U2];
vertices[U2] = vertices[U3];
vertices[U3] = vertices[U4];
vertices[U4] = tempU;
break;
}
case Cell.ROTATE_180: {
float tempU = vertices[U1];
vertices[U1] = vertices[U3];
vertices[U3] = tempU;
tempU = vertices[U2];
vertices[U2] = vertices[U4];
vertices[U4] = tempU;
float tempV = vertices[V1];
vertices[V1] = vertices[V3];
vertices[V3] = tempV;
tempV = vertices[V2];
vertices[V2] = vertices[V4];
vertices[V4] = tempV;
break;
}
case Cell.ROTATE_270: {
float tempV = vertices[V1];
vertices[V1] = vertices[V4];
vertices[V4] = vertices[V3];
vertices[V3] = vertices[V2];
vertices[V2] = tempV;
float tempU = vertices[U1];
vertices[U1] = vertices[U4];
vertices[U4] = vertices[U3];
vertices[U3] = vertices[U2];
vertices[U2] = tempU;
break;
}
}
}
spriteBatch.draw(region.getTexture(), vertices, 0, 20);
}
x += layerTileWidth;
}
y += layerTileHeight;
}
}
}

Collision Detection in a Tile Based Platformer

I have a set of methods in my platformer in progress to detect and resolve collisions between entities and the tilemap, but they are doing a bad job of it.
The bottom method is the one being called, like this:
player.velocity = player.velocity.add(getFinalCollisionVector());
where player.velocity is a Vec2D.
private List<Rectangle2D> getCollidingTiles(){
List<Rectangle2D> collidingTiles = new ArrayList<Rectangle2D>();
for(int x = (int) (this.getX()/Tile.SIZE); x <= (int) (this.getX()/Tile.SIZE) + this.getRect2D().getWidth()/Tile.SIZE; x++){
for(int y = (int) (this.getX()/Tile.SIZE); y <= (int) (this.getX()/Tile.SIZE) + this.getRect2D().getHeight()/Tile.SIZE; y++){
if(map.getTileAt(x, y).getAttribute(Attribute.SOLID))
if(map.getCollisionBoxAt(x,y).isColliding(this.collisionBox))
collidingTiles.add(new Rectangle2D.Double(x, y, Tile.SIZE, Tile.SIZE));
}
}
return collidingTiles;
}
private List<Vec2D> getAllTileCollisionVectors(){
List<Rectangle2D> collidingTiles = getCollidingTiles();
List<Vec2D> collisionVectors = new ArrayList<Vec2D>();
for(Rectangle2D rec : collidingTiles){
collisionVectors.add(getCorrectionVector(rec));
}
return collisionVectors;
}
private Vec2D getCorrectionVector(Rectangle2D target)
{
Vec2D ret = new Vec2D();
double x1 = (this.getX() + this.getSize().x) - target.getX();
double x2 = this.getX() - (target.getX() + target.getWidth());
double y1 = (this.getY() + this.getSize().y) - target.getY();
double y2 = this.getY() - (target.getY() + target.getHeight());
// calculate displacement along X-axis
if (x1 < x2)
{
ret.x = x1;
}
else if (x1 > x2)
{
ret.x = x2;
}
// calculate displacement along Y-axis
if (y1 < y2)
{
ret.y = y1;
}
else if (y1 > y2)
{
ret.y = y2;
}
return ret;
}
protected Vec2D getFinalCollisionVector(){
List<Vec2D> collisionVectors = getAllTileCollisionVectors();
if(collisionVectors.size() < 1)
return new Vec2D(0,0);
Vec2D finalVector = new Vec2D();
for(Vec2D vec : collisionVectors){
finalVector = finalVector.add(vec);
}
return finalVector;
}
What am I doing wrong in my code? This is the behavior that the player shows, where he falls (due to gravity) to that point, and then he freezes.
OP here:
Since there are no other answers, I've posted my own.
I've abandonded the old implementation,started from the ground up, and it works now. This is the new implementation:
public Corners getCornersAreSolid(double x, double y) {
int leftTile = (int)(x / Tile.SIZE);
int rightTile = (int)((x + moveData.collisionBox.getWidth()) / Tile.SIZE);
int topTile = (int)(y / Tile.SIZE);
int bottomTile = (int)((y + moveData.collisionBox.getHeight()) / Tile.SIZE);
boolean topLeft = hasAttribute(map, Attribute.SOLID, topTile, leftTile);
boolean topRight = hasAttribute(map, Attribute.SOLID, topTile, rightTile);
boolean bottomLeft = hasAttribute(map, Attribute.SOLID, bottomTile, leftTile);
boolean bottomRight = hasAttribute(map, Attribute.SOLID, bottomTile, rightTile);
Corners solidCorners = new Corners();
solidCorners.topLeft = topLeft;
solidCorners.topRight = topRight;
solidCorners.bottomRight = bottomRight;
solidCorners.bottomLeft = bottomLeft;
return solidCorners;
}
private boolean hasAttribute(GameMap map, Attribute attribute, int tileY, int tileX) {
boolean result = false;
if (tileX >= 0 && tileX < map.getWidthInTiles() && tileY >= 0 && tileY < map.getHeightInTiles()) {
result = map.getTileAt(tileX, tileY).getAttribute(attribute);
}
return result;
}
public Vec2D getNextPosition() {
int currCol = (int) (getX() / Tile.SIZE);
int currRow = (int) (getY() / Tile.SIZE);
double destX = getX() + moveData.velocity.x;
double destY = getY() + moveData.velocity.y;
double tempX = getX();
double tempY = getY();
Corners solidCorners = getCornersAreSolid(getX(), destY);
boolean topLeft = solidCorners.topLeft;
boolean topRight = solidCorners.topRight;
boolean bottomLeft = solidCorners.bottomLeft;
boolean bottomRight = solidCorners.bottomRight;
this.framesSinceLastCollision += 1;
if(moveData.velocity.y < 0) {
if(topLeft || topRight) {
moveData.velocity.y = 0;
tempY = currRow * Tile.SIZE;
this.framesSinceLastCollision = 0;
}
else {
tempY += moveData.velocity.y;
}
}
else if(moveData.velocity.y > 0) {
if(bottomLeft || bottomRight) {
moveData.velocity.y = 0;
tempY = (currRow + 1) * Tile.SIZE - moveData.collisionBox.getHeight() % Tile.SIZE - 1 ;
this.framesSinceLastCollision = 0;
}
else {
tempY += moveData.velocity.y;
}
}
solidCorners = getCornersAreSolid(destX, getY());
topLeft = solidCorners.topLeft;
topRight = solidCorners.topRight;
bottomLeft = solidCorners.bottomLeft;
bottomRight = solidCorners.bottomRight;
if(moveData.velocity.x < 0) {
if(topLeft || bottomLeft) {
moveData.velocity.x = 0;
tempX = currCol * Tile.SIZE;
this.framesSinceLastCollision = 0;
}
else {
tempX += moveData.velocity.x;
}
}
if(moveData.velocity.x > 0) {
if(topRight || bottomRight) {
moveData.velocity.x = 0;
tempX = (currCol + 1) * Tile.SIZE - moveData.collisionBox.getWidth() % Tile.SIZE -1 ;
this.framesSinceLastCollision = 0;
}
else {
tempX += moveData.velocity.x;
}
}
return new Vec2D(tempX, tempY);
}
private static class Corners{
public boolean topLeft, topRight;
public boolean bottomLeft, bottomRight;
public Corners(){
topLeft = false;
topRight = false;
bottomLeft = false;
bottomRight = false;
}
}

Collision Bug in 2D Platformer Game

I am currently developing a 2D Mario-Like Platformer Game. I ran into a collision problem i've been trying to solve for a while now, but nothing seems to work :/
Basicly, i have a CenterLayer, which stores at which Position what kind of Tile is.
Then i have some Sprites and a Player, which should collide with these Tiles.
Because these Tiles can be triangular shaped (or any other kind of convex polygon), i decided to handle collision via SAT (Seperating Axis Theorem). This works great, but when it comes to collision with the floor where many tiles are adjacent to eachother and the sprite is moving left, it pickes the wrong edge and moves the Sprite to the right, but expected result would be moving it up. This causes the sprite to get stuck.
This is the code im currently using:
package level;
import java.awt.Polygon;
import tiles.Tile;
import sprites.*;
public class Collider {
/** Collide Sprite (or Player) with CenterLayer **/
public static void collide(Sprite s, CenterLayer c){
CollisionPolygon ps = s.getPolygon();
//Get blocks to collide with
int startXTile = (int) (s.getX() / CenterLayer.TILE_WIDTH) - 1;
int endXTile = (int) Math.ceil((s.getX() + s.getWidth()) / CenterLayer.TILE_WIDTH) + 1;
int startYTile = (int) (s.getY() / CenterLayer.TILE_HEIGHT) - 1;
int endYTile = (int) Math.ceil((s.getY() + s.getHeight()) / CenterLayer.TILE_HEIGHT) +1;
//limit to level boundaries
if(startXTile < 0) startXTile = 0;
if(endXTile > c.LEVEL_WIDTH) endXTile = c.LEVEL_WIDTH;
if(startYTile < 0) startYTile = 0;
if(endYTile > c.LEVEL_HEIGHT) endYTile = c.LEVEL_HEIGHT;
int sizeX = endXTile - startXTile;
int sizeY = endYTile - startYTile;
//loop through tiles and collide
for(int xc = 0; xc < sizeX; xc++)
for(int yc = 0; yc < sizeY; yc++){
int xblock = xc + startXTile;
int yblock = yc + startYTile;
Tile t = c.getTile(xblock, yblock);
if(t!=null){ //if tile == null --> tile is air
CollisionPolygon pt = t.getPolygon(xblock, yblock);
double[] projection = PolygonCollision(ps, pt);
//if collision has happened
if(projection[0] != 0 || projection[1] != 0){
//collide
s.moveBy(projection[0], projection[1]);
//update sprites polygon to new position
ps = s.getPolygon();
}
}
}
}
public static double dotProduct(double x, double y, double dx, double dy) {
return x * dx + y * dy;
}
// Calculate the projection of a polygon on an axis (ax, ay)
// and returns it as a [min, max] interval
public static double[] ProjectPolygon(double ax, double ay, Polygon p) {
double dotProduct = dotProduct(ax, ay, p.xpoints[0], p.ypoints[0]);
double min = dotProduct;
double max = dotProduct;
for (int i = 0; i < p.npoints; i++) {
dotProduct = dotProduct(p.xpoints[i], p.ypoints[i], ax, ay);
if (dotProduct < min) {
min = dotProduct;
} else if (dotProduct > max) {
max = dotProduct;
}
}
return new double[] { min, max };
}
// Calculate the distance between [minA, maxA](p1[0], p1[1]) and [minB, maxB](p2[0], p2[1])
// The distance will be negative if the intervals overlap
public static double IntervalDistance(double[] p1, double[] p2) {
if (p1[0] < p2[0]) {
return p2[0] - p1[1];
} else {
return p1[0] - p2[1];
}
}
public static double[] PolygonCollision(CollisionPolygon p1, CollisionPolygon p2){
boolean intersection = true;
int edgeCount1 = p1.npoints;
int edgeCount2 = p2.npoints;
double projectionX = 0;
double projectionY = 0;
double projectionDist = Double.POSITIVE_INFINITY;
//loop through all the edges
for(int edgeIndex = 0; edgeIndex < edgeCount1 + edgeCount2; edgeIndex++){
//find edges
double[] axis;
if(edgeIndex < edgeCount1){
axis = p1.getAxis(edgeIndex);
} else {
axis = p2.getAxis(edgeIndex - edgeCount1);
}
double axisX = axis[0];
double axisY = axis[1];
//System.out.println("edge: " +axisX + ", "+ axisY);
//find the projection of both polygons on current axis
final double[] proj1 = ProjectPolygon(axisX, axisY, p1);
final double[] proj2 = ProjectPolygon(axisX, axisY, p2);
//Check if polygons are intersecting, if not end loop
double id = IntervalDistance(proj1, proj2);
if(id > 0){
intersection = false;
break;
}
//Check if projection would be shorter than previous one
id = Math.abs(id);
if(id < projectionDist){
projectionDist = id;
projectionX = axisX;
projectionY = axisY;
//check if hit from "false" side
double d1x = p1.getCenterX();
double d1y = p1.getCenterY();
double d2x = p2.getCenterX();
double d2y = p2.getCenterY();
double midx = d1x - d2x;
double midy = d1y - d2y;
double dot = dotProduct(midx, midy, projectionX, projectionY);
if(dot < 0){
projectionX = -projectionX;
projectionY = -projectionY;
}
}
}
double[] result = new double[]{0, 0};
if(intersection){
//System.out.println("colliison: " + projectionX +"; "+ projectionY + ", " + projectionDist);
result[0] = projectionX * projectionDist;
result[1] = projectionY * projectionDist;
}
return result;
}
}
Any Ideas?
Tom
I had this bug too , it happens when there are parallel edges on a poly.The easy way to fix this is to project the difference between the polygon centers on the found axis.If the result is negative you would just multiply the axis by -1.
Vector aMidPoint = new Vector();
Vector bMidPoint = new Vector();
for ( Vector v : aVerts) {
aMidPoint = aMidPoint.add(v);
}
for ( Vector v : bVerts) {
bMidPoint = bMidPoint.add(v);
}
aMidPoint = aMidPoint.scalarDivision(aVerts.size());
bMidPoint = bMidPoint.scalarDivision(bVerts.size());
Vector ba = aMidPoint.subtract(bMidPoint);
if (ba.dotProduct(minOverlapVector) < 0) {
minOverlapVector = minOverlapVector.scalarMultiplication(-1);
}

Categories

Resources