본문 바로가기
JAVA

자바 비행기 게임 만들어보기 #4

by binghe819 2020. 5. 4.

이번 포스트에서는 적 비행기를 생성하고, 충돌 로직을 구현할 예정이다.

 

전체적인 구조를 설계했기 때문에, 크게 수정해야하는부분은 없다.


순서

  • 적 비행기 생성
  • 충돌 구현
  • 기타 처리
  • 끝!

 

적 비행기 생성

적 비행기를 생성하고, 자동적으로 움직이는 것 까지 구현을 해보려 한다.

 

전 포스터에서 설계한 클래스가 있기 때문에 그대로 사용하면 된다.

Enemy 클래스

width와 height은 충돌 처리를 위한 변수이다. ( 충돌처리를 하기 위해서 객체마다의 크기가 필요하다. )

x,y는 적 비행기의 위치값이며, moveSpeed는 말그대로 움직이는 속도를 말한다.

 

순서는 아래와 같이 동작하게 된다.

  1. Model객체에 적 비행기 객체를 담을 리스트(enemyFlights)를 선언해준다.
  2. 적 비행기를 원하는 만큼 enemyFlights객체에 add해준다.
  3. Controller객체에서는 enemyFlights에 담긴 각각의 적 비행기 위치 값들을 루프를 통해 움직인다.
  4. View객체는 지속적으로 출력을 해준다.
어느정도 구조를 설계해놓으니, 설계만 잘 이해했다면 쉽게 생성(Model)하고 출력(View)하고 처리(Controller)할 수 있게 된 것 같다.
import java.util.ArrayList;

public class GameModel {

    // Model에 저장할 객체
    Player player;

    ArrayList<Enemy> enemys;

    GameModel() {
        this.player = new Player(100,300);
        this.enemys = new ArrayList<Enemy>();

        // 적 생성 ( 기본 )
        enemys.add(new Enemy(730, 50, 10, 5));
        enemys.add(new Enemy(730, 150, 10, 5));
        enemys.add(new Enemy(730, 250, 10 , 5));
        enemys.add(new Enemy(730, 350, 10 , 5));
        enemys.add(new Enemy(730, 450, 10 , 5));
    }

}

GameModel의 생성자에다가 위처럼 리스트안에 add해주면 된다.

 

View ( 적 비행기 출력 )
Controller ( 적 비행기 움직임 )

 

적 비행기를 Model에 추가해주면, 자동적으로 View와 Controller에서 출력 및 움직임을 할 수 있게 했다.

 

사실 움직임은 View에서 같이해도 상관 없을 것 같다. 그래서 처음 설계한대로 일단 구현을 완료할 예정이다.

 

적 비행기 생성 및 움직임

 

 

충돌 구현

충돌을 구현하기 위해서는 비행기와 미사일의 이미지 크기와 위치값이 필요하다.

 

비행기와 미사일의 이미지 크기와 위치값을 계산하여 만약 부딪히는 경우가 발생하면 충돌 로직을 구현해주면 된다.

 

충돌 체크

충돌체크는 두 객체의 크기와 위치값을 받아서, 사각형 두개의 거리가 겹치는 여부를 확인하는 방식입니다.

( 블로그의 내용을 참고하였습니다. )

 

충돌은 아래 순서로 동작한다.

  1. Controller에서 지속적으로 Model의 적 비행기와 미사일등의 리스트에서 객체를 받아온다.
  2. 만약 리스트들의 적 비행기나 미사일등의 객체와 충돌체크시 true가 나오면
  3. 충돌 처리 해준다. ( 만약 HP가 0이 되면 Model의 적 비행기 리스트에서 해당 비행기를 remove 해준다. )
  4. Model의 리스트에서 적 비행기의 객체가 remove되므로, View에서는 해당 객체를 더이상 출력하지 않는다.

플레이어 미사일 충돌 처리.
플레이어와 적 비행기의 충돌 처리.

전체적인 Controller의 코드는 아래와 같다.

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class GameController extends KeyAdapter implements Runnable {

    // 키 눌림 처리
    boolean keyUp;
    boolean keyDown;
    boolean keyLeft;
    boolean keyRight;
    boolean keySpace;

    // 미사일의 발사 속도를 늦추기 위한 count.
    int cnt = 0;

    // Model에 접근하기 위한 참조변수.
    GameModel model;

    public GameController(GameModel model) {
        this.model = model;
    }

    // 키가 눌렸을 때
    @Override
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()){
            case KeyEvent.VK_UP :
                this.keyUp = true;
                break;
            case KeyEvent.VK_DOWN :
                this.keyDown = true;
                break;
            case KeyEvent.VK_LEFT :
                this.keyLeft = true;
                break;
            case KeyEvent.VK_RIGHT :
                this.keyRight = true;
                break;
            case KeyEvent.VK_SPACE:
                this.keySpace = true;
                break;
        }
    }

    // 키가 눌렀다 때졌을 때.
    @Override
    public void keyReleased(KeyEvent e) {
        switch (e.getKeyCode()){
            case KeyEvent.VK_UP :
                this.keyUp = false;
                break;
            case KeyEvent.VK_DOWN :
                this.keyDown = false;
                break;
            case KeyEvent.VK_LEFT :
                this.keyLeft = false;
                break;
            case KeyEvent.VK_RIGHT :
                this.keyRight = false;
                break;
            case KeyEvent.VK_SPACE:
                this.keySpace = false;
                break;
        }
    }

    // 키에 대한 처리.
    public void keyProcess() {
        cnt++;
        if(this.keyUp){
            if(this.model.player.y >= 26)
                this.model.player.y -= 5;
        }

        if(this.keyDown){
            if(this.model.player.y < 465)
                this.model.player.y += 5;
        }

        if(this.keyLeft){
            if(this.model.player.x > 0)
                this.model.player.x -= 5;
        }

        if(this.keyRight){
            if(this.model.player.x < 790)
                this.model.player.x += 5;
        }

        if(this.keySpace){
            if(cnt % 4 == 0)
                this.model.playerMissiles.add(new playerMissile(this.model.player.x+55, this.model.player.y,0));
        }
    }

    // 플레이어 미사일 처리
    public void missileProcess(){
        for(int i = 0 ; i < this.model.playerMissiles.size(); i++){
            // 움직임 구현.
            playerMissile missile = this.model.playerMissiles.get(i);
            missile.move();
            if (missile.x > 854) // 미사일이 화면에서 나가면 제거.
                this.model.playerMissiles.remove(i);

            // 충돌 처리. ( 플레이어 미사일이 적 비행기에 충돌 )
            for(int j = 0; j < this.model.enemys.size(); j++){
                // 만약 충돌 했다면.
                if(Crash(missile, this.model.enemys.get(j))){
                    this.model.enemys.get(j).HP -= 1; // 적 비행기의 HP 감소
                }
                // 적 비행기의 HP가 0이 되면 제거.
                if(this.model.enemys.get(j).HP < 0)
                    this.model.enemys.remove(j);
            }
        }
    }

    // 적 비행기 움직임 처리
    public void enemyProcess() {
        for(int i = 0; i < this.model.enemys.size(); i++){
            // 움직임 구현
            Enemy enemy = this.model.enemys.get(i);
            enemy.move();
            if(enemy.x < 10) // 적 비행기가 화면에서 나가면.
                enemy.x = 730;

            // 충돌 처리. ( 적 비행기와 플레이어 비행기의 충돌 )
            if(Crash(this.model.player, enemy)){
                this.model.player.HP -= enemy.HP; // 플레이어의 HP 적 비행기의 HP만큼 감소.
                this.model.enemys.remove(i); // 적 비행기 파괴.
            }

        }
    }

    // 충돌 체크
    public boolean Crash(GameObject go1, GameObject go2){
        // 위치값, 이미지의 높이와 길이를 이용하여 충돌 체크
        boolean check = false;
        if(Math.abs((go1.x + go1.width / 2) - ( go2.x + go1.width / 2)) < ( go2.width / 2 + go1.width / 2) &&
                Math.abs( (go1.y + go1.height / 2) - (go2.y + go2.height / 2)) < ( go2.height /2 + go1.height / 2))
            check = true;
        return check;
    }

    @Override
    public void run() {
        try {
            while(true){
                keyProcess();
                missileProcess();
                enemyProcess();
                Thread.sleep(20);
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

충돌 구현 완료

더 자세한 코드는 깃헙 참고바랍니다.

( 아마 블로그에서 정리한 코드랑 다를겁니다. 전체적인 게임을 만드는 과정에서 변경된 부분이 많습니다. )

 

기타 처리

조금 더 완성도를 높이기 위해 배경도 지속적으로 출력하고 상태창도 지속적으로 출력하도록 하였다.

(1) 배경 그리기

회색 배경이 아닌 하늘 이미지를 가져와서 지속적으로 출력하게 하였다.

 

(2) 상태창 출력

플레이어의 HP와 파괴한 적 비행기의 수 ( Score )를 화면에 지속적으로 출력한다.

 

구현 완료.

자세한 코드는 깃헙 참고바랍니다.

 


끝!

게임을 동작하는데 있어서 기본적인 구조는 모두 완성했다.

 

여러가지 추가 할 것이 있지만, 대부분이 이미 설계해둔 기본 틀에서 추가 및 수정하는 것이기때문에 

 

이번 사이트 프로젝트는 여기까지 하려고 한다.  ( 다른 사이드 프로젝트를 하고 싶기도 하고.. )

 

자바를 배운지는 얼마 되지 않아 배울 때마다 어떻게 쓰이는지 모르는 개념들이 많아 배워도 금방 잊어버리는 상황이 많았다.

 

구글링을 통한 얻는 정보들은 100프로 정확한 것이 아니란 것과, 구글의 검색내용들은 정리되지 않은 하나의 도서관이라는생각이 드는 프로젝트였다. 유명하신 개발자분들의 블로그를 보면 공식 문서를 보라는 내용이 많은데 왜 공식문서를 보고공부하라고 하는지 알게된 하나의 프로젝트였다.

 

또한 객체지향과 쓰레드등 기초지식에 대해서 더 깊게 공부해야겠다고 생각이 든다. 뚜렷한 목표가 있게 구조를 짜면 유지보수에도 좋고 개발속도도 크게 향상된다 생각이 든다.

 

다음번에는 더 깊게 공부할 수 있는 주제를 정해 시도해보려한다.

 

'JAVA' 카테고리의 다른 글

자바 비행기 게임 만들어보기 #3  (0) 2020.05.02
자바 비행기 게임 만들어보기 #2  (0) 2020.05.01
자바 비행기 게임 만들어보기 #1  (0) 2020.04.30

댓글