I have a "physics ball" that can bounce around the screen off the edges which works fine. But I wanted to be able to add boxes and have my ball be able to bounce of those, too. I have tried to create something and it feels like it is quite close, but there is flaw that I understand why it exists, but am unsure on how I can get around it.
if (colliding(ball, block))
{
if (ball.velocity.x > 0)
{
ball.velocity.x *= -ball.restitution;
ball.vector.x = block.vector.x - ball.radius;
}
else
{
ball.velocity.x *= -ball.restitution;
ball.vector.x = block.vector.x + block.width + ball.radius;
}
if (ball.velocity.y > 0)
{
ball.velocity.y *= -ball.restitution;
ball.vector.y = block.vector.y - ball.radius;
}
else
{
ball.velocity.y *= -ball.restitution;
ball.vector.y = block.vector.y + block.height + ball.radius;
}
}
colliding():
boolean colliding(MassEntity ball, Block block)
{
return PVector.dist(ball.vector, block.vector) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x + block.width, block.vector.y)) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x + block.width, block.vector.y + block.height)) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x, block.vector.y + block.height)) < ball.radius
|| (ball.vector.x - ball.radius < block.vector.x + block.width && ball.vector.x + ball.radius > block.vector.x
&& ball.vector.y - ball.radius < block.vector.y + block.height && ball.vector.y + ball.radius > block.vector.y);
}
(I know the if statement is a little monstrous, but I don't know what happened to the formatting honestly)
The issue is that when the ball collides with the rectangle, since the ball is "teleported" to outside of the rectangle (so it doesn't stay inside the rectangle due to the velocity being flipped), it teleports on both axes so pretty much the ball will weirdly teleport to the end of one of the edges.
I just need to somehow make if statements for the respective axes to only be considered in the appropriate situation.
First, compute P, the nearest point to the ball that is in the box :
PVector P = new PVector(
max(min(ball.vector.x, box.vector.x + box.width / 2), box.vector.x - box.width / 2),
max(min(ball.vector.y, box.vector.y + box.height / 2), box.vector.y - box.height / 2)
);
To check if there is a collision, you can check if the distance between P and the center of the ball is smaller than the radius of the ball.
To update the speed of the ball, you can do this :
PVector n = normalize(ball.vector.copy().sub(P));
ball.velocity.sub(n.mult(2 * n.dot(ball.velocity)));
n is the normal vector a the collision's position and to reflect the speed on the surface you have to delete the component of the velocity that is parallel to it. Then you have to add this same component multiplied by -1. As those two operations are the same, you can just do it one time with a factor 2.
A last precision I have to make is that you may need to check if the ball is going away from the box to avoid reversing the speed in that case :
PVector n = normalize(ball.vector.copy().sub(P));
float f = n.dot(ball.velocity);
if (f < 0)
ball.velocity.sub(n.mult(2 * f));
I haven't tested my code so tell me if there is a problem.
Related
So I made a game and I want the enemies to bounce off the wall when they hit in a random x and y speed. However, somehow these little buggers are still getting out of the window. Most the time it works but every like 10ish times it will sneak out the border and I can't figure out how.
#Override
public void tick()
{
x += speedX;
y += speedY;
Random r = new Random();
//BUGS
if(y <= 0 || y >= Game.HEIGHT - 48) //This is correct size of the window edges
{
if(speedY <= 0)
speedY = (r.nextInt(8) + 1);
else
speedY = -(r.nextInt(8) + 1);
}
if(x <= 0 || x >= Game.WIDTH - 32) //This is correct size of the window edges
{
if(speedX <= 0)
speedX = (r.nextInt(8) + 1);
else
speedX = -(r.nextInt(8) + 1);
}
Issues:
Don't re-create Random as it's wasteful and sometimes dangerous. Better to create one Random object and assign it to an instance field of the class, and use it throughout.
Avoid "magic numbers". So instead of y >= Game.HEIGHT - 48, do y >= Game.HEIGHT - WINDOW_EDGES (or something similar)
Don't swap speed as you're doing but instead check for y <= 0 or y >= Game.HEIGHT -SOME_CONSTANT separately, and gear the results based on this finding to avoid getting caught in a speed "trap". This is your main problem in fact.
e.g.,
if (y <= 0) {
speedY = r.nextInt(8) + 1; // change 8 to a constant
} else if (y >= Game.HEIGHT - SOME_CONSTANT) {
speedY = -(r.nextInt(8) + 1);
}
Same for x and speedX
Regarding:
2) I would like to do that but since I have multiple object sizes, I have to change the edges.
Then each object should have a method that returns its edge size (or whatever property is needed), and you should use this, again, not magic numbers
3) I tried swapping and they just shot off the screen.
I don't know what you mean by this or what specific code changes you may have made.
If still stuck, consider creating and posting a valid Minimal Reproducible Example
I am trying to see if my camera is facing a building.
I use xDelta and zDelta when moving the camera along the place
xDelta=sine/4 and zDelta=cosine/4 (to limit speed of movement).
Basically, my idea is to take the distance of the building, and apply the deltas scaled to the distance of the building to the camera's coordinates to get a set of projected coordinates. I then try to take the distance between the building coordinates and the projected coordinates to see if the camera is facing it.
I can confirm that the distances to the building are correct, however, I think there is a problem with the projection coordinates. This is my code so far:
building1Distance = Math.pow((double)(CAMERA_X - BUILDING1_X), 2) + Math.pow((double)(CAMERA_Z - BUILDING1_Z), 2);
building1Distance = Math.sqrt(building1Distance);
building2Distance = Math.pow((double)(CAMERA_X - BUILDING2_X), 2) + Math.pow((double)(CAMERA_Z - BUILDING2_Z), 2);
building2Distance = Math.sqrt(building2Distance);
building3Distance = Math.pow((double)(CAMERA_X - BUILDING3_X), 2) + Math.pow((double)(CAMERA_Z - BUILDING3_Z), 2);
building3Distance = Math.sqrt(building3Distance);
building4Distance = Math.pow((double)(CAMERA_X - BUILDING4_X), 2) + Math.pow((double)(CAMERA_Z - BUILDING4_Z), 2);
building4Distance = Math.sqrt(building4Distance);
//sort them
distances = new double[4];
distances[0] = building1Distance;
distances[1] = building2Distance;
distances[2] = building3Distance;
distances[3] = building4Distance;
Arrays.sort(distances);
//search to see if pointing at building
for(int i = 0; i < distances.length; i++){
//projected coordinates of where camera is facing at that distance
pX = CAMERA_X + (xDelta * 4.0 * distances[i]);
pZ = CAMERA_Z + (zDelta * 4.0 * distances[i]);
//check to see which building is at that distance
if(building1Distance == distances[i]){
//check to see if its within diameter of the building.
if(Math.sqrt(Math.pow((double)(CAMERA_X - pX), 2) + Math.pow((double)(CAMERA_Z - pZ), 2)) < 50){
buildingIndex = 1;
return;
}
}else if(building2Distance == distances[i]){
if(Math.sqrt(Math.pow((double)(CAMERA_X - pX), 2) + Math.pow((double)(CAMERA_Z - pZ), 2)) < 30){
buildingIndex = 2;
return;
}
}else if(building3Distance == distances[i]){
if(Math.sqrt(Math.pow((double)(CAMERA_X - pX), 2) + Math.pow((double)(CAMERA_Z - pZ), 2)) < 50){
buildingIndex = 3;
return;
}
}else{
if(Math.sqrt(Math.pow((double)(CAMERA_X - pX), 2) + Math.pow((double)(CAMERA_Z - pZ), 2)) < 10){
buildingIndex = 4;
return;
}
}
}
buildingIndex = -1;
It seems to be always setting buildingIndex to -1.
It's not easy to debug because I am making a Google Cardboard application, and to move in my world I must unplug the USB and plug in a keyboard (I don't have a bluetooth one).
Can anyone see a problem in my logic/code?
Thanks
It looks like your threshold logic is not accurate.
The problem was that I was using CAMERA_X and CAMERA_Z instead of the building's coordinates when testing how far the projected point was.
I found this question that deals with the same issue. The provided answers work, but I need to change it slightly for my case. Below is the answer I went with:
double theta = Math.atan2(pointerY - height / 2, pointerX - width / 2);
if(theta<0)
theta = Math.PI - theta;
int whichSlice = 0;
double sliceSize = Math.PI*2 / 4;
double sliceStart;
for(int i=1; i<=4; i++) {
sliceStart = i*sliceSize;
if(theta < sliceStart) {
whichSlice = i;
break;
}
}
In my case, I need to rotate the quadrants by 45 degrees. Below is an example; red is what this code does, while green is what I want:
I've tried various code alterations, but still can't figure it out.
EDIT:
First off, create your circle in it's own desperate JComponent, and add it's own listeners - basically create a class for this circle, make the circle itself receive mouse events, and MAKE SURE THAT THE CIRCLE OCCUPIES THE ENTIRE RECTANGLE OF THE JCOMPONENT - it must be touching all edges (I will be using this.getHeight() and this must return the height of the bounding box of the circle)!!!
Fixed code below to support such a case, in addition to support y axis which increases downwards:
Step 1:
Check if we are inside the circle.
Step 2:
Check if we are above/below the diagonal lines (note: equations for diagonal lines are y = x, and y = -x)
Point pointWeAreChecking;
Point centerOfCircle;
double radius;
if(Math.pow(Math.pow(pointWeAreChecking.x-centerOfCircle.x , 2) + Math.pow(pointWeAreChecking.y-centerOfCircle.y , 2), 0.5) <= radius)
{
//Means we are in circle.
if(pointWeAreChecking.y>pointWeAreChecking.x)
{
//Means it is either in 2 or 3 (it is below y = -x line)
if(pointWeAreChecking.y>-pointWeAreChecking.x + this.getHeight()){
//We are in 2.
}else
{
//We are in 3.
}
}else
{
if(pointWeAreChecking.y>-pointWeAreChecking.x + this.getHeight())
{
//We are in 4.
}else
{
//We are in 2.
}
}
}
I've been thinking on some fast and brilliant pixel - perfect collision detection between a circle and any sprite. I need to get 2 points of collision to be able to calculate a normal vector out of them later. I managed to come up with some solution but the more scaling is done in my game, the more inaccurate and unprecise this collision is...It seems as if the code I posted below, was good and correct becouse I have been checking it already a few times and spent a few days reading it again and again... I also checked visually that the collision masks and areas of collision are calculated perfectly fine in the code below so the problem definitely doesn't lay there but in this method.
So I guess that the problem here is the loss of data in floating point arithmetic unless somebody finds a flaw in this method?
If however the problem is really with the float loss of data, what other solution would you recommend to find 2 points of collision between circle and any other sprite in pixel perfect? I really liked my solution becouse it was relatively fast
int xOffset1 = (int)colRectLeft; // left boundary of the collision area for the first sprite
int xOffset2 = (int)colCircleLeft; // left boundary of the collision area for the circle sprite
int yOffset1 = (int)colRectBottom; // bottom boundary of the collision area for the first sprite
int yOffset2 = (int)colCircleBottom; // bottom boundary of the collision area for the circle sprite
int width = (int)(colCircleRight - colCircleLeft); //width of the collision area - same for both sprites
int height = (int)(colCircleTop - colCircleBottom); // height of the collision area same for both sprites
// Pixel-perfect COLLISION DETECTION between circle and a sprite
// my custom vector classes - nothing special
Math2D.Vector_2 colRightPoint = new Math2D.Vector_2(-1, -1); // The right point of collision lying on the circle's circumference
Math2D.Vector_2 colLeftPoint = new Math2D.Vector_2(-1, -1); // the left point of collision lying on the circle's circumference
boolean colRightFound = false;
boolean colLeftFound = false;
// I'm going through y in the circle's area of collision
for (float y = yOffset2; y < yOffset2 + height; y += 1)
{
// from equation: (x-Sx)^2 + (y-Sy)^2 = r^2
// x1/2 = (+-)sqrt(r^2 - (y - Sy)^2) + Sx
//(Sx, Sy) is (circle's radius, circle's radius) becouse I want the points on the circle's circumference to have positive coordinates
float x1 = (float) (Math.sqrt(radius*radius - (y - radius)*(y - radius)) + radius); // the right pixel on the circumference
float x2 = (float) (-x1 + 2*radius); // the left pixel on the circumference
//first I check if the calculated x is inside of the previously calculated area of collision for both circle's area and a sprite's area
if (x1 >= xOffset2 &&
x1 <= xOffset2 + width &&
xOffset1 + x1 - xOffset2 < rectFrameW &&
yOffset1 + (int)y-yOffset2 < rectFrameH &&
yOffset1 + (int)y-yOffset2 > 0 &&
xOffset1 + x1 - xOffset2 > 0)
{
//I don't have to check if the point on the circle's circumference is opaque becouse it's always so just check if the same point translated to sprite's area of collision is opaque
boolean opaqueRectPixel = go.gameData.images.get(go.pic_nr)
.collision_mask[(int)((yOffset1 + (int)y-yOffset2)*rectFrameW +
(xOffset1 + x1 - xOffset2))];
if(opaqueRectPixel)
{
if(!colRightFound)
{
colRightPoint.x = (xOffset1 + x1 - xOffset2);
colRightPoint.y = (yOffset1 + (int)y - yOffset2);
colRightFound = true;
}
else if(!colLeftFound)
{
colLeftPoint.x = (xOffset1 + x1 - xOffset2);
colLeftPoint.y = (yOffset1 + (int)y - yOffset2);
}
}
}
//the same logic for the left point on the circle's circumference
if (x2 >= xOffset2 &&
x2 <= xOffset2 + width &&
xOffset1 + x2 - xOffset2 < rectFrameW &&
yOffset1 + (int)y-yOffset2 < rectFrameH &&
yOffset1 + (int)y-yOffset2 > 0 &&
xOffset1 + x2 - xOffset2 > 0)
{
boolean opaqueRectPixel = go.gameData.images.get(go.pic_nr)
.collision_mask[(int)((yOffset1 + (int)y-yOffset2)*rectFrameW +
(xOffset1 + x2 - xOffset2))];
if(opaqueRectPixel)
{
if(!colLeftFound)
{
colLeftPoint.x = (xOffset1 + x2 - xOffset2);
colLeftPoint.y = (yOffset1 + (int)y - yOffset2);
colLeftFound = true;
}
else if(!colRightFound)
{
colRightPoint.x = (xOffset1 + x2 - xOffset2);
colRightPoint.y = (yOffset1 + (int)y - yOffset2);
}
}
}
// if both points are already found, finish
if(colLeftFound && colRightFound)
break;
}
edit: Actually, what I'm doing in this method is finding points of intersection between circle and a sprite
edit: Ok, I'm uploading images to describe my algorithm a bit better. I really tried my best to explain it but if there's still something missing, let me know please!
Also I would accept any other good solutions to find intersection points between a circle and any sprite in pixel perfect, if you don't want to check my code :(... Eh, I'm always having problems with collisions...
If you absolutely want (or need) pixel perfect, your solution looks good.
don't forget to first make a rectangle-to-rectangle collision before testing a pixel perfect detection, to avoid unneeded processings.
If you want another accurate method which maybe more efficient, look for Separating Axis Theorem.
You can find more information about it here :
http://rocketmandevelopment.com/blog/separation-of-axis-theorem-for-collision-detection/
and here :
http://www.metanetsoftware.com/technique/tutorialA.html
The last one have nice interactive explanation and demonstration. Enjoy :)
...as I was not able to show the raster in the comments:
I did not mentally parse your code, however from the image I see that you try to detect borderline collisions. Putting round or diagonal (border)lines into a raster may cause occasions, where two crossing lines do not overlay each other - like this:
1 2
2 1
whereby 1 would be line 1 and 2 would be line 2.
However I still like the idea of checking border lines combined with rectangle pre-checks. If you would render an array of raster proved-closed line coordinates by sprites you could check them against each other. This could also be enriched by border line segmenting (such as North, East, West and South or a bit more fine grain - I guess there is an optimum). A diagonal proved-closed line in the check data set must represent something like this:
x _
x x
whereby the x represent the pixels of your line and the _ is an empty raster seat.
I wrote a program to solve the following:
Implement a diffusion limited aggregation simulation on a toroid plane where seeds are randomly created, and particles move randomly. they move if they particles do not land near a seed or a particle. where user inputs seeds (red pixels), particles (black pixels), steps (no or iterations), plane size.
My code is very slow. How can I make it faster?
I randomly created x and y coordinates and drew red pixels (seeds), then randomly created x and y for black pixels (particles), if a black pixel lands where there is a red or black pixel it can stay, otherwise it moves randomly again until there are no more particles . If the pixel lands out of borders like x > border then x=0; if x <1 then x= border. The same for y.
This just means that if it lands on the border I move it to the opposite border. Then checks for the neighboring pixels again. I have an outer loop to create the seeds, and inner loop for the particles. In the inner loop I check for the x,y positions:
//Place, move, and "stick" the particles; stop if either steps or particles = 0
for (int p = 0; p < particles; p++) {
for (int s = 0; s < steps; s++) {
if (xPos > image.getWidth() ) {
do something
}
else if (xPos < 1) {
do something
}
if (yPos > image.getHeight() - 2) {
do something
}
else if (yPos < 1) {
do something
}
else if (xPos > image.getWidth() && yPos > image.getHeight()) {
do something
}
else if (xPos < 1 && yPos < 1) {
do something
}
//If the surrounding pixels' color is not white, make that particle stick.
if (moveValid()) {
image.setRGB(xPos, yPos, 0xFF000000);
}
//Otherwise, move in a random direction
else {
if(xPos == 1 && image.getRGB(size - 2, yPos) != 0xFFFFFFFF){
draw(xPos,yPos);
}
else if(xPos == size - 2 && image.getRGB(1,yPos) != 0xFFFFFFFF){
draw(xPos,yPos);
}
if(yPos == 1 && image.getRGB(xPos, size - 2) != 0xFFFFFFFF){
draw(xPos,yPos);
}
else if(yPos == size - 2 && image.getRGB(xPos,1) != 0xFFFFFFFF){
draw(xPos,yPos);
}
else {
move();
}
}
}
//Regenerate random x and y positions for the next particle
xPos = random.nextInt(size);
yPos = random.nextInt(size);
}
Although the implementation of draw() is not shown, it looks like you're updating a BufferedImage and then rendering it.
The first step is always to profile your existing code, looking for easily implemented optimizations.
The second step is sometimes to set aside the existing code and try a different approach.
You may be able to leverage the Mode-View-Controller pattern, outlined here and discussed here. In particular, let your DLA model evolve on a background thread at full speed, while updating your view at a more sustainable rate. This article suggests several approaches to synchronization and includes a related example that uses javax.swing.Timer to pace the updates.