I'm making a rudimentary particle simulator in Java. For now, all I've done is make the particles atract each other with an equivalent to the electrical force. This part works fine (or at least as well as you would expect for such a basic model).
However, when I add a few particles, the program loses their values for position, velocity and acceleration, but does not lose other data (like, for example, their ID number). This does not always happens with the same amount of particles. Sometimes it happens when I add the fourth, fifth, second or third particle, but never with the first one. It always happens when I click to add a particle, and after it fails, I can no longer add anything (which is odd), and the particles don't move anymore (as you would expect, being their velocities and accelerations 0).
I am storing the particles in an ArrayList. The array does not lose the data (I've checked, the objects are in there, and I can even call their toString() method and retrieve their ID). The problem seems to be related to synchronization (given that it doesn't always happen at the same moment, it seems to be a bit random), but I can't figure out what it is.
I leave all the relevant code below.
public class Scene implements KeyListener, MouseListener, MouseMotionListener{
public static ArrayList<Particle> particleArray = new ArrayList<Particle>();
public static Object particleLock = new Object();
public void update() {
synchronized(particleLock) {
for(Particle particle: particleArray) {
double resultX = 0;
double resultY = 0;
for(int i = 0; i<particleArray.size(); i++) {
if(i != particleArray.indexOf(particle)) {
double[] result = PhysicsEngine.applyElectircalForce(particle, particleArray.get(i));
resultX += result[0];
resultY += result[1];
}
}
particle.netForceX = resultX;
particle.netForceY = resultY;
particle.update();
}
}
}
public void mousePressed(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
boolean positive = true;
if(e.getButton() == MouseEvent.BUTTON1) {
positive = true;
} else if(e.getButton() == MouseEvent.BUTTON3) {
positive = false;
}
synchronized(particleLock){
particleArray.add(new Particle(mouseX, mouseY, positive));
System.out.println("New particle added at " + mouseX + ", " + mouseY);
}
}
}
public class Particle{
public double x;
public double y;
public Point2D position;
public double velX;
public double velY;
public double acX;
public double acY;
private Color particleColor;
private int radius = 10;
// PHYSICS
public double mass;
public double charge;
public double netForceX;
public double netForceY;
private boolean positive;
public Particle(double x, double y, boolean positive) {
this.x = x - radius;
this.y = y - radius;
this.velX = 3;
this.velY = 2;
this.acX = 0;
this.acY = 0;
this.mass = 100;
this.positive = positive;
if(positive) {
this.charge = defaultCharge;
} else {
this.charge = defaultCharge*(-1);
}
this.position = new Point2D.Double(x, y);
particleColor = Color.WHITE;
}
public void update() {
acX = netForceX / mass;
acY = netForceY / mass;
velX += acX;
velY += acY;
if(x<=0 || x>=Simulation.WIDTH - 23){
velX = velX * -1;
x+= velX;
}
if(y<=0 || y>=Simulation.HEIGHT - 35){
velY = velY * -1;
y+= velY;
}
synchronized(Scene.particleLock) {
for(Particle otherPart: Scene.particleArray) {
if(otherPart.equals(this)) {
continue;
}
double distance = otherPart.position.distance(position);
if(distance <= radius + otherPart.radius) {
//aplicar lo que sé de choques de alguna manera
}
}
}
x+= velX;
y+= velY;
position.setLocation(x, y);
}
}
public class PhysicsEngine {
static double electricalConstant = 100000;
public static double[] applyElectircalForce(Particle thisPart, Particle otherPart) {
double distance = otherPart.position.distance(thisPart.position);
double angle = Math.asin(Math.abs(thisPart.y - otherPart.y)/distance);
double force = (electricalConstant * thisPart.charge * otherPart.charge)/Math.pow(distance, 2);
double forceX = force * Math.cos(angle);
double forceY = force * Math.sin(angle);
if(otherPart.x < thisPart.x) {
forceX = forceX*(-1);
}
if(otherPart.y < thisPart.y) {
forceY = forceY*(-1);
}
double[] result = {forceX, forceY};
return result;
}
}
I once had a similar problem with synchronization when I was working on an android project, try declaring particleArray volatile so that the compiler knows that particleArray will be changed on other or multiple threads. If that does not work I would suggest using a queue to push changes to the particle array from different threads and then pulling the intended changes to the array list in the update method to update your particle array list. In my experience changing values directly between different threads almost always causes problems.
Related
Our restart button is only appearing by itself. Our two tanks disappear when we run the game. We haven't coded the function of the button, we're just trying to figure out how to make the button appear in the lower left corner
We moved the code into the game object class, but it does not change anything
This is our game object class. The button is at the bottom.
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameObject {
// Every moving object in our game is a separate GameObject. However, since there are different types of moving objects (eg. players and
// projectiles), each with their own variables and functions, we don't want to instantiate GameObjects (otherwise, we wouldn't be able
// to distinguish between a player and a projectile). Instead, we want to create derived (also known as child) classes to implement the
// separate functionality, and instantiate the derived/child classes instead. The purpose of this GameObject "parent" class is to
// provide the derived/child classes with a set of useful functions for handling physics interactions (eg. collisions with the terrain
// or the walls of the arena) between different GameObjects (eg. player/player, player/projectile, or even projectile/projectile). Note:
// depending on the features you implement, you may not end up using all of the functions provided in this class.
// Every GameObject contains its own velocity, position, size, and mass information (necessary for moving and handling physics
// interactions). We store this information in the GameObject class instead of the derived/child classes because every type of
// GameObject has the same set of velocity, position, size, and mass variables.
public double vX; // Velocity in the x direction. Positive if moving right. Negative if moving left.
public double vY; // Velocity in the y direction. Positive if moving down. Negative if moving up. Notice how it's flipped from the usual
// coordinate system in math!
public double posX; // Position along the x direction. Ranges from 0 (left edge) to maximumX (see below).
public double posY; // Position along the y direction. Ranges from 0 (top edge) to maximumY (see below).
public int width; // Width of the bounding box of the GameObject.
public int height; // Height of the bounding box of the GameObject.
public int mass; // Used in realistic physics collision calculations. Ignore it if you don't want to implement that feature.
public double radius; // Used for circular GameObjects only.
public int maximumX; // Maximum x position for a GameObject, equal to the arena width subtracted by the game object's width.
public int maximumY; // Maximum y position for a GameObject, equal to the arena height subtracted by the game object's height.
// Constructor. All derived (ie. child) classes call this constructor in their own constructors. The resulting derived/child class
// object can then call the other functions in this class.
public GameObject(int arenaWidth, int arenaHeight, double vX, double vY, double posX, double posY, int width, int height, int mass) {
this.vX = vX;
this.vY = vY;
this.posX = posX;
this.posY = posY;
this.width = width;
this.height = height;
this.mass = mass;
this.maximumX = arenaWidth - width;
this.maximumY = arenaHeight - height;
radius = Math.min(width, height) / 2.0;
}
// Note: No need to change this function since we're going to override it in the the child classes.
public boolean move(Map map, double translateX, double translateY) {
return false;
}
// Check if the calling GameObject currently intersects with the obj GameObject.
public boolean currentlyIntersects(GameObject obj) {
return (posX + width >= obj.posX && posY + height >= obj.posY && obj.posX + obj.width >= posX && obj.posY + obj.height >= posY);
}
// Check if the calling GameObject will intersect with the obj GameObject, after both have moved according to their velocities. A note
// of caution: what might go wrong if either player moves too fast?
public boolean willIntersect(GameObject obj) {
double nextX = posX + vX;
double nextY = posY + vY;
double nextObjX = obj.posX + obj.vX;
double nextObjY = obj.posY + obj.vY;
return (nextX + width >= nextObjX && nextY + height >= nextObjY && nextObjX + obj.width >= nextX && nextObjY + obj.height >= nextY);
}
// Clip the calling GameObject to within the arena's x bounds, if it has moved outside the arena along the x direction.
public boolean xClip() {
if (posX < 0) {
posX = 0;
return true;
} else if (posX > maximumX) {
posX = maximumX;
return true;
}
return false;
}
// Clip the calling GameObject to within the arena's y bounds, if it has moved outside the arena along the y direction.
public boolean yClip() {
if (posY < 0) {
posY = 0;
return true;
} else if (posY > maximumY) {
posY = maximumY;
return true;
}
return false;
}
// If the calling GameObject will move outside the arena along either direction (after moving according to its velocity), this function
// tells you which of the four edges of the arena it hit. If the calling GameObject will stay within the bounds of the arena, this
// function returns null.
public Direction hitEdgeDirection() {
if (posX + vX < 0) {
return Direction.LEFT;
} else if (posX + vX > maximumX) {
return Direction.RIGHT;
}
if (posY + vY < 0) {
return Direction.UP;
} else if (posY + vY > maximumY) {
return Direction.DOWN;
} else {
return null;
}
}
// If the calling GameObject will intersect with the "other" GameObject (after both move according to their velocities), this function
// tells you which of the four sides of the calling GameObject that the "other" GameObject hit. If the calling GameObject will not
// intersect with the "other" GameObject, this function returns null. Note: this function is great for figuring out when and where two
// rectangles intersect, but is it a good choice for handling circle/rectangle or circle/circle intersections?
public Direction hitObjectDirection(GameObject other) {
if (this.willIntersect(other)) {
double dx = other.posX + other.width / 2.0 + other.vX - (posX + width / 2.0 + vX);
double dy = other.posY + other.height / 2.0 + other.vY - (posY + height / 2.0 + vY);
double theta = Math.acos(dx / (Math.sqrt(dx * dx + dy * dy)));
double diagTheta = Math.atan2(height / 2.0, width / 2.0);
if (theta <= diagTheta) {
return Direction.RIGHT;
} else if (theta <= Math.PI - diagTheta) {
if (dy > 0) {
return Direction.DOWN;
} else {
return Direction.UP;
}
} else {
return Direction.LEFT;
}
} else {
return null;
}
}
// Change the calling GameObject's velocity (to simulate a "bouncing" effect) based on which direction it intersected another GameObject
// or the edge of the arena. If the passed in direction is null, this function does nothing (why is this a good idea?). This function is
// best used with the hitEdgeDirection and hitObjectDirection functions above.
public void bounce(Direction d) {
if (d == null) {
return;
}
// Note: We probably should use a "switch" statement here instead. But for pedagogical purposes it's left as a simple if/else
// conditional.
if (d == Direction.UP) {
vY = Math.abs(vY);
} else if (d == Direction.DOWN) {
vY = -Math.abs(vY);
} else if (d == Direction.LEFT) {
vX = Math.abs(vX);
} else if (d == Direction.RIGHT) {
vX = -Math.abs(vX);
}
}
// TODO: (Challenge!) If you want to implement realistic sphere-sphere collisions that take into account the laws of physics, do so in
// the function below.
public boolean bounceWith(GameObject otherObj, Map map, long frames, double[] actualVelocities) {
return false;
}
// Calculate the distance from (pointX, pointY)---perhaps representing the center of a circle---to the closest point on a rectangle
// bounded by minX (left), maxX (right), minY (top), and maxY (bottom). If the point is inside the rectangle, this function returns 0.
public double pointToRectSqrDist(double minX, double maxX, double minY, double maxY, double pointX, double pointY) {
double dx = Math.max(Math.max(minX - pointX, 0), pointX - maxX);
double dy = Math.max(Math.max(minY - pointY, 0), pointY - maxY);
return dx * dx + dy * dy;
}
// Rotate the point (x, y) "degrees" degrees around (centerX, centerY) in counterclockwise fashion, and return the resulting point in an
// array of length 2. If the returned array is "result", then (result[0], result[1]) is the final point.
public double[] rotatePoint(double centerX, double centerY, double degrees, double x, double y) {
double s = Math.sin(Math.toRadians(degrees));
double c = Math.cos(Math.toRadians(degrees));
x -= centerX;
y -= centerY;
double xNew = x * c - y * s;
double yNew = x * s + y * c;
double[] result = new double[2];
result[0] = xNew + centerX;
result[1] = yNew + centerY;
return result;
}
// Note: No need to change this function since we're going to override it in the the child classes.
public void draw(Graphics g) {
}
public static void main(String []args){
JButton b= new JButton("Reset");
JFrame f = new JFrame();
f.setSize(1200,800);
f.setVisible(true);
f.getDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
f.add(p);
p.add(b);
b.setSize(50,50);
b.setVisible(true);
b.setLocation(50, 50);
}
}
This is our arena class:
// TODO: Feel free to import any other libraries that you need.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
#SuppressWarnings("serial")
public class Arena extends JPanel {
public int arenaWidth;
public int arenaHeight;
public Player player1;
public Player player2;
Timer timer;
public static int INTERVAL = 35;
public long lastTick;
// TODO: Add other variables to keep track of the game state or other game objects (eg. the map) that will be in your game. Don't forget
// to instantiate them in reset()!
// Constructor. Called inside Game.java for setting up the Arena on game start.
public Arena() {
// Create a timer that calls the tick() function every INTERVAL milliseconds. Every call of the tick() function is a "frame".
timer = new Timer(INTERVAL, new ActionListener() {
public void actionPerformed(ActionEvent e) {
tick();
}
});
lastTick = System.currentTimeMillis();
timer.start();
setFocusable(true);
// TODO: To recognize key presses, you need to fill in the following.
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent butthole) {
if (butthole.getKeyCode() == KeyEvent.VK_W) {
player1.isWPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_S) {
player1.isSPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_A) {
player1.isAPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_D) {
player1.isDPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_SPACE) {
player1.isSpacePressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_UP) {
player2.isUpPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_DOWN) {
player2.isDownPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_LEFT) {
player2.isLeftPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_RIGHT) {
player2.isRightPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_ENTER) {
player2.isEnterPressed = true;
}
}
public void keyReleased(KeyEvent butthole) {
if (butthole.getKeyCode() == KeyEvent.VK_W) {
player1.isWPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_S) {
player1.isSPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_A) {
player1.isAPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_D) {
player1.isDPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_SPACE) {
player1.isSpacePressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_UP) {
player2.isUpPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_DOWN) {
player2.isDownPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_LEFT) {
player2.isLeftPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_RIGHT) {
player2.isRightPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_ENTER) {
player2.isEnterPressed = false;
}
}
});
}
// Resets the game to its initial state.
public void reset() {
this.removeAll();
this.setBackground(Color.WHITE);
this.setOpaque(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
arenaWidth = (int) screenSize.getWidth();
arenaHeight = (int) screenSize.getHeight();
player1 = new Player(arenaWidth, arenaHeight, 100, 100, 1);
player2 = new Player(arenaWidth, arenaHeight, arenaWidth - Player.INIT_SIZE - 100, arenaHeight - Player.INIT_SIZE - 100, 2);
requestFocusInWindow();
}
// Function called once per "frame".
void tick() {
// While tick() should be called once every INTERVAL amount of time, there's no guarantee of that, particularly if you have a lot
// of background apps running. Thus, we need to calculate the time difference (timeDelta) between every two calls of the tick()
// function. Note: 1 divided by this difference is commonly known as the "frames per second", or fps.
long currentTime = System.currentTimeMillis();
long timeDelta = (currentTime - lastTick)/35;
lastTick = currentTime;
if ((player1.isWPressed && player1.isSPressed) ||
(!player1.isWPressed && !player1.isSPressed)) {
player1.vX = 0;
player1.vY = 0;
} else if (player1.isWPressed) {
//System.out.println("Up");
player1.vX = Math.cos(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
player1.vY = Math.sin(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
//player1.posY -= player1.speed*timeDelta;
// MOVE FORWARD
} else if (player1.isSPressed) {
//System.out.println("Down");
player1.vX = -Math.cos(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
player1.vY = -Math.sin(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
//player1.posY += player1.speed*timeDelta;
// MOVE BACKWARD;
}
if (player1.isAPressed) {
//System.out.println("Left");
player1.RotationDegree -= player1.rotateSpeed* timeDelta;
// MOVE BACKWARD;
}
if (player1.isDPressed) {
//System.out.parintln("Right");
player1.RotationDegree += player1.rotateSpeed* timeDelta;
// MOVE BACKWARD;
}
if(player1.RotationDegree > 360) {
player1.RotationDegree -= 360;
}
else if(player1.RotationDegree < 0) {
player1.RotationDegree += 360;
}
player1.move(null, player1.vX, player1.vY);
if ((player2.isUpPressed && player2.isDownPressed) ||
(!player2.isUpPressed && !player2.isDownPressed)) {
player2.vX = 0;
player2.vY = 0; }
else if (player2.isUpPressed) {
//System.out.println("Up");
player2.vX = Math.cos(player2.RotationDegree*Math.PI/180)*player2.speed* timeDelta;
player2.vY = Math.sin(player2.RotationDegree*Math.PI/180)*player2.speed* timeDelta;
//player2.posY -= player2.speed*timeDelta;
// MOVE FORWARD
}
else if (player2.isDownPressed) {
//System.out.println("Down");
player2.vX = -Math.cos(player2.RotationDegree*Math.PI/180)*player2.speed * timeDelta;
player2.vY = -Math.sin(player2.RotationDegree*Math.PI/180)*player2.speed * timeDelta;
//player2.posY += player2.speed*timeDelta;
// MOVE BACKWARD;
}
if (player2.isLeftPressed) {
//System.out.println("Left");
player2.RotationDegree -= player2.rotateSpeed*timeDelta;
// MOVE BACKWARD;
}
if (player2.isRightPressed) {
//System.out.println("Right");
player2.RotationDegree += player2.rotateSpeed*timeDelta;
// MOVE BACKWARD;
}
if(player2.RotationDegree > 360) {
player2.RotationDegree -= 360;
}
else if(player2.RotationDegree < 0) {
player2.RotationDegree += 360;
}
player2.move(null, player2.vX, player2.vY);
player1.currentReload -= timeDelta;
if (player1.currentReload <= 0)
;
{
if (player1.isSpacePressed) {
// create bullet and fire
BasicWeapon newBullet = new BasicWeapon(arenaWidth, arenaHeight, player1.posX + player1.radius, player1.posY + player1.radius, player1);
player1.bullets.add(newBullet);
player1.currentReload = player1.MaxReload;
}
}
ArrayList<PlayerProjectile> bulletsToDelete1 = new ArrayList<PlayerProjectile>();
for (int i = 0; i < player1.bullets.size(); i++) {
PlayerProjectile bulletToChange = player1.bullets.get(i);
bulletsToDelete1.add(bulletToChange);
}
player1.bullets.removeAll(bulletsToDelete1);
// TODO: Update the game state each frame. This can be broken into the following steps:
// Step 1: Handle the keys pressed during the last frame by both players and calculate their resulting velocities/orientations.
// Step 2: Move the players and detect/handle player/player collisions and player/terrain collisions.
// Step 3: Decide whether a bullet should be fired for each player and create new bullet(s) if so. Also, handle reload mechanics.
// Step 4: Move all bullets via their calculated velocities (up to bullet range). Handle bullet/player & bullet/terrain collisions.
// Step 5: Decide whether the game has ended. If so, stop the timer and print a message to the screen indicating who's the winner.
// Note: If you implement other features (eg. weapon swapping, damage counters...etc.), you might also need to add more steps above.
// Update the display: this function calls paintComponent as part of its execution.
repaint(); }
// TODO: Draw all of the objects in your game.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//AffineTransform at = AffineTransoform.getTranslateInstance();
player1.draw(g);
player2.draw(g);
}
// Returns the dimensions of the Arena (for properly resizing the JPanel on the screen).
#Override
public Dimension getPreferredSize() {
return new Dimension(arenaWidth, arenaHeight);
}
}
THIS IS OUR PLAYER CLASS
// TODO: Feel free to import any other libraries that you need.
//import java.*;
import java.awt.*;
import java.util.ArrayList;
//import javax.swing.*;
//import java.awt.event.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Player extends GameObject {
public static void main(String []args){
}
// TODO: Set these values as you see fit. However, which variables should you not set as negative numbers? Which variables should you
// not set as zero? Which v'ariables should you not set as a very large positive number? Why?
public static final int INIT_SIZE = 30;
public static final int INIT_MASS = 0;
public static final int INIT_DAMAGE = 0;
public static final double INIT_SPEED = 10;
public static final double INIT_ROTATE_SPEED = 10;
public static final int INIT_HEALTH = 100;
// Member variables of the player that you can change over the course of the game.
public int damage = INIT_DAMAGE;
public double speed = INIT_SPEED;
public double rotateSpeed = INIT_ROTATE_SPEED;
public int health = INIT_HEALTH;
public double orientation = 0;
public int id;
// TODO: You may need to set up extra variables to store the projectiles fired by this player, the reload status/time of this player,
// the key press/release status of this player, and any other player-related features you decide to implement. Make sure to update the
// constructor appropriately as well!
long currentReload = 0;
long MaxReload = BasicWeapon.INIT_RELOAD;
ArrayList<PlayerProjectile> bullets = new ArrayList<PlayerProjectile>();
double RotationDegree = 0;
boolean isWPressed = false;
boolean isSPressed = false;
boolean isAPressed = false;
boolean isDPressed = false;
boolean isSpacePressed = false;
boolean isEnterPressed = false;
boolean isUpPressed = false;
boolean isDownPressed = false;
boolean isLeftPressed = false;
boolean isRightPressed = false;
boolean isLeftCLickPressed = false;
// Constructor that calls the super (ie. parent) class's constructor and instantiates any other player-specific variables.
public Player(int arenaWidth, int arenaHeight, double startPosX, double startPosY, int id) {
super(arenaWidth, arenaHeight, 0, 0, startPosX, startPosY, INIT_SIZE, INIT_SIZE, INIT_MASS);
this.id = id;
}
// TODO: This function should move the player and handle any player-terrain interactions.
#Override
public boolean move(Map map, double translateX, double translateY) {
posX += translateX;
posY += translateY;
xClip();
yClip();
return false;
}
//UPDATE PLAYER POSTION HERE
//}
public void draw(Graphics g) {
// TODO: Draw the barrel(s) for the player here
double xChords[] = new double[4];
double YChords[] = new double[4];
xChords[0] = posX + 0.6 * width;
xChords[1] = posX + 0.6 * width;
xChords[2] = posX + 1.5 * width;
xChords[3] = posX + 1.5 * width;
YChords[0] = posY + 0.4 * height;
YChords[1] = posY + 0.5 * height;
YChords[2] = posY + 0.5 * height;
YChords[3] = posY + 0.4 * height;
double[] point0 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[0], YChords[0]);
double[] point1 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[1], YChords[1]);
double[] point2 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[2], YChords[2]);
double[] point3 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[3], YChords[3]);
int rotatedPointsX[] = new int[4];
int rotatedPointsY[] = new int[4];
rotatedPointsX[0] = (int)Math.round(point0[0]);
rotatedPointsX[1] = (int)Math.round(point1[0]);
rotatedPointsX[2] = (int)Math.round(point2[0]);
rotatedPointsX[3] = (int)Math.round(point3[0]);
rotatedPointsY[0] = (int)Math.round(point0[1]);
rotatedPointsY[1] = (int)Math.round(point1[1]);
rotatedPointsY[2] = (int)Math.round(point2[1]);
rotatedPointsY[3] = (int)Math.round(point3[1]);
g.drawPolygon(rotatedPointsX, rotatedPointsY, 4);
g.setColor(Color.BLACK);
g.fillPolygon(rotatedPointsX, rotatedPointsY, 4);
if (id == 1) {
g.setColor(new Color(255, 215, 0));
} else if (id == 2) {
g.setColor(Color.RED);
}
// Body
g.fillOval((int) posX, (int) posY, width, height);
g.setColor(Color.BLACK);
g.drawOval((int) posX, (int) posY, width, height);
// TODO: Draw the health bar for the player here.
}
}
Only the button appears, and player1 and player2 don't.
I think what you are trying to achieve is something like :
public static void main(String []args){
JButton button= new JButton("Reset");
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(button);
frame.add(panel, BorderLayout.NORTH);
frame.add(new Arena(), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
For future question please do not post so much code. See: don't just copy in your entire program!
I'm trying to implement the last game loop on this site
Here is the code from the website:
const int TICKS_PER_SECOND = 25;
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
const int MAX_FRAMESKIP = 5;
DWORD next_game_tick = GetTickCount();
int loops;
float interpolation;
bool game_is_running = true;
while( game_is_running ) {
loops = 0;
while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) {
update_game();
next_game_tick += SKIP_TICKS;
loops++;
}
interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
/ float( SKIP_TICKS );
display_game( interpolation );
}
// GetTickCount() returns the current number of milliseconds
// that have elapsed since the system was started
I have used System.currentTimeMillis() in place of GetTickCount() hoping that it would do the trick.
But I keep getting very strange results with my code:
Does anybody knows how to implement this Game loop in java?
Here is my non working example: (I keep getting large numbers for interpolation)
private final int TICKS_PER_SECOND = 25;
private final int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
private final int MAX_FRAMESKIP = 5;
private double nextGameTick = System.currentTimeMillis() ;
private int loops;
private float interpolation;
private boolean isGameRunning = true;
public GameLoop(){
while( isGameRunning ) {
loops = 0;
while( System.currentTimeMillis() > nextGameTick && loops < MAX_FRAMESKIP) {
// update_game();
nextGameTick += SKIP_TICKS;
loops++;
}
interpolation = (float)( System.currentTimeMillis() + SKIP_TICKS - nextGameTick )/ (float)( SKIP_TICKS );
//display_game( interpolation );
view.display(interpolation);
}
}
I draw a ball at the location using the formula from the website but the ball just keeps spazzing out back and forth
view_position = position + (speed * interpolation)
I add the rest of my code so its less confusing:
(although i have a problem even before i hit this code because the interpolation number is huge when i run the debugger and it should be a fraction)
This is my View Class which paints the ball:
public class View extends JPanel
{
private static final long serialVersionUID = -2744215808270823059L;
Ball myBall = new Ball();
public void paintComponent(Graphics g){
super.paintComponent(g);
paintBall(g);
g.drawString(String.valueOf(interpolation), 20, 20);
g.drawString(String.valueOf(myBall.getLocation()), 20, 50);
}
private void paintBall(Graphics g){
g.drawOval(myBall.getLocation().x, myBall.getLocation().y, myBall.getDiameter(), myBall.getDiameter() );
}
private float interpolation;
public void display(float interpolation){
Point location = new Point();
this.interpolation = interpolation;
location.x = myBall.getLocation().x + (int)(myBall.getSpeedX() * interpolation);
location.y = myBall.getLocation().y + (int)(myBall.getSpeedY() * interpolation);
myBall.setLocation(location);
repaint();
}
}
And my ball class:
public class Ball {
private Point location = new Point();
private int diameter;
private double speed;
private double speedX;
private double speedY;
private double angle;
Ball(){
this(new Point(0, 10));
}
Ball(Point location){
this(10, 3, 60, location);
}
Ball(int diameter, double speed, double angleDeg, Point location ){
this.speed = speed;
setAngleDeg(angleDeg);
setDiameter(diameter);
setLocation(location);
}
//Getters and Setters
public double getAngleRad() {
return angle;
}
public void setAngleRad(double angle) {
this.angle = angle;
speedX = Math.cos(angle)*speed;
speedY = Math.sin(angle)*speed;
}
public double getAngleDeg() {
return angle*180/Math.PI;
}
public void setAngleDeg(double angle) {
this.angle = angle * Math.PI/180;
speedX = Math.cos(angle)*speed;
speedY = Math.sin(angle)*speed;
}
public double getSpeedX() {
return speedX;
}
public void setSpeedX(double speedX) {
this.speedX = speedX;
}
public double getSpeedY() {
return speedY;
}
public void setSpeedY(double speedY) {
this.speedY = speedY;
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
public int getDiameter() {
return diameter;
}
public void setDiameter(int diameter) {
this.diameter = diameter;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
speedX = Math.cos(angle)*speed;
speedY = Math.sin(angle)*speed;
}
}
Using a busy wait in a game loop is bad practice, I'd recommend using a different strategy, like waiting for a display refresh to render the next frame. Most game frameworks include their own game loop logic, and I highly recommend looking into one of those.
The problem looks like you're feeding the time difference into view_position rather than adding it to the time to get the actual time. So the position of the ball is based on the jitter you get between frame times rather than the time itself.
Please try out a game framework before you think of reinventing the wheel with your own; it's a lot more fun working on the game logic rather than debugging the lower level stuff. Come back to this when you're more experienced and if you need it for performance.
I'm working on a program that takes a ship object and it moves it. The trouble I am having is that if it goes past a side, then it is supposed to wrap back around on the other side.
Any help would be great :)
Here is my ship Class: The move method is what I need help with. The code I have doesnt work :/
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import junit.framework.TestCase;
public class Ship {
private BufferedImage _image;
private static final int WIDTH = 50;
private Point location;
private Vector speed = new Vector();
private double facing;
/**
* Generate ship at the given starting location and currently stopped
*
* #param starting
* location to copy for this ship
*/
public Ship(Point starting) {
try {
// Use the RunConfigurations >> Arguments > Working Directory tab so
// that this works. Don't just place the nave.png file in the bin
// directory!
_image = ImageIO.read(new File("nave.png"));
} catch (IOException e) {
System.err.println("Cannot find ship _image: " + e.getMessage());
}
location = starting.clone();
facing = Math.PI;
}
public void accelerate(double force) {
// TODO change the speed (velocity, really) by force in the direction
// the ship is facing.
// add a vector of appropriate magnitude by the facing direction
Vector acc = new Vector(facing);
acc = acc.scale(force);
speed = speed.add(acc);
}
public void rotate(double angle) {
// TODO change the direction the ship is facing. Can accept any angle
// as a parameter but should store it as in [0,2*pi)
while (angle <= 0.0f) {
angle += (Math.PI * 2);
}
while (angle >= Math.PI) {
angle -= (Math.PI * 2);
}
facing += angle;
}
public void move(Dimension bounds) {
// TODO Move the ship its speed. The ship should wrap around
// within its box. (Hint: move the ship by the size of the
// bounding area to wrap it around; you may need to do this
// more than once if the ship is moving fast enough.)
location = speed.move(location);
while (location.getX() > bounds.width) {
Vector v = new Vector(location.getX() - WIDTH);
location = v.move(location);
}
while (location.getX() < -WIDTH) {
Vector v = new Vector(location.getX() + WIDTH);
location = v.move(location);
}
while (location.getY() > bounds.height) {
Vector v = new Vector(location.getY() - WIDTH);
location = v.move(location);
}
while (location.getY() < -WIDTH) {
Vector v = new Vector(location.y() + WIDTH);
location = v.move(location);
}
}
public void draw(Graphics g2d) {
double locationX = _image.getWidth() / 2;
double locationY = _image.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(facing,
locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx,
AffineTransformOp.TYPE_BILINEAR);
// Drawing the rotated image at the required drawing locations
// Code for rotating adapted from StackOverflow.
g2d.drawImage(op.filter(_image, null), location.getX(),
location.getY(), null);
}
And here is my vector class: All this code works :)
public class Vector {
private final double _dx, _dy;
public Vector() {
_dy = 0.0;
_dx = 0.0;
}
public Vector(double x, double y) {
_dx = x;
_dy = y;
}
public Vector(Point a, Point b) {
_dx = b.x() - a.x();
_dy = b.y() - a.y();
}
public Vector(double angle) {
_dx = Math.cos(angle);
_dy = Math.sin(angle);
}
public double dx() {
return _dx;
}
public double dy() {
return _dy;
}
public Point move(Point b) {
double x = b.x();
double y = b.y();
x += _dx;
y += _dy;
return new Point(x, y);
}
public Vector add(Vector a) {
double x = (a._dx + _dx);
double y = (a._dy + _dy);
return new Vector(x, y);
}
public Vector scale(double s) {
double x = _dx * s;
double y = _dy * s;
return new Vector(x, y);
}
public double magnitude() {
double x = Math.pow(_dx, 2);
double y = Math.pow(_dy, 2);
return Math.sqrt(x + y);
}
public Vector normalize() {
double x = _dx / magnitude();
double y = _dy / magnitude();
return new Vector(x, y);
}
public Vector rotate(double rads) {
double theta = angle();
theta += rads;
return new Vector(theta);
}
public double angle() {
double alpha = Math.acos(dx() / magnitude());
if (dy() < 0)
alpha = Math.PI - alpha;
return alpha;
}
#Override
public String toString() {
String vector = "[" + _dx + "," + _dy + "]";
return vector;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Vector) {
Vector vector = (Vector) obj;
if ((Math.abs(_dx - vector._dx) <= (1 / 10000000000f))
&& (Math.abs(_dy - vector._dy) <= (1 / 10000000000f)))
return true;
else
return false;
} else
return false;
}
#Override
public int hashCode() {
return (int) Math.round((angle() * 180) / Math.PI);
}
}
Expanding upon the suggestion to use modulo, you can use it as follows to wrap around easily without loops:
// Assuming move is called for each time frame
// We can update the location of ship using modulo when it exceeds the bounds
public void move(Dimension bounds) {
// TODO Move the ship its speed. The ship should wrap around
// within its box. (Hint: move the ship by the size of the
// bounding area to wrap it around; you may need to do this
// more than once if the ship is moving fast enough.)
location = speed.move(location);
if (location.getX() > bounds.width) {
location.setLocation(location.getX() % bounds.width), location.getY());
}
else if (location.getX() < 0) {
location.setLocation(bounds.width - location.getX(), location.getY());
}
if (location.getY() > bounds.height) {
location.setLocation(location.getX(), location.getY() % bounds.height);
}
else if (location.getY() < 0) {
location.setLocation(location.getX(), bounds.height - location.getY());
}
}
You've provided a lot of code so I may have missed why you need to do this, but rather than create a new delta Vector to move the location, you can alternatively just determine the new wrapped position that the ship should be at set it per setLocation method.
I hope this helps.
So I wrote a Program to draw and display a 3D cube, using these simple conversion formula's as used in isometric graphs:
x2 = x*cos(30) - y*cos(30)
y2 = x*sin(30) + y*sin(30) + z
The coordinate conversion is fine and everything comes out in perspective.
The Issue is rotating, Large degree rotations messes up all the coordinates and gives me an entire shape.
And rotating at small degrees many many times, (ie 1000 1degree rotation or more) reduces the size of the cube.
public void rotateX(double dg) //cube is shrinking along y and z
{
y = (y*Math.cos(dg)-z*Math.sin(dg));
z = (y*Math.sin(dg)+z*Math.cos(dg));
}
public void rotateY(double dg) //cube is shrinking along x and z
{
x = x*Math.cos(dg)-z*Math.sin(dg);
z = x*Math.sin(dg)+z*Math.cos(dg);
}
public void rotateZ(double dg) //cube is shrinking along x and y
{
x = x*Math.cos(dg)-y*Math.sin(dg);
y = x*Math.sin(dg)+y*Math.cos(dg);
}
How can i solve this lack of precision of cos and sin after multiple uses??
Here's the entire code written in 3 seperat classes:
Main class:
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class Frame extends JFrame
{
private Random rnd = new Random();
private cubeGUI cube;
public Frame()
{
super();
}
public void paint(Graphics g)
{
cube = new cubeGUI(75,300.0,300.0);
cube.convertall();
double dg = 0.5; // The Smaller the degree, the less the error after long rotations.
int sl = 5;
int turns, axe;
while (1 == 1)
{
turns = rnd.nextInt(200)-100;
axe = rnd.nextInt(3);
for(int i = 0; i<turns; i++)
{
switch (axe)
{
case 0: cube.rotatx(dg); break;
case 1: cube.rotaty(dg); break;
case 2: cube.rotatz(dg); break;
}
g.clearRect(0,0,600,600);
g.drawLine(cube.a.x2,cube.a.y2,cube.b.x2,cube.b.y2);
g.drawLine(cube.a.x2,cube.a.y2,cube.c.x2,cube.c.y2);
g.drawLine(cube.c.x2,cube.c.y2,cube.d.x2,cube.d.y2);
g.drawLine(cube.b.x2,cube.b.y2,cube.d.x2,cube.d.y2);
g.drawLine(cube.e.x2,cube.e.y2,cube.f.x2,cube.f.y2);
g.drawLine(cube.e.x2,cube.e.y2,cube.g.x2,cube.g.y2);
g.drawLine(cube.g.x2,cube.g.y2,cube.h.x2,cube.h.y2);
g.drawLine(cube.f.x2,cube.f.y2,cube.h.x2,cube.h.y2);
g.drawLine(cube.a.x2,cube.a.y2,cube.e.x2,cube.e.y2);
g.drawLine(cube.b.x2,cube.b.y2,cube.f.x2,cube.f.y2);
g.drawLine(cube.c.x2,cube.c.y2,cube.g.x2,cube.g.y2);
g.drawLine(cube.d.x2,cube.d.y2,cube.h.x2,cube.h.y2);
try
{
Thread.sleep(sl); //Rotation Speed, In relation with Angle of rotation.
} catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
}
}
}
public static void main(String[] args)
{
Frame cube = new Frame();
cube.setSize(600,600);
cube.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cube.setVisible(true);
}
}
cube class:
public class cubeGUI
{
public Point center,a,b,c,d,e,f,g,h;
private double x, y;
public cubeGUI(int m, double x, double y)
{
this.x = x;
this.y = y;
a = new Point(-m,-m,-m);
b = new Point(m,-m,-m);
c = new Point(-m,m,-m);
d = new Point(m,m,-m);
e = new Point(-m,-m,m);
f = new Point(m,-m,m);
g = new Point(-m,m,m);
h = new Point(m,m,m);
}
public void rotatx(double dg)
{
a.rotateX(Math.toRadians(dg));
b.rotateX(Math.toRadians(dg));
c.rotateX(Math.toRadians(dg));
d.rotateX(Math.toRadians(dg));
e.rotateX(Math.toRadians(dg));
f.rotateX(Math.toRadians(dg));
g.rotateX(Math.toRadians(dg));
h.rotateX(Math.toRadians(dg));
convertall();
}
public void rotaty(double dg)
{
a.rotateY(Math.toRadians(dg));
b.rotateY(Math.toRadians(dg));
c.rotateY(Math.toRadians(dg));
d.rotateY(Math.toRadians(dg));
e.rotateY(Math.toRadians(dg));
f.rotateY(Math.toRadians(dg));
g.rotateY(Math.toRadians(dg));
h.rotateY(Math.toRadians(dg));
convertall();
}
public void rotatz(double dg)
{
a.rotateZ(Math.toRadians(dg));
b.rotateZ(Math.toRadians(dg));
c.rotateZ(Math.toRadians(dg));
d.rotateZ(Math.toRadians(dg));
e.rotateZ(Math.toRadians(dg));
f.rotateZ(Math.toRadians(dg));
g.rotateZ(Math.toRadians(dg));
h.rotateZ(Math.toRadians(dg));
convertall();
}
public void convertall()
{
a.convert(x,y);
b.convert(x,y);
c.convert(x,y);
d.convert(x,y);
e.convert(x,y);
f.convert(x,y);
g.convert(x,y);
h.convert(x,y);
}
}
Point class (this calculates all coordinates):
public class Point
{
private double x, y, z, F;
public int x2, y2;
public Point(double a, double b, double c)
{
x = a;
y = b;
z = c;
}
public int getX()
{
return (int)x;
}
public int getY()
{
return (int)y;
}
public int getZ()
{
return (int)z;
}
public void rotateX(double dg) //cube is shrinking along y and z
{
y = (y*Math.cos(dg)-z*Math.sin(dg));
z = (y*Math.sin(dg)+z*Math.cos(dg));
}
public void rotateY(double dg) //cube is shrinking along x and z
{
x = x*Math.cos(dg)-z*Math.sin(dg);
z = x*Math.sin(dg)+z*Math.cos(dg);
}
public void rotateZ(double dg) //cube is shrinking along x and y
{
x = x*Math.cos(dg)-y*Math.sin(dg);
y = x*Math.sin(dg)+y*Math.cos(dg);
}
public void convert(double xx, double yy)
{
x2 = (int)(-(Math.cos(Math.toRadians(30))*x - Math.cos(Math.toRadians(30))*y) + xx);
y2 = (int)(-(Math.sin(Math.toRadians(30))*x + Math.sin(Math.toRadians(30))*y + z) + yy);
}
public String toString()
{
return ("Y = " + y + ", Z = " + z);
}
}
The usual approach is to represent the cube as a point configuration and a current transformation. When rotating, update the transformation but do not update the points themselves. Only when point coordinates are needed (for rendering, displaying coordinate values, etc.) should the transformation be applied to the points. The points themselves should never be modified.
This will eliminate the errors that accumulate when many rotations are applied in sequence. However, it is important that the transformation matrix be maintained as a rotation (determinant 1). Otherwise the transformation will still introduce random artifacts (scaling, skewing, or other distortions). Thus, after each rotation is applied, the transformation matrix should be renormalized so that it remains a pure transformation. The normalization can be as simple as dividing each entry by the determinant.
You use x, which is already changed:
x = x*Math.cos(dg)-y*Math.sin(dg);
y = x*Math.sin(dg)+y*Math.cos(dg);
it is right variant.
double xx =x;
x = x*Math.cos(dg)-y*Math.sin(dg);
y = xx*Math.sin(dg)+y*Math.cos(dg);
I have an object(which is a ball) having its position at the top of the screen that fall directly whenever I start running the program. The problem is that the ball fall to its constant speed, I want it to fall accelerating with gravity effects and when it reach the ground, I want it to bounce a few more times before it stop moving. Could someone help me about this?
Here's what I've tried:
public class Balls
{
private double x;
private double y;
private double speed;
private double mass;
private final double gravity = -9.8;
private final double width = 100;
private double height = 100;
private final Board board;
private boolean isFalling = false;
private double distance_y;
private double distance_x = 0;
public Balls(double x, double y, double speed, double mass, Board board)
{
this.x = x;
this.y = y;
this.board = board;
this.speed = convertToMeterSpeed(speed);
this.mass = mass;
}
private double convertToMeterSpeed(double speed)
{
return speed / 3.6;
}
public void moveBall(long dt)
{
double time = dt / 1e9; // seconds
double diameter_y = height / 2.0;
double radius = (diameter_y / 2.0);
double velocity_y = speed * dt / 1e9;
distance_y = board.getHeight() - y;
if (distance_y - radius > 0)
{
isFalling = true;
}
if (isFalling)
{
if (distance_y >= height)
{
distance_y = distance_y + (0.5 * gravity * (time * time)); // represents the 1/2,
distance_y = board.getHeight() - height;
y += velocity_y;
}
else
{
isFalling = false;
}
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
}
}
public void render(Graphics2D g2d)
{
g2d.fillOval((int) x, (int) y, (int) width, (int) height);
}
}
speed = v0 + gt^2/2,
where
v0 - initial speed
g = 9.81 on Earth.
t - time
Now you can calculate the speed at any time.
You probably want to define a maximum speed (terminal velocity) so your ball doesn't accelerate to an enormous speed. Gravity accelerates at 9.8m/s/s. Once the ball hits the "ground" you just reverse the speed and update the current position to make it bounce, then on your next iteration gravity will be applied again so it will go back down. Eventually, the speed will get to 0 as the ball doesnt bounce so much, and will stop.
Here's an (untested) example:
private static final double GRAVITY = 9.8;
private static final double TERMINAL_VELOCITY = 100;
private double speed;
private int current_y;
public void fallAndBounce() {
speed = speed + GRAVITY;
if (speed > TERMINAL_VELOCITY) { speed = TERMINAL_VELOCITY; }
if (current_y >= bottomOfScreen)
{
//We have hit the "ground", so bounce back up. Reverse
//the speed and divide by 4 to make it slower on bouncing.
//Just change 4 to 2 or something to make it faster.
speed = -speed/4;
}
current_y += speed;
}
Your problem is that you are trying to animate a ball falling, but you are instead writing an algorithm for solving its position given a certain time.
This means you should take your time variable dt completely out of the equation, and just move your ball with each iteration of a loop like this:
while (true)
{
moveBall();
render();
try {
Thread.sleep(10)
} catch(InterruptedException e) {
e.printStackTrace();
}
}
Also, change your variables slightly:
// Add this to your variables
private final double GRAVITY = -9.8; // Final variables should be capitalized
private final double TERMINAL_VELOCITY = -30; // Whatever you want it to be
Here is the main change:
public void moveBall()
{
double diameter_y = height / 2.0;
double radius = (diameter_y / 2.0);
double velocity_y = speed * dt / 1e9;
distance_y = board.getHeight() - y;
if (distance_y - radius > 0)
{
isFalling = true;
}
if (isFalling)
{
if (height < distance_y)
{
if (velocity_y <= TERMINAL_VELOCITY)
velocity_y += GRAVITY; // implementing acceleration (gravity)
// just means adding it to velocity.
y += velocity_y;
}
Really, I don't know how this was possibly working before.