Search code examples
javamultithreadingsimultaneous

Can't get two pictures to "move" at the same time with simultaneous keystrokes using previous answers


In this code I have a graphic (paintcomponent graphic) that has two tanks. The paintcomponent is controlled by variables and then a repaint(). Right now, it works perfectly when only one button is being pressed or held down. However, if I hold the left for tank1 and the right for tank2, for example, the last-most key is the one that "wins": It is the only direction that occurs.

In my code you will find about 4 of my latest attempts kind of ‘iteratingly’ added to the code.

The basic setup is: Keybinding→AbstractAction→...

The setup above without ellipses(…) represents the original attempt. Then, in another attempt, this is chained to a thread. Same result. In another attempt the abstract action is set to change an array value, which is connected to a timer that checks every 20 seconds. The two attempts branching from there is the connection of the timer to the thread, and the directly repaint put in the timer.

My only other option at this point is to make separate key bindings for each combination (i.e., holding left and D [D moves tank1] would be a keybinding with the keycode "LEFT D" and thus would be a separate event that moved both). However, this would be a hassle because I also have turrets that change angles, and bullets that fire… Also I'd like to think there's a more decent way to do this.

Any help will be appreciated. The code is below.

    class OneMoveRightThread implements Runnable { //This is a thread that contains code to 'move' the tank1.

            public void run(){
                tankGraphic.TankOneMoveRight();
                gameArea.repaint();
            }
        }
    class OneMoveLeftThread implements Runnable { //thread to move tank2

            public void run(){
                tankGraphic.TankOneMoveLeft();
                gameArea.repaint();
            }
        }
        //Meow is a [0,0,0,0,0,0,0,0] (8 zeroes) array. When a button is clicked the value is set to 1 for the spot in the array.
        ActionListener taskPerformer = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (meow[0] == 1){ //This attempt uses a timer to constantly check the array for potential movement. Still works for 1 movement at a time.
                    (new Thread(new OneMoveLeftThread())).start();
                    meow[0]=0;
                }
                if(meow[1]==1){
                    (new Thread(new OneMoveRightThread())).start(); //using code in threads by calling the thread. My hope was that this would fix it.
                    meow[1]=0;
                    }
                if (meow[2]==1){ //TANK TWO MOVE LEFT using code in threads
                    tankGraphic.TankTwoMoveLeft();
                    gameArea.repaint();
                    meow[2]=0;
                }

            }
        };
        new Timer(delay, taskPerformer).start();

        //This abstract action is a different part of the code.
        Action doNothing = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                lifeBar1.setText("\n    Player 1: " + player1Name + "    \n    Health: " + player1Health + "    \n    Bullets: A LOT!");
                lifeBar2.setText("\n    Player 2: " + player2Name + "    \n    Health: " + player2Health + "    \n    Bullets: A LOT!");
            }
        };
        //TANK TWO BELOW
        Action leftMove = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) { //This is code for "When LEFT arrow is pressed, set meow[2]=1"
                //tankGraphic.TankTwoMoveLeft();
                //gameArea.repaint();
                meow[2]=1;
            }
        };
        Action rightMove = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) { //This is code for when RIGHT arrow is pressed, move tank directly. This and the above have same erroneous result.
                tankGraphic.TankTwoMoveRight();
                gameArea.repaint();
            }
        };
        Action angleLEFT = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                tankGraphic.TankTwoAngleLeft();
                gameArea.repaint();
            }
        };
        Action angleRIGHT = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                tankGraphic.TankTwoAngleRight();
                gameArea.repaint();
            }
        };

        //TANK ONE BELOW

        Action leftMove2 = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                meow[0]=1;

            }
        };

        Action rightMove2 = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //(new Thread(new OneMoveRightThread())).start();
                meow[1]=1;
            }
        };
        Action angleLEFT2 = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                tankGraphic.TankOneAngleLeft();
                gameArea.repaint();
            }
        };
        Action angleRIGHT2 = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                tankGraphic.TankOneAngleRight();
                gameArea.repaint();
            }
        };

        //these two lines are irrelevant. They match the irrelevant abstractaction.
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("B"), "doNothing");
        gameArea.getActionMap().put("doNothing",doNothing);

        //below is the code for the keybindings.
        //the names are reversed, below. (fire1 corresponds to tank two, but the right side of the keyboard)
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("LEFT"), "leftMove");
        gameArea.getActionMap().put("leftMove",leftMove);
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"), "rightMove");
        gameArea.getActionMap().put("rightMove",rightMove);
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("UP"), "angleLEFT");
        gameArea.getActionMap().put("angleLEFT",angleLEFT);
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("DOWN"), "angleRIGHT");
        gameArea.getActionMap().put("angleRIGHT",angleRIGHT);
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("A"), "leftMove2");
        gameArea.getActionMap().put("leftMove2",leftMove2);
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"), "rightMove2");
        gameArea.getActionMap().put("rightMove2",rightMove2);
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("W"), "angleLEFT2");
        gameArea.getActionMap().put("angleLEFT2",angleLEFT2);
        gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("S"), "angleRIGHT2");
        gameArea.getActionMap().put("angleRIGHT2",angleRIGHT2);
        //gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("E"), "fire2");
        //gameArea.getActionMap().put("fire2",fire2);
        //gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("SLASH"), "fire1");
        //gameArea.getActionMap().put("fire1",fire1);
        frame.requestFocus();

Solution

  • Take a look at Motion Using the Keyboard.

    The KeyboardAnimation.java example does this. The class shows one way to add animation using Key Bindings and a Swing Timer. Each key binding can control a different image and a different Timer.

    One image is controlled by the 4 arrow keys and the other by the "a, d, w, s" keys.