Spidermaninja
Spidermaninja

Reputation: 82

Strange behavior with platform collision and jumping

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()));
        }

    }

}

Upvotes: 0

Views: 285

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347314

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

Upvotes: 2

Related Questions