It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have a code that replicates a gravitational pull towards my mouse cursor when entered in the parameters. When the mouse is click, it creates a reverse effect pushing objects(Rectangle away from it). I am trying to set it so when you do click and hold, when the objects hit a certain number in the x or y coordinate, it will randomly change x and y of that object. Here is my code. The commented area is where I tried to make x and y go random when it hits 500 parameters.
import java.awt.*;
import java.util.Random;
public class Ball
{
private Color col;
private double x, y; // location
private double vx, vy; // velocity
public Ball(int new_x, int new_y, int new_vx, int new_vy)
{
x = new_x;
y = new_y;
vx = new_vx;
vy = new_vy;
}
public Ball()
{
Random gen = new Random();
x = gen.nextInt(480);
y = gen.nextInt(480);
vx = gen.nextInt(10);
vy = gen.nextInt(10);
col = new Color(gen.nextInt(255),gen.nextInt(255),gen.nextInt(255));
}
void paint( Graphics h)
{
h.setColor(col);
h.fillRect((int)x,(int)y,20,20);
}
void move(int currentX, int currentY, boolean isButtonPressed )
{
double dvx, dvy, rx, ry;
double r_mag;
x = x + vx;
y = y + vy;
//bounce
if (x > 480 || x < 0)
vx = -vx;
if (y > 480 || y < 0)
vy = -vy;
if ( currentX <500 && currentY <500) // mouse is on canvas, apply "gravity"
{
rx = currentX - x;
ry = currentY - y;
r_mag = Math.sqrt((rx*rx) + (ry*ry));
// if ( x = 500 || y = 500)
// Random x = new Random();
// x.nextDouble();
// Random y = new Random();
// y.nextDouble();
if (r_mag < 1)
r_mag = 1;
dvx = (rx / r_mag);
dvy = (ry / r_mag);
if (isButtonPressed)
{
vx = vx - dvx; // + makes balls move to cursor.
vy = vy - dvy; // - makes balls move away from cursor.
}
else
{
vx = vx + dvx; // + makes balls move to cursor.
vy = vy + dvy; // - makes balls move away from cursor.
}
}
// reduce speed slowly
vx = .99*vx;
vy = .99*vy;
}
}
The commented area is where I tried to make x and y go random when it hits 500 parameters.
So when x or y reaches 500, you want to randomly relocate the object?
Instead of
// if ( x = 500 || y = 500)
this is assignment, not comparison
// Random x = new Random();
redeclares x, not what you want
// x.nextDouble();
statement without effect
// Random y = new Random();
// y.nextDouble();
see above
you could use Math.random(), as in
if (x == 500 || y == 500) {
x = Math.random()*480;
y = Math.random()*480;
}
(note: Math.random() returns a double in the half-open interval [0,1), so you have to scale it; the scaling factor of 480 I used is a guess), or (less good, IMO) create a new Random instance each time you enter the if.
But,
x, y, and the velocities vx and vy are doubles, so it is very unlikely that the movements let x or y become exactly 500, so you should probably test for >= rather than == in the condition.
In the code, you flip the velocity when either coordinate passes 480, so getting to 500 or farther is difficult and can only be achieved by judicious acceleration using the mouse, so a smaller threshold may be wanted.
Related
I've just started programming and I'm having a bit of an issue with character movement. My issue is that the characters move in diagonal (it's 2D) to, lets say, the left top corner to the bottom right corner in the same time they move only from the left top to the right top. How do I make it respect the Pythagoras' theorem?
public class Player extends Rectangle{
public double spd = 1; //1 is the minimum
public boolean right, up, down, left;
public Player(int x, int y) {
super(x,y,32,32);
}
public void tick() {
if(right) {
x += spd;
}else if(left) {
x -= spd;
}else if (down) {
y += spd;
}else if(up) {
y -= spd;
}
}
Let us add a few more directions:
private double angle = 0; // 0 = left-to-right, Math.PI/2 = up, ...
public void tick() {
x += Math.cos(angle) * spd;
y += Math.sin(angle) * spd;
}
To preserve movement speed, you need to start using trigonometry (cosine and sine) to ensure that total speed is always the same. For a speed of 1, you can easily check that diagonal movement with
x += 0.7f; // ~ 1/sqrt(2) = sin(45 degrees) = sin(pi/4)
y += 0.7f;
is approximately correct, and "looks" about the same speed as horizontal or vertical movement. Quoting Pythagoras,
a^2 + b^2 = h^2 // pythagoras
dx*dx + dy*dy = spd*spd // for this specific instance
1/2 + 1/2 = 1 // plugging in our values, diagonal movement
1 + 0 = 1 // horizontal movement
cos(angle)^2 + sin(angle)^2 = 1 // definition of sine and cosine
Of course, for this to work, x and y should be either double or float - otherwise you will not be able to add fractional amounts.
I'm doing this bouncing ball problem and I have was given this formula: (velocity) vx = v0*cos(angle). and (x-position) x = v0*cos(angle)*t. However, I cannot get the ball to bounce properly.
The problem is that after the ball hits the right vertical wall, it starts to bounce inside certain range on the right-hand-side of the window. (y and vy shouldn't matter in this case.)
How can I fix this weird bouncing problem to make it bounce property in the x direction?
public class GamePanel2 extends JPanel implements KeyListener, ActionListener{
Timer tm = new Timer(60, this); //this refers to the ActionListener
public int score = 0;
public GamePanel2(){
addKeyListener(this);
setFocusable(true);
setBackground(Color.BLACK);
}
public int getScore() {
return score;
}
public double v0 = 100;
public double t = 0;
public double angle = Math.PI/2.5;
public double x = 0;
public double y = 0;
public double vx =0;
public double vy = 0;
public int move = 0;
public int paddlex =0;
public void paintComponent(Graphics g){
int h = getHeight();
int w = getWidth();
vx = v0*Math.cos(angle);
vy = v0*Math.sin(angle);
Graphics2D g2d = (Graphics2D)g;
g2d.translate(0.0,h);
g2d.scale(1.0, -1.0);
//ball
g2d.setColor(Color.GREEN);
g2d.fillOval((int)Math.round(x), (int)Math.round(y+6), 20, 20);
//paddle
g2d.setColor(Color.RED);
g2d.fillRect(paddlex + move, 0, 60, 6);
repaint();
}
//KeyListener methods
#Override
public void keyPressed(KeyEvent arg0) {
if(arg0.getKeyCode() == KeyEvent.VK_SPACE){
tm.start();
}
else if(arg0.getKeyCode()==KeyEvent.VK_ESCAPE){
tm.stop();
}
if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){
move += 30;
}
//if pressed right key
if(arg0.getKeyCode() == KeyEvent.VK_LEFT){
move -= 30;
}
repaint();
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
#Override
public void actionPerformed(ActionEvent arg0) {
t = 0.2;
vy -= 9.8;
x += vx;
y += (vy)*t-(t*t*9.8)*0.5;
if( x<= 0){
vx = v0*Math.cos(angle);
}
if (x>=getWidth()-20){
vx =-(v0*Math.cos(angle));
}
repaint();
}
}
You're not even close. The differential equations of motion for a ball with gravity supplying the only force are
d^2x/dt^2 = -9.8 and d^2x/dt^2 = 0
You need to integrate these equations. For this purpose, you need to get rid of the second degree differentials by introducing a new variable:
dv_y/dt = -9.8 and dv_x/dt = 0
dy/dt = v_y dx/dt = v_x
With Euler forward differences (the simplest possible integration method), this becomes:
v_y[i+i] = v_y[i] + h * -9.8
y[i+1] = y[i] + h * v_y[i]
v_x[i+1] = v_x[i] + h * 0 // x-velocity is constant!
x[i+1] = x[i] + h * v_x[i]
When the ball encounters a vertical wall with a perfectly elastic collision, the x velocity instantly changes sign. When it hits the floor or ceiling, the y velocity changes sign.
Your formula provides only the initial values of v_x and v_y. All x and y values after are results of the above Euler equations. In pseudocode it will look something like this:
// Initialize the velocity components.
vx = v0 * cos(theta)
vy = v0 * sin(theta)
// Initialize the position of the ball.
x = R // in the corner of the first quadrant
y = R
// Choose a time increment.
h = < a very small number of seconds >
// Start the clock.
t = 0
while (t < END_OF_SIMULATION) {
draw_ball(x,y)
x = x + h * vx;
y = y + h * vy;
vy = vy - h * 9.8;
// Check for bounces
// Assumes box has corners (0,0), (W,H)
if ((vx < 0 and x < r) or (vx > 0 && x > W-r)) x = -x;
if ((vy < 0 and y < r) or (vy > 0 && y > H-r)) y = -y;
t = t + h
}
Note that that 9.8 means that the units are meters and seconds. You need to scale pixels in the Java window and use a timer to get a realistic result.
To roughly simulate lossy collision, you can steal some velocity on every bounce:
x = -<a number a bit less than 1.0> * x and
y = -<a number a bit less than 1.0> * y
With these, the ball will slow down a bit every time it hits a wall.
I can't find where you're changing the angle after detecting a bounce. I also don't see bounds checking for all four sides of the windows the ball is in.
There's a related bug you might run into where there's a double bounce in a corner that leaves the ball outside the window after all the calculations are done. Think about ways to handle that case.
I will keep this quick.
Just making a processing program where the ball bounces off the rectangle.
At the moment I can only figure out how to do the top,left,right and bottom sides but when the ball goes into the corners of the rectangle, it doesn't bounce off but instead goes into the rectangle and after 1 or 2 bounces gets realeased.
I just really want to know how I should go about making the ball deflect off the corners of the rectangle so it doens't glitch
Here is my code :
float x = 100, y = 100, radius = 50;
float vx = 3, vy = 3;
float MX = 0;
float MY = 0;
void setup() {
size(500, 700);
}
void draw() {
background(0);
fill(255);
x += vx;
y += vy;
if (x + radius > width) {
vx = vx * -1;
}
//makes the ball bounce off the right
if (x - radius < 0) {
vx = vx * -1;
}
//makes the ball bounce off the left
if (y + radius > height) {
vy = vy * -1;
y = height - radius;
}
//make the ball bounce off the top
if (y - radius < 0) {
vy = vy * -1;
y = radius;
}
//makes the ball bounce off the top
if (y + radius > MY && x + radius > MX + 50 && x + radius < MX + 150 &&
y + radius < MY + 20) {
vy *= -1;
y = MY - 3 - radius;
} //Top Side
if (x - radius < MX + 100 && y > MY - 10 && y < MY + 10 && x - radius > MX) {
vx *= -1;
} // Right Side
if (y - radius < MY + 20 && x + radius > MX + 50 && x + radius < MX + 150 &&
y - radius > MY) {
vy *= -1;
y = MY + 20 + radius;
} //Bottom Side
if (x + radius > MX && y > MY - 10 && y < MY + 10 && x + radius < MX + 100) {
vx *= -1;
} // Left Side
ellipse(x, y, radius * 2, radius * 2);
rect(MX, MY, 100, 20);
}
void mouseMoved() {
MX = mouseX - 50;
MY = mouseY - 10;
if (MX < 0) {
MX = 0;
}
if (MX > 400) {
MX = 400;
}
if (MY < 0) {
MY = 0;
}
if (MY > 680) {
MY = 680;
}
Sorry I do know know how to insert the code very well, I am new to this site, please have mercy haha :)
Cheers
The problem is not that you need to detect corner collision. You've got two other problems:
Problem 1: When you detect a collision, you need to move the ball so it's not colliding with the rectangle anymore.
If you don't, then when the ball intersects the top of the box, you multiply the vy variable by -1. That causes the circle to start moving up. But the next frame, the circle is still colliding with the rectangle, because it hasn't moved up enough yet. So your code detects that collision, multiples vy by -1 again, and the ball moves back down. Next frame the same thing happens, until the ball eventually stop colliding with the rectangle.
Slow the framerate down to see what I'm talking about:
To fix this, you need to "pop" the ball back to a location that's no longer intersecting with the rectangle. That way you know it won't still be colliding in the next frame.
Problem 2: You shouldn't do collision on each side separately. Instead, do collisions between the entire circle and the entire rectangle, and then check one axis of movement at a time.
Checking against one side at a time will lead to plenty of headaches, including the problem where multiple sides are hit at one time. I would also bet that your code isn't doing what you think it is: try adding println() statements to all of your if statements to make sure they're executing when you think they are.
To fix this problem, I would create a collides() function that takes parameters for the next position of the ball, and returns whether the ball will be colliding with any side of the rectangle. Then pass in the next X and Y positions and flip their speeds. It'll look something like this:
if (collides(circleX + vx, circleY)) {
vx*=-1;
}
else {
circleX += vx;
}
if (collides(circleX, circleY + vy)) {
vy*=-1;
}
else {
circleY += vy;
}
You'd also want to add the logic for "popping" the ball so it's no longer colliding.
PS: Did you try searching for your problem on google or the Stack Overflow search? This question is almost an exact duplicate of your question, to the point where I'm pretty sure you're in the same class!
A quick dirty way i have previously achieved this is by first adding bounding boxes to each side and positioning them in a way that if the ball collides with for example on the top right corner i would then have a condition:
If ball intercepts bounds top and bounds right: do this;
I hope this helps!
I've made a simple code to get jumping ball positions, but I definitely missed something, because I get this:
Here's the code for getting x and y positions:
public Vector2f[] draw() {
float x = 0, y = height; // height - float value from class constructor;
ArrayList<Vector2f> graphic = new ArrayList<Vector2f>();
for(;;){
Vector2f a = new Vector2f(x, y);
graphic.add(a);
ySpeed -= 10;
y += ySpeed*Math.cos(angle);
x += xSpeed*Math.sin(angle);
if(y <= 0){
// float coef = -10 * y / ySpeed;
// ySpeed = ((ySpeed + coef) * (-1) ) * bouncyness;
ySpeed = (ySpeed * (-1)) * bouncyness;
System.out.println(ySpeed + " " + y);
y = 0;
}
if(x > Main.width)
break;
}
Vector2f[] graphicArray = new Vector2f[graphic.size()];
for (int i = 0; i < graphic.size(); i++) {
graphicArray[i] = graphic.get(i);
}
return graphicArray;
}
On its iteration y gets lower than the X axis on the first run. Then being zeroed,
So max height that you will get in that iteration is lower than original height.
The same happens later,
Until y will get to 0 without being set to it ( I think it will always converge to it ).
If you will set your height to be divided by 10 it should look alright.
For the bouncing case change if ( y <= 0) to if ( y<= 10 ) and remove y = 0 statement.
The correct situation ( not bouncing ), set y = Math.abs(y)
A few thoughts
I don't have a clue what you are doing with the angle. To me, it looks plain wrong. Try to get rid of it.
You should integrate the acceleration twice over one timestep to make it work physically correct.
x += v + acc * ∆time * ∆time * 0.5;
v += acc * ∆time;
Make y = -y when y < 0.
Where your ∆time is 1 and your acc is -10, I guess.
I'm trying to move a sprite across the screen in a straight line, towards on the location where've I touched the screen, what i did was upon the update() in each loop , it checks to see if the current sprite's location x y is == to the destination x ,y . if it hasn't sprite's x++ and y++...
the thing is ..it ain't moving in a straight line... as there are cases where the x or y coordinate reaches the destination x or y first... how do i changed it so that the both x and y meets the destination together?
my current pseudo code for the sprite object
destX = destination X
destY = destination Y
posX = current X
posY = current Y
public void update(){
if(destX > posX && destY < posY)
{
posX++;
posY--;
}
else if (destX > posX && destY > posY){
posX++;
posY++;
}
else if(destX < posX && destY > posY)
{
posX--;
posY++;
}
else if(destX < posX && destY < posY){
posX--;
posY--;
}
else if(destX < posX)
posX--;
else if(destX > posX)
posX++;
else if(destY < posY)
posY--;
else if(destY > posY)
posY++;
Check out: http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
This simple algorithm will tell you each X,Y coordinate on a line between two points. You could use this algorithm to compute all of the positions it needs to visit, store the coordinates in an array, and iterate over the array as you update the position.
From the Article:
function line(x0, x1, y0, y1)
int deltax := x1 - x0
int deltay := y1 - y0
real error := 0
real deltaerr := abs (deltay / deltax) // Assume deltax != 0 (line is not vertical),
// note that this division needs to be done in a way that preserves the fractional part
int y := y0
for x from x0 to x1
plot(x,y)
error := error + deltaerr
if error ≥ 0.5 then
y := y + 1
error := error - 1.0
This is the most primitive version. The article contains a better generalized algorithm that you should look at.
I am dealing with a similair problem as yours. (I have an arraylist holding the history of positions my player has gone and I want to use that to rewind the game.)
Instead of simply increasing x and y position with 1 you can:
Calculate the angle between the source postion and your destination
position.
Calculate the new direction using a variable which
represents the speed
Update your postion using calculated direction
I made a class of that. I hope it is usefull.
import java.awt.geom.Point2D;
public class MyVelocityCalculator {
public static void main(String[] args) {
Point2D.Double currentPosition = new Point2D.Double();
Point2D.Double destinationPosition = new Point2D.Double();
currentPosition.setLocation(100, 100);
destinationPosition.setLocation(50, 50);
Double speed = 0.5;
Point2D.Double nextPosition = MyVelocityCalculator.getVelocity(currentPosition, destinationPosition, speed);
System.out.println("player was initially at: "+currentPosition);
System.out.println("player destination is at: "+destinationPosition);
System.out.println("half seconds later player should be at: "+nextPosition);
}
public static final Point2D.Double getVelocity(Point2D.Double currentPosition, Point2D.Double destinationPosition, double speed){
Point2D.Double nextPosition = new Point2D.Double();
double angle = calcAngleBetweenPoints(currentPosition, destinationPosition);
double distance = speed;
Point2D.Double velocityPoint = getVelocity(angle, distance);
nextPosition.x = currentPosition.x + velocityPoint.x;
nextPosition.y = currentPosition.y + velocityPoint.y;
return nextPosition;
}
public static final double calcAngleBetweenPoints(Point2D.Double p1, Point2D.Double p2)
{
return Math.toDegrees( Math.atan2( p2.getY()-p1.getY(), p2.getX()-p1.getX() ) );
}
public static final Point2D.Double getVelocity(double angle, double speed){
double x = Math.cos(Math.toRadians(angle))*speed;
double y = Math.sin(Math.toRadians(angle))*speed;
return (new Point2D.Double(x, y));
}
}
Don't use integers. This is a very bad idea to work with ints. Use floats. The main concept is: define the number of steps you want to perform (s). Compute differences in X and Y (diffX and diffY). Don't take absolute values: Compute them this way
float diffX = destX - currentX;
Then compute the xMove and yMove values by dividing diffX and diffY by s (number of steps).
float moveX = diffX / s;
float moveY = diffY / s;
And now you have to add for each iteration the moveX and moveY values to the current position.
And for drawing it, you should use Graphics2D, which supports floating points. If you don't want to use Graphics2D, you can round the floats to ints, using Math.round(float).