ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 명품자바프로그래밍 13장 실습문제 5번 해답, 정답, 풀이, 해설
    [자바 Java] 2020. 12. 2. 13:59
    728x90
    반응형

    사용자가 <ENTER>키를 입력하여 총알을 발사하여 목표물을 맞추는 게임입니다.

     

    제가 작성한 코드 작성 알고리즘은

    • 목표물과 총알이 움직이는 속도는 20ms에 5픽셀이므로 목표물 스레드와 총알 스레드에 sleep(20)에 맞춰 5픽셀씩 setLocation 메소드를 호출
    • 게임이 시작되면 목표물 스레드인 ChickenThread 시작
    • <ENTER>키가 눌리면 총알스레드인 BulletThread가 호출되어 총알 발사
    • 총알이 목표물에 맞으면 ChickenThread에 interrupt가 호출되어 ChickenThread가 0.1초 sleep한 후 재개
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
     
    class ChickenThread extends Thread {
        private JLabel chickenLabel;
        
        public ChickenThread(JLabel chickenLabel) {
            this.chickenLabel=chickenLabel;
        }
        
        @Override
        public void run() {
            int i = 5;
            while(true) {
                if(chickenLabel.getX()>500) { //오른쪽 화면 밖으로 벗어나면
                    chickenLabel.setLocation(0, chickenLabel.getY());
                }
                
                chickenLabel.setLocation(chickenLabel.getX()+i, chickenLabel.getY());
                
                try {
                    Thread.sleep(20); //20ms
                }
                catch(InterruptedException e) { //bulletThread에서 hitChicken()하면 interrupt()호출됨
                    chickenLabel.setLocation(0,0); //초기 위치로
                    try {
                        Thread.sleep(100); 
                    }
                    catch(InterruptedException f) {
                        return;
                    }
                }
            }
        }
    }
     
    class BulletThread extends Thread {
        private JLabel bulletLabel;
        private Thread chickenThread;
        private JLabel chickenLabel;
        
        public BulletThread(JLabel bulletLabel, Thread chickenThread, JLabel chickenLabel) {
            this.bulletLabel=bulletLabel;
            this.chickenThread=chickenThread;
            this.chickenLabel=chickenLabel;
        }
        
        private boolean inChicken(int x, int y) {
            if(((chickenLabel.getX() < x) 
                    && (chickenLabel.getX() + chickenLabel.getWidth()> x)) 
                    &&((chickenLabel.getY() < y) 
                    && (chickenLabel.getY() + chickenLabel.getHeight()> y))) {
                return true;
            }
            else
                return false;
        } 
        // chickenLabel.getX() < x < chickenLabel.getX()+chickenLabel.getWidth()
        // chickenLabel.getY() < y < chickenLabel.getY()+chickenLabel.getHeight()
        
        private boolean hitChicken() { //bullet의 상단 좌우 점 하나에만 닿으면 HIT
            if(inChicken(bulletLabel.getX(), bulletLabel.getY())
                ||inChicken(bulletLabel.getX()+bulletLabel.getWidth(), bulletLabel.getY())) {
                return true;
            }
            else
                return false;
        }
        
        @Override
        public void run() {
            int i = 5;
            while(true) {
                if(hitChicken()) {
                    try {
                        Thread.sleep(100);
                    }
                    catch(InterruptedException e) {return;}
                    
                    chickenThread.interrupt();
                    bulletLabel.setLocation(bulletLabel.getX(), 390); //발사대 위 위치
                    
                    return//스레드 종료
                }
                else {
                    if(bulletLabel.getY()==0) { //위로 넘어가면
                        bulletLabel.setLocation(bulletLabel.getX(), 390); //발사대 위 위치
                        return//스레드 종료
                    }
                    
                    bulletLabel.setLocation(bulletLabel.getX(), bulletLabel.getY()-i);
                    
                    try {
                        Thread.sleep(20);
                    }
                    catch(InterruptedException e) {return;}
                }
            }
        }
    }
     
    public class ShootChickenGameEx extends JFrame {
        public ShootChickenGameEx() {
            setTitle("사격 게임");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            Container c = getContentPane();
            c.setFocusable(true);
            c.requestFocus();
            c.setLayout(null);
            
            ImageIcon chickenImg = new ImageIcon("chicken.png");
            JLabel chickenLabel = new JLabel(chickenImg);
            chickenLabel.setSize(8585);
            chickenLabel.setLocation(0,0);
            c.add(chickenLabel);
            
            JLabel bulletLabel = new JLabel();
            bulletLabel.setSize(15,15);
            bulletLabel.setOpaque(true);
            bulletLabel.setBackground(Color.RED);
            bulletLabel.setLocation(238,390);
            c.add(bulletLabel);
                    
            JLabel baseLabel = new JLabel();
            baseLabel.setSize(60,60);
            baseLabel.setOpaque(true);
            baseLabel.setBackground(Color.BLACK);
            baseLabel.setLocation(215405);
            c.add(baseLabel);
            
            setSize(500,500);
            setVisible(true);
            setResizable(false);
            
            ChickenThread cth = new ChickenThread(chickenLabel);
            cth.start();
            
            c.addKeyListener(new KeyAdapter() {
                public void keyPressed(KeyEvent e) {
                    if(e.getKeyCode() == KeyEvent.VK_ENTER) {
                        BulletThread bth = new BulletThread(bulletLabel, cth, chickenLabel);
                        if(bulletLabel.getY()==390||bulletLabel.getY()<0//발사대 위 or 위로 넘어간 경우
                            bth.start();
                    }
                }
            });
        }
     
        public static void main(String[] args) {
            new ShootChickenGameEx();
        }
    }
     
    cs

     

    총 3개의 Thread를 사용하는 코드를 작성하였습니다.

    1. main thread

    main()이 담긴 class ShootChickenGameEx

    2. ChickenThread

    3. BulletThread

     

    main()이 담긴 class ShootChickenGameEx부터 설명하겠습니다.

    게임을 진행하면서 키보드 입력을 받아야 하기 때문에 컨테이너에 포커스를 할당합니다.

    게임에 사용할 컴포넌트들을 절대위치에 설정하기 위해 배치관리자도 삭제합니다.

    치킨이미지와 총알발사대, 총알 모두 JLabel로 작성하였습니다.

    절대 위치로 배치한 컴포넌트들 때문에 setResizeable을 false로 하여 게임프레임의 크기를 변경할 수 없도록 합니다.

    게임이 시작하면 목표물이 움직이는 ChickenThread가 시작됩니다.

    KeyListener는 익명클래스로 작성하여 컨테이너에 부착하였습니다.

    키보드의 <ENTER>키가 눌리면 총알을 발사하는 BulletThread를 시작합니다.

    총알은 한번에 한 개씩, 목표물을 명중하든지 위로 올라가 실패하여야 다음 발사가 가능하다고 했으므로 if조건문을 만족하여야 BulletThread가 start하도록 작성하였습니다.

    BulletThread에서는 총알과 목표물이 명중될 경우를 포함하여 작성하였기 때문에 parameter로 bulletLabel, chickenThread, chickenLabel을 넘겨받습니다.

     

    ChickenThread는 목표물인 ChickenLabel을 움직입니다.

    while 무한루프를 돌면서 20ms당 5픽셀 씩 움직이도록 setLocation함수를 사용했습니다.

    총알과 목표물이 맞는 경우 BulletThread에서 ChickenThread의 interrupt를 발생시킵니다.

    interrupt가 발생하면 ChickenLabel은 초기위치인 좌상단 (0,0)으로 이동하며 잠시 멈춘 후 게임은 계속 진행됩니다.

     

    BulletThread는 parameter로 bulletLabel, chickenThread, chickenLabel을 넘겨받습니다.

    BulletThread의 함수로 점 (x,y)가 chickenLabel에 포함되는지 판별하는 함수 inChicken()을 작성하였습니다. hitChicken() 함수에서 inChicken()함수에 bulletLabel의 좌표를 넣고 hit 여부를 판단합니다.

    run() 메소드에서는 bulletLabel의 이동을 위해 hit하지 않은 경우 setLocation()과 sleep메소드로 총알을 이동시킵니다.

    총알이 명중하지 못하고 위로 넘어간 경우 다음 엔터키가 입력되어 총알을 발사할때까지 기다려야 하므로 break문으로 while루프를 탈출하고 bulletThread는 스스로 종료합니다.

    총알이 목표물에 명중한 경우 chickenThread에 interrupt를 호출한 후 총알이 발사대 위 위치로 다시 설정한 후 스레드를 종료합니다.

     

     

    출판사 제공 답안

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    import java.awt.*;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
     
    import javax.swing.*;
     
    public class BulletGameFrame extends JFrame{
        public BulletGameFrame() {
            setTitle("사격 게임");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            GamePanel p = new GamePanel();
            setContentPane(p);
            setSize(300,300);
            setResizable(false);        
            setVisible(true);
            p.startGame();
        }
        
        public static void main(String [] args) {
            new BulletGameFrame();
        }
    }
    class GamePanel extends JPanel {
        TargetThread targetThread=null;
        JLabel baseLabel = new JLabel();
        JLabel bulletLabel = new JLabel();
        JLabel targetLabel;
        
        public GamePanel() {
            setLayout(null);
        
            baseLabel.setSize(40,40);
            baseLabel.setOpaque(true);
            baseLabel.setBackground(Color.BLACK);
     
            ImageIcon img = new ImageIcon("images/chicken.jpg");
            targetLabel = new JLabel(img);
            targetLabel.setSize(img.getIconWidth(),img.getIconWidth());
     
            bulletLabel.setSize(10,10);
            bulletLabel.setOpaque(true);
            bulletLabel.setBackground(Color.RED);
            add(baseLabel);
            add(targetLabel);
            add(bulletLabel);
        }
        
        public void startGame() {
            baseLabel.setLocation(this.getWidth()/2-20this.getHeight()-40);
            bulletLabel.setLocation(this.getWidth()/2 - 5this.getHeight()-50);            
            targetLabel.setLocation(00);
            
            targetThread = new TargetThread(targetLabel);
            targetThread.start();
            
            baseLabel.requestFocus();            
            baseLabel.addKeyListener(new KeyAdapter() {
                BulletThread  bulletThread = null;
                public void keyPressed(KeyEvent e) {
                    if(e.getKeyChar() == '\n') {
                        if(bulletThread==null || !bulletThread.isAlive()) {
                            bulletThread = new BulletThread(bulletLabel, targetLabel, targetThread);
                            bulletThread.start();
                        }
                    }
                }
            });
        }
        
        class TargetThread extends Thread {
            JComponent target;
            public TargetThread(JComponent target) {
                this.target = target;
                target.setLocation(00);
                target.getParent().repaint();
            }    
            public void run() {
                while(true) {
                    int x = target.getX()+5;
                    int y = target.getY();
                    if(x > GamePanel.this.getWidth()) 
                        target.setLocation(0,0);
                    else 
                        target.setLocation(x, y);
     
                    target.getParent().repaint();
                    try {
                        sleep(20);
                    }
                    catch(InterruptedException e) {
                        // the case of hit by a bullet
                        target.setLocation(00);
                        target.getParent().repaint();
                        try {
                            sleep(500); // 0.5초 기다린 후에 계속한다.
                        }catch(InterruptedException e2) {}                    
                    }
                }
            }            
        }
        
        class BulletThread extends Thread {
            JComponent bullet, target;
            Thread targetThread;
            public BulletThread(JComponent bullet, JComponent target, Thread targetThread) {
                this.bullet = bullet;
                this.target = target;
                this.targetThread = targetThread;                
            }
            
            public void run() {
                while(true) {
                    // 명중하였는지 확인
                    if(hit()) {
                        targetThread.interrupt();
                        bullet.setLocation(bullet.getParent().getWidth()/2 - 5, bullet.getParent().getHeight()-50);                        
                        return;
                    }
                    else {
                        int x = bullet.getX() ;
                        int y = bullet.getY() - 5;
                        if(y < 0) {
                            bullet.setLocation(bullet.getParent().getWidth()/2 - 5, bullet.getParent().getHeight()-50);
                            bullet.getParent().repaint();
                            return// thread ends
                        }
                        bullet.setLocation(x, y);
                        bullet.getParent().repaint();
                    }
                    try {
                        sleep(20);
                    }
                    catch(InterruptedException e) {}
                }
            }
            
            private boolean hit() {
                if(targetContains(bullet.getX(), bullet.getY()) || 
                        targetContains(bullet.getX() + bullet.getWidth() - 1, bullet.getY()) ||
                        targetContains(bullet.getX() + bullet.getWidth() - 1, bullet.getY()+bullet.getHeight() - 1||
                        targetContains(bullet.getX(), bullet.getY()+bullet.getHeight() - 1))
                    return true;
                else
                    return false;                    
            }
            
            private boolean targetContains(int x, int y) {
                if(((target.getX() <= x) && (target.getX() + target.getWidth() - 1 >= x)) &&
                        ((target.getY() <= y)&& (target.getY() + target.getHeight() - 1 >= y))) {
                    return true;
                }
                else
                    return false;
                
            }
        }    
    }
     
    cs

     

    728x90
    반응형

    댓글

S.B. All Rights Reserved.