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
Related
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.
This is one of the classic Robocode practice questions: Going to the middle of the map. I didn't want to move twice (once for x-axis and once for y-axis), but instead get the bearing towards the middle and move in a single motion after setting my robot's bearing.
To my dismay, it seems to be not working. Instead, the behavior I observed from the robots is that they turn 180 degrees and move ahead for the value of 'r', which is the hypotenuse of the "triangle" towards the middle. The distance seems to be getting smaller each loop, so I think that the formula for 'r' is right. My suspicion is towards the methods setWest() and setEast() I wrote. Their purpose is to set the robot parallel to the horizontal facing either East or West. Then I once again rotate the robot towards the middle by using theta (which is the second iffy part) and move it.
The methods implemented in Robocode are pretty self-explanatory, not mine though. Here is the code I've written:
public class MyFirstRobot extends Robot {
public void setWest(){
// If it's looking NW
if(getHeading() > 270 && getHeading() < 0)
turnLeft(getHeading() - 270);
// If it's looking SE or SW
else if(getHeading() >= 90 && getHeading() <= 270)
turnRight(270 - getHeading());
// If it's looking NE
else if(getHeading() >= 0 && getHeading() < 90)
turnLeft(90 + getHeading());
// If my coding is undercooked spaghetti
else {
System.out.println("Error");
}
}
public void setEast(){
// If it's looking NW
if(getHeading() > 270 && getHeading() < 0)
turnRight(450 - getHeading());
// If it's looking SE or SW
else if(getHeading() >= 90 && getHeading() <= 270)
turnLeft(getHeading() - 90);
// If it's looking NE
else if(getHeading() >= 0 && getHeading() < 90)
turnRight(90 - getHeading());
// If my coding is undercooked spaghetti
else {
System.out.println("Error");
}
}
public void run() {
double x = 0.0;
double y = 0.0;
double r = 0.0;
double theta = 0.0;
while (true) {
x = getBattleFieldWidth() / 2.0 - getX();
y = getBattleFieldHeight() / 2.0 - getY();
r = Math.sqrt(Math.pow(x,2) + Math.pow(y,2));
// Find the angle with respect to the horizontal.
theta = Math.atan((Math.toRadians(y) / Math.toRadians(x)));
/*
* Align tank towards the middle
* depending on the "quadrant" it's in.
*/
// 1st Quadrant
if(x < 0 && y < 0){
setWest();
turnLeft(theta);
}
// 2nd Quadrant
else if(x >= 0 && y < 0){
setEast();
turnRight(theta);
}
// 3rd Quadrant
else if(x >= 0 && y >= 0) {
setEast();
turnLeft(theta);
}
// 4th Quadrant
else if(x < 0 && y >= 0) {
setWest();
turnRight(theta);
}
// Move to the middle after the final rotation.
ahead(r);
}
}
Please don't mind the infinite loop, it's just to observe the behavior. run() is the main method of the robot; it is called by default.
I will answer my own question in case someone wonders what the answer is. There were a few fixes:
getHeading() returns a value between 0 <= getHeading() < 360 in degrees. I mistakenly set one of the if conditions to getHeading() < 0, which isn't possible. I also decided to store the heading in a variable to avoid calling getHeading() multiple times. Here is the fix for that part:
public void setWest(){
double heading = getHeading();
// If it's looking NW
if(heading > 270.0 && heading < 360.0)
turnLeft(getHeading() - 270);
// If it's looking SE or SW
else if(heading >= 90.0 && heading <= 270.0)
turnRight(270 - getHeading());
// If it's looking NE
else if(heading >= 0.0 && heading < 90.0)
turnLeft(90 + getHeading());
// If my coding is god awful
else {
System.out.println("West | Error");
}
The difference here being 0.0 for the first check being changed to 360.0 0.
Theta was stored in radians instead of degrees. turnLeft() or turnRight() needs a parameter in degrees for it to work properly. Putting in radians won't give you an error. It also won't work as intended. Theta also had to be stored as a positive number, since setWest() and setEast() already accounts for the direction. It was simply fixed by using Math.abs() and Math.toDegrees(). As shown below:
// Find the abs value of the angle with respect to the horizontal.
theta = Math.abs(Math.toDegrees(Math.atan((Math.toRadians(y) / Math.toRadians(x)))));
If you had some lingering questions about it, I hope this helped.
For a Minecraft project, I wanted to make the player face (0, 60, 0) gradually. So far, everything I have tried seems to fail when the player moves more than 720° around (0, 60, 0).
Anyone have an idea on how to make the camera move seamlessly to (0, 60, 0)?
Thank you!
Here is my code so far (that runs in a loop when toggled):
int x = 0;
int y = 60;
int z = 0;
player = Minecraft.getMinecraft().thePlayer;
double dirx = player.posX - 0;
double diry = player.posY - 60;
double dirz = player.posZ - 0;
double len = Math.sqrt(dirx*dirx + diry*diry + dirz*dirz);
dirx /= len;
diry /= len;
dirz /= len;
double pitch = Math.asin(diry);
double yaw = Math.atan2(dirz, dirx);
//to degree
pitch = pitch * 180.0 / Math.PI;
yaw = yaw * 180.0 / Math.PI;
yaw += 90f;
if(yaw > player.rotationYaw) {
player.rotationYaw++;
} else if(yaw < player.rotationYaw) {
player.rotationYaw--;
}
This code without the if statement works properly. The yaw and pitch variables are in degrees.
What I am having trouble with is the fact that whenever I turn around (0, 60, 0) for a few times, the screen suddenly does a 360° turn, for no apparent reason.
This is a common problem. Or rather, the problem that people have commonly is they want to do "something across time" and don't know how to make it go "across time."
You need to lerp the camera a small distance each tick until the desired direction is achieved. You either need to:
a) create an event handler and subscribe to one of the TickEvents (pick an appropriate one, and then make sure to pick a phase, either Phase.START or Phase.END, or your code will run multiple times a frame).
b) in whatever method your code is already in (Note: this code must already be called once a tick) and do the lerping there.
Don't know how to calculate lerp? Stack Overflow already has that answered.
I have been working on a nonogram solver in Java, and all of my algorithm works fine, but I have been struggling with the visualization a little bit.
During the execution of the algorithm, I have access to two "solution-arrays". One is of type int[][], and contains values -1 for "certainly white", 0 for "uncertain" and 1 for "certainly black". Another array is of type float[][] which contains values between 0 and 1, here 0 is for certainly white, 1 is for certainly black and a value of .2 indicates it is more likely for the cell to be white than it to be black.
Since I've recently switched from PHP to Java programming (without proper introduction), I don't know much about visualizing this array properly. Of course I have first tried to simply print the first type of array to the console with characters like X, . and ?, but this is far from nice. I then found something about BufferedImage's, and I created the following function (for the float[][], the int[][] is similar):
public void toImage(int w, int h, float[][] solution) throws IOException {
int[] data = toImage1(w, h, solution);
BufferedImage img = toImage2(data, w, h);
toImage3(img);
}
public int[] toImage1(int w, int h, float[][] solution) throws IOException {
int[] data = new int[w * h];
int i = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int a = y / 100;
int b = x / 100;
int z = (int) (255 * Math.sqrt(1 - solution[a][b]));
if (solution[a][b] == 1 && ((((y % 100 >= 10 && y % 100 <= 15) || (y % 100 >= 85 && y % 100 <= 90)) && x % 100 >= 10 && x % 100 <= 90) || (((x % 100 >= 10 && x % 100 <= 15) || (x % 100 >= 85 && x % 100 <= 90)) && y % 100 >= 10 && y % 100 <= 90))) {
z = 100;
} else if (solution[a][b] == 0 && ((((y % 100 >= 10 && y % 100 <= 15) || (y % 100 >= 85 && y % 100 <= 90)) && x % 100 >= 10 && x % 100 <= 90) || (((x % 100 >= 10 && x % 100 <= 15) || (x % 100 >= 85 && x % 100 <= 90)) && y % 100 >= 10 && y % 100 <= 90))) {
z = 230;
}
data[i++] = z << 16 | z << 8 | z;
}
}
return data;
}
public BufferedImage toImage2(int[] data, int w, int h) {
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
img.setRGB(0, 0, w, h, data, 0, w);
return img;
}
public void toImage3(BufferedImage img) throws IOException {
File f = new File("Nonogram.png");
ImageIO.write(img, "PNG", f);
}
Here, w and h are supposed to be the amount of cells in each column resp. row multiplied by 100 (I want each cell to be represented by a block of 100x100 pixels).
I then also want an extra gray box in cells that are certain, that's what the if and else if are for.
This works beautifully, and creates images like this:
However, I run into two problems:
This function is super slow. After profiling the execution, I see 90% of the execution time goes to this function. After breaking down my function into two bits, as suggested in the comments, I get the following profile:
Instead of writing to a .png file, I would like a JFrame to display my image, but (again since I missed my proper introduction to Java), JFrame's don't seem to be my best friends and I can't seem to work out how to use them.
Is there a possibility to fill entire 100x100 px cells at once? Is there a way to not have to create the entire BufferedImage each time over, but just modify the bits that have changed in another method? Should I use something else than BufferedImage? What elements does my code need, could someone code out an example method, or the needed code snippets?
Okay, so it looks like writing to a file is not actually your biggest problem, it looks like your biggest problem is that you're dumping the pixels individually. Here are some things I might do:
Make a smaller image. 100x100 is a lot. Why not 20x20? You can always zoom in with an image editor.
Skip the int[] step entirely, and write to the BufferedImage directly.
Use bufferedImage.setRGB(startX, startY, w, h, rgbArray, offset, scansize) as you've been doing, but only for the section of the image you're drawing in bulk.
Do everything based on the values of a and b (as opposed to w and h, including and especially the loops, see point 4.
Fill the the boxes completely solidly, then add the inner rectangle by overwriting those lines separately. All these complicated if checks are killing your performance. Do it branchless (no if statements), and it will run much faster.
Put the code that's inside the for loops into a separate method that makes it more clear. Call it something like drawSingleBox.
Remember, more methods with good names make it easier to follow what's going on. writeImageToFile is preferred over toImage3. convertArrayToImage is preferred over toImage2.
Also, you asked about how to put an image as the background of a JFrame; once you have your fully drawn BufferedImage object, you can use the information in JFrame background image to do that part of it.
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.