Failed to implement object rotation towards mouse JAVA [closed] - java

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
As title say, just can't do it. Attempted for long time and still fail. (I'm new to java)
My Image or fillRect rotate a little bit but not as it should rotate
Whole code +imgs
*MAIN game class*
package game.main;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
public class game extends Canvas implements Runnable{
private static final long serialVersionUID = -392333887196083915L;
public static final int WIDTH = 640, HEIGHT = WIDTH / 12 * 9;
private Thread thread;
private boolean running = false;
private Handler handler;
public game(){
handler = new Handler();
new window(WIDTH, HEIGHT,"Game",this);
handler.addObject(new Player(WIDTH/2-32, HEIGHT/2-32, ID.Player, handler));
}
public synchronized void start(){
thread = new Thread(this);
thread.start();
running = true;
}
public synchronized void stop(){
try{
thread.join();
running = false;
}catch(Exception e){
e.printStackTrace();
}
}
// ↓ game loop for update
public void run(){
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1){
tick();
delta--;
}
if(running)
render();
frames++;
if(System.currentTimeMillis() - timer > 1000){
timer += 1000;
System.out.print("FPS:" + frames);
frames = 0;
}
}
stop();
}
private void tick(){
handler.tick();
}
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, WIDTH, HEIGHT);
handler.render(g);
g.dispose();
bs.show();
}
public static void main(String args[]) {
new game();
}
}
<br>
package game.main;
import java.awt.Graphics;
public abstract class GameObject {
protected float x, y;
protected ID id;
protected float velX, velY;
public GameObject(float x, float y, ID id){
this.x = x;
this.y = y;
this.id = id;
}
public abstract void tick();
public abstract void render(Graphics g);
public void setX(int x){
this.x = x;
}
public void setY(int y){
this.y = y;
}
public float getX(){
return x;
}
public float getY(){
return y;
}
public void setId(ID id){
this.id = id;
}
public ID getId(){
return id;
}
public void setVelX(int velX){
this.velX = velX;
}
public void setVelY(int velY){
this.velY = velY;
}
public float getVelX(){
return velX;
}
public float getVelY(){
return velY;
}
}
*GameObject class*
package game.main;
import java.awt.Graphics;
public abstract class GameObject {
protected float x, y;
protected ID id;
protected float velX, velY;
public GameObject(float x, float y, ID id){
this.x = x;
this.y = y;
this.id = id;
}
public abstract void tick();
public abstract void render(Graphics g);
public void setX(int x){
this.x = x;
}
public void setY(int y){
this.y = y;
}
public float getX(){
return x;
}
public float getY(){
return y;
}
public void setId(ID id){
this.id = id;
}
public ID getId(){
return id;
}
public void setVelX(int velX){
this.velX = velX;
}
public void setVelY(int velY){
this.velY = velY;
}
public float getVelX(){
return velX;
}
public float getVelY(){
return velY;
}
}
*Handler class*
package game.main;
import java.awt.Graphics;
import java.util.LinkedList;
// render all objects
public class Handler {
LinkedList<GameObject> object = new LinkedList<GameObject>();
public void tick(){
for(int i = 0; i < object.size(); i++){
GameObject tempObject = object.get(i);
tempObject.tick();
}
}
public void render(Graphics g){
for(int i = 0; i <object.size();i++){
GameObject tempObject = object.get(i);
tempObject.render(g);
}
}// handling adding objects
public void addObject(GameObject object){
this.object.add(object);
}
}
*window class*
package game.main;
import java.awt.Canvas;
import java.awt.Dimension;
import javax.swing.JFrame;
public class window extends Canvas{
private static final long serialVersionUID = 3010486623466540351L;
public window(int width, int height, String title, game game){
JFrame frame = new JFrame(title);
frame.setPreferredSize(new Dimension(width, height));
frame.setMaximumSize(new Dimension(width, height));
frame.setMinimumSize(new Dimension(width, height));
// X button
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// ¤ button maximize
frame.setResizable(false);
// window appear in middle of screen instead of top left corner
frame.setLocationRelativeTo(null);
// add game to window
frame.add(game);
frame.setVisible(true);
game.start();
}
}
* ID class*
package game.main;
public enum ID {
Player();
}
ADDED code only to this class
*Player class*
package game.main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
public class Player extends GameObject{
Handler handler;
public Player(int x, int y, ID id, Handler handler) {
super(x, y, id);
this.handler = handler;
}
public void tick() {
}
public void render(Graphics g) {
///////////ADDED//////////////////
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
int mouseX = (int) b.getX();
int mouseY = (int) b.getY();
int centerX = game.WIDTH / 2;
int centerY = game.HEIGHT / 2;
double angle = Math.atan2(centerY - mouseY, centerX - mouseX) - Math.PI / 2;
((Graphics2D)g).rotate(angle, centerX, centerY);
//////////////////////////////
g.setColor(Color.white);
g.fillRect((int)x, (int)y, 32, 32);
}
}
How it now works and how I want it. WHITE COLOR - original/ GREEN - I want it like that
Example 1
Example 2
I looked in this sources:
Get mouse possition (stackoverflow)
Java 2d rotation in direction mouse point (stackoverflow)
Rotating an object to point towards the mouse

The problem is that MouseInfo.getPointerInfo().getLocation(); returns the absolute mouse location. You need the mouse location relative to your game canvas. You can modify the render method in your game class as follows:
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mouseLocation, this);
handler.render(g, mouseLocation);
This requires you to modify the method signatures of your rendering methods accordingly. This is only one way to pass the mouseLocation from your game canvas to your Player's rendering method.
Use the mouseLocation instead of MouseInfo.getPointerInfo().getLocation(); in your Player's render method.
There are a few more things you have to change to place the player in the center of the canvas and make him rotate around his center:
You should set the size of the game canvas instead of the size of the window(JFrame). Do this by calling setPreferredSize(new Dimension(width, height)); on the game canvas and by calling frame.pack() before frame.setVisible(true). This will ensure that your game canvas has exactly the size specified by WIDTH and HEIGHT.
You could add two fields refx and refy to your GameObject class which describe the reference point of your GameObject (e.g. its center). You could then construct a new Player by calling new Player(WIDTH/2-16, HEIGHT/2-16, 16, 16, ID.Player, handler) where the player's initial position is at (WIDTH/2-16, HEIGHT/2-16) and its reference point is (16,16) - the center of the player when the player is represented by a 32x32 rectangle.
In the Player's render method initialize the center you want to rotate around with int centerX = Math.round(x + refx); int centerY = Math.round(y + refy); where (x,y) is the position of the object you want to rotate and (refx, refy) the point you want to rotate around relative to the object's position (e.g. x = WIDTH/2-16, y = HEIGTH/2-16, refx = 16, refy = 16).

Related

My Java Bouncing Ball Bounces up with a larger velocity

I'm making a simple Java program to bounce a ball up and down. The problem is that the ball bounces up higher than its starting point with each bounce. I expect the ball to bounce back up exactly to the height that it started from.
The ball physics can be found in the circle class in the doPhysics() method where I suspect the problem can be found
import java.awt.*;
import java.util.*;
public class Main{
public static Frame frame = new Frame();
public static Physics physics = new Physics();
public static ArrayList<Circle> circles = new ArrayList<Circle>(); //array for the points
public static void main(String args[]) {
Circle circle = new Circle(100, 300, 50, Color.BLACK);
circles.add(circle);
run();
}
public static void run() {
physics.timer.start();
}
}
import java.awt.*;
public class Circle {
private int x;
private int y;
private double xAccel= 0;
private double yAccel = 0;
private double xVel= 0;
private double yVel = 0;
private Color colour;
private int radius;
public Circle(int x, int y, int radius, Color colour) {
setX(x);
setY(y);
setRadius(radius);
setColour(colour);
}
public void draw(Graphics2D g2d) {
g2d.setColor(colour);
g2d.fillOval(x, y, radius*2, radius*2);
}
public void doPhysics() {
hitGround();
System.out.println(yVel);
yVel += Physics.getGravity();
y -= yVel;
}
public void hitGround() {
if(y + radius*2 > Frame.panel.h ) {
yVel = -yVel;
}
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setColour(Color colour) {
this.colour = colour;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColour() {
return colour;
}
public int getRadius() {
return radius;
}
}
import java.awt.*;
import javax.swing.*;
class Frame extends JFrame {
public static Panel panel;
public Frame() {
panel = new Panel();
this.setTitle("Fun");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
class Panel extends JPanel {
public int w = 500;
public int h = 500;
public Panel() {
this.setPreferredSize(new Dimension(w, h));
this.setBackground(Color.red);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
for(Circle circle : Main.circles) {
circle.draw(g2d);
}
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Physics implements ActionListener {
private static double gravity = -.1;
public Timer timer;
public Physics() {
timer = new Timer(1, this);
}
public static double getGravity() {
return gravity;
}
#Override
public void actionPerformed(ActionEvent e) {
for(Circle circle : Main.circles) {
circle.doPhysics();
}
Main.frame.repaint();
}
}
The problem is mainly caused by using integer values for position (x and y). On each iteration the values are rounded and the errors get accumulated.
Solution: declare double x and double y and only use the rounded integer values for drawing.
Above should reduce the problem, but not completely solve it. The code is doing a rough integration over time¹ by using the velocity calculated after the time interval (see Numerical Integration). This can be improved by doing an average of the velocities before and after it was changed. Roughly:
double preVel = yVel;
yVel += Physics.getGravity();
y -= (preVel + yVel)/2;
which can be simplified (pure math) to:
yVel += Physics.getGravity();
y -= yVel - Physics.getGravity()/2;
This should work fine since the acceleration is constant. Not the case if the acceleration is also changing. And it is also susceptible to precision errors being accumulated over time.
1 - see Numerical integration and Temporal discretization

Swing animation optimization

I have been working on a simple animation using a Timer on a JComponent. However, I experience incredibly choppy motion when I view the animation. What steps should I take to optimize this code?
MyAnimationFrame
import javax.swing.*;
public class MyAnimationFrame extends JFrame {
public MyAnimationFrame() {
super("My animation frame!");
setSize(300,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new AnimationComponent(0,0,50,50));
setVisible(true);
}
public static void main(String[] args) {
MyAnimationFrame f = new MyAnimationFrame();
}
}
AnimationComponent
import javax.swing.*;
import java.awt.*;
public class AnimationComponent extends JComponent implements ActionListener {
private Timer animTimer;
private int x;
private int y;
private int xVel;
private int yVel;
private int width;
private int height;
private int oldX;
private int oldY;
public AnimationComponent(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
animTimer = new Timer(25, this);
xVel = 5;
yVel = 5;
animTimer.start();
}
#Override
public void paintComponent(Graphics g) {
g.fillOval(x,y,width,height);
}
#Override
public void actionPerformed(ActionEvent e) {
oldX = x;
oldY = y;
if(x + width > getParent().getWidth() || x < 0) {
xVel *= -1;
}
if(y + height > getParent().getHeight() || y < 0) {
yVel *= -1;
}
x += xVel;
y += yVel;
repaint();
}
}
Not sure if this matters, but I am using OpenJDK version 1.8.0_121.
Any help is appreciated.
After a wonderful discussion with Yago it occurred to me that the issue revolves around number of areas, alot comes down to the ability for Java to sync the updates with the OS and the hardware, some things you can control, some you can't.
Inspired by Yago's example and my "memory" of how the Timing Framework works, I tested you code by increasing the framerate (to 5 milliseconds, ~= 200fps) and decreasing the change delta, which gave the same results as using the Timing Framework, but which leaves you with the flexibility of your original design.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.add(new AnimationComponent(0, 0, 50, 50));
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationComponent extends JComponent implements ActionListener {
private Timer animTimer;
private int x;
private int y;
private int xVel;
private int yVel;
private int width;
private int height;
private int oldX;
private int oldY;
public AnimationComponent(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
animTimer = new Timer(5, this);
xVel = 1;
yVel = 1;
animTimer.start();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
);
g2d.setRenderingHints(hints);
g2d.fillOval(x, y, width, height);
}
#Override
public void actionPerformed(ActionEvent e) {
oldX = x;
oldY = y;
if (x + width > getParent().getWidth() || x < 0) {
xVel *= -1;
}
if (y + height > getParent().getHeight() || y < 0) {
yVel *= -1;
}
x += xVel;
y += yVel;
repaint();
}
}
}
If you need to slow down the speed more, then decrease the change delta more, this will mean you have to use doubles instead, which will lead into the Shape's API which supports double values
Which should you use? That's up to you. The Timing Framework is really great for linear animations over a period of time, where you know you want to go from one state to another. It's not so good for things like games, where the state of the object can change from my cycle to another. I'm sure you could do it, but it'd be a lot easier with a simple "main loop" concept - IMHO
Timing Framework offers a way to provide animations highly optimized which may help in this case.
MyAnimationFrame
import javax.swing.*;
public class MyAnimationFrame extends JFrame {
public MyAnimationFrame() {
super("My animation frame!");
setSize(300,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new AnimationComponent(0,0,50,50));
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MyAnimationFrame f = new MyAnimationFrame();
}
});
}
}
AnimationComponent
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.TimeUnit;
import org.jdesktop.core.animation.rendering.*;
import org.jdesktop.core.animation.timing.*;
import org.jdesktop.core.animation.timing.interpolators.*;
import org.jdesktop.swing.animation.rendering.*;
import org.jdesktop.swing.animation.timing.sources.*;
#SuppressWarnings("serial")
public class AnimationComponent extends JRendererPanel {
protected int x;
protected int y;
protected int width;
protected int height;
protected Animator xAnimator;
protected Animator yAnimator;
public AnimationComponent(int x, int y, int width, int height) {
setOpaque(true);
this.x = x;
this.y = y;
this.height = height;
this.width = width;
JRendererFactory.getDefaultRenderer(this,
new JRendererTarget<GraphicsConfiguration, Graphics2D>() {
#Override
public void renderSetup(GraphicsConfiguration gc) {
// Nothing to do
}
#Override
public void renderUpdate() {
// Nothing to do
}
#Override
public void render(Graphics2D g, int w, int h) {
Color c = g.getColor();
g.setColor(g.getBackground());
g.fillRect(0, 0, w, h);
g.setColor(c);
g.fillOval(AnimationComponent.this.x, AnimationComponent.this.y,
AnimationComponent.this.width, AnimationComponent.this.height);
}
#Override
public void renderShutdown() {
// Nothing to do
}
}, false);
this.xAnimator = new Animator.Builder(new SwingTimerTimingSource())
.addTargets(new TimingTargetAdapter() {
#Override
public void timingEvent(Animator source, double fraction) {
AnimationComponent.this.x = (int) ((getWidth() - AnimationComponent.this.width) * fraction);
}})
.setRepeatCount(Animator.INFINITE)
.setRepeatBehavior(Animator.RepeatBehavior.REVERSE)
.setInterpolator(LinearInterpolator.getInstance()).build();
this.yAnimator = new Animator.Builder(new SwingTimerTimingSource())
.addTargets(new TimingTargetAdapter() {
#Override
public void timingEvent(Animator source, double fraction) {
AnimationComponent.this.y = (int) ((getHeight() - AnimationComponent.this.height) * fraction);
}})
.setRepeatCount(Animator.INFINITE)
.setRepeatBehavior(Animator.RepeatBehavior.REVERSE)
.setInterpolator(LinearInterpolator.getInstance()).build();
addComponentListener(new ComponentAdapter() {
private int oldWidth = 0;
private int oldHeight = 0;
#Override
public void componentResized(ComponentEvent event) {
Component c = event.getComponent();
int w = c.getWidth();
int h = c.getHeight();
if (w != this.oldWidth) {
AnimationComponent.this.xAnimator.stop();
AnimationComponent.this.xAnimator = new Animator.Builder()
.copy(AnimationComponent.this.xAnimator)
.setDuration(w * 5, TimeUnit.MILLISECONDS) // Original speed was 200 px/s
.build();
AnimationComponent.this.xAnimator.start();
}
if (h != this.oldHeight) {
AnimationComponent.this.yAnimator.stop();
AnimationComponent.this.yAnimator = new Animator.Builder()
.copy(AnimationComponent.this.yAnimator)
.setDuration(h * 5, TimeUnit.MILLISECONDS) // Original speed was 200 px/s
.build();
AnimationComponent.this.yAnimator.start();
}
this.oldWidth = w;
this.oldHeight = h;
}
});
}
}
I'm getting good results but has one issue: any item you resize, the animation is reset.

Particles not rendering

I put in a particle system but when i run the program, when I spawn some particles, they don't render. I looked at the ArrayList and its value would always be 0 even when i added a particle to it.
heres the code for main class:
package Main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import me.mango.rendering.Particle;
//do double buffering
public class Game extends JPanel {
private static final long serialVersionUID = 1L;
public static final int height = 400;
public static final int width = height * 16 / 9;
JPanel p;
Game game;
Graphics g;
JFrame frame;
KeyListener kl;
MouseListener ml;
public boolean running = true;
private ArrayList<Particle> particles = new ArrayList<Particle>(500);
public Game(){
kl = new KeyListener(){
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
};
ml = new MouseListener(){
public void mousePressed(MouseEvent e) {
addParticle(true);addParticle(false);addParticle(true);
addParticle(false);addParticle(true);addParticle(false);
}
public void mouseReleased(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
};
}
public void addParticle(boolean b){
int dx,dy;
int x = 100;
int y = 100;
if(b){
dx = (int) (Math.random()*5);
dy = (int) (Math.random()*5);
}else{
dx = (int) (Math.random()*-5);
dy = (int) (Math.random()*-5);
}
int size = (int) (Math.random()*12);
int life = (int) Math.random()*(120)+380;
particles.add(new Particle(x,y,dx,dy,size,life,Color.blue));
}
public void update(double delta){
for(int i = 0; i<= particles.size() - 1;i++){
if(particles.get(i).update()) particles.remove(i);
}
System.out.println(particles.size());
}
#Override
public void paint(Graphics g){
g.clearRect(0, 0, getWidth(), getHeight());
//render here
renderParticles(g);
g.dispose();
}
public void renderParticles(Graphics g){
for(int i =0;i <= particles.size() - 1;i++){
particles.get(i).render(g);
System.out.println("spawned");
}
}
public void run(){
//initialize time loop variables
long lastLoopTime = System.nanoTime();
final int TARGET_FPS = 60;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
double lastFpsTime = 0;
//Main game loop
while(running)
{
//Calculate since last update
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double)OPTIMAL_TIME);
//update frame counter
lastFpsTime += updateLength;
//update FPS counter
if(lastFpsTime >= 1000000000)
{
lastFpsTime = 0;
}
//game updates
game.update(delta);
//graphics (gameState)
game.repaint();
try{
Thread.sleep((Math.abs(lastLoopTime - System.nanoTime() + OPTIMAL_TIME)/1000000));
}catch(Exception e){
System.out.println("Error in sleep");
}
}
}
public void start(){
frame = new JFrame("Game");
game = new Game();
frame.add(game);
frame.pack();
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.addKeyListener(kl);
frame.addMouseListener(ml);
frame.setVisible(true);
run();
}
public static void main(String[] args){
new Game().start();
}
}
and for the particle class:
package me.mango.rendering;
import java.awt.Color;
import java.awt.Graphics;
public class Particle {
private int x;
private int y;
private int dx;
private int dy;
private int size;
private int life;
private Color color;
public Particle(int x, int y, int dx, int dy, int size, int life, Color c){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.size = size;
this.life = life;
this.color = c;
}
public boolean update(){
x += dx;
y += dy;
life--;
if(life <= 0){
return true;
}
return false;
}
public void render(Graphics g){
g.setColor(color);
g.fillRect(x-(size/2), y-(size/2), size, size);
g.dispose();
}
}
Thanks!
You have a thing called game inside the class Game: that's not good design at all. Apparently you dont understand the meaning of creating an object.
In main() you created an object game: that should be enough. That thing you have to manipulate.
Therefore calling game.something() inside the class game is a convolution. Get rid of it.
game = new Game();
Game game;
These things must go.
And any reference to game.someMethod()
should be replaced with just someMethod(), if you are inside Game.
Plus you have things like run() and start() etc: do you think you are creating some threads?? by just using those names for your methods?
No.

Updating two entities simultaenously in Java Pong

I've started using LWJGL recently since I stopped using Java and wanted a quick refresher with something new tied in. Unfortunately, I've encountered a problem while working on a small Java pong game. I sort of sped through it, but I've reviewed my code multiple times and I cannot figure out what is wrong.
Problem: I can't update multiple entities on the screen at the same time. It is weird because I have no problem moving them, and their values update accordingly according to the debugger, but the graphics will not update on the screen. Here is my code for the main program:
package lwjgl2;
import static org.lwjgl.opengl.GL11.*;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
public class Main {
private Box ball = new Box(640>>1, 480>>1, 5, 5);
private Box paddle = new Box(10, 10, 10, 40);
private long lastTime;
private long getTime(){
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
}
private int getDelta(){
long currentTime = getTime();
int delta = (int) (currentTime - lastTime);
lastTime = getTime();
return delta;
}
public Main() {
//display
try{
Display.setDisplayMode(new DisplayMode(640,480));
Display.setTitle("Hello");
Display.create();
} catch(LWJGLException e){
e.printStackTrace();
}
//ogl
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 640, 480, 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
lastTime = getTime();
//main loop
while(!Display.isCloseRequested()){
glClear(GL_COLOR_BUFFER_BIT);
if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)){
Display.destroy();
System.exit(0);
}
paddle.setDX(0.1);
ball.setDX(0.3);
ball.setDY(0.2);
//main game loop goes nyah
ball.update(getDelta());
paddle.update(getDelta());
ball.draw();
paddle.draw();
Display.update();
Display.sync(60);
}
//destroy display
Display.destroy();
}
private class Box extends Entity{
public Box(int x, int y, int height, int width) {
super(x, y, height, width);
// TODO Auto-generated constructor stub
}
public void update(int delta){
super.x += dx * delta;
super.y += dy * delta;
}
public void draw(){
glRecti(x, y, x + height, y + width);
}
}
public static void main(String args[]){
new Main();
}
}
Like I said, the only entity that will actually render properly on the screen is whatever I call the update() method on first, in this case, the ball. If I call the paddle first, then the paddle will move, but the ball will stay still. I know I must have made a stupid mistake somewhere but I can't find it.
Also, the Entity class, possibly the main culprit:
package lwjgl2;
import java.awt.Rectangle;
public abstract class Entity {
protected int x;
protected int y;
protected int height;
protected int width;
protected double dx;
protected double dy;
protected Rectangle bounds = new Rectangle();
public Entity(int x, int y, int height, int width) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
this.dx = 0;
this.dy = 0;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public double getDX(){
return dx;
}
public double getDY(){
return dy;
}
public void setX(int x){
this.x = x;
}
public void setY(int y){
this.y = y;
}
public void setDX(double dx){
this.dx = dx;
}
public void setDY(double dy){
this.dy = dy;
}
public void update(int delta){
}
public void draw(int delta){
}
}
I've figured it out. It happens that updating multiple entities at varying times through the loop isn't a good idea, so you should create a method for updating every entity at once.

java space ship game

here is the entire code for the classes Ship,Asteroids,BaseShapeClass. Ship Class inherits from the BaseShapeClass for its shape. Asteroid class is the main source code which declares the Graphics2D object,AffineTransform(for identity creation),declares double image buffer...
Code for BaseShapeClass..
package baseshapeclass;
import java.awt.Shape;
public class BaseShapeClass {
private Shape shape;
private double x, y;
private double velX, velY;
private double moveAngle, faceAngle;
private boolean alive;
//accessors and mutators
public Shape getShape(){return shape;}
public void setShape(Shape shape){ this.shape = shape; }
public double getX() { return x; }
public void setX(double x) { this.x = x; }
public void incX(double ix) { this.x += ix; }
public double getY() { return y; }
public void setY(double y) { this.y = y; }
public void incY(double iy) { this.y += iy; }
public double getVelX() { return velX; }
public void setVelX(double velX) { this.velX = velX; }
public void incVelX(double ivX) { this.velX += ivX; }
public double getVelY() { return velY; }
public void setVelY(double velY) { this.velY = velY; }
public void incVelY(double ivY) { this.velY += ivY; }
//MoveAngle refers to the objects angular movement
public double getMoveAngle() { return moveAngle; }
public void setMoveAngle(double mAngle) { this.moveAngle = mAngle; }
public void incMoveAngle(double imAngle) { this.moveAngle += imAngle; }
//FaceAngle refers to the objects face/heads angular movement
public double getFaceAngle() { return faceAngle; }
public void setFaceAngle(double fAngle) { this.faceAngle = fAngle; }
public void incFaceAngle(double ifAngle) { this.faceAngle += ifAngle; }
public boolean isAlive() { return alive; }
public void setAlive(boolean alive) { this.alive = alive; }
//default constructor everything will be set to original state
//when update is called everything will start to move
BaseShapeClass(){
setShape(null);
setAlive(false);
//all of them are set to '0' representing their initial position,
//which will be called during the update() Event of the graphics objects
setX(0.0);
setY(0.0);
setVelX(0.0);
setVelY(0.0);
setMoveAngle(0.0);
setFaceAngle(0.0);
}
}
Code for Ship class...
package baseshapeclass;
import java.awt.Rectangle;
import java.awt.Polygon;
public class Ship extends BaseShapeClass {
//ships shape along the x and y cordinates
private final int[] shipx = {-6,3,0,3,6,0};
private final int[] shipy = {6,7,7,7,6,-7};
public Rectangle getBounds(){
Rectangle r = new Rectangle((int)getX()-6, (int)getY()-6, 12, 12);
return r;
}
Ship(){
setShape(new Polygon(shipx, shipy, shipx.length));
setAlive(true);
}
}
Code for Asteroid(Main source code)...
package baseshapeclass;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.applet.*;
import java.util.*;
public abstract class Asteroid extends Applet implements Runnable, KeyListener {
BufferedImage backbuffer;
Graphics2D g2d;
Ship ship = new Ship();
boolean showBounds= true;
AffineTransform identity = new AffineTransform();
#Override public void init(){
backbuffer = new BufferedImage(640,480,BufferedImage.TYPE_INT_RGB);
g2d = backbuffer.createGraphics();
ship.setX(320);
ship.setY(240);
addKeyListener(this);
}
#Override public void update(Graphics g){
g2d.setTransform(identity);
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, getSize().width, getSize().height);
g2d.setColor(Color.WHITE);
g2d.drawString("Ship: "+Math.round(ship.getX())+" , "+Math.round(ship.getY()),2, 150);
g2d.drawString("Face Angle: "+Math.toRadians(ship.getFaceAngle()),5, 30);
g2d.drawString("Move Angle: "+Math.toRadians(ship.getMoveAngle())+90,5,50);
drawShip();
paint(g);
}
public void drawShip(){
g2d.setTransform(identity);
g2d.translate(ship.getX(),ship.getY());
g2d.rotate(Math.toRadians(ship.getFaceAngle()));
g2d.setColor(Color.ORANGE);
g2d.fill(ship.getShape());
}
}
I hope you guys get a better idea with all the code in place. Just wanted to know on the part of Ship class why are the ships x and y cordinates such as under:
public class ship extends BaseShapeClass{
private int[] shipx = {-6,3,0,3,6,0};
private int[] shipy = {6,7,7,7,6,-7};
}
I cant follow on how those values will make upto a Polygon??
Ship(){
setShape(new Polygon(shipx,shipy,shipx.length));
setAlive(true);
}
You can see that the two arrays you are confused about go into the initialization of a Polygon. These two arrays, taken as a pair, give the x and y coordinates of each point in the Polygon.
This post is in answer to your comment in Kronion's answer; I was going to post it as a comment, but there is too much to say and I wanted to show you some code, which is not as legible in the comments.
As Kronion said, the Polygon class does indeed accept an array of X coordinates, and an array of Y coordinates. The reason for this is that the X and Y coordinate are stored at the same position in both arrays. So if int index = 0, then that X,Y coordinate pair would be xArray[index] and yArray[index].
If that doesn't make any sense, examine the Polygon class source code. For example, you'll see this happening in the contains method, here:
for (int i = 0; i < npoints; lastx = curx, lasty = cury, i++) {
curx = xpoints[i];
cury = ypoints[i];
// remainder of loop
}
So in short, they are assigned in this manner because the X and Y are paired by their index positions.
Hope that helps.

Categories