Hi all,i have a question about my game engine?

Hello, i just started to game programing with java, before that i was using c# Xna.I wrote my engine,and it is working fine.But i didn’t use a canvas.You can see my game engine below.I’m wondering should i use canvas to draw (if so why?) or not?

And i saw nobody preferred using timers to a while loop and my fps should be fixed 60 but it is more than 60 so am i doing something wrong ?


package kutuyatopat;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class Engine extends JFrame {
    
    int frameRate = 0;
    int frameCounter = 0;

    long StartTime = System.currentTimeMillis();
    long EndTime;
    long ElapsedTime = 0;

    JPanel panel;
    JLabel label;

    Vector<IComponent> Components;

    GraphicsConfiguration gc;

    public Engine(){
        
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(800,600);
        this.setLocationRelativeTo(null);
        panel = new JPanel();
        this.add(panel);
        
        label = new JLabel();
        panel.add(label);
        Components = new Vector<IComponent>();
        gc = GraphicsEnvironment.getLocalGraphicsEnvironment().
                             getDefaultScreenDevice().getDefaultConfiguration();
    }

    protected void Initialize(){
        Timer t = new Timer();
        TimerTask tt = new TimerTask(){
            @Override
            public void run() {
                EndTime = System.currentTimeMillis();
                long elapsedTime = EndTime - StartTime;
                GameLoop(elapsedTime);
                StartTime = System.currentTimeMillis();
            }
        };
        t.scheduleAtFixedRate(tt, 10, 1000 / 60);
    }
    
    void GameLoop(long elapsedTime){
        Update(elapsedTime);
        Draw();
    }

    public void Update(long elapsedTime){
        ElapsedTime += elapsedTime;
        if(ElapsedTime > 1000){
            ElapsedTime -= 1000;
            frameRate = frameCounter;
            frameCounter = 0;
        }
        for(IComponent c : Components){
            c.Update(ElapsedTime);
        }
    }

    public void Draw(){
        frameCounter++;
        String fps = "Fps: " + frameRate;
        BufferedImage i = gc.createCompatibleImage(this.getWidth(), this.getHeight(),Transparency.BITMASK);
        Graphics2D g2 = i.createGraphics();
        for(IComponent c : Components){
            c.Draw(g2);
        }
        g2.setColor(Color.white);
        g2.drawString(fps, 30, 40);
        g2.dispose();
        ImageIcon ic = new ImageIcon(i);
        label.setIcon(ic);
    }

}

t.scheduleAtFixedRate(tt, 10, 1000 / 60);

1000ms / 60fps = 16ms (not 16.6666)

1000ms / 16ms = 62.5fps

yeah but i just executed it and fps was changed like 0-99-86-95-82-86-93-91-85 … and there was no component.

You’re leaking time! What about the time the GameLoop() call takes? This means the time you measure is longer (potentially much longer) than the real life time, causing your frame rate calculation to be wrong.

If you only call System.currentTimeMillis() once per call, you won’t leak any time:


            public void run() {
                now = System.currentTimeMillis();
                long elapsedTime = lastTime - now;
                lastTime = now;
                GameLoop(elapsedTime);
            }

Aaaaaahhhh! Time is leaking! It’s the end of the universe!

hehhe ;D My bad. I was so sleepy when i was writing it.Thanks for catching that leaking,it was stacking me.

In the meantime I did some changes too and i’m wondering is it ok now or am i doing somethings wrong again.

here, new engine



package MyGame;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import MyGame.Input.Keyboard;
import java.awt.GraphicsConfiguration;
import java.awt.image.VolatileImage;
import java.util.Timer;
import java.util.TimerTask;
import net.phys2d.math.Vector2f;
import net.phys2d.raw.World;
import net.phys2d.raw.strategies.QuadSpaceStrategy;

public class Engine extends JFrame {

    protected float frameCounter = 0;

    long StartTime = System.currentTimeMillis();
    long EndTime;
    public long TotalGameRuningTime = 0;
    long elapsedTime = 0;

    JPanel panel;
    Canvas Canvas;
    BufferStrategy BackBuffer;

    ArrayList<IComponent> Components;

    /*
     * Desing resolution values.Set them as your design resolution,engine will handle scaling canvas to the active resolution
     */
    public int designX = 800,designY = 600;

    protected static ResourceManager ResourceManager;

    protected World world;

    public static Keyboard keyboard;

    private int FPS = 60;

    protected boolean DebugMode = true; //TODO: Make it false as default after fnished developing

    Toolkit toolkit; //for vsync syncronization

    public int WorldStepCount = 5;

    private static GraphicsConfiguration gc;

    private boolean isFullScreen = false;

    public Engine(){
        world = new World(new Vector2f(0f,9.8f), 100, new QuadSpaceStrategy(100, 100));
        ResourceManager = new ResourceManager();
        panel = (JPanel) this.getContentPane();
        panel.setLayout(null);
        Components = new ArrayList<IComponent>();
        keyboard = new Keyboard();
        Canvas = new Canvas();
        this.pack();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(designX,designY);
        this.setLocationRelativeTo(null);
        Canvas.setBounds(0, 0, this.getWidth(), this.getHeight());
        Canvas.setIgnoreRepaint(true);
        panel.add(Canvas);
        Canvas.createBufferStrategy(2);
        BackBuffer = Canvas.getBufferStrategy();
        toolkit = Toolkit.getDefaultToolkit();
        gc = getGraphicsConfiguration();
    }

    public static VolatileImage createVolatileImage(int widht,int height,int transparancy){
        return gc.createCompatibleVolatileImage(widht, height, transparancy);
    }

    /*
     * Call before calling statLoop() to set avarage fps other than 60
     */
    public void setFPS(int FPS) {
        this.FPS = FPS;
    }

    /*
     * Call before calling statLoop() to sync avarage fps to screen refresh rate
     */
    public void SyncFPStoScreenRefreshRate(){
        this.FPS = gc.getDevice().getDisplayMode().getRefreshRate();
    }

    public void setFullScreen(boolean fullScreen){
        isFullScreen = fullScreen;
        gc.getDevice().setFullScreenWindow(isFullScreen ? this : null);
    }

    public void toogleFullScreen(){
        isFullScreen = !isFullScreen;
        gc.getDevice().setFullScreenWindow(isFullScreen ? this : null);
    }

    protected void StartLoop(){
        final Timer t = new Timer();
        TimerTask tt = new TimerTask(){
            @Override
            public void run() {
                GameLoop();
            }
        };
        //if fps = 0 loop immediately (this can use for a benchmark to calculate how many fps we can reach)
        t.scheduleAtFixedRate(tt, 0, FPS == 0 ? 1 : 1000 / FPS);
    }
    protected void GameLoop(){
        EndTime = System.currentTimeMillis();
        elapsedTime = EndTime - StartTime;
        StartTime = EndTime;

        Graphics2D g2 = (Graphics2D)BackBuffer.getDrawGraphics();
        Clear(g2);
        Update(elapsedTime);
        Draw(g2);
        if(DebugMode)DebugDraw(g2);
        Render();
        CheckCanvasSize();

        g2.dispose();
    }

    protected void Clear(Graphics2D g2){
        g2.setColor(Color.black);
        g2.fillRect(0, 0, this.getWidth(), this.getHeight());
    }

    protected void Update(long elapsedTime){
        TotalGameRuningTime += elapsedTime;
        for(int i = 0; i < WorldStepCount; i++) world.step();
        for(IComponent c : Components) c.Update(elapsedTime);
    }

    protected void Draw(Graphics2D g2){
        frameCounter++;
        if(this.getWidth() != designX && this.getHeight() != designY){
            double zoomX = this.getWidth() / 800.0;
            double zoomY = this.getHeight() / 600.0;
            g2.scale(zoomX, zoomY);
        }
        for(IComponent c : Components){
            c.Draw(g2);
        }
    }

    protected void DebugDraw(Graphics2D g2){
        g2.setColor(Color.white);
        g2.drawString("Fps: " + CalculateFPS(), 30, 40);
        g2.drawString("Total Energy: " + world.getTotalEnergy(), 30, 60);
        g2.drawString("Delta Time: " + elapsedTime, 30, 80);
        g2.drawString("Total Frame Count: " + frameCounter + "s", 30, 100);
        g2.drawString("Total Game Time: " + TotalGameRuningTime / 1000 + "s", 30, 120);
    }

    protected void Render(){
        if(!BackBuffer.contentsLost())
            BackBuffer.show();
        toolkit.sync();//i don't know is it really necessary,didn't notice any changes after putting this line here
    }

    /*
     * if frame and canvas size isn't equal,set canvas size to frame size
     */
    protected void CheckCanvasSize(){
        if(this.getSize()!= Canvas.getSize())Canvas.setSize(this.getSize());
    }

    protected float CalculateFPS(){
        return  frameCounter * 1000 / TotalGameRuningTime;
    }

    private int GetLostFrameCount(){
        float fps = CalculateFPS();
        return (int) (fps > 1000 / FPS ? 0 : 1000 / FPS - fps);
    }
}


And how can i catch when window bounds or size change except checking it on every loop to resize canvas?

[quote]And how can i catch when window bounds or size change except checking it on every loop to resize canvas?
[/quote]
You can use a componentListener http://java.sun.com/docs/books/tutorial/uiswing/events/componentlistener.html.