Статьи

Создание программы Tetris (часть вторая) в скомпилированном скрипте JavaFX

Это вторая часть мини-серии, в которой мы вместе создаем игру Tetris в скомпилированном JavaFX Script. Если вы еще этого не сделали, ознакомьтесь с публикацией « Давайте создадим игру тетрис в сценарии скомпилированного JavaFX-сценария» .

Вот снимок экрана исполняемой программы в ее текущем состоянии:

Tetris_2

Как вы можете видеть, я сделал несколько изменений в пользовательском интерфейсе, чтобы он выглядел немного больше как игра в тетрис. Я также реструктурировать часть кода, ввели новый класс для представления игрового поля, переместил TetrisShapeType класса в tetris_model пакет, и добавил некоторые функциональные возможности .

Скомпилируйте и запустите код, который вы видите ниже, и нажмите кнопку Play, когда появится экран, показанный выше. Это приводит к тому, что один из семи типов тетромино начинает падать с игрового поля. Нажатие кнопки Повернуть вращает тетромино по часовой стрелке, нажатие левой кнопки перемещает его влево, а нажатие правой кнопки перемещает его вправо. Когда тетромино достигает дна, оно исчезает и начинает падать новая случайно выбранная форма тетромино. Для выхода из программы закройте окно.

В своем текущем состоянии программа не обладает всеми функциями, необходимыми для игры в тетрис (например, тетромино не складывается). Мы доберемся до этого, и, надеюсь, мы все узнаем больше о разработке приложений JavaFX Script в процессе. Вот текущий исходный код:

Основная программа с именем TetrisMain.fx , которая декларативно выражает пользовательский интерфейс и запускает программу:

/*
* TetrisMain.fx - The main program for a compiled JavaFX Script Tetris game
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a compiled JavaFX Script example.
*/
package tetris_ui;

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.lang.System;
import tetris_model.*;

Frame {
var model =
TetrisModel {

}
var canvas:Canvas
width: 480
height: 500
title: "TetrisJFX"
background: Color.LIGHTGREY
content:
BorderPanel {
center:
FlowPanel {
content: [
Canvas {
content:
TetrisPlayingField {
model: model
}
}
]
}
bottom:
FlowPanel {
content: [
Button {
text: "Play"
action:
function() {
model.t.start();
}
},
Button {
text: "Rotate"
action:
function() {
model.rotate90();
}
},
Button {
text: "Left"
action:
function() {
model.moveLeft();
}
},
Button {
text: "Right"
action:
function() {
model.moveRight();
}
}
]
}
}
visible: true
onClose:
function():Void {
System.exit(0);
}
}

 

TetrisShape пользовательский графический компонент. Кстати, я реорганизовал большое уродливое утверждение i f / else из этого класса:

/*
* TetrisShape.fx - A Tetris piece, configurable to the
* different shape types. They are:
* I, J, L, O, S, T, and Z
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a compiled JavaFX Script example.
*
*/
package tetris_ui;

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.awt.Point;
import java.lang.System;
import tetris_model.*;

class TetrisShape extends CompositeNode {
private static attribute squareOutlineColor = Color.BLACK;
private static attribute squareOutlineWidth = 1;
private attribute squareColor;

public attribute model:TetrisModel;

public attribute shapeType:TetrisShapeType on replace {
squareLocs =
for (block in shapeType.squarePositions) {
new Point(block.x * model.SQUARE_SIZE,
block.y * model.SQUARE_SIZE)
};
squareColor = shapeType.squareColor;
};

private attribute squareLocs:Point[];

public function composeNode():Node {
Group {
transform: bind [
Translate.translate(model.SQUARE_SIZE * model.tetrominoHorzPos,
(model.a / model.SQUARE_SIZE).intValue() * model.SQUARE_SIZE),
Rotate.rotate(model.tetrominoAngle,
squareLocs[0].x + model.SQUARE_SIZE / 2,
squareLocs[0].y + model.SQUARE_SIZE / 2)
]
content: bind [
for (squareLoc in squareLocs) {
Rect {
x: squareLoc.x
y: squareLoc.y
width: bind model.SQUARE_SIZE
height: bind model.SQUARE_SIZE
fill: bind squareColor
stroke: squareOutlineColor
strokeWidth: squareOutlineWidth
}
}
]
};
}
}

 

Недавно представленный пользовательский графический компонент TetrisPlayingField , управляющий областью, в которую попадают тетромино:

/*
* TetrisPlayingField.fx -
* A custom graphical component that is the UI for the
* playing field.
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a compiled JavaFX Script example.
*
*/
package tetris_ui;

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.lang.System;
import tetris_model.*;

class TetrisPlayingField extends CompositeNode {
public attribute model:TetrisModel;

public function composeNode():Node {
Group {
content: [
Rect {
x: 0
y: 0
width: model.NUM_COLS * model.SQUARE_SIZE
height: model.NUM_ROWS * model.SQUARE_SIZE
strokeWidth: 1
stroke: Color.BLACK
fill: Color.WHITE
},
TetrisShape {
model: model
shapeType: bind model.activeShapeType
}
]
}
}
}

 

Класс TetrisShapeType определяет типы tetromino, и я решил, что это действительно часть модели, поэтому я переместил его в пакет tetris_model:

/*
* TetrisShapeType.fx - A Tetris shape type, which are
* I, J, L, O, S, T, and Z
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a compiled JavaFX Script example.
*
*/
package tetris_model;

import javafx.ui.*;
import java.awt.Point;
import java.lang.Math;

/**
* This class contains the model information for each type
* of tetromino (Tetris piece).
*/
public class TetrisShapeType {
public attribute id: Integer;
public attribute name: String;

/**
* A sequence containing positions of each square in a
* tetromino type. The first element in the sequence is
* the one around which the tetromino will rotate.
* Note that the "O" tetromino type doesn't rotate.
*/
public attribute squarePositions:Point[];

public attribute allowRotate:Boolean = true;

public attribute squareColor:Color;

/** The "I" shape (four squares in a straight line) */
public static attribute I =
TetrisShapeType {
id: 1
name: "I"
squarePositions: [
new Point(1, 0),
new Point(0, 0),
new Point(2, 0),
new Point(3, 0)
]
squareColor: Color.RED
};

/** The "T" shape (looks like a stout "T") */
public static attribute T =
TetrisShapeType {
id: 2
name: "T"
squarePositions: [
new Point(1, 0),
new Point(0, 0),
new Point(2, 0),
new Point(1, 1)
]
squareColor: Color.GREEN
};

/** The "L" shape (looks like an "L") */
public static attribute L =
TetrisShapeType {
id: 3
name: "L"
squarePositions: [
new Point(1, 0),
new Point(0, 0),
new Point(2, 0),
new Point(0, 1)
]
squareColor: Color.MAGENTA
};

/** The "J" shape (looks sort of like a "J", but
* more like a backwards "L") */
public static attribute J =
TetrisShapeType {
id: 4
name: "J"
squarePositions: [
new Point(1, 1),
new Point(1, 0),
new Point(1, 2),
new Point(0, 2)
]
squareColor: Color.YELLOW
};

/** The "S" shape (looks sort of like an "S") */
public static attribute S =
TetrisShapeType {
id: 5
name: "S"
squarePositions: [
new Point(1, 0),
new Point(2, 0),
new Point(0, 1),
new Point(1, 1)
]
squareColor: Color.CYAN
};

/** The "Z" shape (looks sort of like an "Z") */
public static attribute Z =
TetrisShapeType {
id: 6
name: "Z"
squarePositions: [
new Point(1, 0),
new Point(0, 0),
new Point(1, 1),
new Point(2, 1)
]
squareColor: Color.ORANGE
};

/** The "O" shape (looks sort of like an "O", but
* more like a square) */
public static attribute O =
TetrisShapeType {
id: 7
name: "O"
squarePositions: [
new Point(0, 0),
new Point(0, 1),
new Point(1, 0),
new Point(1, 1)
]
squareColor: Color.BLUE //TODO:Research official color
allowRotate: false
};

/**
* A sequence of the shape types for use in generating a
* random shape type.
*/
private static attribute allShapeTypes:TetrisShapeType[];

/**
* A function that returns a random TetrisShapeType
*/
public static function randomShapeType():TetrisShapeType {
if (sizeof allShapeTypes <= 0) {
insert I into allShapeTypes;
insert T into allShapeTypes;
insert L into allShapeTypes;
insert J into allShapeTypes;
insert S into allShapeTypes;
insert Z into allShapeTypes;
insert O into allShapeTypes;
}
allShapeTypes[(Math.random() * sizeof allShapeTypes) as Integer]
}
}

 

И, наконец, вот класс TetrisModel . Я добавил несколько атрибутов в этот класс, в основном, чтобы служить модельными переменными для нового класса TetrisPlayingField :

/*
* TetrisModel.fx - The model behind the Tetris UI
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a compiled JavaFX Script example.
*
*/
package tetris_model;

import javafx.ui.animation.*;
import java.lang.System;
import com.sun.javafx.runtime.PointerFactory;
import java.lang.Double;

public class TetrisModel {
/** Size of each tetromino in pixels */
public static attribute SQUARE_SIZE = 20;

/** Number of rows in the playing field */
public static attribute NUM_ROWS = 20;

/** Number of columns in the playing field */
public static attribute NUM_COLS = 10;

/**
* Sequence of objects that represent the
* type of shapes in each playing field cell
*/
public attribute fieldCells:TetrisShapeType[];

/**
* The active tetromino shape type.
*/
public attribute activeShapeType:TetrisShapeType;

public attribute running:Boolean = true;
public attribute a:Integer on replace {
if (a >= stopValue) {
activeShapeType = TetrisShapeType.randomShapeType();
tetrominoAngle = 0;
}
};
private attribute pf = PointerFactory {};
private attribute bpa = bind pf.make(a);
private attribute pa = bpa.unwrap();
private attribute interpolate = NumberValue.LINEAR;
private attribute stopValue = 370;
public attribute t =
Timeline {
keyFrames: [
KeyFrame {
keyTime: 0s;
keyValues:
NumberValue {
target: pa;
value: 0;
interpolate: bind interpolate
}
},
KeyFrame {
keyTime: 10s;
keyValues:
NumberValue {
target: pa;
value: stopValue
interpolate: bind interpolate
}
}
]
repeatCount: Double.POSITIVE_INFINITY
};
public attribute tetrominoAngle:Number;
public attribute tetrominoHorzPos:Number = (NUM_COLS / 2) as Integer;

public function rotate90():Void {
if (activeShapeType.allowRotate) {
(tetrominoAngle += 90) % 360;
}
}

public function moveLeft():Void {
if (tetrominoHorzPos > 0) {
tetrominoHorzPos--;
}
}

public function moveRight():Void {
//TODO: Need to allow pieces to bump up against the
// right side. For now, hardcode 3.
if (tetrominoHorzPos < NUM_COLS - 3) {
tetrominoHorzPos++;
}
}
}

Некоторая оставшаяся функциональность

Еще многое предстоит сделать, чтобы превратить эту полностью функциональную игру в тетрис. Например, модель должна:

  • Следите за тем, какие клетки на игровом поле пусты, а какие содержат остатки тетромино.
  • Знайте, когда тетромино приземлилось на дно (или на обломки тетромино), чтобы оно могло перестать падать.
  • Удалите остатки тетромино из заполненного ряда и дайте остаткам тетромино, лежащим выше, свалиться в освобожденный ряд.
  • Используйте состояние поворота падающего в данный момент тетромино, чтобы оно точно ударялось о левую и правую стороны.

Кроме того, необходимо добавить функциональность, которая отображает счет, показывает следующую фигуру тетромино, которая упадет, и т. Д. Получите удовольствие от запуска и изучения этого кода!

С уважением,
Джим Уивер
JavaFX Script: динамические сценарии Java для многофункциональных интернет-приложений и приложений на стороне клиента.

Немедленная загрузка электронных книг (PDF) доступна на сайте книги Apress.