Jim has posted 66 posts at DZone. You can read more from them at their website. View Full User Profile

Let's Create a Tetris Game in Compiled JavaFX Script

02.22.2008
| 11418 views |
  • submit to reddit

I thought it would be fun to spend a few posts creating a Tetris game in compiled JavaFX Script together.  Today I made a rough start on it, and if you promise not to laugh, I'll show you the humble beginnings.  Here's a screenshot of its current status:

Tetris_1

In Tetris, there are several types of tetrominoes, each having a letter that it resembles.  The four buttons on the left represent four of these shapes.  When you press one of these buttons, the corresponding tetromino appears at the top and begins moving down the screen.  When you click the Rotate button, the tetromino rotates to the right, and the Left/Right buttons move the tetromino left and right, respectively.

The code is contained in four FX program files, and needs some refactoring already. :-)  Before showing you the code in its current state, I'd like to point out a couple of helpful things: 

  • As explained in the Spinning Wheel post, the Key Frame animation syntax that you see here will become much less verbose as the JavaFX Script compiler team continues to address animation. 
  • JavaFX Script programs should always be designed with the UI binding to a model.  In this program, the model is represented in one class named TetrisModel, located in the FX file of the same name.  You may find it helpful to take a look the Creating a Compiled JavaFX Script Program with Multiple FX Source Files post to see a Hello World style program that has more than one FX file.  Please notice the package statments in this Tetris program, as that influences where you need to put the source files and how you build them.
  • You can obtain the JavaFX Compiler by following the instructions in the Obtaining the OpenJFX Script Compiler post.

The Source Code (so far)

Here's the main program, named TetrisMain.fx, that declaratively expresses the UI, and starts things up:

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

 

I made the TetrisShape class a custom graphical component.  Therefore, it is a subclass of the CompositeNode class, and overrides the composeNode function.  Note: There is a typo in line 62 of the TetrisShape.fx listing.  "returnreturn" should read "return".  It will be corrected ASAP.

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

 

The TetrisShapeType class defines the tetromino types:

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

 

And finally, here's a model class, named 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++;
}
}
}

 

Compile and execute this example, and examine the code.  I'll get busy making it behave a little more like a Tetris game, and show you some progress in the next post.  Please feel free to get ahead of me, and make your own version!

Regards,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

Immediate eBook (PDF) download available at the book's Apress site

Published at DZone with permission of its author, Jim Weaver.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

John Denver replied on Sat, 2008/02/23 - 7:25am

Why SUN is wasting time with JavaFX script, Similar or better functionality you can find in Groovy builders and is also a JSR.

SUN doesn't learn it, everything comes from them is a failure, ejb->jsf->javafx.

 I hope they open the eyes and realize their mistakes they are doing before is to late again. I would like to said to SUN wipeout JavaFX and put effort on the things are already working as the java language(Java many years had problems in the client side) and also support more Groovy project it is getting great lately.

 2c. 

Jacek Furmankiewicz replied on Sat, 2008/02/23 - 9:24am

I have to admit thar Sidewinder is at least partially right. It would be better to support a language that already has some traction. instead of creating a brand new one from scratch.

 On the  bright side, most of the power of JavaFX comes from the Scenegraph project. so I am sure someone in Groovy will write a builder for it...

Rick Ross replied on Sat, 2008/02/23 - 10:17am in response to: John Denver

[quote=alpha512]Why SUN is wasting time with JavaFX script, Similar or better functionality you can find in Groovy builders and is also a JSR.[/quote]

While you certainly are not alone in your question, it completely baffles me that you feel it is reasonable to turn Jim Weaver's excellent "how-to" article into an ad hoc venue for a referendum on the wisdom of Sun's JavaFX push? I mean, seriously, give the man a break!

I'm happy enough to support the posting of a new thread that asks the above question poignantly and perhaps expands upon the issue. I will not, however, allow this "how-to" to get turned into a referendum on JavaFX itself. Please have enough respect for Jim's efforts to start another seaparate thread where your questions an comments along those lines can be both entirely on-topic and appropriate.

Thank you or understanding,
Rick Ross

Ronald Miura replied on Sat, 2008/02/23 - 11:32am

It doesn't work with Netbeans' JavaFX plugin.

- one can't initialize attributes at declaration acording to the documentation (you do that outside the class declaration)

- class com.sun.javafx.runtime.PointerFactory doesn't exist in javafx's jars

Is the current language spec different from the one the plugin uses?

Jim Weaver replied on Sat, 2008/02/23 - 12:27pm

Ronald,

This example uses compiled JavaFX Script, which is the "real" version of JavaFX Script.  The NetBeans plug-in supports interpreted JavaFX Script, which served as a prototype.  There is a NetBeans plug-in under development for compiled JavaFX Script, but until then, please follow the instructions in the links of this post to obtain the compiler and build this example.

By the way, there is a typo in line 62 of the TetrisShape.fx listing.  "returnreturn" should read "return".  It will be corrected ASAP.

If you have any further questions building this example, please post another comment.

Thanks,
Jim Weaver

Darren Martin replied on Fri, 2014/08/22 - 4:55am

Thanks to this, I would love to try this one as I already have knowledge about Java scripting.. Regards. Darren Martin/http://www.iphysicsgames.com/

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.