JavaFX Games

August 26, 2008

Creating Games with JavaFX: Silveira is Having Too Much Fun :-)

I've featured Silveira Neto's JavaFX work (play?) in a couple of recent posts, and it appears that he's had gaming on his mind lately.  Silveira is a CS student, CEJUG member and Sun Campus Ambassador at Federal University of Ceará.

Netoblackdotgame

His latest creation, shown above, is a game in which the player tries to vacuum up the dirty clouds with a sphere-shaped cleaner.  It demonstrates several techniques in JavaFX, such as creating a side-scrolling game, capturing keyboard input, and ascertaining when a graphical object intersects with a pixel in another graphical object.  You can see more information, including the source code and a YouTube video of the game being played, from Silveira's blog post.

In a prior post, Silveira discusses how to create a side-scrolling game in JavaFX, showing the classes that he's building to support it.

Have fun!

Jim Weaver
JavaFXpert.com blog

May 15, 2008

Video of Cool JavaFX Demos at JavaOne 2008

Note from Jim Weaver: Since this was written, an updated version of JavaFX has been released.  You can get the new version of JavaFX at at JavaFX.com.

During the Tuesday afternoon general session at JavaOne 2008, there were some cool JavaFX-related announcements and demos.  Grab some popcorn and watch this video of a portion of the session, which includes:

  • An announcement that ON2 and Sun now have a relationship that will make the ON2 video codec available as part of the Java and JavaFX platforms.  This will allow video to be converted to a common format and played on any machine or device that supports Java.
  • The JavaFX version of the parleys.com website, which is the Belgium Java Users Group's (JUG) site for sharing video presentations about Java.  Stephan Janssen, who leads the JUG as well as the JavaPolis conference in Belgium, presented the site with Jo Voordeckers, the JavaFX developer of the site.  Stephan also announced that any JUG can post presentation videos to the new parleys.com site, which up to now has received most of its content from the JavaPolis conferences.
  • An early peek at a design tool for JavaFX in which graphics can be created in Photoshop and Illustrator, and exported for use by a JavaFX program.  This tool is currently named Distiller, and will support the collaboration of graphic designers and software developers.  As a side note, ReportMill is also making some great progress on their JavaFX designer tool.
  • A demo of a three-dimensional multi-player game named MoonTank developed in JavaFX.  One of the demonstrators is Chris Oliver, the founder of JavaFX.

Enjoy the video.  This JavaFX stuff is getting exciting!
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

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

May 01, 2008

Watch for Falling Blocks: Take TetrisJFX for a Spin!

You may know that I've been progressively building a Tetris game in JavaFX on this blog, the most recent post being Game Over: Improving upon the Compiled JavaFX Tetris Program.  In each post I've shown you the code and pointed out some highlights.  Since then I've added some finishing touches, and would be honored if you've try it out and give me (kind) feedback for improving it further.  The screenshot below shows what TetrisJFX should look like when you click this Java Web Start link.  By the way, you'll need the JRE (Java Runtime Environment) 1.5 or later.  Also, please keep in mind that the JavaFX Script JAR files will be included with the JRE at some point. Until then please understand that when you click this link those JAR files will be downloaded as well, causing a bit of a delay.

Tetrisjfx_w_image_buttons

In addition to clicking the image buttons at the bottom of the game, you can use keystrokes (hover over the images to see tooltips that tell you what the keystrokes are).  In a future version, the arrow keys will be used for game control.  I also plan to provide the ability to cause the current tetromino to fall faster.  By the way, the tetrominoes fall progressively faster as your score increases, so be warned. :-)

170x93_speaker_v4_4 If you have any questions or input for improvements, please post a comment.  Also, if you'll be at JavaOne 2008, please attend my JavaFX Script Programming Language Tutorial session and introduce yourself afterward!

 

Have fun!
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

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

March 27, 2008

Create a Yahtzee Dice Roller and Scorer in Compiled JavaFX Script

I'm still at The Server Side Java Symposium in Las Vegas, and I think that walking by all these slot machines has inspired this blog post. Today's example builds on the program in the Roll the Dice post in order to create a Yahtzee dice roller and scorer.  Each time you click the Roll button, the five dice assume random values, and the combination of values is scored according to the possible categories in the bottom portion of a Yahtzee scoring sheet.  Here's a screenshot:

Yahtzee_2

In addition to the Dice.fx and PipPlacement.fx files from the Roll the Dice post, this program consists of the following source code files.

YahtzeeMain.fx

/*
*  YahtzeeMain.fx -
*  A compiled JavaFX program that demonstrates creating custom
*  components with CompositeNode and evaluates Yahtzee dice rolls.
*
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a JavaFX Script example.
*/

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

Frame {
  var model = YahtzeeModel {}
  width: 510
  height: 400
  title: "Roll Dice and Evaluate Yahtzee Combinations"
  background: Color.WHITE
  content:
    BorderPanel {
      center:
        Box {
          var evalFont =
            Font {
              size: 20
            }
          orientation: Orientation.VERTICAL
          content: [
            Canvas {
              content:
                for (diceNum in [0 .. model.numDice - 1]) {
                  model.newDice =
                  Dice {
                    x: diceNum * 100 + 10
                    y: 10
                    width: 80
                    height: 80
                    faceColor: Color.RED
                    pipColor: Color.WHITE
                  }
                }
            },
            GroupPanel {
              var fiveOfKindRow = Row { alignment: Alignment.BASELINE }
              var largeStraightRow = Row { alignment: Alignment.BASELINE }
              var smallStraightRow = Row { alignment: Alignment.BASELINE }
              var fullHouseRow = Row { alignment: Alignment.BASELINE }
              var fourOfKindRow = Row { alignment: Alignment.BASELINE }
              var threeOfKindRow = Row { alignment: Alignment.BASELINE }
              var chanceRow = Row { alignment: Alignment.BASELINE }

              var labelsColumn = Column {
                alignment: Alignment.TRAILING
              }
              var fieldsColumn = Column {
                alignment: Alignment.LEADING
              }
              rows: [
                fiveOfKindRow,
                largeStraightRow,
                smallStraightRow,
                fullHouseRow,
                fourOfKindRow,
                threeOfKindRow,
                chanceRow
              ]
              columns: [
                labelsColumn,
                fieldsColumn
              ]
              content: [
                SimpleLabel {
                  font: evalFont
                  row: fiveOfKindRow
                  column: labelsColumn
                  text: "Five of a Kind (Yahtzee):"
                },
                SimpleLabel {
                  font: evalFont
                  row: fiveOfKindRow
                  column: fieldsColumn
                  text: bind
                    if (model.fiveOfKind)
                      "{model.fiveOfKindScore}"
                    else "N/A"
                },
               
                SimpleLabel {
                  font: evalFont
                  row: largeStraightRow
                  column: labelsColumn
                  text: "Large Straight:"
                },
                SimpleLabel {
                  font: evalFont
                  row: largeStraightRow
                  column: fieldsColumn
                  text: bind
                    if (model.largeStraight)
                      "{model.largeStraightScore}"
                    else "N/A"
                },
               
                SimpleLabel {
                  font: evalFont
                  row: smallStraightRow
                  column: labelsColumn
                  text: "Small Straight:"
                },
                SimpleLabel {
                  font: evalFont
                  row: smallStraightRow
                  column: fieldsColumn
                  text: bind
                    if (model.smallStraight)
                      "{model.smallStraightScore}"
                    else "N/A"
                },
               
                SimpleLabel {
                  font: evalFont
                  row: fullHouseRow
                  column: labelsColumn
                  text: "Full House:"
                },
                SimpleLabel {
                  font: evalFont
                  row: fullHouseRow
                  column: fieldsColumn
                  text: bind
                    if (model.fullHouse)
                      "{model.fullHouseScore}"
                    else "N/A"
                },
               
                SimpleLabel {
                  font: evalFont
                  row: fourOfKindRow
                  column: labelsColumn
                  text: "Four of a Kind:"
                },
                SimpleLabel {
                  font: evalFont
                  row: fourOfKindRow
                  column: fieldsColumn
                  text: bind
                    if (model.fourOfKind)
                      "{model.sumOfDiceValues}"
                    else "N/A"
                },
               
                SimpleLabel {
                  font: evalFont
                  row: threeOfKindRow
                  column: labelsColumn
                  text: "Three of a Kind:"
                },
                SimpleLabel {
                  font: evalFont
                  row: threeOfKindRow
                  column: fieldsColumn
                  text: bind
                    if (model.threeOfKind)
                      "{model.sumOfDiceValues}"
                    else "N/A"
                },
               
                SimpleLabel {
                  font: evalFont
                  row: chanceRow
                  column: labelsColumn
                  text: "Chance:"
                },
                SimpleLabel {
                  font: evalFont
                  row: chanceRow
                  column: fieldsColumn
                  text: bind "{model.sumOfDiceValues}"
                },
              ]
            },
          ]
        } 
      bottom:
        FlowPanel {
          content:
            Button {
              text: "Roll"
              defaultButton: true
              action:
                function():Void {
                  model.roll();
                }
            }
        }
    }
  visible: true
  onClose:
    function():Void {
      System.exit(0);
    }
}

YahtzeeModel.fx

/*
*  YahtzeeModel.fx -
*  The model behind the Yahtzee dice roll and combination evaluation
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a JavaFX Script example.
*/
import javafx.lang.Sequences;
import java.lang.System;

class YahtzeeModel {
  attribute numDice:Integer = 5;
  attribute diceDistribution:Integer[];
  attribute newDice:Dice on replace {
    insert newDice into dice;
  }
  attribute dice:Dice[];
 
  attribute fiveOfKind:Boolean;
  attribute largeStraight:Boolean;
  attribute smallStraight:Boolean;
  attribute fullHouse:Boolean;
  attribute fourOfKind:Boolean;
  attribute threeOfKind:Boolean;

  attribute fiveOfKindScore:Integer = 50;
  attribute largeStraightScore:Integer = 40;
  attribute smallStraightScore:Integer = 30;
  attribute fullHouseScore:Integer = 25;

  attribute sumOfDiceValues:Integer = 0;
 
  function roll():Void {
    for (die in dice) {
      die.roll();
    }
    evalYahtzeeCombos();
  }
 
  function evalYahtzeeCombos() {
    var values =
    for (val in dice) {
      val.value;
    }

    var maxVal:Integer = Sequences.max(values, null) as Integer;
    var minVal:Integer = Sequences.min(values, null) as Integer;

    // Create a sequence that contains the distribution of values
    // and Calclulate the sum of the dice values
    diceDistribution =
      for (i in [1 .. 6]) 0;

    sumOfDiceValues = 0;
   
    for (val in values) {
      diceDistribution[val - 1]++;
      sumOfDiceValues += val;
    }
   
    // Determine if five-of-a-kind
    fiveOfKind =
      ((for (occurance in diceDistribution
           where occurance >= 5) occurance) <> []);       
   
    // Determine if four-of-a-kind
    fourOfKind =
      ((for (occurance in diceDistribution
           where occurance >= 4) occurance) <> []);
   
    // Determine if three-of-a-kind
    threeOfKind =
      sizeof (for (occurance in diceDistribution
              where occurance >= 3) occurance) > 0;      

    // Determine if full house
    fullHouse =
      sizeof (for (occurance in diceDistribution
              where occurance == 3) occurance) > 0 and      
      sizeof (for (occurance in diceDistribution
              where occurance == 2) occurance) > 0;   

    // Determine if large straight
    largeStraight =
      sizeof (for (occurance in diceDistribution
              where occurance > 1) occurance) == 0 and
      (maxVal - minVal == 4);
             
    // Determine if small straight
    smallStraight =
      sizeof (for (occurance in diceDistribution
              where occurance == 2) occurance) == 1 and
      (maxVal - minVal == 3);
  }
}

A JavaFX Script concept that we haven't covered yet is the set of functions available in the new javafx.lang.Sequences package.  These functions enable you to perform operations (such as sorting) on a sequence. In this program I'm using the max and min functions of that class to find the largest and smallest value in a dice roll.

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

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

March 22, 2008

Roll the Dice - A Compiled JavaFX Script Example

I'm on my way to Vegas to speak at The Server Side Java Symposium, so in keeping with that theme I wanted to create a simple dice program as today's example.  When you click the Roll button, random values appear on the dice.  Here's a screenshot of this application:

Dice

GameMain.fx

/*
 *  GameMain.fx - 
 *  A compiled JavaFX program that demonstrates creating custom
 *  components with CompositeNode
 * 
 *
 *  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
 *  to serve as a JavaFX Script example.
 */

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

Frame {
  var model = GameModel {}
  width: 210
  height: 170
  title: "Dice"
  background: Color.WHITE
  content: 
    BorderPanel {
      center:
        Canvas {
          content:
            for (diceNum in [0 .. model.numDice - 1]) {
              model.newDice =
              Dice {
                x: diceNum * 100 + 10
                y: 10
                width: 80
                height: 80
                faceColor: Color.RED
                pipColor: Color.WHITE
              }
            }
        }
      bottom:
        FlowPanel {
          content:
            Button {
              text: "Roll"
              defaultButton: true
              action:
                function():Void {
                  model.roll();
                }
            }
        }
    } 
  visible: true
  onClose:
    function():Void {
      System.exit(0);
    }
}

Dice.fx

/*
 *  Dice.fx - 
 *  A compiled JavaFX program that demonstrates creating custom
 *  components with CompositeNode
 * 
 *
 *  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
 *  to serve as a JavaFX Script example.
 */

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

public class Dice extends CompositeNode {
  public attribute value:Integer = 5;
  public attribute x:Integer;
  public attribute y:Integer;
  public attribute width:Integer;
  public attribute height:Integer;
  public attribute faceColor:Color;
  public attribute pipColor:Color;
  
  attribute pipPlace:PipPlacement[];
  
  postinit {
    insert
      PipPlacement {
        pipLocsX: [
          .5, 0, 0, 0, 0, 0
        ]
        pipLocsY: [
          .5, 0, 0, 0, 0, 0
        ]
      }
      into pipPlace;
    insert
      PipPlacement {
        pipLocsX: [
          0.3, 0.7, 0, 0, 0, 0
        ]
        pipLocsY: [
          0.7, 0.3, 0, 0, 0, 0
        ]
      }
      into pipPlace;
    insert
      PipPlacement {
        pipLocsX: [
          0.2, 0.5, 0.8, 0, 0, 0
        ]
        pipLocsY: [
          0.8, 0.5, 0.2, 0, 0, 0
        ]
      }
      into pipPlace;
    insert
      PipPlacement {
        pipLocsX: [
          0.25, 0.25, 0.75, 0.75, 0, 0
        ]
        pipLocsY: [
          0.25, 0.75, 0.25, 0.75, 0, 0
        ]
      }
      into pipPlace;
    insert
      PipPlacement {
        pipLocsX: [
          0.2, 0.5, 0.8, 0.8, 0.2, 0
        ]
        pipLocsY: [
          0.8, 0.5, 0.2, 0.8, 0.2, 0
        ]
      }
      into pipPlace;
    insert
      PipPlacement {
        pipLocsX: [
          0.3, 0.3, 0.3, 0.7, 0.7, 0.7
        ]
        pipLocsY: [
          0.8, 0.5, 0.2, 0.8, 0.5, 0.2
        ]
      }
      into pipPlace;
  } 
    
  public function roll():Void {
    value = (Math.random() * 6 + 1) as Integer;
  }
  
  public function composeNode():Node {
    Group {
      transform: bind [
        Translate.translate(x, y)
      ]
      content: bind [
        Rect {
          x: 0
          y: 0
          width: this.width;
          height: this.height;
          fill: faceColor
        },
        for (pipIdx in [0 .. value-1]) {
          Circle {
            cx: bind pipPlace[value - 1].pipLocsX[pipIdx] * width
            cy: bind pipPlace[value - 1].pipLocsY[pipIdx] * height
            radius: width * .1
            fill: pipColor
          }
        }
      ]  
    }
  }
}

GameModel.fx

/*
 *  GameModel.fx - 
 *  The model behind the dice game
 *
 *  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
 *  to serve as a JavaFX Script example.
 */

class GameModel {
  attribute numDice:Integer = 2;
  attribute newDice:Dice on replace {
    insert newDice into dice;
  }
  attribute dice:Dice[];
  
  function roll():Void {
    for (die in dice) {
      die.roll();
    }
  }
}


PipPlacement.fx

/*
 *  PipPlacement.fx - 
 *  The placement of the pips on a dice
 *
 *  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
 *  to serve as a JavaFX Script example.
 */

public class PipPlacement {
  public attribute pipLocsX:Number[];
  public attribute pipLocsY:Number[];
}

Good luck!
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

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

February 27, 2008

Part Three of Creating a Tetris Program in Compiled JavaFX Script

Doh!  I accidentally hit the Save button with the Publish option selected, before this post was ready (again!!!).  Article coming soon, and sorry for the inconvenience.  Meanwhile, since it is the third part of a series on building a Tetris game in compiled JavaFX Script, you may want to check out the first two parts:

1) Let's Create a Tetris Game in Compiled JavaFX Script

2) Creating a Tetris Program (Part Two) in Compiled JavaFX Script

By the way, I'll be speaking on JavaFX Script at Sun Tech Days in Sydney, Australia, on March 4 - 6, 2008.  If you're in the area, please check it out and say hi!

Suntechdaysaus

Regards,

Jim Weaver


February 22, 2008

Let's Create a Tetris Game in Compiled JavaFX Script

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:

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

JavaFX Script Boot Camp Registration Now Open!

Javafxpertbootcamp300x250bannerindy

I will be offering a 2.5 day JavaFX Script day "boot camp" on Wednesday, April 9 through Friday, April 11, 2008 (ending at noon) in Indianapolis, Indiana.  By the way, I'm scouting out the next location, so if you have suggestions for where I should offer a future BootCamp please let me know!

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