I'm making a small game in Java using an extension of Swing's JPanel as my drawing surface.
I draw everything inside panel's paintComponent()
The game runs smoothly until I start moving my mouse. When I do I get huge FPS drops, especially if i move it very fast, making the game unplayable.
That happens even when I stop drawing the mouse cursor.
Is this normal when drawing on JComponent objects?
P.S. I couldn't find any registered MouseListener or MouseMotionListener objects on any of my components.
EDIT:
MCVE
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class AnimationSlowDownOnMouse {
public static void main(String[] args) {
// TODO Auto-generated method stub
JFrame mainWindow = new JFrame("dotShoot");
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setExtendedState(JFrame.MAXIMIZED_BOTH);
final GamePanel gp = new GamePanel();
gp.setPreferredSize(new Dimension(1920, 1080));
mainWindow.add(gp);
mainWindow.pack();
gp.init();
Thread gameThread = new Thread(new Runnable() {
#Override
public void run() {
final int maxTicksPerSecond = 100;
final int optimalTimePerTick = 1000 / maxTicksPerSecond;
final int maxFrameSkips = 5;
long tickCount = 0L;
float AvgIES = 0f;
//int FPS = 0;
int DCPS = 0;
int TPS = 0;
long timeStarted = System.currentTimeMillis();
long timeElapsed = 0L;
int tickReset = 0;
int drawCallsReset = 0;
long timeReset = timeStarted;
float interpolationReset = 0f;
long nextLoop = timeStarted;
int frameSkips = 0;
float interpolation;
while (true) {
synchronized (this) {
frameSkips = 0;
while (System.currentTimeMillis() > nextLoop && frameSkips < maxFrameSkips) {
gp.update(tickCount);
nextLoop += optimalTimePerTick;
tickCount++;
tickReset++;
frameSkips++;
}
interpolation = (float) (System.currentTimeMillis() + optimalTimePerTick - nextLoop) / (float) optimalTimePerTick;
gp.setInterpolation(interpolation);
gp.repaint();
interpolationReset += interpolation;
drawCallsReset++;
timeElapsed = System.currentTimeMillis() - timeStarted;
if (System.currentTimeMillis() - timeReset >= 1000) {
AvgIES = interpolationReset / (float) drawCallsReset;
interpolationReset = 0f;
TPS = tickReset;
tickReset = 0;
DCPS = drawCallsReset;
drawCallsReset = 0;
timeReset = System.currentTimeMillis();
}
}
}
}
});
gameThread.start();
mainWindow.setVisible(true);
gp.requestFocus();
}
}
class GamePanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 3110478596996378903L;
public GamePanel() {
this.getInputMap().put(KeyStroke.getKeyStroke("pressed A"), "pressed Key");
this.getInputMap().put(KeyStroke.getKeyStroke("released A"), "released Key");
this.getInputMap().put(KeyStroke.getKeyStroke("pressed D"), "pressed Key");
this.getInputMap().put(KeyStroke.getKeyStroke("released D"), "released Key");
this.getInputMap().put(KeyStroke.getKeyStroke("pressed W"), "pressed Key");
this.getInputMap().put(KeyStroke.getKeyStroke("released W"), "released Key");
this.getInputMap().put(KeyStroke.getKeyStroke("pressed S"), "pressed Key");
this.getInputMap().put(KeyStroke.getKeyStroke("released S"), "released Key");
this.getActionMap().put("pressed Key", new AbstractAction() {
private static final long serialVersionUID = 1296609706338138539L;
#Override
public void actionPerformed(ActionEvent arg0) {
if (!pks.contains(arg0.getActionCommand())) {
pks += arg0.getActionCommand() + ", ";
}
}
});
this.getActionMap().put("released Key", new AbstractAction() {
private static final long serialVersionUID = 4364732373538162119L;
#Override
public void actionPerformed(ActionEvent arg0) {
pks = pks.replace(arg0.getActionCommand() + ", ", "");
}
});
this.setBackground(new Color(0x6495ed));
}
public void init() {
}
private String pks = "";
public void update(long currentTick) {
}
private float interpolation = 0;
public void setInterpolation(float interpolation) {
this.interpolation = interpolation;
}
private int frames = 0;
private long timeForFPS = 0;
private int ActualFPS = 0;
public int getFPS() {
return ActualFPS;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
g.drawString("FPS: " + ActualFPS, 0, 10);
frames++;
if (System.currentTimeMillis() - timeForFPS >= 1000) {
ActualFPS = frames;
frames = 0;
timeForFPS = System.currentTimeMillis();
}
}
}
EDIT 2:
http://tinypic.com/r/9bkf4k/8
http://tinypic.com/r/345m24i/8
As with the other comments, there is no problem with FPS when I tried the code above here in my computer. I also tried it on my virtual machine having one processor only. Still there is no dramatic drop of the FPS when the mouse is moving fast. Although the processor always shows 100% usage. Have you tried this with other computers? I suspect, you too won't see the FPS problem in there. So maybe the issue lies in your computer and not on your program code.
Related
I'm trying to write a program that solves an equation for a school project, but I can't figure out how to create a space for writing in the screen instead of eclipse's console. Like, creating a space in the bottom of the window so the user can input the requested values and the program can read that and make the equation, finally showing the user the final result.
public class Main extends Canvas implements Runnable{
public static JFrame frame;
private Thread thread;
private boolean isRunning = true;
private final int WIDTH = 160;
private final int HEIGHT = 120;
private final int SCALE = 4;
private BufferedImage image;
public Main() {
setPreferredSize(new Dimension(WIDTH*SCALE,HEIGHT*SCALE));
initFrame();
image = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
}
public void initFrame() {
frame = new JFrame("Tempo de Queda (Física)");
frame.add(this);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public synchronized void start() {
thread = new Thread(this);
isRunning = true;
thread.start();
}
public synchronized void stop() {
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Main main = new Main();
main.start();}
public void tick() {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if(bs==null) {
createBufferStrategy(3);
return;
}
Graphics g = image.getGraphics();
//Back Ground---------------------------------------
g.setColor(new Color(19,19,19));
g.fillRect(0,0,WIDTH,HEIGHT);
g = bs.getDrawGraphics();
g.drawImage(image,0,0,WIDTH*SCALE,HEIGHT*SCALE,null);
//Back Ground---------------------------------------
g.setFont(new Font("Arial",Font.PLAIN,20));
g.setColor(Color.green);
g.drawString("Write Here: ", 5,20);
bs.show();
}
public void run() {
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
int frames = 0;
double timer = System.currentTimeMillis();
while(isRunning) {
long now = System.nanoTime();
delta+= (now-lastTime) / ns;
lastTime = now;
if(delta >= 1) {
tick();
render();
frames++;
delta--;
}
if(System.currentTimeMillis() - timer >= 1000) {
System.out.println("FPS: "+frames);
frames = 0;
timer+=1000;
}
}
}
}
So in your case, I'd make some additions to the initFrame method. Specifically, you'll want to add a JPanel to it.
JPanel panel = new JPanel();
frame.add(panel);
Then, define your JTextFields, a JButton and any JLabels to go with them. Java will want you to define any that you're messing with the text value as "final." The other components are ok to set as "final" if you don't plan on reinitializing them:
final JLabel labelX = new JLabel();
final JLabel labelY = new JLabel();
final JLabel output = new JLabel();
final JTextField inputX = new JTextField(5);
final JTextField inputY = new JTextField(5);
final JButton button = new JButton("multiply");
Setting the text of any labels is a good idea:
labelX.setText("X");
labelY.setText("Y");
output.setText("NaN");
Then add them all to the panel:
panel.add(labelX);
panel.add(inputX);
panel.add(labelY);
panel.add(inputY);
panel.add(button);
panel.add(output);
Finally, you'll need to add a listener for the button action. This is where you'll process your inputs and display the output.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
float valueX = 0;
float valueY = 0;
boolean validInputs = true;
try {
valueX = Float.parseFloat(inputX.getText());
valueY = Float.parseFloat(inputY.getText());
} catch (NumberFormatException ex) {
output.setText("Invalid input value(s)");
validInputs = false;
}
if (validInputs) {
//multiply
float result = valueX * valueY;
output.setText(Float.toString(result));
}
}
});
Running that should give you something like this:
Link to Github: FloatMathGUI
I attempted to create my own implementation of a "game loop" using javax.swing and java.awt, but when my target fps was 60, I only got 49-52 fps on my implementation, while another implementation got 59-61 fps. Why would my implementation get a lower framerate if it's doing fewer calculations?
My Implementation:
public class MainContainer implements Runnable {
public Thread thread;
private final String title = "Window";
private int width = 800, height = 600;
private float scale = 1f;
private GameWindow window;
private final int FPS = 60;
private final long NS_PER_UPDATE = (long)((1.0d/FPS) * 1000000000);
private int current_total = 0;
private boolean running;
public MainContainer(){
running = true;
start();
}
public void start(){
window = new GameWindow(this);
thread = new Thread(this);
thread.run();
}
public void stop(){
}
#Override
public void run() {
long old = System.nanoTime();
long counterOld = System.nanoTime();
long missedTime;
double frames = 0;
long current;
long delta;
long counterDelta;
while(running){
current = System.nanoTime();
delta = current - old;
counterDelta = current - counterOld;
if(counterDelta >= 1000000000){
System.out.println(frames / (counterDelta/1000000000.0));
frames = 0;
counterOld = System.nanoTime();
}
if(delta >= NS_PER_UPDATE){
render();
missedTime = delta - NS_PER_UPDATE;
old = System.nanoTime() - missedTime;
frames++;
}else{
try {
thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void render(){
window.update();
}
public static void main(String[] args) {
MainContainer main = new MainContainer();
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public float getScale(){
return scale;
}
public void setScale(float s){
scale = s;
}
public String getTitle(){
return title;
}
public GameWindow getWindow(){
return window;
}
}
Other Implementation:
public class MainContainer implements Runnable {
public Thread thread;
private final String title = "Window";
private int width = 800, height = 600;
private float scale = 1f;
private final int FPS = 60;
private final long NS_PER_UPDATE = (long)((1.0d/FPS) * 1000000000);
private boolean running;
private GameWindow window;
public MainContainer(){
running = true;
start();
}
public void start(){
window = new GameWindow(this);
thread = new Thread(this);
thread.start();
}
public void stop(){
}
#Override
public void run() {
long unprocessedTime = 0;
long frameTime = 0;
double frames = 0;
boolean render = false;
long current;
long delta;
long old = System.nanoTime();
while(running){
current = System.nanoTime();
delta = current - old;
old = current;
unprocessedTime += delta;
frameTime += delta;
while(unprocessedTime >= NS_PER_UPDATE){
old = System.nanoTime();
unprocessedTime -= NS_PER_UPDATE;
render = true;
if(frameTime >= 1000000000){
System.out.println(frames / (frameTime/1000000000.0));
frameTime = 0;
frames = 0;
}
}
if(render){
render();
frames++;
render = false;
}else{
try {
thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void render(){
window.update();
}
public static void main(String[] args) {
MainContainer main = new MainContainer();
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public float getScale(){
return scale;
}
public void setScale(float s){
scale = s;
}
public String getTitle(){
return title;
}
public GameWindow getWindow(){
return window;
}
}
GameWindow Class:
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
public class GameWindow {
private JFrame frame;
private BufferedImage image;
private Canvas canvas;
private BufferStrategy bs;
private Graphics g;
private boolean blue;
private MainContainer mc;
public GameWindow(MainContainer mc) {
this.mc = mc;
image = new BufferedImage(mc.getWidth(), mc.getHeight(), BufferedImage.TYPE_INT_RGB);
canvas = new Canvas();
Dimension s = new Dimension((int)(mc.getWidth() * mc.getScale()), (int)(mc.getHeight() * mc.getScale()));
canvas.setPreferredSize(s);
canvas.setMinimumSize(s);
canvas.setMaximumSize(s);
frame = new JFrame(mc.getTitle());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(canvas, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
canvas.createBufferStrategy(2);
bs = canvas.getBufferStrategy();
g = bs.getDrawGraphics();
}
public void update(){
int[] p = ((DataBufferInt)(this.getImage().getRaster().getDataBuffer())).getData();
for(int i = 0; i < p.length; i++){
p[i] += i;
}
g.drawImage(image, 0, 0, canvas.getWidth(), canvas.getHeight(), null);
bs.show();
}
public BufferedImage getImage(){
return image;
}
}
Because in the "Other Implementation" its calculating the old time before the rendering process and in your implentation it is calculating it after the rendering process which make the value bigger because it is now also including the time it takes to render. So if you flip the code in your if statement around to look like this it will run at around 60 fps:
if(delta >= NS_PER_UPDATE){
missedTime = delta - NS_PER_UPDATE;
old = System.nanoTime() - missedTime;
render();
frames++;
}
I am coding a little Asteroids game, but it seems to be lagging a little bit. I am using a swing.Timer in order to update my JFrame and display the graphics. I have two questions,
the first one being:
"Could the timer be the reason for the lags?" and the second one being:
"Is using a Timer the optimal way to handle game programming in Java, or is it not?"
When browsing the net, it seemed like everyone is using a Timer in order to handle animations, but I can't help but feel that it is a suboptimal way of doing this. Can someone pls explain this to me? Thank you in advance :)
Here is the code of my Timer, if it helps. First the Base class:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Base implements ActionListener {
// Attributes
protected static int cd = 3; // Length of Countdown in seconds
private int nrOfAsteroids = 10; // Amount of Asteroids spawned
protected static int fps = 60; // Frames-per-second
// Various variables and constants
protected static BufferedImage image;
protected static int height;
protected static int width;
protected static boolean colorMode = false;
// Variables needed for Key-register
protected static boolean isWpressed = false;
private boolean isQpressed = false;
private boolean isEpressed = false;
private boolean isSpacePressed = false;
private boolean stop = false; // TODO remove after game is finished
// Various complex-objects
private static Base b = new Base();
private Asteroid[] a = new Asteroid[nrOfAsteroids];
private JFrame frame;
private JButton start;
private JButton colorButton;
private JLabel dummy;
private JLabel gameLabel;
protected static JLabel screen = new JLabel();
private ImageIcon icon;
private Timer t;
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public static void main(String[] args) {
height = (int) (screenSize.height * 0.9);
width = (int) (screenSize.width * 0.9);
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
screen.setSize(width, height);
b.frameSetup();
} // end main
private void frameSetup() {
// Frame Setup
frame = new JFrame("yaaasssss hemorrhoids");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.BLACK);
frame.setBounds((int) (screenSize.width * 0.05), (int) (screenSize.height * 0.03), width, height);
frame.setLayout(new GridBagLayout());
// creating a "color" button
colorButton = new JButton("CLASSIC");
GridBagConstraints cb = new GridBagConstraints();
cb.weightx = 1;
cb.weighty = 1;
cb.gridx = 2;
cb.gridy = 0;
cb.anchor = GridBagConstraints.FIRST_LINE_END;
cb.insets = new Insets(10, 0, 0, 10);
colorButton.setPreferredSize(new Dimension(100, 30));
frame.add(colorButton, cb);
// creating a "ASTEROIDS" Label
gameLabel = new JLabel("ASSTEROIDS");
GridBagConstraints gl = new GridBagConstraints();
gl.weightx = 1;
gl.weighty = 1;
gl.gridwidth = 3;
gl.gridx = 0;
gl.gridy = 1;
gl.anchor = GridBagConstraints.CENTER;
gl.fill = GridBagConstraints.BOTH;
gameLabel.setPreferredSize(new Dimension(100, 30));
gameLabel.setFont(gameLabel.getFont().deriveFont(60.0f));
gameLabel.setForeground(Color.WHITE);
gameLabel.setHorizontalAlignment(SwingConstants.CENTER);
frame.add(gameLabel, gl);
// Dummy Component
dummy = new JLabel();
GridBagConstraints dc = new GridBagConstraints();
dummy.setPreferredSize(new Dimension(100, 30));
dc.weightx = 1;
dc.weighty = 1;
dc.gridx = 0;
dc.gridy = 0;
frame.add(dummy, dc);
// creating a "start" button
start = new JButton("START");
GridBagConstraints sb = new GridBagConstraints();
sb.weightx = 1;
sb.weighty = 1;
sb.gridx = 1;
sb.gridy = 2;
sb.anchor = GridBagConstraints.PAGE_START;
sb.insets = new Insets(15, 0, 0, 0);
start.setPreferredSize(new Dimension(100, 30));
frame.add(start, sb);
// Implementing a function to the buttons
start.addActionListener(this);
colorButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (colorButton.getText() == "CLASSIC") {
colorMode = true;
colorButton.setText("LSD-TRIP");
} else {
colorMode = false;
colorButton.setText("CLASSIC");
}
}
});
// Show Results
frame.setVisible(true);
}
private void addImage() {
// Implementing the Image
icon = new ImageIcon(image);
screen.setIcon(icon);
frame.add(screen);
}
protected void setWindowSize() {
width = frame.getBounds().width;
height = frame.getBounds().height;
screen.setSize(width, height);
}
#Override
public void actionPerformed(ActionEvent ae) {
// Cleaning the screen
frame.remove(start);
frame.remove(gameLabel);
frame.remove(colorButton);
frame.remove(dummy);
// Checking if Window has been resized, and acting according to it
setWindowSize();
// Creating the image
for (int i = 0; i < nrOfAsteroids; ++i) {
a[i] = new Asteroid();
}
gameStart();
}
private void gameStart() {
t = new Timer(1000/fps, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
clearScreen();
for (int i = 0; i < nrOfAsteroids; ++i) {
a[i].drawAsteroid();
}
// Managing Controlls
if (isWpressed) {}
if (isQpressed) { }
if (isEpressed) { }
if (isSpacePressed) { }
if (stop) { }
// Updating the screen
b.addImage();
}
});
t.setInitialDelay(0);
actions();
t.start();
}
private void actions() {
// Defining all the constants for more order when handling the actions
final int focus = JComponent.WHEN_IN_FOCUSED_WINDOW;
String move = "Movement started";
String noMove = "Movement stopped";
String shoot = "Shooting started";
String noShoot = "Shooting stopped";
String turnLeft = "Rotation left started";
String noTurnLeft = "Rotation left stopped";
String turnRight = "Rotation right started";
String noTurnRight = "Rotation right stopped";
String stopIt = "stop"; // TODO remove when game is finished
// Getting the input and trigger an ActionMap
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("W"), move);
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("released W"), noMove);
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("SPACE"), shoot);
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("released SPACE"), noShoot);
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("Q"), turnLeft);
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("released Q"), noTurnLeft);
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("E"), turnRight);
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("released E"), noTurnRight);
screen.getInputMap(focus).put(KeyStroke.getKeyStroke("S"), stopIt);
// Triggered ActionMaps perform an Action
screen.getActionMap().put(move, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
isWpressed = true;
} });
screen.getActionMap().put(noMove, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
isWpressed = false;
} });
screen.getActionMap().put(shoot, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
isSpacePressed = true;
} });
screen.getActionMap().put(noShoot, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
isSpacePressed = false;
} });
screen.getActionMap().put(turnLeft, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
isQpressed = true;
} });
screen.getActionMap().put(noTurnLeft, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
isQpressed = false;
} });
screen.getActionMap().put(turnRight, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
isEpressed = true;
} });
screen.getActionMap().put(noTurnRight, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
isEpressed = false;
} });
screen.getActionMap().put(stopIt, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
stop = true;
} });
} // end actions()
private void clearScreen() {
Graphics2D pen = image.createGraphics();
pen.clearRect(0, 0, Base.width, Base.height);
}
} // end class
Now the Asteroid class:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
public class Asteroid {
// Attributes
private int amountOfCornerPoints = 12;
private int size = 50;
private int rotationSpeed = 2;
private int movementSpeed = 3;
// Fields needed to construct the Asteroid
private Polygon asteroidShape;
private int xCenter = (int) (Math.random() * Base.width);
private int yCenter = (int) (Math.random() * Base.height);
private int[] y = new int[amountOfCornerPoints];
private int[] x = new int[amountOfCornerPoints];
private int[] random = new int[amountOfCornerPoints];
private int rmax = 20; //Das Maximum für r
private int rmin = -rmax; //Das Minimum für r
// Field needed to transport the Asteroid
private boolean transporting = false;
// Field needed to rotate the Asteroid
private int cornerAddition = 0;
// Fields needed to detect Collision
// Fields needed to determine the direction of the Asteroid
private int direction = (int) Math.round((Math.random()*7));
private int xMove = 0;
private int yMove = 0;
// Fields for determining the color of the Asteroid
private Color col;
private int red = 255;
private int green = 255;
private int blue = 255;
public Asteroid() {
// Activating colorMode
if (Base.colorMode == true) {
do {
red = (int) Math.round((Math.random()*127));
green = (int) Math.round((Math.random()*127));
blue = (int) Math.round((Math.random()*127));
} while (red < 64 && green < 64 && blue < 64); }
col = new Color(red, green, blue);
// Zufallszahlen Generator
for (int i = 0; i < random.length; ++i) {
random[i] = (int) (Math.random()*rmax + rmin); }
asteroidShape = new Polygon();
whichDirection();
}
protected void drawAsteroid() {
move();
rotate();
int degreeHolder;
int degrees;
for (int i = 0; i < amountOfCornerPoints; ++i) {
degreeHolder = i*(360/amountOfCornerPoints) + cornerAddition;
if (degreeHolder >= 360) {
degrees = degreeHolder - 360;
} else {
degrees = degreeHolder;
}
x[i] = getXvalue(size + random[i])[degrees];
y[i] = getYvalue(size + random[i])[degrees];
}
asteroidShape.invalidate();
asteroidShape = new Polygon(x, y, amountOfCornerPoints);
Graphics2D pen = Base.image.createGraphics();
pen.setColor(col);
pen.draw(asteroidShape);
pen.dispose();
}
private void rotate() {
cornerAddition += rotationSpeed;
if (cornerAddition >= 360)
cornerAddition = cornerAddition - 360;
}
private void move() {
detectTransport();
xCenter += xMove;
yCenter += yMove;
}
private void detectTransport() {
boolean transportImmunity = false;
if (xCenter <= -size || xCenter >= Base.width + size) {
if (transportImmunity == false)
transporting = !transporting;
transportImmunity = true;
transport();
}
if (yCenter <= -size || yCenter >= Base.height + size) {
if (transportImmunity == false)
transporting = !transporting;
transportImmunity = true;
transport();
}
}
private void transport() {
while (transporting) {
xCenter -= xMove;
yCenter -= yMove;
detectTransport();
}
}
private void whichDirection() {
switch (direction) {
case 0: // Gerade Oben
xMove = 0;
yMove = -movementSpeed;
break;
case 1: // Diagonal Oben-rechts
xMove = movementSpeed;
yMove = -movementSpeed;
break;
case 2: // Gerade rechts
xMove = movementSpeed;
yMove = 0;
break;
case 3: // Diagonal Unten-rechts
xMove = movementSpeed;
yMove = movementSpeed;
break;
case 4: // Gerade Unten
xMove = 0;
yMove = movementSpeed;
break;
case 5: // Diagonal Unten-links
xMove = -movementSpeed;
yMove = movementSpeed;
break;
case 6: // Gerade links
xMove = -movementSpeed;
yMove = 0;
break;
case 7: // Diagonal Oben-links
xMove = -movementSpeed;
yMove = -movementSpeed;
break;
}
} // end WhichDirection
private int[] getXvalue(int radius) {
int[] xPoint = new int[360];
for (int i = 0; i < 360; ++i) {
double xplus = Math.cos(Math.toRadians(i+1)) * radius;
xPoint[i] = (int) Math.round(xCenter + xplus); }
return xPoint;
}
private int[] getYvalue(int radius) {
int[] yPoint = new int[360];
for (int i = 0; i < 360; ++i) {
double yPlus = Math.sin(Math.toRadians(i+1)) * radius;
yPoint[i] = (int) Math.round(yCenter - yPlus); }
return yPoint;
}
}
PS.: My computer is most likely not the cause, since it can run a lot bigger games with at least 100fps
Edit: None of the other methods, as for example the rotate() method, is causing the lag, as I have already tried the entire code with only the most essential methods and the result was the same.
Edit2: Maybe its worth noting, that the lag actually is only barely noticeable. However, for a game as small as an Asteroids is, there really shouldn't be any lag, especially if it only runs on 60 fps.
Edit3: MRE is added
I would suggest an/fps-limiting approach instead because the lag that can happen in the game gets amplified by the strict time intervals of the Timer class. After you set the frame to be visible add the following code(or something like it):
long time = System.nanoTime();
while(!gameOver) {
long nTime = System.nanoTime();
float diff = (nTime - time) * 0.000000001f;
if(diff > 1.0f / fps) {
time = nTime;
// do rendering here and multiply any speeds or accelerations by diff
}
}
I am using NetBeans IDE 8.2 and I created a simple clone game from tutorials.
I am looking to add a background to the app and my research keeps pointing to using JFrame Forms and a JLabel.
None of the tutorials touched on backgrounds or JFrame Forms/JLabels. So I am uncertain how to take my completed project and add a background. I have attempted to reproduce JFrame Forms and JLabel code only to be unable to put my classes/interfaces "inside?" or "on top?" of the JFrame Form/JLabel.
I apologize if this really isn't an ideal first question, I just signed up and this is my first dip into the Java pool. Game class with JFrame (not Form) settings
EDIT: Adding full paste of my Game.java class.
package game;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import javax.swing.*;
public class Game {
public final static int WIDTH = 1920, HEIGHT = 1080;
private String gameName = "Tutorial Game";
private Canvas game = new Canvas();
private Input input;
private ArrayList<Updatable> updatables = new ArrayList<>();
private ArrayList<Renderable> renderables = new ArrayList<>();
// Helper methods for update/render Arrays
public void addUpdatable(Updatable u) {
updatables.add(u);
}
public void removeUpdatable(Updatable u) {
updatables.remove(u);
}
public void addRenderable(Renderable r) {
renderables.add(r);
}
public void removeRenderable(Renderable r) {
renderables.remove(r);
}
public void start() {
// Initialize windows
Dimension gameSize = new Dimension(Game.WIDTH, Game.HEIGHT);
JFrame gameWindow = new JFrame(gameName);
gameWindow.setDefaultCloseOperation(3);
gameWindow.setSize(gameSize);
gameWindow.setResizable(false);
gameWindow.setLocationRelativeTo(null);
gameWindow.add(game);
game.setSize(gameSize);
game.setMinimumSize(gameSize);
game.setMaximumSize(gameSize);
game.setPreferredSize(gameSize);
gameWindow.setVisible(true);
// Initialize Input
input = new Input();
game.addKeyListener(input);
// Game loop
final int TICKS_PER_SECOND = 60;
final int TIME_PER_TICK = 1000 / TICKS_PER_SECOND;
final int MAX_FRAMESKIPS = 5;
long nextGameTick = System.currentTimeMillis();
int loops;
float interpolation;
long timeAtLastFPSCheck = 0;
int ticks = 0;
boolean running = true;
while(running) {
// Updating
loops = 0;
while(System.currentTimeMillis() > nextGameTick && loops < MAX_FRAMESKIPS) {
update();
ticks++;
nextGameTick += TIME_PER_TICK;
loops++;
}
// Rendering
interpolation = (float) (System.currentTimeMillis() + TIME_PER_TICK - nextGameTick)
/ (float) TIME_PER_TICK;
render(interpolation);
// FPS Check
if(System.currentTimeMillis() - timeAtLastFPSCheck >= 1000) {
System.out.println("FPS: " + ticks);
gameWindow.setTitle(gameName + " - FPS: " + ticks);
ticks = 0;
timeAtLastFPSCheck = System.currentTimeMillis();
}
}
}
private void update() {
for(Updatable u : updatables) {
u.update(input);
}
}
private void render(float interpolation) {
BufferStrategy b = game.getBufferStrategy();
if(b == null) {
game.createBufferStrategy(2);
return;
}
Graphics2D g = (Graphics2D) b.getDrawGraphics();
g.clearRect(0, 0, game.getWidth(), game.getHeight());
for(Renderable r : renderables) {
r.render(g, interpolation);
}
g.dispose();
b.show();
}
}
I have just started on learning how to use keyBindings and I cannot find out what I am doing wrong as when I press the UP arrow on my keyboard it does nothing.
My Main Game Window
public class GameWindow extends JFrame{
private static final long serialVersionUID = 1L;
public int WIDTH = 160, HEIGHT = WIDTH/12 *9, SCALE = 3;
public boolean running = false;
BackGround bg = new BackGround();
Ranger R = new Ranger();
TimerClass T = new TimerClass();
public static void main(String[] args) {
new GameWindow();
}
public GameWindow() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WIDTH * SCALE, HEIGHT * SCALE);
setResizable(false);
running = true;
add(bg);
bg.add(R);
bg.setFocusable(true);
R.setFocusable(true);
setFocusable(true);
setVisible(true);
bg.repaint();
run();
}
public void run() {
while (running) {
render();
}
}
public void render() {
bg.setLocation(Ranger.bgX, Ranger.bgY);
R.setLocation(Ranger.X, Ranger.Y);
R.setIcon(Ranger.rangerA[Ranger.I]);
R.repaint();
bg.repaint();
}
}
And My Ranger Class
public class Ranger extends JLabel {
private static final long serialVersionUID = 1L;
public static int X, Y, dX, dY, bgX, bgY, I = 0, jumpTime = 100;
public static boolean moving = false, movingLeft = false,
movingRight = false, onFloor = false, jumping = false,
movingUp = false, movingDown = false;
public int totalImages = 6;
public BufferedImage ranger1, ranger2, ranger3, ranger4, ranger5, ranger6;
public static ImageIcon[] rangerA;
static TileMap TileMap = new TileMap();
public Ranger() {
try {
// not moving
ranger1 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger0.png"));
ranger2 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger1.png"));
// moving Left
ranger3 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger2.png"));
ranger4 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger3.png"));
// moving Right
ranger5 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger4.png"));
ranger6 = ImageIO.read(getClass().getResource(
"/Images/Sprites/ranger/Ranger5.png"));
} catch (IOException e) {
e.printStackTrace();
}
array();
}
public void array() {
rangerA = new ImageIcon[6];
{
rangerA[0] = new ImageIcon(ranger1);
rangerA[1] = new ImageIcon(ranger2);
rangerA[2] = new ImageIcon(ranger3);
rangerA[3] = new ImageIcon(ranger4);
rangerA[4] = new ImageIcon(ranger5);
rangerA[5] = new ImageIcon(ranger6);
}
}
public void move() {
AbstractAction moveUp = new AbstractAction() {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
System.out.println("Move up");
}
};
this.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("W"), "moveUp");
this.getActionMap().put("moveUp", moveUp);
X += dX;
Y += dY;
dX = 0;
dY = 0;
if (movingRight || movingLeft) {
moving = true;
}
}
}
I am trying it to output that it will move up in the console so that i can lean how to make new KeyBindings but I don't know why it dosn't work. Any solutions/tips will be much appreciated.
P.S. I am new to Java so sorry for simple mistakes and I am also aware of the wild loop in the main game window class.
EDIT: move() is called every few miliseconds in a seperate timer class.
KeyStroke.getKeyStroke("W") doesn't do what you think it does. Using this form requires a verbose description of the action, ie typed w.
For this reason, I never use it. Instead, I prefer to use KeyStroke.getKeyStroke(KeyEvent.VK_W, 0) which is much more direct
See KeyStroke#getKeyStroke(int, int) for more details.
You're also not binding the key strokes as you never call the move method. Instead, bind the key strokes in the classes constructor or some other method which should be called only once.