Hey guys, I'm making a little pokemon game in java and i've come across a problem.

The basic algorithm i use for movement is as follows:
- if the player has pressed a particular key set the speed(float) accordingly (if key stroke was up, X speed should drop (since i'm going up), etc)

the function update() is in charge of updating the current location of the sprite (the player), and i do it as follows:

(assume the location of a sprite is stored in 2 floats called x and y, also assume the velocity is stored in 2 floats called dx and dy).

/**
        Updates this Sprite's Animation and its position based
        on the Sprite's current velocity.
    */
    @Override
    public void update(final long elapsedTime) {
        
        x += dx;
        y += dy;
        
        resetVelocity();
        anim.update(elapsedTime);
    }

now, when i run the game on my desktop it looks flawlessly, everything moves smooth as hell and at a constant 60fps, however, when i run it on my laptop, it looks a little choppy (which is to be expected, it's an old laptop), however, my issue is that whenever i move, i move at a MUCH faster rate than on my desktop, (specially when i move diagonally).

So.. i decided to use the update() function's parameter called elapsedTime, this variable holds the time it took the grand game loop to call this function a second time, (varies from 5ms to 30ms), the updated version of the update() function looks as follows:

/**
        Updates this Sprite's Animation and its position based
        on the velocity.
    */
    @Override
    public void update(long elapsedTime) {
        
        x += dx * elapsedTime;
        y += dy * elapsedTime;
        
        resetVelocity();
        anim.update(elapsedTime);
    }

it works!.. not really, you see, now the speed is pretty much the same on both machines, which is good, but on my desktop movement looks choppy as hell (even though i'm still running at a constant 60fps) it is playable sure, but it looks pretty choppy, and at 60fps it kind of defeats the purpose.

with the information i've given so far, does anyone have an idea as to why this occurs? or do you have an even better way to deal with movement?

Also, no, this is not homework.

Thanks in advance!

Recommended Answers

All 7 Replies

How is update called - do you use a javax.swing.Timer?

Thank you for answering.

No i don't use .swing.Time, i do the following:

I have an abstract class called GameCore which holds 4 important functions: run(), gameLoop(), update() and draw(), and also init(), which does some more initialization.

both update and draw are abstract functions which are called by child classes and run and gameLoop look like so:

/**
        Calls init() and gameLoop()
    */
    public void run() {
        try {
            init();
            gameLoop();
        }
        finally {
            screen.restoreScreen();
            lazilyExit();
        }
    }
/**
        Runs through the game loop until stop() is called.
    */
    public void gameLoop() {
        long startTime = System.currentTimeMillis();
        long currTime = startTime;

        while (isRunning) {
            long elapsedTime =
                System.currentTimeMillis() - currTime;
            currTime += elapsedTime;

            // update
            update(elapsedTime);

            // draw the screen
            Graphics2D g = screen.getGraphics();
            //g.drawString("a", 100, 100);
            draw(g);
            g.dispose();
            screen.update();

        }
    }

So, i essentially have a tester class called GameManager which extends GameCore, its main function looks like the following:

public static void main(String[] args) {
        new GameManager().run();
    }

GameManager naturally overrides all abstract functions from GameCore, (update() and draw())

the draw() function in GameManager looks like so:

/**
     * In this method, all the functions needed to draw should be called.
     * TileMap.draw (to draw the map)
     * SpriteRenderer.draw (to draw people for example), etc.
     * @param g 
     */
    @Override
    public void draw(Graphics2D g) {
        
        g.setClip(0, 0, screen.getWidth(), screen.getHeight());
        
        g.translate(-player.getX(), -player.getY());
        
        //calls draw method from TileMapRender in order to draw a location.
        tileRenderer.draw(g);
        
        //calls draw from PlayerRenderer in order to draw the player and NPCs
        playerRenderer.draw(g);
        
        g.translate(player.getX(), player.getY());
    }

update in GameManager looks like so:

/**
        Updates Animation, position, and velocity of all Sprites
        in the current map.
    */
    @Override
    public void update(long elapsedTime) {
        
        // get keyboard/mouse input
        checkInput(elapsedTime);
        // update the player
        player.update(elapsedTime);
    }

player is a variable for a Player object which also has an update function, and a Player object is a Sprite, which also has its own update function, which is what i showed at the start.

this way i don't really need to deal with the program randomly calling paint() whenever the hell it feels like.

OK, you are doing two things that will cause problems - you are running at an arbitrary speed, depending on available CPU time, and you are trying to bypass Swing's normal mechanisms by drawing to screen.getGraphics(); from another thread.

There is an absolutely standard "correct" way to do this, which separates the real-time simulation from the Swing-managed screen updates and other Swing UI activity. It goes like this:
Use a Timer to call a "physics update" routine at a fixed speed - 60/second is plenty. In that routine update everything's position and velocity variables. but do no painting. Just call repaint() on the appropriate panel to let Swing know the screen needs to be updated.
In the panel's overridden paintComponent method, draw everything according to its current position.
Handle keyboard and mouse input vie the normal Swing listener interfaces, in them just set variables that the physics update routine will use when it runs.

That way you ensure that the simulation proceeds at a steady fixed speed, regardless of hardware (assuming it's "enough" hardware, of course). Swing will schedule screen updates as fast as it can - if repaint() gets called more than once between screen updates the duplicate repaints will get merged int one. If you have fast enough hardware you will get a 60fps display, if you have slower hardware you will get a lower fps, but still a constant-speed sprite movement.

oh, alright thank you very much, i decided not to deal with timers because i recall my professor saying timers are very unreliable since you can never be sure when exactly they are going to call paint(), but i'll try to do it this way, thanks!

Don't call paint. Let the timer run the simulation, and let Swing run the screen.

Sorry to be back, but I seem to have issues regarding JPanels.

you mentioned that i should use a timer to update the values of all relevant objects in game and also to call repaint on all relevant panels.

well, my problem is that the game requires that a panel have access to the entire screen, since i need to print a map, people walking all over it, etc etc.

I've read up on JFrames and JPanels, but none of the Layouts seem to be helpful to me, it was working awesome at the beginning when i only had 1 JPanel in the JFrame but once i tried to add 2 different ones it all went to hell.

I'm not sure what the question is, if any?
I said "panel" because that's what is usually used, but you can use whatever conmponent(s) suit you best for painting the screen as you want it. The important point is that you never call directly any of the paint methods for those components. Just update your values and call repaint() for the affected components. repaint() is just a request for Swing to schedule a call to the paint or paintComponent methods.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.