EDIT: Problem solved, turns out I needed to call main.repaint() instead of frame.repaint()
I am using a class that extends Canvas and overrides the 'paint(Graphics graphics)' method. In a loop which activates 60 times a second (it works like it's supposed to), I have called frame.repaint() (the canvas is correctly added to the frame). The paint method gets called about 4 or 5 times, then stops getting called. My other method in the loop, does not stop however, proving that it's the frame.repaint() method.
To make the problem clear, the JFrame.repaint() method stops getting called after 4 or 5 attempts within a second.
To prove this, I've increased an integer every second in my update method (which is getting called 60 times per second) and I'm using that as the x cordanite as a rectangle in my frame, which should make the rectangle larger each second. The rectangle paint's for 2 seconds or so, then stops growing, however the integer is still increasing. One thing to keep in mind that the rectangle does draw for the first few times, indicating that it's some sort of issue with the frame.
Is there a better way to call the paint(Graphics graphics) method? Do
I have some flaw in my code?
Sorry if my explanation was confusing, but I attached the code below (and in a pastebin file that you can find here: http://pastebin.com/WNnK54gq)
I have been looking for the past few hour's, and haven't found any replacement for the frame.repant() method.
Thanks in advanced!
public class Main extends Canvas {
//Static Variables
public static Main main;
public static String name = "Game";
public static double version = 1.0;
public static int FPS;
//Object Variables
private JFrame frame;
private boolean running;
private int screenX;
private int screenY;
private int x = 0;
//Constructor
public Main() {
setSize(new Dimension(500, 500));
}
//Main Method
public static void main(String[] args) {
main = new Main();
main.init();
}
//Object Methods
private void init() {
frame = new JFrame(name);
frame.setSize(500, 500);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(main);
loop();
}
public void loop() {
running = true;
int fps = 0;
long timer = System.currentTimeMillis();
long lastTime = System.nanoTime();
final double ns = 1000000000.0 / 60;
double delta = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
if (fps <= 60) {
fps++;
update();
frame.repaint();
delta--;
}
}
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
log("Running at " + fps + " FPS and UPS");
FPS = fps;
fps = 0;
}
}
}
public void update() {
screenX = frame.getWidth();
screenY = frame.getHeight();
x++;
if (x >= 500) x = 0;
log("update");
//update gametstate
}
public void log(String string) {
System.out.println("[" + name + "] [" + version + "] " + string);
}
public void log() {
System.out.println("[" + name + "] [" + version + "]");
}
#Override
public void paint(Graphics graphics) {
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, screenX, screenY);
//update gamestate
graphics.setColor(Color.BLUE);
graphics.fillRect(0, 200, x, 300);
log("rendered");
}
while (delta >= 1) {
if (fps <= 60) {
fps++;
update();
frame.repaint();
delta--;
}
}
Once fps hits 61, it will stop rendering or updating, because you never set fps back to 0.
while (delta >= 1) {
if (fps <= 60) {
fps++;
update();
frame.repaint();
delta--;
if(fps == 60) fps = 0;
}
}
You need to set fps back to 0.
Related
hi am trying to make a small game using canvas and bitmaps i want my game to run the same on all devices i found delta time is the best practice for this but for some reason when i try to implement it into my code i have display issues for example am trying to move my coluds in the sky but when i add the delta they all disapere i dont know if im doing it wrong so please can sombody help me heres the code
private float c1x = 0.0f;
private float c2x = cloudWidth;
private float c3x = cloudWidth * 2;
private float cloudSpeed = 0.1f;
private long curentTime;
private long lastTime = 0;
private double delta;
#Override
public void run(){
while(running){
if(!holder.getSurface().isValid()){
continue;
}
curentTime = System.nanoTime();
delta = curentTime - lastTime;
lastTime = curentTime;
cloudMovement();
canvas = holder.lockCanvas();
canvas.drawBitmap(bg, 0, 0, null);
canvas.drawBitmap(sun, 20, 20, null);
canvas.drawBitmap(cloud1, c1x, c1y, null);
canvas.drawBitmap(cloud2, c2x, c2y, null);
canvas.drawBitmap(cloud3, c3x, c3y, null);
holder.unlockCanvasAndPost(canvas);
}
}
private void cloudMovement(){
if(c1x <= 0 - cloudWidth){
c1x = w;
c1y = y.nextInt(rand);
}
if(c2x <= 0 - cloudWidth){
c2x = w;
c2y = y.nextInt(rand);
}
if(c3x <= 0 - cloudWidth){
c3x = w;
c3y = y.nextInt(rand);
}
c1x-=cloudSpeed * delta;
c2x-=cloudSpeed * delta;
c3x-=cloudSpeed * delta;
}
You could use a global FPS mechanism instead which forces a steady FPS on your game :)
If you track the FPS the game will run the same way on any device and you dont need to include delta-times on all update processes.
Here's a code snippet from a FpsTracker i used in an old project:
private static final long SECOND = 1000;
private static final long TARGET_FPS = 40;
private static final long FRAME_PERIOD = SECOND / TARGET_FPS;
private long time = System.currentTimeMillis();
/**
*
* #param startTime
* #return <code>true</code> if the interval between startTime and the time
* when this method was called is smaller or equal to the given
* frame period.
*
* Will return <code>false</code> if the interval was longer.
*/
public boolean doFpsCheck(long startTime) {
if (System.currentTimeMillis() - time >= SECOND) {
time = System.currentTimeMillis();
}
long sleepTime = FRAME_PERIOD
- (System.currentTimeMillis() - startTime);
if (sleepTime >= 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
//TODO handle this properly
e.printStacktrace()
}
return true;
} else {
return false;
}
}
If this method returns false it means that your operations took longer that the timeperiod you gave to one frame. You can react to this by checking the doFpsCheckreturn parameter.
Implementing this in your code would look like this:
#Override
public void run()
{
while(running)
{
if(!holder.getSurface().isValid())
{
continue;
}
startTime = System.currentTimeMillis();
cloudMovement();
canvas = holder.lockCanvas();
canvas.drawBitmap(bg, 0, 0, null);
canvas.drawBitmap(sun, 20, 20, null);
canvas.drawBitmap(cloud1, c1x, c1y, null);
canvas.drawBitmap(cloud2, c2x, c2y, null);
canvas.drawBitmap(cloud3, c3x, c3y, null);
holder.unlockCanvasAndPost(canvas);
doFpsCheck(startTime);
}
}
By the way - it is good practice to devide your game loop into pro sub processes, one being the update process, the other being the draw process.
For many different Bitmaps you should consider extracting the fields and functionalities into seperate classes containing a draw(Canvas c) and update() method. So you wont get a trillion fields on your main class.
I'm trying to render the positions of multiple fighters onscreen. The relevant code is as follows:
public void run() {
double ns = 1000000000.0 / tps;
double delta = 0;
int frames = 0;
int updates = 0;
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1) {
update();
updates++;
delta--;
}
frame.getContentPane().repaint();
frames++;
if(System.currentTimeMillis() - timer >= 1000) {
timer += 1000;
frame.setTitle(title + " | " + updates + " ups, " + frames + " fps");
frames = 0;
updates = 0;
}
}
stop();
}
private void update() {
if (Math.random() < .1) {
Fighter newFighter = new Fighter();
fighterList.add(newFighter);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // paint background
setBackground(Color.BLUE);
System.out.println(fighterList.size());
for (int i = 0; i<fighters; i++) {
System.out.println("Attempted");
g.setColor(Color.GREEN);
g.drawRect((int) fighterList.get(i).xPos,
(int) fighterList.get(i).yPos,
fighterList.get(i).radius,
fighterList.get(i).radius);
System.out.println("Rendered");
}
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setContentPane(new Game());
game.frame.setResizable(false);
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null);
game.frame.setVisible(true);
game.start();
}
Issue is, nothing is being drawn to the screen. Additionally, running System.out.println(fighterList.size()); gives different outputs based on where it's run - when run inside paintComponent it always returns zero, wheras when run inside update it returns the proper amount. Is this an issue with scope, or is there something else I'm missing?
Most likely a synchronization issue. Your paintComponent() method is always called from the EDT (Event Dispatch Thread) while your run() method runs in its own separate thread. This is where update() gets called which adds new Fighters to the list.
You need proper synchronization so both (or all) threads will see the same consistent data.
Also since your model (data) may be modified during a repaint, you should also "clone" the model to avoid inconsistent model being painted. Or if you don't want to clone it, synchronize access to the model so it can't get modified while it is painted.
I've just exported my Eclipse project to a runnable jar file. I have no lag issues when I run it in Eclipse, but have extreme lag when running it in the jar. (Running MacOSX 10.9.4)
According to my FPS counter, I'm getting over 900 frames a second, and the game is actually running, but nothing is actually being rendered for a while.
Not sure what code to show because there is a lot of it, as it is an almost completed game. But let me know what code you might need.
Here's my game loop:
public void run() {
long start = System.nanoTime();
final double numUpdates = 30.0;
double ns = 1000000000 / numUpdates;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while(running) {
long current = System.nanoTime();
delta += (current - start) / ns;
start = current;
if(delta >= 1) {
update();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000) {
timer = System.currentTimeMillis();
Window.frame.setTitle("Rage Mage UPS: " + updates + ", FPS: " + frames);
updates = 0;
frames = 0;
}
}
}
render() method:
private void render() {
statusHandler.render(g); // statusHandler is a class that handles the current state of the game
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
}
I don't think the game loop is the problem though, because I was getting the same issues before with a different loop, and then I changed to this loop, and nothing has changed.
Thanks!
I'm making simple 2D games. I have a game loop, and in the game loop i have an update
method. I make things move by adding 1 to xPos whenever it loops. This means that if you have a slow fps then everything goes in slow motion and if you have a high fps everything moves
really quick.
This is my code:
long fpsTimer;
int frames;
public void run(){
running = true;
fpsTimer = System.nanoTime();
while(running){
render();
update();
try{
Thread.sleep(6);
}catch(InterruptedException e){}
frames++;
if(System.nanoTime() >= fpsTimer+1000000000){
System.out.println(frames+" fps");
frames = 0;
fpsTimer = System.nanoTime();
}
}
}
All Code
import java.awt.*;
import java.awt.image.*;
import javax.swing.JFrame;
import java.awt.event.*;
public class Game extends Canvas implements Runnable{
public static final int WIDTH = 800;
public static final int HEIGHT = 300;
public JFrame f;
private String title = "Untitled Test";
private Image image;
private Sprite player;
public Game(){
player = new Sprite(100, 100);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setMaximumSize(new Dimension(WIDTH, HEIGHT));
setMinimumSize(new Dimension(WIDTH, HEIGHT));
addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
player.keyPressed(e.getKeyCode());
}
public void keyReleased(KeyEvent e){
player.keyReleased(e.getKeyCode());
}
});
}
public static void main(String[] args){
Game g = new Game();
g.f = new JFrame(g.title);
g.f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
g.f.add(g);
g.f.setResizable(false);
g.f.pack();
g.f.setLocationRelativeTo(null);
Thread gameLoop = new Thread(g);
gameLoop.start();
g.f.setVisible(true);
}
private void render(){
BufferStrategy bs = getBufferStrategy();
if(bs==null){
createBufferStrategy(3);
return;
}
image = createImage(WIDTH, HEIGHT);
Graphics g = image.getGraphics();
player.draw(g);
g.dispose();
Graphics bsg = bs.getDrawGraphics();
bsg.drawImage(image, 0, 0, WIDTH, HEIGHT, null);
bsg.dispose();
bs.show();
}
private void update(){
player.move();
}
long fpsTime;
int frames;
public void run(){
fpsTime = System.nanoTime();
while(true){
render();
update();
try{
Thread.sleep(6);
}catch(InterruptedException e){}
frames++;
if(System.nanoTime() >= fpsTime+1000000000){
System.out.println(frames+" fps");
frames = 0;
fpsTime = System.nanoTime();
}
}
}
}
First of all you should not have a constant sleep time. Instead, the value should be dynamically computed. Maybe its easier to use Timer#scheduleAtFixedRate(...) cause this already takes care of that for you.
Then, 6 ms per iteration seems much too less. 60 frames per second is ideal (if you just have 30, its ok too i think), so 16 ms is enough (or about 32 for 30 fps). (Note that the refresh rate of your screen is the upper limit - it should be about 60 Hz - more doesn't make sense).
Thirdly think about dynamically computing the moves of your objects. Instead of having a constant delta that you add to the coordinates you should better have some kind of 'move function' that calculates the coordinates based on the current time.
Let's say you want to move an object along the x-axis with a constant speed of pps pixels per second. The function for the x coordinate of that object would then be:
x(t) := x0 + (t - t0) * pps / 1000
(t is the time that has passed since start in ms, x0 is the initial x coordinate of the object when it appeared first, t0 is the time at which the object appeared, pps is the number of pixels the object should move per second).
This makes your objects move with the same speed - no matter what framerate you've got.
Note that this approach gets more complex if your objects should react to user input or other events (like collisions etc.).
I'm working on a simulation for the growth of an organism, using jLabels for the organisms. However, when I implement a for loop and a timer to try and show it moving, it freezes and then displays the final position of the label rather than showing it move. Could anyone explain to me why this is happening?
public class TestView extends FrameView {
public TestView(SingleFrameApplication app) {
super(app);
initComponents();
picture = new JLabel();
picture.setIcon(new ImageIcon(System.getProperty("user.dir") +
File.separator + "mouse.gif"));
picture.setBounds(0, 0, 100, 100);
mainPanel.add(picture);
for (int x = 0; x < 20; x++) {
move();
wait(50);
}
}
public static void wait(int n) {
long t0, t1;
t0 = System.currentTimeMillis();
do {
t1 = System.currentTimeMillis();
} while (t1 - t0 < n);
}
public static void move() {
picture.setBounds(picture.getX() + 5, picture.getY(), 100, 100);
}
You might like this example of diffusion limited aggregation and other simulations.