0

I have developed a simple game in java using AWT and Swing. All you do is control a square with the spacebar that jumps up and down onto a never-ending stream of platforms.

Weeee!

Everything works fine, but when a player jumps off a platform, holds the spacebar, and lands on another platform, he/she will randomly bounce or simply drop through the platform. From what I can tell, the error lies somewhere with the Player's behavior.

Here's the all the code needed to test it:

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class main {

    private static JFrame gui;
    private static Container pane;
    private static JPanel panel;    

    public static void main(String[] args) {
        gui = new JFrame();
        gui.setSize(500,500);
        gui.setTitle("An Experiment");
        gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pane=gui.getContentPane();
        panel = new ColorPanel(Color.WHITE, 500, 500);
        pane.add(panel);
        gui.setVisible(true);
        gui.pack();
    }

    //****************
    //ColorPanel class
    //****************

    private static class ColorPanel extends JPanel{
        private static final long serialVersionUID = -1101242028703991986L;
        private Timer timer, dtimer;
        private KeyListener k;
        private Platform platform;
        private Player player;
        private int difficulty, score;

        public ColorPanel(Color backColor, int width, int height){
            setBackground(backColor);
            setPreferredSize(new Dimension(width, height));
            difficulty=-1;
            score=0;

            platform = new Platform(new Point(240,250), 50);

            player=new Player(width/2, height/2-100);

            k=new keyboardListener();
            addKeyListener(k);
            setFocusable(true);

            timer = new javax.swing.Timer(15, new timerListener());
            timer.start();

            dtimer = new javax.swing.Timer(50000, new difficultyListener());
            dtimer.start();
        }

        public void paintComponent(Graphics g){
            super.paintComponent(g);
            if(player.getY()<500){
                platform.draw(g);
                player.draw(g);
            }else{
                timer.stop();
                dtimer.stop();
                setBackground(Color.BLACK);
                g.setColor(Color.RED);
                g.drawString("Game Over.", 215, 230);
                g.setColor(Color.YELLOW);
                g.drawString("Platforms cleared: "+score, 200, 240);
                g.drawString("End difficulty: "+Math.abs(difficulty), 200, 250);

            }
        }

        private class timerListener implements ActionListener{
            public void actionPerformed(ActionEvent e){         
                //Player control
                player.move(platform);

                repaint();
            }
        }

        private class difficultyListener implements ActionListener{
            public void actionPerformed(ActionEvent e){
                difficulty--;
                if(difficulty<=-10){
                    dtimer.stop();
                }
            }
        }
        private class keyboardListener implements KeyListener{
            @Override
            public void keyPressed(KeyEvent e){
                //space=32
                if(e.getKeyCode()==32){
                    player.setJumping(true);
                }
            }
            @Override
            public void keyReleased(KeyEvent e){
                if(e.getKeyCode()==32){
                    player.setJumping(false);
                }
            }
            @Override
            public void keyTyped(KeyEvent e) {
            }   
        }
    }

    //************
    //Player class
    //************

    private static class Player {

        private int x, y, w, h, initialSpeed, speed, endingSpeed, acceleration, jumpHeight, timeCount;
        private boolean isJumping, onPlatform;
        private Timer accel;

        public Player(int x1, int y1){
            x=x1;
            y=y1;
            w=15;
            h=15;
            isJumping=false;
            onPlatform=false;
            speed=3;
            accel=new Timer(50, new timerListener());
        }

        public void draw(Graphics g){
            g.drawRect(x, y, w, h);
        }

        public void move(Platform p){
            //platform collision
            onPlatform=false;
            if((x>p.getP1().getX() && x<p.getP2().getX()) || (x+w>p.getP1().getX() && x+w<p.getP2().getX())){
                if(Math.abs(y+h-p.getP1().getY())<2){
                    onPlatform=true;
                }
            }   
            //speed update and movement
            if(isJumping && y>0 && jumpHeight>=-140){
                y+=speed;
                jumpHeight+=speed;
            }else if(onPlatform){
                speed=0;
            }else if(!onPlatform){
                jumpHeight=0;
                if(!isJumping && speed!=3){
                    setSpeed(3);
                }
                y+=speed;
            }
            if(jumpHeight<-140){
                jumpHeight=0;
            }
        }

        public void setJumping(boolean b){
            if(onPlatform && b && jumpHeight>=-140){
                isJumping=true;
                speed=-4;
            }else{
                isJumping=false;
                setSpeed(3);
            }
        }

        public int getY(){
            return y;
        }

        public void setSpeed(int s){
            //speed=starting speed + acceleration*time
            timeCount=0;
            initialSpeed=speed;
            endingSpeed=s;
            if(speed>s){
                acceleration=-1;
            }else{
                acceleration=1;
            }
            accel.start();
        }

        private class timerListener implements ActionListener{
            public void actionPerformed(ActionEvent e){
                timeCount++;
                if(Math.abs(speed-endingSpeed)<=0.5){
                    accel.stop();
                }else if(acceleration<0 && initialSpeed+acceleration*timeCount>0){
                    speed--;
                }else{
                    speed=initialSpeed+acceleration*timeCount;
                }
            }
        }

    }

    //**************
    //Platform class
    //**************

    private static class Platform {

        private Point p1, p2;

        public Platform(Point p, int l){
            p1=p;
            p2=new Point((int)(p1.getX()+l), (int)(p1.getY()));
        }

        public Point getP1(){
            return p1;
        }
        public Point getP2(){
            return p2;
        }

        public void draw(Graphics g){
            g.drawLine((int)(p1.getX()), (int)(p1.getY()), (int)(p2.getX()), (int)(p2.getY()));
        }

    }

}
Spidermaninja
  • 82
  • 1
  • 1
  • 10
  • *"If you want the full source code to actually run the game,.."* Nobody (worth listening to) wants that. For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) as an [edit to the question](http://stackoverflow.com/posts/21081972/edit). – Andrew Thompson Jan 14 '14 at 05:36
  • As an aside, publishing an email address on a public web page is an invitation to spam. I edited the question to remove the address. – Andrew Thompson Jan 14 '14 at 05:39
  • ..Why did you edit the question to link to a Jar? – Andrew Thompson Jan 20 '14 at 22:20
  • So that people can run the entire thing without having to get all the code. – Spidermaninja Jan 24 '14 at 16:40
  • If you prepare an MCVE and post it as an edit to the question, people ***can*** do just that. That is the *point* of making an MCVE. In case you had not figured it out yet, people don't tend to follow external links, and they certainly won't be downloading a 'Jars worth' of code to try trawling through a complex project to isolate a bug that you should have narrowed down to an MCVE. Don't forget - whatever help we offer, we offer for free. Over to you.. – Andrew Thompson Jan 24 '14 at 17:11
  • Yeah, you're probably right. I still don't get how a MCVE is supposed to work when one needs _all_ the code in order to see the behavior of the bug. Specifically, the Player class's actions are dependent upon the collision-detection of the Platform class and the user-input of the ColorPanel. In order to run the ColorPanel, you need the main class. In order to generate a Platform, you need the Point class (and yes I know there already is one, but it isn't completely satisfactory for everything I need). Do you see where I'm getting? That's a lot of code, and I cannot see anyway to simplify it. – Spidermaninja Jan 25 '14 at 21:34
  • Furthermore, I am sorry for being a jerk earlier. I was tired and grumpy. – Spidermaninja Jan 25 '14 at 21:34
  • Perhaps it would help to see some MCVEs. 2 notes: 1) 'minimal' can be quite long if all the code is *really* necessary. 2) It should be one source file, but can contain *multiple classes* in that one file. -- E.G. 1) [File Drop List](http://stackoverflow.com/a/13336902/418556) 2 classes, 125 LOC 2) [DooDoodle](http://stackoverflow.com/a/12683632/418556) 3 classes, *471 LOC.* 2) [File Browser GUI](http://codereview.stackexchange.com/q/4446/7784) is a whopping 649 LOC, 3 classes. *It hit the posting limit for SE sites, and I had to trim it back.* Just about all of it (less icons) was needed. – Andrew Thompson Jan 26 '14 at 00:50
  • Ohhhhhhhh, I get it. Thank you so much. I'll start working on that. – Spidermaninja Jan 26 '14 at 21:21
  • Here's my attempt at a MCVE. Thanks for putting up with me for so long. – Spidermaninja Jan 26 '14 at 21:45

1 Answers1

2

It's difficult to be 100% sure, but what it "looks" like is happening, is you have a fight between what the Space event wants to do and what your "gravity" wants to do.

Remember, while the Space is down, you will be receiving multiple key events depending on the OS's keyboard delay/timing (you may receive several events per second for example)

Instead of allowing the user to keep the key down like...

private class keyboardListener implements KeyListener{
    @Override
    public void keyPressed(KeyEvent e){
        //space=32
        if(e.getKeyCode()==32){
            player.setJumping(true);
        }
    }
    @Override
    public void keyReleased(KeyEvent e){
        if(e.getKeyCode()==32){
            player.setJumping(false);
        }
    }
    @Override
    public void keyTyped(KeyEvent e) {
    }   
}

Try ignoring any new Space events...

What I would do is add a flag called something like hasJumped. While it's true, you would simply ignore any new jump events. Once the user releases the Space, you reset the flag so another press on Space would cause a new jump.

In you game engine, I would reset the jumping flag so that once you've registered the fact that a jump has occurred, until they release and press the Space again, it won't trigger a jump...

private class keyboardListener implements KeyListener{
    @Override
    public void keyPressed(KeyEvent e){
        //space=32
        if(e.getKeyCode()==32){
            if (!player.hasJumped()) {
                player.setJumping(true);
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e){
        if(e.getKeyCode()==32){
            player.hasJumped(false);
        }
    }
    //...
}

I'd also recommend taking a look at Key Bindings over KeyListener

You could take a look at something like JApplet creates a ball that bounces and gets progressively less high in Java for example

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366