I have been using android's RectF in my game to detect collisions on movement. When an entity moves, it gets the object in the world at the position it would like to move. If this object is an instance of a RectF or my Entity class, the entity is told it cannot move there. Here is the code for my map checking method:
public Object getCollisionObject(Entity ent){
for(Entity e : entities){
if(e == ent)continue;
if(e.collides(ent.getBoundsF())return e;
}
for(RectF r : collisions){
if(ent.collides(r))return r;
}
return null;
}
public void moveHere(Entity e, float x, float y){
float originalX = e.getX();
float originalY = e.getY();
e.setX(x);
e.setY(y);
Object o = getCollisionObject(e):
boolean moved = true;
if(o instanceof RectF || o instanceof Entity)moved = false;
e.setX(originalX);//actual movement is done in the entity class if this method returns true
e.setY(originalY);
return moved;
}
Here are the relevant methods in the Entity class:
public RectF getBoundsF(){
return new RectF(x, y, x + width, y + height);
}
public boolean collides(RectF rect){
return RectF.intersects(getBoundsF(), rect);
}
Here is a quick video to demonstrate my problem (the red boxes are drawn around collisions and the blue ones are drawn around bitmap bounds):
VidMe Link
From the video, it seems that on the bottom and right walls, the entity cannot move closer than its width (32 pixels) to the wall. However, on the left and top walls, the entity is able to successfully move as close as possible. The same problem happens on the left and top sides of the walls (on every wall). I am very confused and have spent hours trying to solve this but to no avail. If any more information is required, please do ask in the comments.
Related
I am programming a game of sorts which would be kinda long to explain, but in short the player is an ellipse() and follows the mouse around, whilst the rect() is the obstacle that moves down the screen and needs to be dodged by the player, otherwise it's game over. There are multiple rect as I am using an ArrayList to store each obstacle object.
Currently, the player can just pass straight through the rect without anything happening to it. I have tried to solve it multiple times but it got extremely messy and I couldn't understand much due to being extremely new to Java (only 1 week of experience), so I have just placed the empty code below.
tldr; I need to figure out how to get an ellipse/rect collision to work (in its own method). I only have 1 week of Processing/Java experience. I've cut out most of the code that you don't need to look at, mainly just kept the variables used to define the shapes and the code for the shapes just in case you need that. Also, if possible could the collision method could be placed inside the Enemy Class.
Enemy Class (all the variables used to define the rect)
class Enemy {
int enemyNumber; // used to determine enemy type
//VARIABLES FOR ENEMY
boolean redEnemy = false; // determine enemy colour
color enemyColour = color(#B9B9E8); // sets default colour to blue
PVector position, velocity;
float xDist, yDist; // x and y distance for Bar
float smallCircleRad, bigCircleRad; // radius for circles
// **************************************************************************
Enemy() { //CONSTRUCTOR
position = new PVector(width/2, random(-300000, -250));
//println(position.y);
velocity = new PVector(0, 10);
smallCircleRad = 200;
bigCircleRad = 400;
xDist = width;
yDist = 200;
enemyNumber = int(random(1, 6));
}
// **************************************************************************
void redBar(float xPos, float yPos, float xDist, float yDist) {
redEnemy = true;
noStroke();
enemyColour = color(#E38585);
fill(enemyColour);
rect(xPos, yPos, xDist, yDist);
}
void blueBar(float xPos, float yPos, float xDist, float yDist) {
redEnemy = false;
noStroke();
enemyColour = color(#B9B9E8);
fill(enemyColour);
rect(xPos, yPos, xDist, yDist);
}
Player Class (all the variables used to define the ellipse)
class Player {
int r = 50; //player radius
float playerX = width/2; //starting x coordinate
float playerY = height/2+500; //starting y coordinate
float speed = 20; //player speed
float angle; //angle used to calculate trajectory for player
void playerChar() { //method for player model and general rules
stroke(10);
rectMode(CENTER);
fill(playerColour);
ellipse(playerX, playerY, r*2, r*2);
}
Make your life easier by treating the player as a rectangle instead of a circle. You can still draw them as a circle, but for collision detection, use a rectangle. This is called a bounding box and is very popular in collision detection.
Then you can use rectangle-rectangle collision detection, which is much simpler.
Some basic google searches return these results:
Axis-Aligned Bounding Box
What is the fastest way to work out 2D bounding box intersection?
Processing Collision Detection
If for some reason you absolutely need the player to be a circle when calculating the collision, then I'd start by googling something like "circle rectangle collision detection".
If you still can't get it figured out, please post a MCVE in a new question and we'll go from there. Good luck.
I started learning LibGdx and Java recently, and it has been going well so far.
I'm facing an issue with collision detection.
I have two sprites which can be represented as two shapes, a polygon and a circle, which will collide/intersect at any given moment. Once these two shapes collide, something will get triggered.
So far, this is what I have done. It kinda works but it is not accurate. This is called inside the Render() function:
public boolean CollectPowerUp(PowerUps powerUp) {
if (powerUp.position.dst(position) < Constants.PLAYER_HEIGHT -3) {
Gdx.app.log("Collected PowerUp", "TRUE");
EnablePowerUp(powerUp);
return true;
}
return false;
I have searched many websites, and most of the solutions include other softwares like 2DCube or PhysicsEditor. Is it possible to perform this intersection solely by using LibGdx and Java? If so, what should I look into?
Thanks
Intersector class having many static method that can be used for collision detection.
If your polygon is rectangle you can use :
Intersector.overlaps(Circle c, Rectangle r)
else
Polygon polygon=new Polygon();
polygon.setVertices(new float[]{0,0,.......});
Circle circle=new Circle(x, y, radius);
float points[]=polygon.getTransformedVertices();
for (int i=0;i<points.length;i+=2){
if(circle.contains(points[i],points[i+1])){
System.out.println("Collide");
}
}
EDIT
Above code only detect collision if polygon vertices are inside circle, what if
circle is completely inside polygon
some part of circle is inside polygon but vertices are outside the circle
Create a polygon for circle that act as circle in view and polygon in model
float radius=100;
FloatArray floatArray=new FloatArray();
int accuracy=24; // can be use 1 for complete circle
for (int angle=0;angle<360;angle += accuracy){
floatArray.add(radius * MathUtils.cosDeg(angle));
floatArray.add(radius * MathUtils.sinDeg(angle));
}
Polygon circle=new Polygon(floatArray.toArray()); // This is polygon whose vertices are on circumference of circle
float[] circularPoint=circle.getTransformedVertices();
for (int i=0;i<circularPoint.length;i+=2){
if(polygon.contains(circularPoint[i],circularPoint[i+1])){
System.out.println("Collide With circumference");
break;
}
}
There's a nice article on collision detection on www.gamedevelopment.blog which shows how to detect collisions with most shapes. This is the Libgdx circle, polygon collision detection method shown in the article.
public boolean contains (Polygon poly, Circle circ) {
final float[] vertices = poly.getTransformedVertices(); // get all points for this polygon (x and y)
final int numFloats = vertices.length; // get the amount of points(x and y)
// loop through each point's x and y values
for (int i = 0; i < numFloats; i += 2) {
// get the first and second point(x and y of first vertice)
Vector2 start = new Vector2(vertices[i],vertices[i + 1]);
// get 3rd and 4th point (x and y of second vertice) (uses modulo so last point can use first point as end)
Vector2 end = new Vector2(vertices[(i + 2) % numFloats], vertices[(i + 3) % numFloats]);
// get the center of the circle
Vector2 center = new Vector2(circ.x, circ.y);
// get the square radius
float squareRadius = circ.radius * circ.radius;
// use square radius to check if the given line segment intersects the given circle.
return Intersector.intersectSegmentCircle (start, end, center, squareRadius);
}
}
There are many useful methods in the Intersector class which can be used for collision detection.
I have a libgdx application that contains a class Button. The constructor of Button takes three arguements: Filename of graphics, position, and game (the latter being used for callbacks of various sorts).
The button scales itself based on the graphics provided, thus setting its width and height based on the properties of the graphics.
The main class, Game, when a click is detected compares the coordinates of the click up against the coordinates of the button combined with its width and height.
Now, the main issue is that there is a little bit of a horizontal offset between the button and the click coordinates, so the effect is that the graphics show up a few pixels to the right of the clickable area. I cannot for the life of me figure out the source of this discrepancy, so I would greatly appreciate some fresh eyes to see where I'm going wrong here.
Button, constructor and polling-method for clickable area.
public Rectangle getClickArea() {
return new Rectangle(pos.x - (img.getWidth() / 2), pos.y + (img.getHeight() / 2), w, h);
}
public Button(String assetfile, int x, int y, Game game) {
this.game = game;
img = new Pixmap(new FileHandle(assetfile));
pos = new Vector2(x, y);
this.w = img.getWidth();
this.h = img.getHeight();
}
A relevant snippet from InputHandler. It listens for input and passes on the event. Please note that the vertical click position is subtracted from the vertical size of the screen, as vertical 0 is opposite in InputHandler:
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
tracker.click(screenX, Settings.windowSize_Y - screenY);
return false;
}
ClickTracker (referenced as tracker in the above snippet), the Class that does the actual comparison between clicks and clickables:
public void click(int x, int y) {
Vector2 clickPos = new Vector2(x, y);
for (Tickable c : world.getPaintables())
{
if (!(c instanceof Unit))
continue;
if (((Clickable)c).getClickArea().contains(clickPos)) {
System.out.println("Clicked on unit");
}
}
for (Clickable c : clickables)
{
if (c.getClickArea().contains(clickPos)) {
c.clicked(x, y);
}
}
In short: The vertical alignment works as intended, but the horizontal is slightly off. The button graphics appear maybe around 10-20 pixels to the right of the clickable area.
I'll gladly post more info or code if needed, but I believe I have the relevant parts covered.
Edit:
As Maciej Dziuban requested, here's the snipped that draws the UI elements. batch is a SpriteBatch as provided by libgdx:
for (Paintable p : ui) {
batch.draw(new Texture(p.getImg()), p.getImgPos().x, p.getImgPos().y);
}
the getImgPos() is an interface method implemented by all drawable items:
public Vector2 getImgPos() {
return new Vector2(pos.x - (getImg().getWidth() / 2), pos.y);
}
It's worth noting that half of the horizontal image size is subtracted from the X pos, as X pos refers to the bottom center.
You have inconsistency in your position transformations:
Your clickArea's corner is pos translated by [-width/2, height/2] vector.
Your drawArea's corner is pos translated by [-width/2, 0] vector
They clearly should be the same, so if you want your pos to represent bottom-center of your entity (as you've explicitly stated) you have to change your getClickArea() method to, so it matches getImgPos().
public Rectangle getClickArea() {
return new Rectangle(pos.x - (img.getWidth() / 2), pos.y, w, h);
}
Side note: as Tenfour04 noticed, you create new texture each frame and this is huge memory leak. You should make it a field initialized in constructor or even a static variable given some buttons share the texture. Don't forget to call dispose() on resources. For more powerful asset management check out this article (note it may be an overkill in small projects).
I have made a simple game and I have a simple way to detect when I have collected a coin but it is very hard to match its position exactly.
public class Token {
private String name;
int x;
int y;
private BufferedImage image;
public Token (String nameIn, int xIn, int yIn, BufferedImage imageIn)
{
name = nameIn;
x = xIn;
y = yIn;
image = imageIn;
}
public boolean collected(Hero frylark) {
if (frylark.getX() == x && frylark.getY() == y) {
return true;
}
else {
return false;
}
}
}
Is there any way i can have a buffer of say 10 pixels instead of
matching the position of the coin exactly.
A distance between two points in a two-dimensional field is the sum of the squares of the differences between their corresponding coordinates:
public boolean collected(Hero frylark) {
return Math.sqrt(Math.pow(frylark.getX() - x , 2) +
Math.pow(frylark.getY() - y , 2)
) <= 10.0;
}
Based on Mureinik's answer, you can do this faster by not use Math.pow nor Math.sqrt.
double dx = frylark.getX() - x;
double dy = frylark.getY() - y;
return dx*dx + dy*dy <= 10.0*10.0;
I have made a simple game and I have a simple way to detect when I have collected a coin but it is very hard to match its position exactly.
I will propose a slightly different approach for you. If you attempt to detect collision by using only the x and y coordinates, it is very hard to detect collision since you need both pixels to hit at the same spot.
This problem arises especially when you try to check collision for images of different sizes:
Exmaple:
With your current implementation, in order for the Game Character to hit the coin, the red pixel (top left hand corner) has to collide, and you end up needed to add a buffer for images of different sizes to check for collision.
I will advise returning a bounding box for each object and check weather their bounding boxes intersects:
public boolean collected(Hero h){
Rectangle heroBox = new Rectangle (h.getX(), h.getY(), h.getWidth(), h.getHeight());
Rectangle coinBox = new Rectangle (x, y, width, height);
return(coinBox.intersects(heroBox));
}
You will need the width and height (which is usually the width and height of your images) of your objects for creating the bounding box.
Advantage:
You no longer have to check the size of each image and set the buffer for them individually.
Is there any way i can have a buffer of say 10 pixels instead of
matching the position of the coin exactly.
Adding a buffer:
If you still want a buffer, say 10 pixel. We can still apply it in this implementation:
public boolean collected(Hero h, int buffer){
Rectangle heroBox = new Rectangle (h.getX(), h.getY(), h.getWidth() + buffer, h.getHeight() + buffer);
Rectangle coinBox = new Rectangle (x, y, width + buffer , height + buffer);
return(coinBox.intersects(heroBox));
}
By adding the given buffer, we enlarge the area of the bounding boxes, hence making it more sensitive. You can always tweak from my example to add the buffer on one of the objects, both objects, or on only the width or the height of either objects.
I'm currently trying to make a small Entity-Relationship-Modell creating tool for Databases.
To avoid errors I try to don't allow the rectangles to overlap with a other rectangle.
I got most of my code working and it detects almost every collusion but in one case I can still overlap them and I don't know how to fix this error.
The problem is that it will look on only on rectangle not on both. But I really don't know how to intercept that case.
The strange thing to me is that my Debug line
System.out.println("still intersects?");
never triggers. Can somebody help me out in this error?
Image to the error in my Colision
This Function gets triggered every time I move a Rectangle.
The attribute Selected is a Boolean which says me which Rectangle I'm moving.
Then I check on which side its overlapping and revert back to the old position.
boolean Move(Point point) {
boolean moved = false;
boolean foundone = false;
for (ERMRectangle rectangle : Rectangles) {
if (rectangle.selected) {
foundone = true;
moved = true;
//calculated new Rectangle after Move
Rectangle Temp = new Rectangle();
Temp.x = point.x + rectangle.click.x;
Temp.width = rectangle.position.width;
Temp.height = rectangle.position.height;
Temp.y = point.y + rectangle.click.y;
for (ERMRectangle rectangle2 : Rectangles) {
if (rectangle != rectangle2 && rectangle2.position.intersects(Temp))//prevent overlapping
{
Rectangle intersection = rectangle2.position.intersection(Temp);
if (intersection.height > intersection.width) {
Temp.x = rectangle2.position.x - rectangle2.position.width;
if (intersection.x != rectangle2.position.x) {
Temp.x = rectangle2.position.x + rectangle2.position.width;
}
} else {
Temp.y = rectangle2.position.y - rectangle2.position.height;
if (intersection.y != rectangle2.position.y) {
Temp.y = rectangle2.position.y + rectangle2.position.height;
}
}
}
if(rectangle != rectangle2 && rectangle2.position.intersects(Temp))
{
System.out.println("still intersects?");
}
}
rectangle.position = Temp;
}
}
return moved;
}
ERMRectangle class:
import java.awt.Rectangle;
public class ERMRectangle {
public Rectangle position;
public boolean selected;
public Point click;
}
Edit: i resolved it by adding one more for loop into my methode which checks if it still interselects the rectangle and reverts both x and y cordinnates.
If at any moment, there will only be one rectangle moving (dragged by the user) there is no need to use nested loops to check for collision n-square times.
All you need is a method like this in your ERMRectangle class:
These codes assume your ERMRectangle class extends Rectangle class from Java
public boolean isColliding(ArrayList<ERMRectangle> rects)
{
for(ERMRectangle r : rects)
if(this != r && this.intersects(r))
return true;
return false;
}
You can then invoke isColliding() in the mouseReleased(MouseEvent e) in the mouse listener for your rectangles, and trigger the necessary actions when collision is detected. So, upon every mouse release after dragging, it will check for collisions on the rectangle which you have just moved.
To check for collision as you drag, place the similar codes as you would in mouseReleased(MouseEvent e) into mouseDragged(MouseEvent e). This will do a real-time collision detection check as you drag the rectangle.