블럭 격파

By Pavel Porvatov, September 22, 2008

JavaFX 기술은 데스크탑이나 모바일 플랫폼을 위한 화려한 그래픽들이 모두 구현된 게임을 쉽게 작성하는 것을 가능하게 해준다. 양쪽 플랫폼에서 같은 코드를 사용할 수 있고 더욱이 매우 간단한 코드로 되어 있다.

Java Webstart로 예제 구동

코드 이해하기

예제의 코드 전부는 게임의 모든 요소를 나타내는 몇개의 class들로 나누어져 있다: Brick, Ball, Bat, 그리고 Bonus class들. 더 복잡한 class들은 SplashLevel 이다.

코드 상세

Main class는 게임의 진입점을 포함한다. 그림 1처럼, 게임은 run 메소드에서 시작된다.

Source Code
function run(__ARGS__ : String[]) {
    // Initialization should be the first
    Config.initialize(IS_MOBILE);

    mainFrame = MainFrame {
        title: "Brick Breaker"
        resizable: false

        scene: Scene {
            fill: Color.BLACK
            width: Config.screenWidth
            height: Config.screenHeight
        }
    }
}

그림 1: Main.run Method

run 메소드는 2가지 목적으로 사용한다. 첫번째 목적은 Config class(이 class는 나중에 설명한다)의 초기화다. 두번째 목적은 게임에 사용되는 제목, 넓이, 높이, 그밖의 속성들이 정의되는 틀로서 사용되는, MainFrame class의 생성이다. 사용자가 게임을 하는동안, MainFrame.scene 의 내용은 Splash class나 Level class에 의해 교체된다. 또한 MainFrame class는 다음과 같은 공용으로 쓰이는 게임 속성들을 포함한다:

  • lifeCount - 남아있는 목숨 수
  • score - 현재 점수

Config class는 많은 상수들과 게임의 모든 이미지들을 포함하고 있다. Config.initialize 메소드는 그림 1처럼, 한번 초기화를 실행할 것이다. 이 메소드는 모든 이미지들을 불러오고 게임의 상수들을 준비시킨다. 단일 매개 변수는 대상 시스템을 식벽한다. 이동 전화에서는, 작은화면에 작은 이미지들이 사용된다. 데스크탑 버전에서는, 큰 화면 해상도에 맞게 큰 이미지들이 사용된다.

그림 2처럼, Ball class는 게임에서 공을 나타낸다.

소스 코드
// Default size of the ball
public def DEFAULT_SIZE = 2;

// Maximum size of the ball
public def MAX_SIZE = 5;

public class Ball extends CustomNode {
    // Size of the ball
    public var size = DEFAULT_SIZE on replace {
        if (imageView == null) {
            imageView = ImageView {};
        }

        imageView.image = Config.images[Config.IMAGE_BALL_0 + size];
        diameter = imageView.image.width - Config.shadowWidth as Integer;
    };

    // Diameter of the ball (without shadow)
    public-read var diameter;

    var imageView: ImageView;

    override public function create(): Node {
        imageView
    }
}

그림 2: Ball Class

이 게임에서, 공은 size 속성이 소개된 이유를 설명하는, 몇 개의 크기들을 가질 수 있다. 이 속성은 0MAX_SIZE 사이 값을 갖는다 . 읽기 속성인 diameter 속성은 공의 현재 size 에 해당하는 지름을 나타낸다.

그림 3처럼, Bat class는 방망이를 나타낸다. 공처럼, 다른 크기들을 가질 수 있고 size 속성에 의해 조정 할 수 있다. 방망이의 현재 넓이를 얻기 위해서는, width 속성을 사용한다.

소스 코드
// Default size of the bat
public def DEFAULT_SIZE = 2;

// Maximum size of the bat
public def MAX_SIZE = 7;

public class Bat extends CustomNode {
    // Size of the bat
    public var size = DEFAULT_SIZE;

    // Width of the bat (without shadow)
    public-read var width: Integer = bind (size * 12 + 45) * Config.scale as Integer;

    // Height of the bat (without shadow)
    public-read var height;

    ...

그림 3: Bat Class

게임 속에선 여러 종류의 벽돌들이 나타난다. 그림 4처럼, 모든 벽돌들은 Brick class에 의해 표현된다. type 속성은 벽돌의 시각적 표현과 몇가지 추가적인 특성들이 정의된 벽돌의 종류 중 하나이다. type 속성은 오직, TYPE_... 형태의 상수들의 목록에서만 값을 가질 수 있다. 어떤 벽돌들은 다른 벽돌들이 전혀 깨지지 않는 동안, 여러번 두들겨서 깨야 한다.(Some bricks need to be beaten several times before they break, while other bricks cannot be broken at all.)

소스 코드
// Types of bricks
public def TYPE_BLUE = 0;
public def TYPE_BROKEN1 = 1;
public def TYPE_BROKEN2 = 2;
public def TYPE_BROWN = 3;
public def TYPE_CYAN = 4;
public def TYPE_GREEN = 5;
public def TYPE_GREY = 6;
public def TYPE_MAGENTA = 7;
public def TYPE_ORANGE = 8;
public def TYPE_RED = 9;
public def TYPE_VIOLET = 10;
public def TYPE_WHITE = 11;
public def TYPE_YELLOW = 12;

public class Brick extends CustomNode {
    // Type of the brick
    public-init var type: Integer;

    // Invoked when the ball kicks the brick.
    // Returns true if the brick was broken or false otherwise
    public function kick(): Boolean {

    ...

그림 4: Brick Class

임의의 벽돌은 깨진 후에, 아래로 떨어지는 보너스를 만들 수 있다. 플레이어는 이 보너스를 획득해서 추가 목숨 같은, 이득을 얻을 수 있다. 그림 5의 Bonus class는 모든 게임 보너스들을 나타낸다. Bonus class의 type 속성은 보너스의 종류를 포함한다. 상수들 중 TYPE_... 항목에서 값들중 하나를 할당할 수 있다.

소스 코드
// Types of bonuses
public def TYPE_SLOW = 0;
public def TYPE_FAST = 1;
public def TYPE_CATCH = 2;
public def TYPE_GROW_BAT = 3;
public def TYPE_REDUCE_BAT = 4;
public def TYPE_GROW_BALL = 5;
public def TYPE_REDUCE_BALL = 6;
public def TYPE_STRIKE = 7;
public def TYPE_LIFE = 8;

// Total count of all bonuses
public def COUNT = 9;

...

public class Bonus extends CustomNode {
    // Type of the bonus
    public-init var type: Integer;

    // Width of the bonus (without shadow)
    public-read var width: Integer;

    // Height of the bonus (without shadow)
    public-read var height: Integer;

    ...

그림 5: Bonus Class

게임이 시작되면 번쩍이는 화면이 보인다. 그것은 Splash class에 의해 표현된다(그림 6을 보라). Splash class는 CustomNode class를 상속하고 몇가지 움직이는 효과들을 포함한다. 사용자가 임의의 키나 마우스 버튼을 누르면, 게임은 시작된다. 이 작동은 background 필드에 포함되어 있다: onMousePressed 이벤트는 마우스 버튼 동작을 잡고 onKeyPressed 이벤트는 키보드 동작을 잡는다.

소스 코드
public class Splash extends CustomNode {
    ...
    
    def background = ImageView {
        image: Config.images[Config.IMAGE_BACKGROUND]

        onMousePressed: function( e: MouseEvent ):Void {
            Main.mainFrame.startGame();
        }

        onKeyPressed: function( e: KeyEvent ):Void {
            Main.mainFrame.startGame();
        }
    }

그림 6: Splash Class

그림 7에서 처럼, 예제에서 가장 큰 class는 Level class이다. 그것은 예를 들어, 단계 시각 표현, 방망이의 움직임 그리고 공의 움직임, 같이 게임 내의 모든 처리를 제공한다. 플레이어가 게임을 하는 동안 몇개의 상태들이 가능해진다. 그것들은 state 필드에 저장이 된다. 0 state 필드에 담기면, 사용자에게 "준비(READY)"라는 메시지가 보여진다. 이 상태가 끝나면, 다음 상태가 시작된다: state = 1. 이 의미는 방망이를 움직일 수 있고 공이 잡혔다(방방이에 닿았다)란 뜻이다. 플레이어가 공을 움직이고 게임을 시작하기 위해 마우스 버튼이나 스페이스 버튼을 누르면, 이 동작들은 state = 2 와 관련이 있다. 플레이어가 모든 목숨을 잃은 후에는, 마지막 상태가 시작된다: state = 4. 화면에는 "게임 끝(Game over)" 이란 메시지가 보여진다. 마우스 버튼이나 아무 키나 누르면 플레이어를 초기 화면으로 돌려보낸다.

소스 코드
public class Level extends CustomNode {
    ...

    // States
    // 0 - starting level
    // 1 - ball is catched
    // 2 - playing
    // 3 - game over
    var state = 0;

    ...

그림 7: Level Class

코드 커스터마이징

또한 2개의 유틸리티 파일들이 예제에 포함되어 있다. 첫번째 유틸리티는 Utils.fx 파일로, 유용한 몇가지 수학적 함수들을 제공한다. 두번째 유틸리티는 LevelData.fx 파일이다. 이것은 문자 형식으로 단계들에 대한 모든 정보를 축적한다. 어떤 새로운 단계를 추가하거나 기존의 단계들을 수정할 때, 이 파일을 수정하면 된다.

게임 방법

게임이 작동되면 후에, 몇개의 움직이는 효과들이 나오는 초기 화면을 볼 것이다. 게임을 시작하려면, 아무 키나 마우스 버튼을 눌러라. 게임이 실행 모드가 된다. 블럭들이 나오는 큰 게임 필드를 볼 것이다. 필드의 바닥에는 키보드의 좌우키를 눌러서 움직일 수 있는 방망이가 있다. 마우스 움직임으로도 방망이를 움직일 수 있다. 또한 보시다시피, 게임에는 방망이에 붙들려 있는 공이 보인다. 공을 장전하려면, 스페이스 버튼이나 마우스 버튼을 눌러라. 게임중에서 공은, 방망이로 되돌려 보내길 시도하라. 공과의 접촉하기 위한 조정을 잘 못하면 공을 놓치게 되고, 목숨 하나를 잃는다. 마지막 목숨을 잃게되면, 게임은 끝난다.

게임에서 각 단계의 목표는 모든 벽돌을 깨는 것이다. 공으로 부술수 있는 모든 벽돌을 깨도록 시도하라. 어떤 벽돌들은 부셔졌을때 아래로 떨어지는 보너스들을 가지고 있다. 보너스들을 잡는다면, 몇몇 이득을 얻을 수 있다. 어떤 단계들에선 전혀 깰 수 없는 벽돌들이 있다. 그 단계들에서는, 깰 수 있는 벽돌들만 깨면 된다