1

I created a very simple shooting game utilizing JPanel as shown in the code below. I also wanted to experiment with rotating the game and trying it out. I have one issue, where the game I was able to successfully rotate the game but the dimension seems to cut out, and I have to set each position of the enemy and myself to the rotated position.

I was wondering if there was a way to rotate the result as a whole, instead of simply rotating the shape so that the position of the ship and the missiles would also rotate all at once.

Edited code: added three lives to the player and tried implementing heart image with BufferedImage.

public class game extends JFrame{
    public game(){

    }
    public static void main(String[] args){
        new game();
    }
    public class MyJPanel extends JPanel implements ActionListener, MouseListener,
    MouseMotionListener,KeyListener
    {
        //variables for player
        int my_x;
        int player_width,player_height;
        
        private int lives = 3;
        int heart_width, heart_height;
        //variables for player's missiles
        int my_missile_x, my_missile_y;
        int missile_flag;
        public static final int MY_Y = 600;

        //variables for enemies' missiles
        int e_missile_flag[];
        int e_missile_x[];
        int e_missile_y[];
        int e_missile_move[];

        Image image,image2;
        Timer timer;

        private BufferedImage heart; 
        
        public MyJPanel(){
            missile_flag = 0;

            /*** initialize enemies' info ***/
            ImageIcon icon2 = new ImageIcon("enemy.jpg");
            image2 = icon2.getImage();
            enemy_width = image2.getWidth(this);
            enemy_height = image2.getHeight(this);
            
            try {
            heart = ImageIO.read(getClass().getResource("heart.jpg"));
            
            }catch(IOException e) {
            }
            heart_width = heart.getWidth(this);
            heart_height = heart.getHeight(this);

            n = 14; //number of enemies
            enemy_x = new int[n];
            enemy_y = new int[n];
            enemy_move = new int[n];
            enemy_alive = new int[n];
            int distance = 40;

            e_missile_flag = new int[n];
            e_missile_x = new int[n];
            e_missile_y = new int[n];
            e_missile_move = new int[n];

            // place enemies in 7x2
            for (int i = 0; i < 7; i++) {
                enemy_x[i] = (enemy_width + distance) * i + 50;
                enemy_y[i] = 50;
            }
            for (int i = 7; i < n; i++) {
                enemy_x[i] = (enemy_width + distance) * (i - 5) + 50;
                enemy_y[i] = 100;
            }
            for (int i = 0; i < n; i++) {
                enemy_alive[i] = 1; //all alive
                enemy_move[i] = -10; //moves to left
            }

            for (int i = 0; i < n; i++) {
                e_missile_flag[i] = 0;
                e_missile_x[i] = 0;
                e_missile_y[i] = 0;
                e_missile_move[i] = 7 + n%3;
            }

            /*** setup system ***/
            setBackground(Color.black);
            setFocusable(true);
            addMouseListener(this);
            addMouseMotionListener(this);
            addKeyListener(this);
            timer = new Timer(50, this);
            timer.start();
        }
        
        private void updateEnemiesPosition(){
            //update enemies' position
            Dimension dim = getSize();
            for (int i = 0; i < n; i++) {
                enemy_x[i] += enemy_move[i];
                if ((enemy_x[i] < 0) || (enemy_x[i] > (dim.width - enemy_width))) {
                    enemy_move[i] = -enemy_move[i];
                }
            }
        }
        private void updateMyPosition() {
            if(my_x < 0) {
                my_x = 800;
            }
            if(my_x > 800) {
                my_x = 0;
            }
        }
        
        private void activateMyMissile(){
            //shoot a missile
            if(missile_flag == 0){
                my_missile_x = my_x + player_width / 2;
                my_missile_y = MY_Y; //MY_Y=400
                missile_flag = 1;
            }
        }
        private void updateMyMissile(){
            //update missile position if alive
            if (missile_flag == 1) {
                my_missile_y -= 15;
                if (0 > my_missile_y) {
                    missile_flag = 0;
                }
            }
        }
        private void activateEnemiesMissile(){
            //activate enemies' missile if enemy is alive and its missile is not alive
            for(int i = 0; i < n; i++){
                if (enemy_alive[i] == 1 && e_missile_flag[i] == 0) {
                    e_missile_x[i] = enemy_x[i] + enemy_width/2;
                    e_missile_y[i] = enemy_y[i];
                    e_missile_flag[i] = 1;
                }
            }
        }
        private void updateEnemiesMissile(){
            //update enemies' missile position if alive
            Dimension dim = getSize();
            for(int i = 0; i < n; i++){
                if (e_missile_flag[i] == 1) {
                    e_missile_y[i] += e_missile_move[i];
                    if (e_missile_y[i] > dim.height) {
                        e_missile_flag[i] = 0;
                    }
                }
            }
        }
        private void checkHitToEnemy(){
            for(int i = 0; i < n; i++){
                if(missile_flag == 1 && enemy_alive[i] == 1){
                    if(
                        my_missile_x > enemy_x[i] &&
                        my_missile_x < (enemy_x[i] + enemy_width) &&
                        my_missile_y > enemy_y[i] &&
                        my_missile_y < (enemy_y[i] + enemy_height)
                    ){
                        //hit
                        missile_flag = 0;
                        enemy_alive[i] = 0;
                    }
                }
            }
        }
        private boolean checkClear(){
            int cnt = 0;
            for(int i = 0; i < n; i++){
                if(enemy_alive[i] == 0) cnt++;
            }
            return (n == cnt);
        }   
            if(lives>0) {
                int x = 0;
                int y = getHeight()- heart.getHeight();
                for(int index = 0; index < lives; index++) {
                    g.drawImage(heart, x, y, this);
                    x += heart.getWidth();
                }
            }
            
            g2d.dispose();
        }
        public void actionPerformed(ActionEvent e){
            if (e.getSource() == timer) {
                updateEnemiesPosition();
                updateMyPosition();

                updateMyMissile();
                updateEnemiesMissile();

                activateEnemiesMissile();

                if(checkHitToPlayer()){
                    System.out.println("===== Game Over =====");
                    System.exit(0);
                }
                checkHitToEnemy();
                if(checkClear()){
                    System.out.println("===== Game Clear =====");
                    System.exit(0);
                }
                repaint();
            }
        }
        public void mouseClicked(MouseEvent me)
        { }
        public void mousePressed(MouseEvent me)
        {
            activateMyMissile();
        }
        public void mouseReleased(MouseEvent me)
        { }
        public void mouseExited(MouseEvent me)
        { }
        public void mouseEntered(MouseEvent me)
        { }
        public void mouseMoved(MouseEvent me)
        { }
        public void mouseDragged(MouseEvent me)
        { }
        public void keyReleased(KeyEvent e){
        }
        public void keyTyped(KeyEvent e){
        }
    }
}

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • The original size of the window is 800x500, but you rotate the context 90 degrees, so now the size is going to be (more like) 500x800. So the general answer is, no, not really a "simple" solution. One solution might be to make the panel's size square, by overriding `getPreferredSize` – MadProgrammer Jan 10 '22 at 00:40
  • @MadProgrammer no i tried the code ... the game has a black perfect square in the center of the 800x500 window – Yasser CHENIK Jan 10 '22 at 00:47
  • @YasserCHENIK Try filling a rect (in the paint component method) with the bounds of the panel AFTER it's rotated – MadProgrammer Jan 10 '22 at 00:49

1 Answers1

1

The original size of the component is going to be less then 800x500 (I say less then, because the actual size will be 800x500 - the frame decorations - this is why you should be setting the size of the window directly).

When you rotate it 90 degrees, it becomes (less then) 500x800. So, no, there's no "easy" way to resolve this, without making the game square.

I added...

g2d.setColor(Color.DARK_GRAY);
g2d.fillRect(0, 0, getWidth(), getHeight());

after the rotation, which highlights the issue.

enter image description here

To do this, you should override getPreferredSize of the JPanel and the call pack on the frame. This will ensure that that game canvas is sized to it's preferred size and the window decorations are then packed around it.

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Lec12 extends JFrame {

    public Lec12() {
        setTitle("Game Example");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        MyJPanel myJPanel = new MyJPanel();
        Container c = getContentPane();
        c.add(myJPanel);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        new Lec12();
    }

    public class MyJPanel extends JPanel implements ActionListener, MouseListener,
            MouseMotionListener, KeyListener {

        //variables for player
        int my_x;
        int player_width, player_height;

        //variables for enemies
        int enemy_width, enemy_height;
        int n;
        int enemy_x[];
        int enemy_y[];
        int enemy_move[];
        int enemy_alive[];

        //variables for player's missiles
        int my_missile_x, my_missile_y;
        int missile_flag;
        public static final int MY_Y = 400;

        //variables for enemies' missiles
        int e_missile_flag[];
        int e_missile_x[];
        int e_missile_y[];
        int e_missile_move[];

        Image image, image2;
        Timer timer;

        public MyJPanel() {
            /**
             * * initialize player's info **
             */
            my_x = 250;
            ImageIcon icon = new ImageIcon("player.jpg");
            image = icon.getImage();

            player_width = image.getWidth(this);
            player_height = image.getHeight(this);

            missile_flag = 0;

            /**
             * * initialize enemies' info **
             */
            ImageIcon icon2 = new ImageIcon("enemy.jpg");
            image2 = icon2.getImage();
            enemy_width = image2.getWidth(this);
            enemy_height = image2.getHeight(this);

            n = 14; //number of enemies
            enemy_x = new int[n];
            enemy_y = new int[n];
            enemy_move = new int[n];
            enemy_alive = new int[n];
            int distance = 40;

            e_missile_flag = new int[n];
            e_missile_x = new int[n];
            e_missile_y = new int[n];
            e_missile_move = new int[n];

            // place enemies in 7x2
            for (int i = 0; i < 7; i++) {
                enemy_x[i] = (enemy_width + distance) * i + 50;
                enemy_y[i] = 50;
            }
            for (int i = 7; i < n; i++) {
                enemy_x[i] = (enemy_width + distance) * (i - 5) + 50;
                enemy_y[i] = 100;
            }
            for (int i = 0; i < n; i++) {
                enemy_alive[i] = 1; //all alive
                enemy_move[i] = -10; //moves to left
            }

            for (int i = 0; i < n; i++) {
                e_missile_flag[i] = 0;
                e_missile_x[i] = 0;
                e_missile_y[i] = 0;
                e_missile_move[i] = 7 + n % 3;
            }

            /**
             * * setup system **
             */
            setBackground(Color.black);
            setFocusable(true);
            addMouseListener(this);
            addMouseMotionListener(this);
            addKeyListener(this);
            timer = new Timer(50, this);
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 800);
        }

        private void updateEnemiesPosition() {
            //update enemies' position
            Dimension dim = getSize();
            for (int i = 0; i < n; i++) {
                enemy_x[i] += enemy_move[i];
                if ((enemy_x[i] < 0) || (enemy_x[i] > (dim.width - enemy_width))) {
                    enemy_move[i] = -enemy_move[i];
                }
            }
        }

        private void updateMyPosition() {
            if (my_x < 0) {
                my_x = 800;
            }
            if (my_x > 800) {
                my_x = 0;
            }
        }

        private void activateMyMissile() {
            //shoot a missile
            if (missile_flag == 0) {
                my_missile_x = my_x + player_width / 2;
                my_missile_y = MY_Y; //MY_Y=400
                missile_flag = 1;
            }
        }

        private void updateMyMissile() {
            //update missile position if alive
            if (missile_flag == 1) {
                my_missile_y -= 15;
                if (0 > my_missile_y) {
                    missile_flag = 0;
                }
            }
        }

        private void activateEnemiesMissile() {
            //activate enemies' missile if enemy is alive and its missile is not alive
            for (int i = 0; i < n; i++) {
                if (enemy_alive[i] == 1 && e_missile_flag[i] == 0) {
                    e_missile_x[i] = enemy_x[i] + enemy_width / 2;
                    e_missile_y[i] = enemy_y[i];
                    e_missile_flag[i] = 1;
                }
            }
        }

        private void updateEnemiesMissile() {
            //update enemies' missile position if alive
            Dimension dim = getSize();
            for (int i = 0; i < n; i++) {
                if (e_missile_flag[i] == 1) {
                    e_missile_y[i] += e_missile_move[i];
                    if (e_missile_y[i] > dim.height) {
                        e_missile_flag[i] = 0;
                    }
                }
            }
        }

        private void checkHitToEnemy() {
            for (int i = 0; i < n; i++) {
                if (missile_flag == 1 && enemy_alive[i] == 1) {
                    if (my_missile_x > enemy_x[i]
                            && my_missile_x < (enemy_x[i] + enemy_width)
                            && my_missile_y > enemy_y[i]
                            && my_missile_y < (enemy_y[i] + enemy_height)) {
                        //hit
                        missile_flag = 0;
                        enemy_alive[i] = 0;
                    }
                }
            }
        }

        private boolean checkHitToPlayer() {
            for (int i = 0; i < n; i++) {
                if (e_missile_flag[i] == 1) {
                    if (e_missile_x[i] > my_x
                            && e_missile_x[i] < (my_x + player_width)
                            && e_missile_y[i] > MY_Y
                            && e_missile_y[i] < (MY_Y + player_height)) {
                        e_missile_flag[i] = 0;
                        return true;
                    }
                }
            }
            return false;
        }

        private boolean checkClear() {
            int cnt = 0;
            for (int i = 0; i < n; i++) {
                if (enemy_alive[i] == 0) {
                    cnt++;
                }
            }
            return (n == cnt);
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g); //reset graphics
            Graphics2D g2d = (Graphics2D) g.create();

            System.out.println(getWidth() + "x" + getHeight());

            int w2 = getWidth() / 2;
            int h2 = getHeight() / 2;
            g2d.rotate(-Math.PI / 2, w2, h2);
            g2d.setColor(Color.DARK_GRAY);
            g2d.fillRect(0, 0, getWidth(), getHeight());
            //draw player
            g2d.setColor(Color.BLUE);
            g2d.fillRect(my_x, 400, 10, 10);
//            g.drawImage(image, my_x, 400, this);
            //draw enemies
            for (int i = 0; i < n; i++) {
                if (enemy_alive[i] == 1) {
                    g2d.setColor(Color.RED);
                    g2d.fillRect(enemy_x[i], enemy_y[i], 10, 10);
//                    g.drawImage(image2, enemy_x[i], enemy_y[i], this);
                }
            }
            //draw players missiles
            if (missile_flag == 1) {
                g2d.setColor(Color.white);
                g2d.fillRect(my_missile_x, my_missile_y, 2, 5);
            }
            //draw enemies' missiles
            for (int i = 0; i < n; i++) {
                if (e_missile_flag[i] == 1) {
                    g2d.setColor(Color.white);
                    g2d.fillRect(e_missile_x[i], e_missile_y[i], 2, 5);
                }
            }
            g2d.dispose();
        }

        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == timer) {
                updateEnemiesPosition();
                updateMyPosition();

                updateMyMissile();
                updateEnemiesMissile();

                activateEnemiesMissile();

                if (checkHitToPlayer()) {
                    System.out.println("===== Game Over =====");
                    System.exit(0);
                }
                checkHitToEnemy();
                if (checkClear()) {
                    System.out.println("===== Game Clear =====");
                    System.exit(0);
                }
                repaint();
            }
        }

        public void mouseClicked(MouseEvent me) {
        }

        public void mousePressed(MouseEvent me) {
            activateMyMissile();
        }

        public void mouseReleased(MouseEvent me) {
        }

        public void mouseExited(MouseEvent me) {
        }

        public void mouseEntered(MouseEvent me) {
        }

        public void mouseMoved(MouseEvent me) {
        }

        public void mouseDragged(MouseEvent me) {
        }

        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            switch (key) {
                case KeyEvent.VK_S:
                    my_x = my_x + 10;
                    break;
                case KeyEvent.VK_W:
                    my_x = my_x - 10;
                    break;
                case KeyEvent.VK_DOWN:
                    my_x = my_x + 10;
                    break;
                case KeyEvent.VK_UP:
                    my_x = my_x - 10;
                    break;
                case KeyEvent.VK_X:
                    if (missile_flag == 0) {
                        my_missile_x = my_x + player_width / 2;
                        my_missile_y = MY_Y;// MY_Y=400
                        missile_flag = 1;
                    }
                    break;
            }
        }

        public void keyReleased(KeyEvent e) {
        }

        public void keyTyped(KeyEvent e) {
        }
    }
}

Also, beware, transformations of this nature are accumulative. You should, instead, take a snapshot of the Graphics context and dispose of it once you're done (see the above for an example).

Also, have look at How to Use Key Bindings as it will help resolve the focus issues related to KeyListener

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I thought the player's vision might get bigger and that's why he made the game's bounds not equal to the size of the window – Yasser CHENIK Jan 10 '22 at 00:58
  • @YasserCHENIK I've not spent a lot of time looking over the code, but, one of the problems is, the "game panel" will be smaller then the "window size" by the amount of insets applied to the window decorations. This means that on Windows the size of the game container will be different then on MacOS, as they have different window decorations. Better to define the size of the "game panel" through `getPreferredSize`, then you get a much more accurate picture. – MadProgrammer Jan 10 '22 at 01:04
  • @YasserCHENIK Another idea might be to re-calculate the "rotated size" (using something like https://stackoverflow.com/questions/37758061/rotate-a-buffered-image-in-java/37758533#37758533) and the re-size the panel/window to match. Don't know if that would be a desirable solution though, as you'd need to constantly be making these calculations when doing your bounds checking – MadProgrammer Jan 10 '22 at 01:06
  • yeah i agree . But what aboute pivoting the x and y science he only wants 90 degrees rotations ? – Yasser CHENIK Jan 10 '22 at 01:14
  • Thank you for the in depth response , I think I was able to understand the solution for the most part. I'm still stuck with two issues tho 1. the code you have shown seems to break when I made the panel fullscreen *player becomes invisible 2. I additionally tried implementing three lives to the player, and it works as the player needs to get hit three times to die. I also called the heart image using a bufferedimage, However the problem seems to be that the heart isnt showing up. is this related to the rotation of the panel? Thanks again for taking the time to answer – user17286087 Jan 10 '22 at 01:53
  • 1
    @user17286087 There's a lot of hard coded positioning going, this needs to be modified to take into account the change in orientation. – MadProgrammer Jan 10 '22 at 02:02