Using the thread.sleep method in painting - java

I have a working code which basically paints 15 rectangles on the screen that you can drag around. I made it so that the rectangles falls to the bottom of the screen as time passes. While I have the thread.sleep method at bigger numbers such as 500, I can still drag the rectangles around the screen as they fall with no problems. But as I start to decrease the thread.sleep method to smaller numbers such as 50, suddenly problems arises. Problems such as I can only drag up to 2 rectangles before the rectangles start glitching back to the places where I did not drag them. Sometimes I can only drag up to one rectangles, and once I selected that rectangle, I can't select any other rectangles to drag. I know my codes are definitely right, since it works while the thread.sleep method is at at bigger number, so my question is: why does it start glitching when I make thread.sleep to smaller numbers? Here's part of my code.
while (true) {
for (int i = 0; i < 15; i++) {
P.fY[i]++;
}
Thread.sleep(500);
frame.repaint();
} //the 15 stands for 15 rectangles, and the P.fY stands for the position of y.

So based off of your comment, it seems like you just really need a hand with figuring out how to calculate the distance as a function of time.
By adding 1 each frame loop, you're really saying the speed of each square is 1 pixel / 1 frame.
Instead, you should utilize time and update the distance by a function of time, so that it will be 1 pixel / unit of time. This means the velocity of the squares will then be independent of the frames per second.
I whipped up a code example. The important method is the Square#doUpdate() method. This pertains to exactly what you're looking for.
The procedure it follows is:
Calculate time from last update, store it in delta.
Update the time of the last update to the current time
Calculate deltaX, which is deltaX = delta * velocityX
Calculate deltaY, which is deltaY = delta * velocityY
Add deltaX to x - this updates the x coordinate
Add deltaY to y - this updates the y coordinate
The code is as follows:
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
/**
* #author Obicere
*/
public class MovingSquare {
private volatile int viewportWidth;
private volatile int viewportHeight;
private final LinkedList<Square> squares = new LinkedList<>();
public MovingSquare() {
final JFrame frame = new JFrame("Moving Square");
final JPanel displayPanel = new JPanel() {
#Override
protected void paintComponent(final Graphics g) {
synchronized (squares) {
for (final Square square : squares) {
// Update the square's locations, ideally this will
// be separate of the painting thread
square.doUpdate();
final int x = (int) square.getX();
final int y = (int) square.getY();
g.setColor(square.getColor());
g.drawRect(x, y, square.squareSize, square.squareSize);
}
}
}
};
displayPanel.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(final MouseEvent e) {
final Color nextColor = Color.getHSBColor((float) Math.random(), 1, 0.5f);
final float speedX = (float) Math.random();
final float speedY = (float) Math.random();
synchronized (squares) {
final Square newSquare = new Square(nextColor, speedX, speedY);
squares.add(newSquare);
newSquare.x = e.getX();
newSquare.y = e.getY();
}
}
});
displayPanel.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
viewportWidth = displayPanel.getWidth();
viewportHeight = displayPanel.getHeight();
}
});
final Timer repaintTimer = new Timer(20, null);
repaintTimer.addActionListener(e -> {
if (!frame.isVisible()) {
repaintTimer.stop();
return;
}
frame.repaint();
});
repaintTimer.start();
displayPanel.setPreferredSize(new Dimension(200, 200)); // Sorry MadProgrammer
frame.add(displayPanel);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(MovingSquare::new);
}
private class Square {
private final int squareSize = 25;
private volatile float x;
private volatile float y;
private volatile long lastUpdateTime;
private volatile boolean negateX;
private volatile boolean negateY;
private final float speedX;
private final float speedY;
private final Color color;
public Square(final Color color, final float speedX, final float speedY) {
this.color = color;
this.speedX = speedX;
this.speedY = speedY;
lastUpdateTime = System.currentTimeMillis();
}
/**
* Important method here!!
* <p>
* This updates the location of the squares based off of a set
* velocity and the difference in times between updates.
*/
public void doUpdate() {
// Gets the change in time from last update
final long currentTime = System.currentTimeMillis();
final long delta = currentTime - lastUpdateTime;
if (delta == 0) {
return;
}
// be sure to update the last time it was updated
lastUpdateTime = currentTime;
// Calculate the speed based off of the change in time
final float deltaX = getSpeedX(delta);
final float deltaY = getSpeedY(delta);
// Move each square by the change of distance, calculated from
// the change in time and the velocity.
final float nextX = x + deltaX;
final float nextY = y + deltaY;
handleBouncing(nextX, nextY);
}
private void handleBouncing(final float nextX, final float nextY) {
if (nextX < 0) {
x = 0;
flipX();
} else if (nextX + squareSize >= viewportWidth) {
x = viewportWidth - squareSize;
flipX();
} else {
x = nextX;
}
if (nextY < 0) {
y = 0;
flipY();
} else if (nextY + squareSize >= viewportHeight) {
y = viewportHeight - squareSize;
flipY();
} else {
y = nextY;
}
}
private float getSpeedX(final long delta) {
return (negateX ? -1 : 1) * delta * speedX;
}
private float getSpeedY(final long delta) {
return (negateY ? -1 : 1) * delta * speedY;
}
protected void flipX() {
negateX = !negateX;
}
protected void flipY() {
negateY = !negateY;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public Color getColor() {
return color;
}
}
}
And it in action:
This might seem a bit overwhelming. Step through it, change some things up. Go crazy and see what the results are.
There are also some websites that can help with velocity and how to calculate things like this. If you need further help, just drop a comment down below and I'll see what I can do.

Related

What am I doing wrong that is causing this graph to scroll incorrectly?

I'm trying to create a simple Java program that takes a math function as input and displays a 2D (x and y) graph in a JPanel. I've got the graph displaying correctly initially.
I tried adding scrolling to the canvas (I called the JPanel subclass I made a GraphCanvas), by which I mean that when the mouse is dragged across it, the 2D plane scrolls as well and the graph is updated for the newly revealed areas.
To display this actual function, I am using a Path2D object that is made up of segments connecting enough points so that the graph is in maximum resolution, and I've been using AffineTransforms to translate the path.
It's worth noting that I've only tested the program so far with functions in the form f(x), and not parametrics, even though I've created a class and supporting code in the GraphedInstance class for parametric functions.
I didn't want to include all of the code for the project, but I pushed it to a GitHub repository here (I have very little knowledge of how GitHub works but that should be right).
My problem is that at the moment, whenever I try to scroll the graph, the function just kind of does its own thing and doesn't connect correctly with the initialized piece. It isn't displaying the newly generated parts correctly.
I've done some work on it and improved the problem significantly by waiting to transform the path describing the function until after I've added the new pieces to it, but it still doesn't connect correctly.
This is the method that is supposed to create and stitch together the new parts of the function path, and it's where I think the issue probably lies. This and the rest of the project can also be found in the GitHub repository. Also, sorry for any messy code and/or lack of comments, I'm only a freshman in college and haven't really had any formal training in Java, I've just kind of picked it up as I go along. I would be happy to take any suggestions or recommendations of better ways to do anything as well!
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
public class Minimal {
private static int[] lastPointer = new int[2];
private static double xMin, xMax, yMin, yMax;
private static double xScale, yScale;
private static double minXGraphed, maxXGraphed;
public static Path2D.Double function;
//The frame enclosing the graphing canvas
private static JFrame frame;
//The graphing canvas
private static JPanel canvas = new JPanel() {
private static final int DEFAULT_CANVAS_SIZE = 600;
//Ensures the frame initialized to the right size
#Override
public Dimension getPreferredSize() {
return new Dimension(DEFAULT_CANVAS_SIZE, DEFAULT_CANVAS_SIZE);
}
//Paints the graph whenever it needs updating
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
xScale = getWidth() / (xMax - xMin);
yScale = getHeight() / (yMax - yMin);
g2.setColor(Color.BLUE);
try {
g2.draw(function);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
};
/**
* I'm using the function y = exp(x) as an example. Feel free to replace it.
* #param x the x-value
* #return exp(x), the y-value
*/
private static double getYVal(double x) {
return Math.exp(x);
}
//Initializes the function path
public static void initPath() {
final double STEP_X = (xMax - xMin) / canvas.getWidth();
function = new Path2D.Double();
double x = xMin;
double y = getYVal(x);
function.moveTo(getGraphX(x), getGraphY(y));
for(x = xMin + STEP_X; x <= xMax; x += STEP_X) {
y = getYVal(x);
function.lineTo(getGraphX(x), getGraphY(y));
}
}
/**
* Gets the pixel x-coordinate on the canvas
* that corresponds to a given x-value of the function
* #param x the x-value of the function
* #return the adjusted x-coordinate
*/
private static int getGraphX(double x) {
return (int) (canvas.getWidth() * (x - xMin) / (xMax - xMin));
}
//Same thing as getGraphX except for y-coords
private static int getGraphY(double y) {
return (int) (canvas.getHeight() * (yMax - y) / (yMax - yMin));
}
/*
* This is probably where the problem is, this extends the path so that it
* covers the entire visible range
*/
public static void updateFunctionBounds(double dx) {
double newXMin = xMin - dx / xScale;
double newXMax = xMax - dx / xScale;
final int WIDTH = canvas.getWidth();
final double STEP_X = (xMax - xMin) / WIDTH;
double drawTo;
if((drawTo = newXMin) < xMin && drawTo < minXGraphed) {
minXGraphed = drawTo;
double x = drawTo;
function.moveTo(getGraphX(x), getGraphY(getYVal(x)));
for(x = drawTo + STEP_X; x < xMin; x += STEP_X) {
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
x = xMin;
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
} else if((drawTo = newXMax) > xMax && drawTo > maxXGraphed) {
maxXGraphed = drawTo;
double x = xMax;
function.moveTo(getGraphX(x), getGraphY(getYVal(x)));
for(x = xMax + STEP_X; x < drawTo; x += STEP_X) {
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
x = drawTo;
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
}
public static void main(String[] args) {
initBounds();
initFrame();
}
//Initializes the graph boundaries. Feel free to change this
private static void initBounds() {
xMin = yMin = minXGraphed = -10;
xMax = yMax = maxXGraphed = 10;
}
//Initializes the frame and a MouseAdapter that controls the scrolling
private static void initFrame() {
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
lastPointer[0] = e.getX();
lastPointer[1] = e.getY();
}
#Override
public void mouseDragged(MouseEvent e) {
super.mouseDragged(e);
AffineTransform t = new AffineTransform();
double dx = e.getX() - lastPointer[0];
double dy = e.getY() - lastPointer[1];
t.translate(dx, dy);
updateFunctionBounds(dx);
function.transform(t);
xMin -= dx / xScale;
xMax -= dx / xScale;
yMin -= dy / yScale;
yMax -= dy / yScale;
canvas.repaint();
lastPointer[0] = e.getX();
lastPointer[1] = e.getY();
}
};
canvas.addMouseListener(adapter);
canvas.addMouseMotionListener(adapter);
frame = new JFrame("Minimal Reproducible Approach");
frame.setContentPane(canvas);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
initPath();
canvas.repaint();
}
}
If it was working correctly, the stitching would be seamless and the function would smoothly scroll as the mouse pointer was dragged across the canvas, sort of like it does in Desmos. In reality, it doesn't retain the correct height (y-value) and doesn't actually connect to the previous segments in all cases.
Thank you for any and all help!
Edit: I've updated the code into what I think is a minimal reproducible example.

Delaying bullets with reload function

Hi i'm currently confused on how to add a timer into my program to add a delay in between shooting bullets, and timing a reload function. This is my current code to shoot a bullet where the mouse is located at when I click E. I want to add a delay to this and a reload function on top of it.
#Override
public void keyPressed(KeyEvent e) {
keyPressed[e.getKeyCode()] = true;
if(e.getKeyCode()==KeyEvent.VK_E){
double xVel = mouseX - (player.x+player.width/2-cameraX);
double yVel = mouseY - (player.y-cameraY);
double ratio = Math.sqrt(xVel*xVel+yVel*yVel)/10;
xVel /= ratio;
yVel /= ratio;
lasers.add(new Laser(player.x+player.width/2,player.y,10,10,xVel,yVel));
//System.out.println("Laser added at "+(player.x+player.width/2)+","+player.y);
}
}
This is called from my Laser class.
import java.awt.Color;
import java.awt.Graphics;
public class Laser {
double x, y;
int width, height;
double horizVel, vertVel;
boolean debug = false;
public Laser(int x, int y, int width, int height, double horizVel, double vertVel) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.horizVel = horizVel;
this.vertVel = vertVel;
}
public void draw(Graphics g, int x, int y) {
g.setColor(Color.GREEN);
g.fillRect(x, y, width, height);
if (debug) {
g.drawString("horizVel=" + horizVel, x + width + 3, y);
}
}
public void update() {
x += horizVel;
y += vertVel;
}
public static int getHeight() {
// TODO Auto-generated method stub
return 0;
}
public static int getWidth() {
// TODO Auto-generated method stub
return 0;
}
}
My 1st question is how to implement a timer to my program which will cause a delay between shots. From what I believe I'd have to have a while(True) statement when pressing e to check when the timer is over.
My 2nd question is on how to add a reload timer which tracks the amount of bullets fired, creates a delay "while your reloading" so that you can't fire while this is happening.
Any help is greatly appreciated.
System.currentTimeMillis() will return the current time in milliseconds
so if you do
long pastTime = System.currentTimeMillis();
long currentTime = System.currentTimeMillis();
long timeElapsed = currentTime-pastTime;
// I would control both the reloading time and delay between firing
// time with two separate variables
if(bulletsleft>0){ //Not out of ammo
if( reloadTimer<=0 && bulletDelay<=0){ //delays
fireShot();}
}
else{
reload();
}
This obviously doesn't give a full solution to your questions but is at least one way of thinking about it

How to 'Get' a Specific Point From A Shape Object Derived from AffineTransform

As a self-project, I'm trying to make the game 'Asteroids'.
Currently, I'm stuck on trying to figure out how to make it so the lasers fired from my ship appear from the tip of the ship. So far, I've tried experimenting with using the Shape object's .getBounds2D().getX() methods, but because getBounds2D() draws a rectangle around the polygon, the lasers end up appearing from the corner of the imaginary 'box' around my Polygon ship.
Here's a gif of what I have so far.
Is there a way to 'get' a specific point from a Shape object; where, in this case, that specific point is the tip of the ship.
Main Class:
public class AsteroidGame implements ActionListener, KeyListener{
public static AsteroidGame game;
public Renderer renderer;
public boolean keyDown = false;
public int playerAngle = 0;
public boolean left = false;
public boolean right = false;
public boolean go = false;
public boolean back = false;
public boolean still = true;
public double angle = 0;
public int turnRight = 5;
public int turnLeft = -5;
public Shape transformed;
public ArrayList<Laser> lasers;
public ArrayList<Shape> transformedLasers;
public final int WIDTH = 1400;
public final int HEIGHT = 800;
public Ship ship;
public Rectangle shipHead;
public Shape shipHeadTrans;
public Point headPoint;
public AffineTransform transform = new AffineTransform();
public AffineTransform lasTransform = new AffineTransform();
public AffineTransform headTransform = new AffineTransform();
public AsteroidGame(){
JFrame jframe = new JFrame();
Timer timer = new Timer(20, this);
renderer = new Renderer();
jframe.add(renderer);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setSize(WIDTH, HEIGHT);
jframe.setVisible(true);
jframe.addKeyListener(this);
jframe.setResizable(false);
int xPoints[] = {800, 780, 800, 820};
int yPoints[] = {400, 460, 440, 460};
//(800, 400) is the initial location of the 'tip' of the ship'.
headPoint = new Point(800, 400);
lasers = new ArrayList<Laser>();
transformedLasers = new ArrayList<Shape>();
ship = new Ship(xPoints, yPoints, 4, 0);
transformed = transform.createTransformedShape(ship);
shipHead = new Rectangle(headPoint);
shipHeadTrans = transform.createTransformedShape(shipHead);
//shipHeadTrans.getBounds2D().
timer.start();
}
public void repaint(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);
Graphics2D g2d = (Graphics2D)g;
//drawing the ship
g2d.setColor(Color.WHITE);
g2d.draw(transformed);
//drawing lasers
g2d.setColor(Color.RED);
for (int i = 0; i < transformedLasers.size(); i++){
System.out.println(i);
g2d.draw(transformedLasers.get(i));
}
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
/*The for if and else if statements are just to send the ship
* to the other side of the canvas if it ever leaves the screen
*/
if (transformed.getBounds2D().getMinX() > WIDTH){
double tempAng = ship.getAng();
double diff = 90-tempAng;
transform.rotate(Math.toRadians(diff), ship.getCenterX(), ship.getCenterY());
transform.translate(0,WIDTH);
transform.rotate(Math.toRadians(-diff), ship.getCenterX(), ship.getCenterY());
}
else if (transformed.getBounds2D().getX() < 0){
double tempAng = ship.getAng();
double diff = 90-tempAng;
transform.rotate(Math.toRadians(diff), ship.getCenterX(), ship.getCenterY());
transform.translate(0,-WIDTH);
transform.rotate(Math.toRadians(-diff), ship.getCenterX(), ship.getCenterY());
}
else if (transformed.getBounds2D().getY() > HEIGHT){
double tempAng = ship.getAng();
double diff = 180-tempAng;
transform.rotate(Math.toRadians(diff), ship.getCenterX(), ship.getCenterY());
transform.translate(0,HEIGHT);
transform.rotate(Math.toRadians(-diff), ship.getCenterX(), ship.getCenterY());
}
else if (transformed.getBounds2D().getY() < 0){
double tempAng = ship.getAng();
double diff = 180-tempAng;
transform.rotate(Math.toRadians(diff), ship.getCenterX(), ship.getCenterY());
transform.translate(0,-HEIGHT);
transform.rotate(Math.toRadians(-diff), ship.getCenterX(), ship.getCenterY());
}
if (right){
ship.right();
//rotating the ship
transform.rotate(Math.toRadians(turnRight), ship.getCenterX(), ship.getCenterY());
//rotating the 'tip' of the ship.
headTransform.rotate(Math.toRadians(turnRight), ship.getCenterX(), ship.getCenterY());
}
else if (left){
ship.left();
//rotating the ship
transform.rotate(Math.toRadians(turnLeft), ship.getCenterX(), ship.getCenterY());
//rotating the 'tip' of the ship
headTransform.rotate(Math.toRadians(turnLeft), ship.getCenterX(), ship.getCenterY());
}
if (go){
ship.go();
}
else if (back){
ship.reverse();
}
//moving and shaping each individual laser that had been shot
for (int i = 0; i < transformedLasers.size(); i++){
lasers.get(i).move();
lasTransform = new AffineTransform();
lasTransform.rotate(Math.toRadians(lasers.get(i).getAng()), transformed.getBounds2D().getX(), transformed.getBounds2D().getY());
transformedLasers.set(i, lasTransform.createTransformedShape(lasers.get(i)));
}
//moving the ship
ship.move();
//moving the 'tip'
shipHead.y -= ship.getSpeed();
transformed = transform.createTransformedShape(ship);
shipHeadTrans = headTransform.createTransformedShape(shipHead);
renderer.repaint();
}
//defining a new laser
public void fireLaser(){
Laser tempLaser = new Laser((int)transformed.getBounds2D().getX(), (int)transformed.getBounds2D().getY(), 5, 10, ship.getAng());
lasers.add(tempLaser);
lasTransform = new AffineTransform();
lasTransform.rotate(Math.toRadians(ship.getAng()), transformed.getBounds2D().getX(), transformed.getBounds2D().getY());
transformedLasers.add(lasTransform.createTransformedShape(tempLaser));
}
public static void main(String[] args){
game = new AsteroidGame();
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
right = true;
keyDown = true;
}else if (e.getKeyCode() == KeyEvent.VK_LEFT){
left = true;
keyDown = true;
}
else if (e.getKeyCode() == KeyEvent.VK_UP){
go = true;
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN){
back = true;
}
//fire laser
if (e.getKeyCode() == KeyEvent.VK_SPACE){
fireLaser();
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
right = false;
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
left = false;
}
if (e.getKeyCode() == KeyEvent.VK_UP){
go = false;
}
if (e.getKeyCode() == KeyEvent.VK_DOWN){
back = false;
}
still = true;
keyDown = false;
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
Ship Class (I don't think it's relevant though)
package asteroidGame;
import java.awt.Polygon;
import java.util.Arrays;
public class Ship extends Polygon{
/**
*
*/
private double currSpeed = 0;
private static final long serialVersionUID = 1L;
public double angle;
public int[] midX;
public int[] midY;
public Ship(int[] x, int[] y, int points, double angle){
super(x, y, points);
midX = x;
midY = y;
this.angle= angle;
}
public void right(){
angle += 5;
}
public void left(){
angle -= 5;
}
public void move(){
for (int i = 0; i < super.ypoints.length; i++){
super.ypoints[i] -= currSpeed;
//System.out.println(super.ypoints[i]);
//System.out.println(super.xpoints[i]);
}
//System.out.println(Arrays.toString(super.ypoints));
}
public double getSpeed(){
return currSpeed;
}
public void reverse(){
if (currSpeed > -15) currSpeed -= 0.2;
}
public void go(){
if (currSpeed < 25) currSpeed += 0.5;
}
public int getCenterX(){
return super.xpoints[2];
}
public int getCenterY(){
return super.ypoints[2];
}
public double getAng(){
return angle;
}
public void test(){
for (int x = 0; x < super.ypoints.length; x++){
super.ypoints[x] += 1000;
}
}
/*
public void decrement(){
if(currSpeed == 0){}
else if (currSpeed > 0 && currSpeed < 15){
currSpeed -= 0.05;
}
else if (currSpeed < 0 && currSpeed > -15){
currSpeed += 0.05;
}
System.out.println("losing speed");
}
*/
}
Laser Class (I don't think this is relevant either, but here ya go.)
package asteroidGame;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
public class Laser extends Rectangle{
private double angle;
public Laser(int x, int y , int width, int height, double ang){
super(x, y, width, height);
angle = ang;
Rectangle tst = new Rectangle();
}
public void move(){
super.y -= 35;
}
public double getAng(){
return angle;
}
public boolean intersects (Rectangle2D r){
//if intersects
if (super.intersects(r)){
return true;
}
else{
return false;
}
}
}
I was thinking of maybe turning the the Shape object transformed back into a Polygon to get the point, but I'm not sure how or if that would work.
You can use AffineTransform.transform(Point2D, Point2D) to transform a single point on your polygon.
Things would be a lot simpler for you if instead of trying to move the ship by using a rotation transform you kept a single (x,y) location of where the ship is. You'd move the ship's location in move() instead of trying to translate the polygon. Then when you want to paint the ship you e.g. do:
// Optionally copying the Graphics so the
// transform doesn't affect later painting.
Graphics2D temp = (Graphics2D) g2d.create();
temp.translate(ship.locX, ship.locY);
temp.rotate(ship.angle);
temp.draw(ship);
To move a point based on speed you can do this to find the movement vector:
double velX = speed * Math.cos(angle);
double velY = speed * Math.sin(angle);
locX += timeElapsed * velX;
locY += timeElapsed * velY;
That is essentially a conversion from polar to Cartesian coordinates. The x and y velocities are the legs of a triangle whose hypotenuse is speed and whose known angle is angle:
/|
/ |
/ |
/ |
speed / |
/ |
/ |velY
/ angle |
/)_______|
velX
There's an example of doing movement this way in an answer of mine here: https://stackoverflow.com/a/43692434/2891664.
For your comments:
Are you saying that, unlike my initial move function, just to make ship hold a single point, and thus I would only translate that instead?
More or less, yes. You'd still have a polygon to hold the ship's shape, but the points on the polygon would be relative to (0,0).
Suppose the following definitions:
Each (x,y) point on the polygon is pi. (In other words, one of p0, p1, p2 and p3.)
The (x,y) coordinates of the translation are T
Then, after translating the Graphics2D, each pi coordinate becomes pi+T on the panel. So if your polygon points are defined relative to (0,0) then translating to the ship's (locX,locY) will move the polygon to a location relative to (locX,locY).
It could be simplest then to define the point which is the tip of the polygon as being (0,0) so that after the translation the tip of the ship is the ship's location:
// Your original points:
int xPoints[] = {800, 780, 800, 820};
int yPoints[] = {400, 460, 440, 460};
// Become these points relative to (0,0):
int xPoints[] = {0, -20, 0, 20};
int yPoints[] = {0, 60, 40, 60};
And to e.g. start the ship in the same place, you would initialize its location to (800,400).
I was thinking about this again and realized the rotation is a little more complicated, because you probably don't want to rotate the ship around the tip. You probably want to rotate the ship around its center.
So, here's an MCVE demonstrating how to do all of this.
package mcve.game;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.GraphicsConfiguration;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
public class MovementExample implements ActionListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(MovementExample::new);
}
final int fps = 60;
final int period = 1000 / fps;
final JFrame frame;
final GamePanel panel;
final Controls controls;
final Ship ship;
final List<Bullet> bullets = new ArrayList<>();
MovementExample() {
frame = new JFrame("Movement Example");
Dimension size = getMaximumWindowSize(frame);
size.width /= 2;
size.height /= 2;
frame.setPreferredSize(size);
panel = new GamePanel();
frame.setContentPane(panel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
controls = new Controls();
ship = new Ship(panel.getWidth() / 2,
panel.getHeight() / 2);
new Timer(period, this).start();
}
#Override
public void actionPerformed(ActionEvent e) {
double secondsElapsed = 1.0 / fps;
ship.update(secondsElapsed);
bullets.forEach(b -> b.update(secondsElapsed));
Rectangle bounds = panel.getBounds();
bullets.removeIf(b -> !bounds.contains(b.locX, b.locY));
panel.repaint();
}
class GamePanel extends JPanel {
GamePanel() {
setBackground(Color.WHITE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (ship != null) {
ship.draw(g2);
}
bullets.forEach(b -> b.draw(g2));
g2.dispose();
}
}
abstract class AbstractGameObject {
double maxSpeed;
double rotationAngle;
double locX;
double locY;
double velX;
double velY;
AbstractGameObject(double initialX, double initialY) {
locX = initialX;
locY = initialY;
}
abstract void update(double secondsElapsed);
abstract void draw(Graphics2D g2);
}
class Ship extends AbstractGameObject {
Polygon shape;
double rotationRate;
Ship(double initialX, double initialY) {
super(initialX, initialY);
maxSpeed = 128; // pixels/second
rotationAngle = Math.PI * 3 / 2;
rotationRate = (2 * Math.PI) / 2; // radians/second
int xPoints[] = {0, -20, 0, 20};
int yPoints[] = {0, 60, 40, 60};
shape = new Polygon(xPoints, yPoints, 4);
}
Point2D.Double getTip() {
Point2D.Double center = getCenter();
// The tip is at (0,0) and it's already centered
// on the x-axis origin, so the distance from the
// tip to the center is just center.y.
double distance = center.y;
// Then find the location of the tip, relative
// to the center.
double tipX = distance * Math.cos(rotationAngle);
double tipY = distance * Math.sin(rotationAngle);
// Now find the actual location of the center.
center.x += locX;
center.y += locY;
// And return the actual location of the tip, relative
// to the actual location of the center.
return new Point2D.Double(tipX + center.x, tipY + center.y);
}
Point2D.Double getCenter() {
// Returns the center point of the ship,
// relative to (0,0).
Point2D.Double center = new Point2D.Double();
for (int i = 0; i < shape.npoints; ++i) {
center.x += shape.xpoints[i];
center.y += shape.ypoints[i];
}
center.x /= shape.npoints;
center.y /= shape.npoints;
return center;
}
#Override
void update(double secondsElapsed) {
// See my answer here: https://stackoverflow.com/a/43692434/2891664
// for a discussion of why this logic is the way it is.
double speed = 0;
if (controls.isUpHeld()) {
speed += maxSpeed;
}
if (controls.isDownHeld()) {
speed -= maxSpeed;
}
velX = speed * Math.cos(rotationAngle);
velY = speed * Math.sin(rotationAngle);
locX += secondsElapsed * velX;
locY += secondsElapsed * velY;
double rotation = 0;
if (controls.isLeftHeld()) {
rotation -= rotationRate;
}
if (controls.isRightHeld()) {
rotation += rotationRate;
}
rotationAngle += secondsElapsed * rotation;
// Cap the angle so it can never e.g. get so
// large that it loses precision.
if (rotationAngle > 2 * Math.PI) {
rotationAngle -= 2 * Math.PI;
}
if (controls.isFireHeld()) {
Point2D.Double tipLoc = getTip();
Bullet bullet = new Bullet(tipLoc.x, tipLoc.y, rotationAngle);
bullets.add(bullet);
}
}
#Override
void draw(Graphics2D g2) {
Graphics2D copy = (Graphics2D) g2.create();
copy.setColor(Color.RED);
// Translate to the ship's location.
copy.translate(locX, locY);
// Rotate the ship around its center.
Point2D.Double center = getCenter();
// The PI/2 offset is necessary because the
// polygon points are defined with the ship
// already vertical, i.e. at an angle of -PI/2.
copy.rotate(rotationAngle + (Math.PI / 2), center.x, center.y);
copy.fill(shape);
}
}
class Bullet extends AbstractGameObject {
Ellipse2D.Double shape = new Ellipse2D.Double();
Bullet(double initialX, double initialY, double initialRotation) {
super(initialX, initialY);
maxSpeed = 512;
rotationAngle = initialRotation;
velX = maxSpeed * Math.cos(rotationAngle);
velY = maxSpeed * Math.sin(rotationAngle);
double radius = 3;
shape.setFrame(-radius, -radius, 2 * radius, 2 * radius);
}
#Override
void update(double secondsElapsed) {
locX += secondsElapsed * velX;
locY += secondsElapsed * velY;
}
#Override
void draw(Graphics2D g2) {
Graphics2D copy = (Graphics2D) g2.create();
copy.setColor(Color.BLACK);
copy.translate(locX, locY);
copy.fill(shape);
}
}
// See https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
class Controls {
final Set<Integer> keysHeld = new HashSet<>();
Controls() {
bind(KeyEvent.VK_A, "left");
bind(KeyEvent.VK_D, "right");
bind(KeyEvent.VK_W, "up");
bind(KeyEvent.VK_S, "down");
bind(KeyEvent.VK_SPACE, "fire");
}
boolean isLeftHeld() { return keysHeld.contains(KeyEvent.VK_A); }
boolean isRightHeld() { return keysHeld.contains(KeyEvent.VK_D); }
boolean isUpHeld() { return keysHeld.contains(KeyEvent.VK_W); }
boolean isDownHeld() { return keysHeld.contains(KeyEvent.VK_S); }
boolean isFireHeld() { return keysHeld.contains(KeyEvent.VK_SPACE); }
void bind(int keyCode, String name) {
bind(keyCode, name, true);
bind(keyCode, name, false);
}
void bind(int keyCode, String name, boolean isOnRelease) {
KeyStroke stroke = KeyStroke.getKeyStroke(keyCode, 0, isOnRelease);
name += isOnRelease ? ".released" : ".pressed";
panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(stroke, name);
panel.getActionMap()
.put(name, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if (isOnRelease) {
keysHeld.remove(keyCode);
} else {
keysHeld.add(keyCode);
}
}
});
}
}
// This returns the usable size of the display which
// the JFrame resides in, as described here:
// http://docs.oracle.com/javase/8/docs/api/java/awt/GraphicsEnvironment.html#getMaximumWindowBounds--
static Dimension getMaximumWindowSize(JFrame frame) {
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Dimension size = config.getBounds().getSize();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
size.width -= insets.left + insets.right;
size.height -= insets.top + insets.bottom;
return size;
}
}
There are other ways the tip of the ship could be calculated, but the way I did it in the MCVE is this:
Get the center point of the ship, relative to (0,0).
Get the distance from the center point to the tip. The tip is at (0,0) so this is just the y-coordinate of the center.
Then calculate the (x,y) location of the tip, relative to the center. This is done in a very similar way to the figure above for speed and velocity, but the hypotenuse is the distance between the center and the tip of the ship.
Translate the center to be relative to the ship's location.
Translate the location of the tip (which is relative to the center) to be relative to the ship's location.
It could also all be done with an AffineTransform, similar to what you are doing in the code in the question, but you'd set it on every update. Something like this:
AffineTransform transform = new AffineTransform();
#Override
void update(double secondsElapsed) {
...
// Clear the previous translation and rotation.
transform.setToIdentity();
// Set to current.
transform.translate(locX, locY);
Point2D.Double center = getCenter();
transform.rotate(rotationAngle + (Math.PI / 2), center.x, center.y);
if (controls.isFireHeld()) {
Point2D.Double tip = new Point2D.Double(0, 0);
transform.transform(tip, tip);
Bullet bullet = new Bullet(tip.x, tip.y, rotationAngle);
bullets.add(bullet);
}
}
You could still use a transform to do calculations that way, but you don't end up with any strangeness from depending on the transform for movement. (In the code in the question, the ship is e.g. only ever moved along the y-axis. The apparent sideways movement is due to the series of rotation concatenations.)

Calculating time of simulation in Java

I am doing a project on a bird killing game. Every thing working fine but i want that my bird cross the screen in 35 seconds for any given mobile screen. And after every 20 seconds its time reduced to 31. What will be the mathematical formula(for speed) to cross the screen in 35 seconds? Currently am updating x-axis value in update method and creating rectangle for the bird-sprites.
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.Random;
public class Birds extends GameObject implements View.OnTouchListener{
private Bitmap spritesheet;
private Rect rect;
public boolean firstTym = true;
private Animation animation = new Animation();
private String tag = "";
private int y,touchX,touchY;
public int x=0;
private long startTime;
public Birds(String tag)
{
this.tag = tag;
spritesheet = BitmapFactory.decodeResource(Constants.res, R.drawable.bird_sprites);
dy = 0;
if(Constants.Width > 1300) {
width = 120;
height = 140;
}
else {
width = 80;
height = 72;
}
Bitmap[] image = new Bitmap[5];
for (int i = 0; i < image.length; i++)
{
image[i] = Bitmap.createBitmap(spritesheet, i*width, 0, width, height);
}
animation.setFrames(image);
animation.setDelay(10);
startTime = System.nanoTime();
}
public void update()
{
if(!firstTym) {
// here i am updating speed of bird in x-axis.
//i want bird to cross the screen in 35 seconds
x += Constants.speed;
Log.e("speed = ","" + Constants.speed);
}
else
{
Random r = new Random();
r.nextInt(Constants.Width);
}
if(x > GamePanel.WIDTH){
Constants.missed ++;
x = 0;
}
if(y > GamePanel.HEIGHT)
{
x = 0;
}
}
public void draw(Canvas canvas) {
Random r = new Random();
if (x == 0 || firstTym) {
y = r.nextInt(GamePanel.HEIGHT - 150);
Constants.RAND = r.nextInt(1);
firstTym = false;
}
animation.update();
y += Constants.RAND;
rect = new Rect(x, y, x + 80, 72 + y);
setRect(rect);
setTag(tag);
canvas.drawBitmap(animation.getImage(), null, rect, null);
if (x < 0) {
canvas.drawBitmap(animation.getImage(), null, rect, null);
}
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
touchX = (int)event.getX();
touchY = (int)event.getY();
return true;
}
}
Speed is distance over time. If I move 10 meters in 2 seconds then I am moving at 10 divide by 2 meters per second which is 5 meters per second. We can write it as s = D / t where s is speed, D is distance, and t is time. With that we can arrange it so that we can define any of the components in terms of the other two.
thus we get
s = D / t; // get the speed in term of distance and time
t = D / s; // get the time in term of distance and speed
D = t * s; // get the distance in term of time and speed
You have the distance D in pixels (ie the width) and the time, in seconds. You will need to get the system clock time startTime when you start the bird flying, the current time timeNow. You will need the x and y position where the bird starts, and the x and y position xEnd,yEndof where the bird will be in the time, and the time you want it to take time
At the start you need to set up the variables you need. I Don't do java (yuck) so can not remember the class to get system time. Just look it up in the reference docs.
// set up
time = 35; // how long to cross the screen
x = 0; // start pos
y = 100; //
xEnd = screenWidth; // end pos
yEnd = 100;
startTime = ?.now() // ? is whatever class you get the system time from
With all that to get the position of the bird.
// each frame get the time in seconds
timeNow = ?;
if( timeNow - startTime < time) { // make sure time is not over
currentX = x + ((xEnd - x) / time) * (timeNow - startTime))
currentY = y + ((yEnd - y) / time) * (timeNow - startTime))
}else{
// All done
}
You know its all over when timeNow - startTime > time
If the time is in millisecond or smaller you will need to convert it to seconds by dividing. Ie for milliseconds time = timeInMillisecond / 1000;

Drawing Four Leaf Rose in Java

Really having trouble trying to draw a four leaf rose: This is the exercise:
Draw a picture of the “four­leaved rose” whose equation in polar coordinates is
r =cos(2θ) . Let θ go from 0 to 2*pi in 100 steps. Each time, compute r and then com­pute the (x, y) coordinates from the polar coordinates by using the formula
x = r ⋅ cos( θ) , y = r ⋅ sin(θ )
My Code:
public Rose(double awidth, double aheight)
{
width = awidth;
height = aheight;
theta = 0;
}
public void drawRose(Graphics2D g2)
{
Ellipse2D.Double test ;
double r = 0;
for(int i = 0; i <= 100; i++)
{
r = Math.cos(Math.toRadians(2*theta) );
x = r *( Math.cos( Math.toRadians(theta) ) * width ) + 300;
y = r * ( Math.sin( Math.toRadians(theta) ) * height ) + 300 ;
test = new Ellipse2D.Double(x, y, width, height);
theta += 3.6;
g2.draw(test);
}
}
}
Any help will be greatly appreciately.
Your biggest mistake is here:
test = new Ellipse2D.Double(x, y, width, height);
You're creating 100 Ellipses with the points that are on the rose, but that with heights and widths of the desired rose. You really don't want 100 ellipses, but rather you want to connect lines between the x and y points you've created, that is connect the current x, y with the previous ones created (as long as there is a previous x and y).
One way is via these suggestions, but there are other ways to do this:
Use a Path2D object, the concrete implementation would be a Path2D.Double, to hold your data points. Create this before creating data points.
Use a for loop that goes from 0 to 100, and do this in the class's constructor
set your double theta in the loop
set your double r variable in the loop
Calculate your x and y double points
Scale your x and y points by multiplying them with a scale factor so that the drawing has some size. I used 150.0
Translate your x and y values by adding a translation constant. I used 200 and it worked nicely in a 400 x 400 JPanel. Else the center of the rose will be at 0, 0 and only a fourth of it will be visible.
In the first iteration of the for loop call the Path2D's moveTo(...) method to add a starting point
In all other iterations call the lineTo(...) method. This will draw a line between neighboring points.
After the for loop, close the path by calling closePath() on it.
Draw the path in your JPanel's paintComponent method by casting your Graphics parameter into a Graphics2D object (actually you don't need this since your draw method gets a Grahpics2D object), and calling draw(path) with the Graphics2D object, passing in your Path2D object.
For example, this is created:
with this code:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class RosePanel extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int MAX = 100;
private static final double SCALE = 150.0;
private static final double DELTA_X = 200;
private static final double DELTA_Y = DELTA_X;
private static final Color ROSE_COLOR = Color.red;
private static final Stroke ROSE_STROKE = new BasicStroke(8f);
private Path2D path = new Path2D.Double();
public RosePanel() {
for (int i = 0; i < MAX; i++) {
double theta = i * 2 * Math.PI / MAX;
double r = Math.cos(2 * theta);
double dX = SCALE * r * Math.cos(theta) + DELTA_X;
double dY = SCALE * r * Math.sin(theta) + DELTA_Y;
if (i == 0) {
path.moveTo(dX, dY);
} else {
path.lineTo(dX, dY);
}
}
path.closePath();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(ROSE_COLOR);
g2.setStroke(ROSE_STROKE);
g2.draw(path);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
RosePanel mainPanel = new RosePanel();
JFrame frame = new JFrame("RosePanel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that the key difference between my code and yours, other than the translations and the scaling is that I'm connecting the line between points created.

Categories