Статьи

Давайте создадим игру тетрис в скомпилированном скрипте JavaFX

Я подумал, что было бы весело потратить несколько постов на создание игры Tetris в скомпилированном скрипте JavaFX. Сегодня я начинал с грубого начала, и если вы пообещаете не смеяться, я покажу вам скромные начинания. Вот скриншот его текущего статуса:

Tetris_1

В тетрисе есть несколько типов тетромино , каждый из которых имеет букву, на которую он похож. Четыре кнопки слева представляют четыре из этих форм. Когда вы нажимаете одну из этих кнопок, соответствующее тетромино появляется вверху и начинает двигаться вниз по экрану. Когда вы нажимаете кнопку « Повернуть» , тетромино вращается вправо, а кнопки « Влево / Вправо» перемещают тетромино влево и вправо соответственно.

Код содержится в четырех файлах программы FX и уже нуждается в некотором рефакторинге. ? Прежде чем показывать вам код в его текущем состоянии, я хотел бы отметить пару полезных вещей: 

  • Как объясняется в статье « Spinning Wheel» , синтаксис анимации ключевого кадра, который вы здесь видите, станет намного менее подробным, так как команда компилятора JavaFX Script продолжает заниматься анимацией. 
  • Программы JavaFX Script всегда должны разрабатываться с привязкой пользовательского интерфейса к модели. В этой программе модель представлена ​​в одном классе с именем TetrisModel , расположенном в одноименном файле FX. Возможно, вам будет полезно взглянуть на статью « Создание скомпилированной скриптовой программы JavaFX с несколькими исходными файлами FX», чтобы увидеть программу в стиле Hello World, содержащую более одного файла FX. Обратите внимание на статистику пакетов в этой программе Tetris, так как она влияет на то, где вам нужно разместить исходные файлы и как вы их создаете.
  • Вы можете получить компилятор JavaFX, следуя инструкциям в разделе Получение компилятора скриптов OpenJFX .

Исходный код (пока)

Вот основная программа с именем 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.WHITE
content:
BorderPanel {
center:
canvas = Canvas {}
bottom:
FlowPanel {
content: [
Button {
text: "I"
action:
function() {
canvas.content = [];
insert
TetrisShape {
model: model
shapeType: TetrisShapeType.I
}
into canvas.content;
model.t.start();
}
},
Button {
text: "T"
action:
function() {
canvas.content = [];
insert
TetrisShape {
model: model
shapeType: TetrisShapeType.T
}
into canvas.content;
model.t.start();
}
},
Button {
text: "L"
action:
function() {
canvas.content = [];
insert
TetrisShape {
model: model
shapeType: TetrisShapeType.L
}
into canvas.content;
model.t.start();
}
},
Button {
text: "S"
action:
function() {
canvas.content = [];
insert
TetrisShape {
model: model
shapeType: TetrisShapeType.S
}
into canvas.content;
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 пользовательским графическим компонентом. Следовательно, он является подклассом класса CompositeNode и переопределяет функцию composeNodeПримечание. В строке 62 списка TetrisShape.fx приведена опечатка. «returnreturn» следует читать «return». Это будет исправлено как можно скорее.

/*
* 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 = 2;
private attribute squareColor;

public attribute model:TetrisModel;
public attribute shapeType:TetrisShapeType on replace {
if (shapeType == TetrisShapeType.I) {
squareLocs = [];
insert new Point(0, model.SQUARE_SIZE * 1) into squareLocs;
insert new Point(0, 0) into squareLocs;
insert new Point(0, model.SQUARE_SIZE * 2) into squareLocs;
insert new Point(0, model.SQUARE_SIZE * 3) into squareLocs;
squareColor = Color.RED;
}
else if (shapeType == TetrisShapeType.T) {
squareLocs = [];
insert new Point(model.SQUARE_SIZE * 1, 0) into squareLocs;
insert new Point(0, 0) into squareLocs;
insert new Point(model.SQUARE_SIZE * 2, 0) into squareLocs;
insert new Point(model.SQUARE_SIZE * 1, model.SQUARE_SIZE * 1) into squareLocs;
squareColor = Color.GREEN;
}
else if (shapeType == TetrisShapeType.L) {
squareLocs = [];
insert new Point(0, model.SQUARE_SIZE * 1) into squareLocs;
insert new Point(0, 0) into squareLocs;
insert new Point(0, model.SQUARE_SIZE * 2) into squareLocs;
insert new Point(model.SQUARE_SIZE * 1, model.SQUARE_SIZE * 2) into squareLocs;
squareColor = Color.MAGENTA;
}
else if (shapeType == TetrisShapeType.S) {
squareLocs = [];
insert new Point(model.SQUARE_SIZE * 1, 0) into squareLocs;
insert new Point(model.SQUARE_SIZE * 2, 0) into squareLocs;
insert new Point(0, model.SQUARE_SIZE * 1) into squareLocs;
insert new Point(model.SQUARE_SIZE * 1, model.SQUARE_SIZE * 1) into squareLocs;
squareColor = Color.CYAN;
}
}

private attribute squareLocs:Point[];

public function composeNode():Node {
return
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: [
for (squareLoc in squareLocs) {
Rect {
x: bind squareLoc.x
y: bind squareLoc.y
width: bind model.SQUARE_SIZE
height: bind model.SQUARE_SIZE
fill: bind squareColor
stroke: squareOutlineColor
strokeWidth: squareOutlineWidth
}
}
]
};
}
}

 

Класс TetrisShapeType определяет типы tetromino:

/*
* 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_ui;

import javafx.ui.*;

class TetrisShapeType {
public attribute id: Integer;
public attribute name: String;

public static attribute O =
TetrisShapeType {id: 0, name: "O"};
public static attribute I =
TetrisShapeType {id: 1, name: "I"};
public static attribute T =
TetrisShapeType {id: 2, name: "T"};
public static attribute L =
TetrisShapeType {id: 3, name: "L"};
public static attribute S =
TetrisShapeType {id: 4, name: "S"};
}

 

И, наконец, вот модельный класс с именем TetrisModel:

/*
* 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;

public class TetrisModel {
public static attribute SQUARE_SIZE = 20;

public attribute a:Integer;
private attribute pf = PointerFactory {};
private attribute bpa = bind pf.make(a);
private attribute pa = bpa.unwrap();
private attribute interpolate = NumberValue.LINEAR;
public attribute t =
Timeline {
keyFrames: [
KeyFrame {
keyTime: 0s;
keyValues:
NumberValue {
target: pa;
value: 0;
interpolate: bind interpolate
}
},
KeyFrame {
keyTime: 20s;
keyValues:
NumberValue {
target: pa;
value: 370
interpolate: bind interpolate
}
}
]
};
public attribute tetrominoAngle:Number;
public attribute tetrominoHorzPos:Number = 10;

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

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

public function moveRight():Void {
if (tetrominoHorzPos < 20) { //TODO:Replace 10 with a calculated number
tetrominoHorzPos++;
}
}
}

 

Скомпилируйте и выполните этот пример и изучите код. Я буду занят, заставляя его вести себя немного больше как игра в тетрис, и покажу вам некоторый прогресс в следующем посте. Пожалуйста, не стесняйтесь опередить меня и сделать свою собственную версию!

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

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