I'm trying to make balls fall from the top of the window. I store ball objects in an ArrayList and, at the moment, I am doing this.
for (int i = 0; i < balls.size(); i++) {
Ball b = (Ball) balls.get(i);
if (b.isVisible()) {
b.move();
}
the move function just changes the y co-ordinate of the ball so it drops down the screen.
At the moment, it is all being painted at exactly the same time and fall at exactly the same time.
e.g. http://puu.sh/xsGF
How do I make it so they fall at random intervals?
My move() function is as follows.
public void move() {
if (y > 480) {
this.setVisible(false);
System.out.println("GONE");
}
y += 1;
}
You could add balls randomly during the game loop.
//add new balls randomly here:
if(<randomtest>) {
balls.add(new Ball());
}
for (int i = 0; i < balls.size(); i++) {
Ball b = (Ball) balls.get(i);
if (b.isVisible()) {
b.move();
}
else {
//also might be good idea to tidy any invisible balls here
//if you do this make sure you reverse the for loop
}
}
There are 2 things you can do:
Add a Timer. When the Timer goes off (every 10 ms for example), select a random ball, and let that one drop 1px. (Mind, you will get balls that will fall at different speeds at different times, because of the random factor)
Use a random value for the speed when initializing the ball. Increase the y coordinate by that speed value, so the balls will all fall at a different rate through the sceen.
The simplest approach, if you want constant velocity, is to place them im random positions putside the top of your viewport.
Since I guess you already draw them outside the screen just add a random displacement there and you are done. eg:
ball.y = -radius + random.nextInt(100);
Ok, seeing your move function, this is not really physically correct. You should have a acceleration. This makes the ball fall more realistically (of course there is air resistance etc, but I think this is enough for now). In order the let them fall at random times, you could either add them at random times (make them existing/visible at random time instances) or so.
class Ball {
private double acc = 9.81; // or some other constant, depending on the framerate
private double velocity = 0;
private double startFallTime = Math.random()*100; // set from outside, not here!
public void move() {
// check if ball is already here
if (startFallTime-- > 0) return;
if (y > 480) {
this.setVisible(false);
System.out.println("GONE");
}
velocity += acc;
y += velocity;
}
}
EDIT: Of course the acceleration stuff is optional, depending on what you want. If you want linear movement, then your approach is fine, it just looks better if the ball has an acceleration. ;) Also, I recommend adding the balls at random instances and not work with this startFallTime that I used, because this is physically not really correct. Depends on your needs though, so you have to figure out the right way by yourself.
Related
I'm trying to make a graphic project where a Ball runs away from my cursor, I already did the other way around where the ball seeks my cursor and when she arrives she loses velocity so it's like she's running fast until she comes around a range of 10 pixels and then she loses velocity until she touches the cursor.
The thing is, I can't find a way to make the ball run away from the cursor in a way that when I enter a diameter(from the ball), she runs slow, if I approach more she starts to run faster to get away but when my cursor leaves the diameter, she runs slow until she stops once again.
I hope I made it clear, I thought about a solution but I don't know if there's a library or some built function in Java that I could use guys:
-have like a percentage from 0 to 100 where the distance between my cursor and the ball fits inside, 0% is velocity=0, 100% is velocity=4 for example, do you have any idea if there is such thing that I could implement?
Thank you in advance!
I've made a Vector class where I change it and access the X and Y coordinates to make the ball move, I used basic trigonometry to make the vector ALWAYS the same length.
code of my ball (Chaser) class:
public class Chaser {
private double x;
private double y;
private double vel = 1;
private double hyp;
private Vector vector = new Vector(0, 0);
private double distance;
public Chaser(int width, int height){
x = width/2;
y = height/2;
}
public void setVel(Point m){
if(m.x != x)
hyp = Math.sqrt(Math.pow(Math.abs(m.x - x), 2) + Math.pow(Math.abs(m.y - y), 2));
else
hyp = Math.abs(m.y - y);
}
public void setDirection(Point m){
if(hyp == 0) return;
vector.change((m.x - x)/hyp, (m.y - y)/hyp);
}
public void draw(Graphics g){
g.setColor(Color.RED);
g.fillOval((int)x - 10, (int)y - 10, 20, 20);
g.setColor(Color.BLACK);
g.drawLine((int)x, (int)y, (int)(vector.getX()*15*vel) + (int)x, (int)(vector.getY()*15*vel) + (int)y);
}
public void move(Point m){
setVel(m);
setDirection(m);
useVector();
}
public void useVector(){
if(vector == null) return;
x -= vector.getX() * vel;
y -= vector.getY() * vel;
}
public void calculateVelocity(Point m){
if(vector == null) return;
// I don't know what to do yet
}
}
If you want to just push the ball around you can do something simple. Let's use vectors to make it easier to understand. Say ball holds the ball's center (x,y) and mouse contains the mouse position (x,y).
You can compute the distance between ball and mouse, that is (mouse - ball).length() to get how far away the mouse is from the ball.
If the distance > ball radius then the mouse is outside.
Otherwise you can do:
tmp = ball - mouse // get the vector from mouse to the ball.
tmp = tmp / tmp.length() * ball_radious // change the vector's length to match the radious of the ball.
ball = mouse + tmp // Move the ball such that the mouse will be on the edge.
As you move the mouse the ball will get pushed by the mouse.
If you want a bit of inertia, so the ball doesn't just stop when you don't push it anymore then you need to keep an additional vector speed and use tmp to get an acceleration.
Something like this:
tmp = ball - mouse // get the vector from mouse to the ball.
force = max(0, ball_radious - tmp.length()) // how strong we push the ball.
acceleration = tmp / tmp.legnth() * f(force) // compute the acceleration vector. f(force) is some function based on force, try k*f or k*f*f and see what looks better for your setup.
speed = speed * kDrag + acceleration // update the speed, kDrag should be between 0 and 1, start with something like 0.8 and try different values.
ball = ball + speed * time_delta // Update the ball's position.
You can play with the constants to get the right feel that you're looking for. time_delta is meant to normalize the speed between frams so you don't need to worry too much if there's some inconsistency between them. You can use a constant as well, but the movement might become jerky at times.
All operations above are vector operations.
So, my problem is that I'm currently working on a path-finding technique in an open world with tiles of various sizes. The object needs to find an optimal path to a destination inside an infinite world (it generates on the fly), which is filled with tiles of various sizes (which are not located on a set grid - they can have any location and size - and neither have to be integers). (The object has access to the data of all the tiles via and ArrayList). Now some factors that make this problem more difficult:
The objects itself has a size and cannot move through tiles. Therefore, it is possible for a path to exist that is too narrow for the object to move through.
The target destination may itself be a moving object.
It is possible for there to be dozens of such objects at the same time - so it is necessary for the algorithm to either be light on the system or for the path to be calculated in a few separate ticks of the program.
I tried implementing solutions for maze-solving techniques, but the main problem is the in most mazes, the tiles can only have very specific coordinates (such as whole numbers) and are always the same size.
I also tried rendering the scene as a giant conventional maze where tiles are actually pixels of tiles (so if i have a 20x40 tile it becomes a 20x40 block of 1x1 tiles), but ran into performance issues and the still didn't solve the issue with a path potentially being to narrow for the object to fit through.
EDIT:
Terribly sorry for my poor wording before, that happens when I'm trying to rush to a solution without fully understanding the question. So what I'm using the algorithm for at the moment is for NPC enemies to find their way to the player around obstacles. Here is an example of a scene:
The black circle with an arrow is the player, the black bush-like blobs are the NPC enemies. So this my my current algorithm I'm using for the enemy AI:
void move() { //part of the Enemy class, this function is called once each tick for every enemy
PVector velocity = new PVector(speed*game.dt, 0); //speed is a pre-set float denoting the enemy's speed, game.dt is deltatim
velocity.rotate(atan2(game.player.location.y-location.y, game.player.location.x-location.x)); //game.player.location is a PVector of the player's position, location is a PVector of this enemy's position
boolean init_collision = getTileCollision(); //getTileCollision is a boolean of whether this enemy is colliding with any tile
location.add(velocity);
boolean collision = getTileCollision();
if (!init_collision && collision) { //if the enemy happens to spawn inside a tile, let is move out of it before checking for collision
location.sub(velocity);
if (desired_heading != -1) { //desired heading is the angle, in radians, of which 90-degree angle the enemy wants to move in, by default set to -1 (see my descrition of this algorithm below)
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(current_heading); //current heading the an angle, in radians, of which 90-degree angle the enemy is currently moving in. set to -1 by default but can not equal -1 if desired_heading is not -1
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
desired_heading = -1;
current_heading = -1;
}
} else {
desired_heading = -1;
current_heading = -1;
}
} else {
float original_heading = velocity.heading();
desired_heading = radians(round(degrees(velocity.heading())/90.0)*90.0); //round to the nearest 90 degrees
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
}
float turn = radians(90);
while (true) { //if it cant move, try rotating 90 degrees and moving
velocity.rotate(turn);
location.add(velocity);
if (!getTileCollision() && abs(round(degrees(current_heading)) - round(degrees(velocity.heading()))) != 180) {
current_heading = velocity.heading();
break;
} else {
location.sub(velocity);
}
}
}
} else {
desired_heading = -1;
current_heading = -1;
}
}
So what my terrible code hopes to accomplish is the the enemy first tries to move directly at the player. If it encounters an obstacle, it will round its angle to the nearest 90 degrees, set desired_heading to this and try to move through. If it cant, it will rotate another 90 degrees and so forth, always keeping the original rounded angle in mind.
This doesn't work remotely well as first of all, rotating 90 degrees has a 50% chance to go in the exact wrong diretion, so I tried adding
if (abs(original_heading - velocity.heading()+turn) < abs(original_heading - velocity.heading()-turn)) {
turn = radians(-90);
}
right before the while (true) but that broke the algorithm completely (sometimes the enemy will freeze in deep thought and not move ever again).
What am I doing terribly wrong? Should I try a different algorithm or does this one have potential?
I hope this is a better question now...
Hey I am trying to figure out the best way to implement gravity in my program that is simply a ball bouncing. The program uses a method that is called 50 times a second (the rate at which the gametimer ticks) and in this method I call the gravity method. In the gravity method I currently have
public void Gravity(){
this.currentPositionY = this.currentPositionY + 9;
if (this.currentPositionY >= 581){
this.currentPositionY=581;
}
}
Problems with my code: In gravity velocity is not constant it varies with the time, but I am unsure of how to implement time with the gravity method being called so often. Also currently I have it so that the ball stops at 581, so that it does not fall through the screen. How would I implement a bounce that is higher when the ball falls for longer and shorter when the ball falls less? Thanks for your time!
Have a variable outside of the method for its y-velocity. Every tick, increase its velocity accounting for gravity.
If the ball is past a boundary, set it to the boundary and set the velocity to -1*velocity to make it "bounce" in the other direction.
Maybe something like this:
private int currentVelocityY = 0;
private int gravity = 3;
public void Gravity(){
this.currentPositionY = this.currentPositionY + this.currentVelocityY;
if (this.currentPositionY >= 581){
this.currentPositionY=581;
this.currentVelocityY = -1 * this.currentVelocityY;
}
currentVelocityY = currentVelocityY + gravity;
}
Im trying to get a rectangle to move from a fixed location, to a click point in my game. I have it working to the point that im getting good coordinates from the e.getX and e.getY methods in the mouse listener class. I also have the rectangle moving in straight lines to the side or up and down by increasing and decreasing the positionX and Y values but i need a way to move it to any point that i click on, basically i need to set the change in x and y. The way i was thinking is to compute the rise and run of the line from the click points and the start point and then set the x and y values to the rise and run. If i do it this way, i need a way to decrease the rise and run values down to their lowest terms so it moves fluidly. Or if there is a way im not thinking of that you can offer any guidence that would be helpfull.
How are you drawing it at the moment? Below is a method I used to shoot bullets in a little game that I made/really should finish sometime.
This is by no means all the code you will need, and in some places there are better ways to do things (which I'm sure people will start to tell me in the comments section after :) ) but it should give you a basis to work from.
The one thing missing from this is regulating the speed at which it repaints (fps), sorry but I can't remember the simple way to do this right now (need to get back to my actual job!) Hope it helps.
The barebones of it will be something like this:
//these set out the current position of your rectangle
double recX, reYy, recH, recW;
//this gives something to manage speed
int speed;
public void paintComponent(Graphics g) {
g.drawRectangle(recX,rexY,recH,recW);
}
//here we work out the movement
public void clickedScreen(double x, double y){
double newX = x;
double newY = y;
//calculate the speed to move at
vX = newX - recX;
vY = newY - recY;
//get the distance
length = Math.sqrt((v1*v1)+(v2*v2));
//make it a unit vector
v1 = v1/length;
v2 = v2/length;
}
public moveRec(){
recX = recX+(v1*speed);
recY = recY+(v2*speed);
}
while(true) {
moveRec();
repaint();
}
I am working on a game in which you are a simple circle that fires bullets and its multiplayer and so on. Well, I am trying to make boundaries sort of like a maze type thing that u have to go through I have tried collision detection like this:
public void checkCollisions(){
Rectangle r1 = bo.getBounds();
Rectangle d = p.getBounds();
if (d.intersects(r1))
border = true;
}
And basically if border = true then i stop the character from moving. I have 2 problems when i do this,
He doesnt stop just goes REALLY slow.
He stays at the REALLY slow state even off the border.
I use border like this:
boolean border = false;
then in my paint method i state this:
if (border)
p.dx = 0;
p.dy = 0;
p represents the Guy class :P
More of the dx and dy:
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_A)
dx = -2;
if (key == KeyEvent.VK_D)
dx = 2;
if (key == KeyEvent.VK_W)
dy = -2;
if (key == KeyEvent.VK_S)
dy = 2;
and for keyReleased i just change the value of dx and dy to 0
also for how the guy moves:
public void move() {
x = x + dx;
y = y + dy;
}
Please help me figure out why this isn't working.
OK, I still think a full restructuring of your game logic is in order, but I think I can shed light as to what's going on. Let's look at the various places where things are happening:
PAINT: On the Swing thread, when paint() is called, you see if there were collisions and if so zero out the speeds (assuming you fix that if block).
KEY: On the Swing thread, when a key is pressed, you set the speed according to the key pressed.
CHECK: At some unknown point, you check for collisions and record whether there was one.
MOVE: At some unknown point, you update your "guy's" position with the speed.
So here's the problem: in Java, just like any other program, you get multiple key pressed events when you're holding down a key. There will be a short delay between the first and second, and then they will repeat rapidly. Try it in a text box in your browser, the same behaviour occurs there.
So how does that affect you? Well, you're probably getting into a scenario like this:
PAINT -> speed set to zero
KEY -> speed set back to -2
MOVE -> guy is moved -2
CHECK -> border = false
PAINT -> speed set to zero again
Really, if you restructure the code so that you get a game loop that looks something like this:
public void runGame() {
while(true) {
updateSpeeds();
updatePositionFromSpeed();
repaint();
}
}
Where updateSpeeds() would instead query whether the key is down or up and also compute whether the guy could move in that direction, and updatePositionFromSpeed() would update the guy's position. Then paint() would rely only on the guy's x and y coordinates, would not write to them, and would not need to know about the speed.
here's a very easy solution.
Here's a bit of my pseudo code.
if(player.getBounds().intersects(wall.getBounds())){
//Go Back to prior position, regardless of direction coming from. Since the reverse velocity X and velocity Y directions are taken care off
x -= velX;
y -= velY;
//Then Stop at that prior position to make next move
velX = 0;
velY = 0;
}