I need to make the game impossible to win, so I used this code to move AI paddle.
My code may be slightly unreadable at first, so here's the theory behind it: the paddle always hits the ball with the middle point, unless the ball is closer to the edge than half of the paddle length, then the paddle stops to move with one of its ends touching top or bottom window frame.
if (ball.getY() < getHeight() - HEIGHT / 2
&& ball.getY() > HEIGHT / 2) {
paddleRight.setLocation(getWidth() - 3 * WIDTH, ball.getY()
- HEIGHT / 2);
image2.setLocation(getWidth() - 3 * WIDTH, ball.getY() - HEIGHT
/ 2);
image2.sendToFront();
} else if (ball.getY() < HEIGHT / 2) {
paddleRight.setLocation(getWidth() - 3 * WIDTH, 0);
image2.setLocation(getWidth() - 3 * WIDTH, 0);
image2.sendToFront();
} else {
paddleRight.setLocation(getWidth() - 3 * WIDTH, getHeight()
- HEIGHT);
image2.setLocation(getWidth() - 3 * WIDTH, getHeight() - HEIGHT);
image2.sendToFront();
}
My ball also speeds up randomly during the game:
boolean bool = rand.nextBoolean();
if (bool)
if (dx > 0)
dx += 1;
else
dx -= 1;
else if (dy > 0)
dy += 0.5;
else
dy -= 0.5;
ball movement consist of X and Y axis movement
And at some specific speed, if the paddle gets to one of the corners it starts to blink back and forth between top and bottom corner. I can't find the reason for that in my code.
Full code here
Your if statement does not correctly handle conditions where the ball is exactly at boundaries between conditions. You have:
if (ball.getY() < getHeight()-HEIGHT/2 && ball.getY() > HEIGHT/2) {
...
} else if (ball.getY() < HEIGHT/2) {
...
} else {
...
}
But what happens when ball.getY() == HEIGHT/2? In this case, it will fall through to the third block, but this is not the desired behavior - it will briefly jump to the third condition when ball.getY() == HEIGHT/2, hence flicker when the ball's Y position is at that edge. Instead, make a simple change to your second condition:
if (ball.getY() < getHeight()-HEIGHT/2 && ball.getY() > HEIGHT/2) {
...
} else if (ball.getY() <= HEIGHT/2) { // note <= instead of <
...
} else { // ball.getY() >= getHeight()-HEIGHT/2
...
}
This should take care of the transition cases.
By the way, unrelated, you should use curly braces in nested if statements like the one you have, e.g.:
if (bool) {
if (dx > 0)
dx += 1;
else
dx -= 1;
} else {
if (dy > 0)
dy += 0.5;
else
dy -= 0.5;
}
While your original logic happens to have the same effect in this case, your original code, despite its slightly misleading indentation, actually had this structure:
if (bool)
...
else if (dy > 0)
...
else // !bool && dy <= 0
...
That probably doesn't reflect the actual intent of that if and, even though it happens to work here, adding the curly braces is a good habit to get into because it can prevent potential subtle logic errors in more complex code.
Related
I was working on an animation on processing.
But, I have a question about the code below:
So, you can see on the output, the ball is going everywhere on the window. I want to make a barrier for the ball. The ball can pass from the middle but if it hit the line it goes somewhere with the speed of (int)random(1, 3).
How to make that?
Here is a picture of what I would like to achieve:
One way to do this is to check for collision when the ball passes through the middle, specifically when the ball's velocity would make y pass from < height/2 to > height/2.
Something like this:
if (((y < height/2 && y+dirY > height/2) || (y > height/2 && y+dirY < height/2)) && (x < width/2-middle || x > width/2+middle)){
dirY = (int) random (1,3);
dirX = (int) random (1,3);
}
(A more appropriate solution would be to create the collision when the edge of the ball passes the barrier, but I'll leave that up to you)
I'm not going to write code, but I'll tell you how to calculate it, it's not that hard.
Taken a circle, we know the center and the radius.
Taken a line we know the startpoint and endpoint.
The line intersects the circle if the distance from them center to startpoint or endpoint is smaller than radius (e.g. point is inside circle)
A line intersects circle if startpoint X is on the left of center (sX < cX), endpoint X on the right (eX > cX), and the distance between their Y and the center Y is less than radius (cY - sY < r)
Note that this really applies only in your simple case with a horizontal line. Normally is better to really work with vectors calculations to determine distance from center to line for example. That would allow you to work with diagonal lines etc.
For your toy model, the easiest way to handle collisions is considering a collision zone. So,
Establish the collision zone limits x1, x2, y1, y2. When the center of the ball enters this zone, collision rules apply.
Consider elastic collisions: your ball only collides with an horizontal line, so only the vertical component of the velocity can be affected, and it never looses speed.
Consider to test your toy model with a good bunch of initial conditions, so you can discover any artifacts due to the smallness of your model, the values you are using, and so on. (I have modified the initial position of the ball for a random initial position, run the model several times to see how it behaves under different initial conditions).
int dirX = (int)random(1, 3);
int dirY = (int)random(1, 3);
//int x = 20, y = 20;
int middle = 20;
int x = (int)random(20, 180);
int y = (int)random(20, 80);
int x1, x2, y1, y2;
void setup() {
size(200,200);
// collision zone limits
x1 = (width/2 - middle) + 20/2 - 1; // 89
x2 = (width/2 + middle) - 20/2 + 1; // 111
y1 = height/2 - 20/2 + 1; // 91
y2 = height/2 + 20/2 - 1; // 109
}
void draw() {
background(255);
ellipse(x, y, 20, 20);
line(0, height/ 2, width/2 - middle, height/2); // (0, 100, 80, 100)
line(width/2 + middle, height/2, width, height/2); // (120, 100, 200, 100)
x += dirX;
y += dirY;
if (x > width || x < 0)
dirX *= -1;
if (y > height || y < 0)
dirY *= -1;
if ((x < x1 && y > y1 && y < height/2 && dirY > 0) || // upper left and going down
(x < x1 && y > height/2 && y < y2 && dirY < 0) || // lower left and going up
(x > x2 && y > y1 && y < height/2 && dirY > 0) || // upper right and going down
(x > x2 && y > height/2 && y < y2 && dirY < 0)) // lower right and going up
dirY *= -1;
}
For a more complex model, you could consider more sophisticated situations as, for example, the backwards collision due to the ends of the lines, the collision of the contour of the ball with the lines, etc.
I'm trying to use Bresenham's Line Drawing Algorithm to draw a line on a 20x20 grid of tiles.
So basically, when the variable deltaerror (from the wiki) is greater than 1 (when the change in y is greater than the change in x), the y value becomes almost double what it should be. Hopefully, I explain it better in the comments of the code. I also think I found the source of the error, which I'll also explain in the comments.
This is the code I made, mostly copied from the pseudocode in the wiki.
public static void drawLine(Tile t1, Tile t2) {
int dx = t2.x_index - t1.x_index;
int dy = t2.y_index - t1.y_index;
double error = 0;
double d_error = Math.abs((double) dy / dx); // dx =/= 0, therefore no vertical lines
// when d_error is greater than 1, the bug occurs
int y = t1.y_index;
if (d_error <= 1) { // if related acute angle is < 45 degrees
if (dx < 0) { // line drawn towards left side of screen
for (int x=t1.x_index; x>=t2.x_index; x--) {
Board.tiles[x][y].setColour(Color.red);
error += d_error;
while (error >= 0.5) {
Board.tiles[x][y].setColour(Color.red);
y += dy > 0 ? +1 : -1;// this is where I think the error occurs. In the
// wiki for the algorithm, this should be some
// function sign(T) which "decides whether t is
// positive or negative". I wasn't really sure
// what this meant, so I just assumed it returned
// -1 if it was negative and +1 if it was positive.
// Also it's the only place where y is edited
// that seems to be where the error is occurring.
error -= 1.0;
}
}
} else if (dx > 0) { // line drawn towards right side of screen
for (int x=t1.x_index; x<=t2.x_index; x++) {
Board.tiles[x][y].setColour(Color.red);
error += d_error;
while (error >= 0.5) {
Board.tiles[x][y].setColour(Color.red);
y += dy > 0 ? +1 : -1;
error -= 1.0;
}
}
}
// update: switched x and y values when d_error is greater than 1.
} else { // if related acute angle is > 45 degrees
dx = t2.y_index - t1.y_index; // switch x and y values
dy = t2.x_index - t1.x_index;
d_error = Math.abs((double) dy / dx); // recalculate d_error
y = t1.x_index;
if (dx < 0) { // line drawn towards left side of screen
for (int x=t1.y_index; x>=t2.y_index; x--) {
Board.tiles[x][y].setColour(Color.red);
error += d_error;
while (error >= 0.5) {
Board.tiles[x][y].setColour(Color.red);
y += dy > 0 ? +1 : -1;
error -= 1.0;
}
}
} else if (dx > 0) { // line drawn towards right side of screen
for (int x=t1.y_index; x<=t2.y_index; x++) {
Board.tiles[x][y].setColour(Color.red);
error += d_error;
while (error >= 0.5) {
Board.tiles[x][y].setColour(Color.red);
y += dy > 0 ? +1 : -1;
error -= 1.0;
}
}
}
}
}
Thanks for your help!
EDIT: Switched x and y values when d_error is greater than 1. The line disappears when the slope is above 1 and below -1.
To quote from a quick google-result:
http://www.math.ubc.ca/~cass/courses/m308-02b/projects/puhalovic/#alldirections
"With slopes greater than 1 or less than -1, we must take the previous implementation and swap all x and y values to "move" the calculations back into the "First Octant"."
IOW: The textbook implementation of the bresenham only works for lines with (in your terms) d_error<=1. You'll have to implement the swapping mentioned in the link above.
I am making a tank game like the Atari tank game and I ran into some troubles. I'm trying to make the enemy's tank move towards the player's tank but it can't move diagonally since the player isn't allowed to do that also. However, the way I implemented it, it goes diagonally when the distance from the x and y axis are equal to each other. Is there a way I can make it so that it would be forced go in one direction for a while after changing direction? The way do it is that it will compare its x and y values with the player's tank ( the tank that is being passed in ) and the four cases are for if the x-component is bigger than y and is it on theright or left, and if the y-component is bigger than xand is it above or below the player's tank. Thank you for the help!
public void enemyMove(Tank t) {
if ( Math.abs(getX() - t.getX()) >= Math.abs(getY() - t.getY()) && getX() > t.getX() )
goLeft();
else if ( Math.abs(getX() - t.getX()) > Math.abs(getY() - t.getY()) && getX() < t.getX() )
goRight();
else if ( Math.abs(getX() - t.getX()) <= Math.abs(getY() - t.getY()) && getY() > t.getY() )
goUp();
else if ( Math.abs(getX() - t.getX()) < Math.abs(getY() - t.getY()) && getY() < t.getY() )
goDown();
setX(getX() + dx);
setY(getY() + dy);
}
public void goUp() {
dx = 0;
dy = -1;
}
public void goDown() {
dx = 0;
dy = 1;
}
public void goLeft() {
dx = -1;
dy = 0;
}
public void goRight() {
dx = 1;
dy = 0;
}
You can use the Manhattan distance and find the median then move the tank in both directions.
The code below is a little gross, but should do what you want. In your current code the enemy is moving one pixel in the X direction in the first frame, then one pixel in the Y direction in the next frame, which makes the movement look diagonal. The code below sets a short-term target point for the enemy tank to head towards in either the X or Y direction, at some distance away defined by MOVE_BLOCK. The enemy will move until it passes that target point, then recalculate which direction it should be moving. Note that enemyMove will be called every time a new frame is created, so probably 60 times a second.
// The tank will move this distance in x or y before changing directions
private final int MOVE_BLOCK = 120;
// The short-term target of the enemy tank
Point target = null;
public void enemyMove(Tank t) {
/* true if enemy is moving left and enemy is already left of target point,
* or moving right and right of target... */
boolean passedTarget = target == null ||
(dx < 0 && getX() < target.getX()) ||
(dx > 0 && getX() > target.getX()) ||
(dy < 0 && getY() < target.getY()) ||
(dy > 0 && getY() > target.getY());
// Calculate a new target point if the old target was passed
if(passedTarget) {
int xDist = Math.abs(getX() - t.getX());
int yDist = Math.abs(getY() - t.getY());
if ( xDist > yDist ) {
// Cover the remaining distance when close to the target
int moveLength = xDist < MOVE_BLOCK ? xDist : MOVE_BLOCK;
if( getX() >= t.getX() )
goLeft();
target = new Point(getX() - moveLength, getY());
else
goRight();
target = new Point(getX() + moveLength, getY());
} else {
int moveLength = yDist < MOVE_BLOCK ? yDist : MOVE_BLOCK;
if ( getY() >= t.getY() ) {
goUp();
target = new Point(getX(), getY() - moveLength);
} else {
goDown();
target = new Point(getX(), getY() + moveLength);
}
}
}
setX(getX() + dx);
setY(getY() + dy);
}
I'm really new to Java and am working on a class project - I need to draw some pixels in a panel. I was given the jar code for the panel, and now I need to make different trails - specifically, I need to create a trail of pixels that will go around the perimeter of the panel, and I need to create some circles.
Regarding the boxes - I've gotten part of it to work. My pixels starts in the upper left corner and run to the upper right corner, go down the right side of the panel, and then it goes a little crazy - I'm not sure if it stops at the bottom right corner or goes below the bounds of the panel itself, but it doesn't complete its trip around the perimeter. My code is:
import cs251.lab1.Display;
public class Visualizer {
private static final int PIXEL_SIZE = 50;
public static void main(String[] args) {
Display panel = new Display(10, PIXEL_SIZE);
drawWrappingDots(panel);}
public static void drawWrappingDots(Display panel) {
int x = 1;
int y = 1;
while (x > 0 && y > 0){
if (x < panel.getWidth()){
panel.drawNextPixel(x, y);
x++;
}
if (x == panel.getWidth()){
panel.drawNextPixel(x, y);
y++;
}
if (x > 0 && y == panel.getHeight()){
panel.drawNextPixel(x, y);
x--;
}
if (x == 0 && y == panel.getHeight()){
panel.drawNextPixel(x, y);
y--;
}
What am I doing wrong?
Second, how do I draw a circle? I know it needs to use the math library, but I'm not sure how to go about this. Any help on this is much appreciated. Thank you.
As for the first question, you could use multiple if-statements:
int x = 1; //x starts a little to the right of 0
int y = 0; //y starts at 0
while (x > 0 && y > 0){
if (x < panel.getWidth() && y == 0){
panel.drawNextPixel(x, y);
x++;
}
else if (x == panel.getWidth() && y != panel.getHeight()){
panel.drawNextPixel(x, y);
y++;
}
else if (x > 0 && y == panel.getHeight()){
panel.drawNextPixel(x, y);
x--;
}
else {
panel.drawNextPixel(x, y);
y--;
}
}
This way, you check both the x and y coordinates when deciding which way you want to draw the pixels. It starts with x=1 and y=0, then moves to the right until x hits the border, then moves downward until y hits the border, then moves left until x hits 0, and finally moves upwards until y hits 0 and the while condition becomes false.
As for the circle, you need to use trigonometry. Designate a point in the middle of the panel to draw the circle. Then designate a radius you want for the circle.
int middle = 50;
int radius = 20;
Then, you use trigonometry:
int deg = 0;
while ( deg <= 360 ) {
x = middle + (int)(Math.cos(deg)*radius);
y = middle + (int)(Math.sin(deg)*radius);
panel.drawNextPixel(x, y);
deg++;
}
That should do it.
Change while cycle to:
while (x > 0 && y > 0){
if (x < panel.getWidth()){
panel.drawNextPixel(x, y);
x++;
}
if (x == panel.getWidth() && y != panel.getHeight()){
panel.drawNextPixel(x, y);
y++;
}
if (x > 1 && y == panel.getHeight()){
panel.drawNextPixel(x, y);
x--;
}
if (x == 1 && y > 0){
panel.drawNextPixel(x, y);
y--;
}
Maybe, this is not perfect solution.
In this program I simulate the gravity. All works but when there is no more movement for the ball it keep bouncing 1-5 pixels depending on the gravity value I set. How could i stop the ball when the energy is lost? I want the xSpeed to become 0 and the ball to stay on a fix position.
Edit: The gravity variate from 1 to 100. The user can change the gravity.
energyLoss = 0.9 and dt = 0.2
// right and left wall collision
if (x + xSpeed > this.getWidth() - radius - 1) {
x = this.getWidth() - radius - 1;
xSpeed = -xSpeed;
} else if (x + xSpeed < 0 + radius) {
x = 0 + radius;
xSpeed = -xSpeed;
} else
x += xSpeed;
if (y == this.getHeight() - radius - 1) {
}
if (y > this.getHeight() - radius - 1) {
y = this.getHeight() - radius - 1;
ySpeed *= energyLoss;
ySpeed = -ySpeed;
// friction with the ground
xSpeed *= xFriction;
if (Math.abs(xSpeed) < .4)
xSpeed = 0;
} else {
ySpeed += gravity * dt; // velocity formula
y += ySpeed * dt + .5 * gravity * dt * dt; // position formula
}
repaint();
Simplistic Answer
After this line:
ySpeed *= energyLoss;
Change this line:
ySpeed = -ySpeed;
To something like this:
if (ySpeed < SomeMinimumValue)
{
ySpeed = 0;
}
else // invert speed, i.e. change direction.
{
ySpeed = -ySpeed;
}
Edit; Second try:
if (Math.abs(ySpeed) < SomeMinimumValue)
... as above.
Less code, more talk
It appears that the problem stems from the ball impacting the "ground" with a low y and or x speed. If this is the case, you need to zero the speed when impacting the "ground" with a sufficiently low y or x speed; zero y speed when y speed is low enough and zero x speed when x speed is low enough, not necessarily both at the same time. You also need to make sure you are not zeroing the x speed on a y impact and not zeroing the y speed on an x impact.
The simplest way to fix this problem is to detect a special case where the ball is on the wall with 0 (or very small) Y velocity. This is basically what DwB is suggesting.
However, you then need to go further and make sure that you stop applying gravity when you're in that situation.
Something like this:
// right and left wall collision
if (x + xSpeed > this.getWidth() - radius - 1) {
x = this.getWidth() - radius - 1;
xSpeed = -xSpeed;
} else if (x + xSpeed < 0 + radius) {
x = 0 + radius;
xSpeed = -xSpeed;
} else
x += xSpeed;
if (y == this.getHeight() - radius - 1
&& ySpeed == 0) { // Check speed too here!
// Do nothing for Y.
// friction with the ground
xSpeed *= xFriction;
if (Math.abs(xSpeed) < .4)
xSpeed = 0
}
else if (y > this.getHeight() - radius - 1) {
y = this.getHeight() - radius - 1;
ySpeed *= energyLoss;
if (Math.abs(ySpeed) < SomeMinimumValue)
ySpeed = 0;
else // invert speed, i.e. change direction.
ySpeed = -ySpeed;
// friction with the ground
xSpeed *= xFriction;
if (Math.abs(xSpeed) < .4)
xSpeed = 0;
} else {
ySpeed += gravity * dt; // velocity formula
y += ySpeed * dt + .5 * gravity * dt * dt; // position formula
}
repaint();