JavaFX Online Game Prototype: Wish Tree (2)

Posted: under JavaFX Games, Wish Tree.
Tags: , , ,

Last week, I introduced a JavaFX prototype of an online game. Readers have been leaving all kinds of wishes on my Wish Tree. It is interesting to see there are more than 20 lucky stars hanging on the Wish Tree now. If you have not tried the program yet, you are encouraged to click on the screenshot on the right to take a look or make an online wish.

Let’s come back to the technical part of the program again. In part 1, we introduced the architecture of this online application and the JavaFX rich client. In this post, I will explain the details of the data exchange between the client and the server. For your convenience, I bring back the architectural figure as shown below:

click to start wish tree online program


Like any client/server system, the server provides data services to the client. Based on the requirement of the Wish Tree, we define two services provided by the server: getAllStars and saveStar.

Service name Parameters Notes
getAllStars none  
saveStar location, name, content, time, color, email for create, returns id
id, content, email for update


The first one getAllStars is for the client to retrieve the data of all the stars(wishes) in the database. The data of a star contains its (x,y) coordinates on the screen and its color. The wish information associated with a star is also returned. Such information includes the wisher’s name, email address, content of the wish and the time when the wish was made. Based on this data, the client restores the stars at the exact location where people hung them on the tree. In this way, every time you start the wish tree client, you always see your star staying at the same place.


The second service is saveStar. When a user creates a star on the tree, the data of the star is sent to the server and saved into the database. The id of the star is returned to the client for later update purpose. The id is nothing more than a unique identifier of each wish stored in the database. You can think of it as the primary key of a database table. We will re-use this service to update a wish. This is due to the fact that most of the handling logic is the same for create and update action. The service first checks whether the client has sent in an id as a request parameter. If id is included, then it is a request of update; otherwise, it is a request of create.


The sequence diagram of these services are depicted as below:




Now that we have a high level design of services, we can elaborate on the protocol between the client and the server. Since our application is stateless, a RESTful style of request and response should be good enough. Therefore, we use HTTP GET request for the getAllStars service and POST for the saveStar service. The format of the response data could be HTML, XML or JSON. Because JavaFX provides a PullParser class which can parse XML or JSON data, we choose JSON in our application. Below is a sample of the JSON response of the getAllStars service:

{
 "Star": {
   "id" : "102",
   "name" : "John Smith",
   "content" : "My first wish",
   "location" : "388.0_248.0",
   "email" : "j.smith@example.com",
   "time": "2009-08-21 16:38:37",
   "color": 3
 },
 "Star": {
   "id" : "108",
   "name" : "Homer Simpson",
   "content" : "This is another wish.",
   "location" : "188.0_228.0",
   "email" : "homer@simpson.com",
   "time": "2009-08-28 18:28:48",
   "color": 4
 }
}


Once we finalize the protocol between the client and the server, we can start to implement the code on both sides. In a large scale project, this usually results in two teams concurrently working on the code of the client and the server respectively. At the client side, the code to parse the above JSON data is in Main.fx :

var parser = PullParser {
    documentType: PullParser.JSON;

    onEvent: function(event: Event) {
      var content: String;
      var name: String;
      var id: String;
      var email: String;
      var color: Integer;
      var time: String;
      var location: String;

      // parse the JSON data and populate the object
      if(event.type == PullParser.END_VALUE) {

        if(event.name == "name") {
          star.name = event.text;
        }
        else if ( event.name == "content" ){
          star.wish = event.text;
        }
        else if ( event.name == "id" ){
          star.id = event.text;
         }
        else if ( event.name == "color" ){
          star.whichColor = event.integerValue;
        }
        else if ( event.name == "time" ){
          star.time = event.text;
        }
        else if ( event.name == "location" ) {
          var pos = event.text.indexOf( "_" );
          var x = event.text.substring(0,pos);
          var y = event.text.substring( pos+1 );

          star.translateX = Float.parseFloat(x);
          star.translateY = Float.parseFloat(y);
        }
        else if ( event.name == "Star" ){
          star.changeStatus();
          star.onMousePressed = handleClick;

          insert star after stage.scene.content[currentIndex++];
          star = Star {};
        }
      }
    }
  }


The JavaFX client uses the class HttpRequest to communicate with the server. HttpRequest allows us to use GET and POST method to send requests to the server. Usually, we only need to override some methods of this class. As shown in ServerConnector.fx below, we rewrote the onOutput and onInput functions to invoke the saveStar service from the server.

/*
 * ServerConnector.fx
 * @author Henry Zhang   http://www.javafxgame.com
 */
package wishtree;
import java.net.URLEncoder;
import java.io.*;
import javafx.io.http.*;

var baseURL = "http://localhost:8888/wishtree/";

public var serverURL1 = "{baseURL}getallstar.php";
var serverURL2 = "{baseURL}savestar.php";

public class ServerConnector extends HttpRequest {
  var star : Star = null;
  var paramString : String ;

  override var onOutput = function( os: java.io.OutputStream): Void {
        try {
            os.write(paramString.getBytes());
        } finally {
            os.close();
        }
    };

  override var onInput = function(is: java.io.InputStream) {
     try {
           var br = new BufferedReader( new InputStreamReader( is ) );
           var line: String;

           while ( ( line=br.readLine() ) != null )
           {
             if ( (star != null) and (star.id == "")  )
                star.id = line;
            }
        } finally {
            is.close();
        }
  };

public function saveStar( s: Star ) {
  if ( s.id.length() == 0 )
    star = s;

  paramString = encode("name", s.name);
  paramString += "&{encode("id", s.id)}&" ;
  paramString += encode("location", "{s.translateX}_{s.translateY}" );
  paramString += "&{encode("content", s.wish )}";
  paramString += "&{encode("email", s.email)}" ;
  paramString += "&{encode("time", s.time)}&" ;
  paramString += encode("color", "{s.whichColor}" );

  headers = [
        HttpHeader {
            name: HttpHeader.CONTENT_LENGTH;
            value: "{paramString.getBytes().length}";
        }
       ];

  method = POST;
  location = serverURL2;
  start();
 }
}

function encode( k: String, v:String): String {
    var result = URLEncoder.encode( k, "UTF-8");
    var value = URLEncoder.encode( v, "UTF-8");
    result += "={value}"
}


On the server side, the PHP code is quite straightforward. Basically, it gets the parameters from the request and convert them into SQL statement for MySQL database. After getting the data from MySQL, it returns the data in JSON format.


Finally, I would discuss how to tie up all the components together. First, we build the JavaFX client in NetBeans. The products are a jar file and a JNLP file. We modify attributes in the JNLP file to match the deployment environment. The most important is to set the correct value of the “codebase” attribute. Then we upload the jar and JNLP files to a folder on a web server. This folder should match the one specified in the codebase attribute. The next step is to deploy the PHP scripts, for simplicity, we just upload the scripts under the same folder where we put the jar. In the scripts, be sure to change the user name and password of the database. In your MySQL database, create a table to store the wish data. The last thing would be putting a link in a web page which contains the web start link to your JNLP file.


For source code, you can get it from the download page of this site. As shown in the architectural figure, there are codes of server side(Mysql and PHP) and client side(JavaFX), respectively. Be sure to read the file README.txt before you try the code.


If you have any questions, please leave comments.



Other JavaFX articles:

JavaFX Online Game Prototype: Wish Tree (1)

JavaFX Menu       Java Code Calls JavaFX APIs


Comments (3) Aug 28 2009

JavaFX Online Game Prototype: Wish Tree (1)

Posted: under JavaFX Technology, Wish Tree.
Tags: , , , ,

 

I finished a JavaFX prototype of an online game, it is called “Wish Tree”. Strictly speaking, this prototype is not a game. Instead, it is an online multi-user application of fun (esp. for kids). However, it embodys all the essential elements of an online game: a JavaFX rich client for user interaction, a server handling communication among multiple clients, and a database for data persistence. Though it is still relatively simple, it lays a foundation of building an online multi-user game. I am going to demostrate in two posts how to design and write such an application in JavaFX, PHP and MySQL .

 

  Not long ago, I wrote a JavaFX program for fun. You may have heard of the song “Tie a Yellow Ribon on an Old Oak Tree”, my program was called “Hang a Lucky Star on a Wish Tree“. It allowed people hang colorful stars on a tree to keep their wishes. These wishes could be about absolutely anything. Usually it was used by children, who might wish for exciting birthday presents or good results at school, but there were often stars hung by adults wishing to do well in a game of online bingo or hoping to be successful in a job interview. The program appealed to people from all walks of life that were keen to share their wishes for the future. The program was running as a standalone application. Recently, I added in some code to make it an online version. People all over the world now can share their wishes on the same tree. 

This online version is a true RIA(Rich Internet Application) now. After a person leaves a lucky star on the tree, he/she can see the star next time she/he runs the program. People can see others’ wishes as well ( sorry, no privacy for now ^_^ ). However, an email address is needed to modify an existing wish.

 

The main architecture of the whole system is depicted in the below figure. First, the program gets started from a browser by Java Web Start technology. Then the program connects back to the server to obtain data and draws the wish tree hanging with stars. The server component retrieves data from the database and returns to the client. If the client makes a new wish, the data of the wish is sent to the server for persistence.

 

The requirements of the system are listed below:

 

1) On the client side, we need a browser that supports JavaFX 1.2. Basically, on our client machine we need JRE 1.5 +, or better with JRE 1.6 U13 later.

2) On the server side, we need a web server that supports PHP and MySQL. We can choose the popular “LAMP” stack (Linux, Apache web server, MySQL and PHP), but the two required things are ‘MySQL’ and ‘PHP’ only. In fact, in my development environment, I use Windows XP, Sun Java Web Server 7.0, MySQL and PHP plugin for JWS.

 

Before we get into any technical details, you can click on the below screenshot to start the JavaFX
client to see how it works. You can click on existing stars to see others’ wishes, or click on the tree
to leave your wish. Be sure to leave your email address so that you can modify your wish later. After you save your wish, you can check your wish next time you start the program. For testing purpose, please do not leave more than 2 wishes(stars).
click to start wish tree online program

 

Since this is a typical client/server application, let’s look at the JavaFX client first. A GUI is presented to the user for interaction. The Main.fx contains the main Windows(a Stage instance) of the application. We put in some images of trees and clouds on the stage. To make them look more realistic, the clouds are applied with the GaussianBlur effect.

 

The Bubble.fx defines a Bubble class which draws a bubble rising from the bottom of the stage. Animation is used to let the bubble eventually vanish into the tree. The Main class has a few instances of Bubble to create a boiling effect.

 

When the tree is clicked, a star is shown and a dialog is popup for the user to enter a wish. The
Star is a subclass of the Polyon class. It has a few pre-defined color schemes. The part of drawing a star is achieved by the following few lines, a little bit of geometry knowledge here:

def r1 : Double = 15; def r2 : Double = r1 / 1.6; var r = [r1, r2]; … … points = for ( i in [0..9] ) [ r[i mod 2] * cos( toRadians(i*36) ), r[i mod 2] * sin( toRadians(i*36) ) ];

 

The Dialog is a subclass of CustomNode which contains a few buttons and text fields. When the “OK” button of the dialog is pressed, the user’s wish is saved on the server. The user can also click on a star that’s already on the tree to view its content. If the user is the owner of the wish( based on the email address), the user can modify the content of the wish and then save it.

 

Finally, the class ServerConnector uses HttpRequest to communicate with the server and store wishes in the server’s database. We will walk through the server code next week and illustrate the data interaction between the server and the client.

 

If you are interested in the source code, you can visit the download page of this site. As shown in the architectural figure, there are source code of server side(Mysql and PHP) and client side(JavaFX), respectively. Be sure to read the file README.txt before you try the code.

 

If you have any questions, please leave your comments.

 

Next Article: JavaFX Online Game Prototype: Wish Tree (2)
 

 
Other articles on JavaFX:

JavaFX Scene in Swing       JavaFX API for Java

JavaFX Menu       How Java Code Calls JavaFX APIs ?

Review on Book “Essential JavaFX”

Comments (3) Aug 16 2009

How to Write Pac-Man Game in JavaFX(5)

Posted: under JavaFX Technology, pac-man.
Tags: , , ,

Yesterday, my last article of a series, “Writing the Pac-Man Game in JavaFX Part 5“, had been published on insiderRIA.com. This final article detailed the chasing algorithms of the ghosts. I think it is probably one of the most interesting things in the code.


When writing the game, there are a few points we need to consider before designing an algorithm of the ghosts, such as effectiveness, randomness, simplicity. You can refer to the article about considerations on these aspects. An excerpt from the article is listed below in blue text. It discussed the choice of a proper algorithm. This algorithm not only serves as the chasing logic, it can also control the escaping behavior of the ghosts.


. . . . . .
After some thinking, I found that the distance between a ghost and the Pac-Man is a good ranking metric. The shorter the distance is, the higher the score is given to a particular choice. The advantages of using the distance as a metric are obvious. It is very simple and can be caculated easily. Besides, this algorithm makes a ghost move in the direction that has the shortest distance to thePac-Man. To illustrate this algorithm, let’s look at the below figure.

In the figure, the ghost Blinky is moving into an intersection from the right to the left. When it reaches the intersection, it has three possible choices of its next movement: to go up, to go down and to continue heading left. Going down is not a valid move because it hits the border of the maze. So we need to compare the other two options. The below table shows the computation of the distance of the two possible moves:

Choice X distance Y distance Total
Intersection 3 10 13
Up 3 9 12
Left 4 10 14

As shown in the table, the distance from the intersection to the Pac-Man character is 13 (The distance between two adjacent dots is 1). If Blinky goes up, the distance is reduced to 12. If it heads left, the distance becomes 14. Therefore, going up seems a better choice for Blinky. In this way, Blinky should be able to get closer and closer to the Pac-Man and eventually catches him.


Of course, this simple algorithm does not take into consideration for the walls in the maze. For this reason, sometimes the calculated score does not in fact represent the shortest path. However, this inaccuracy makes the ghosts appear “stupid” in the game, which is the randomness we want to achieve in the behavior. So we are going to implement it in our code. We rewrite the class MoveDecision. When the function evaluate() calculates a score, it takes in two arguments: the reference to Pac-Man instance and whether the ghost is in a hollow state. The variable distance is used to compute the score. If the ghost is going after the Pac-Man character, the score is 500-distance, which means a shorter distance yields a higher score. If the Pac-Man is hunting the ghosts(when they are hollow), the score is caculated as 500+distance. This makes the ghosts running away from the Pac-Man.
. . . . . .


Now that all the articles had been published and I hope you enjoyed reading them. The game was originally written in JavaFX 1.0, and was compatible with JavaFX 1.1. Because multi-inheritance has been removed in JavaFX 1.2, I made some minor changes to the code. The abstract class MovingObject had been changed to mixin class. The code for JavaFX 1.2 can be download from JavaFX Game Download Page.


You can now click on the below image to play the completed Pac-Man game, it is based on the newly released JavaFX 1.2 . With the improved performance, the game runs very smoothly.


click to run

click to run

Related Articles:

Develop Games in JavaFX

JavaFX MineSweeper Demo Game

JavaFX Demo Game: LinkUP

WidgetFX Game Widgets: Pac-Man

My First JavaFX Game Demo

JavaFX Discussion Blogs

JavaFX Game Article

The Featured Articles on insideRIA.com:

May 14, 2009: Writing the Pac-Man Game in JavaFX - Part 1

May 21, 2009: Writing the Pac-Man Game in JavaFX - Part 2

May 28, 2009: Writing the Pac-Man Game in JavaFX - Part 3

June 4, 2009: Writing the Pac-Man Game in JavaFX - Part 4

June 11, 2009:Writing the Pac-Man Game in JavaFX - Part 5

Comments (0) Jun 13 2009

How to Write a Pac-Man Game in JavaFX (4)

Posted: under pac-man.
Tags: , , ,

My latest article of the series, “Writing the Pac-Man Game in JavaFX Part 4“, was out on June 4.


In this article, the interaction between Pac-Man character and the ghosts was introduced in detail. The article showed how to determine whether the Pac-man character and a ghost touched each other. A simplified formla was applied to achieve better performance. When Pac-man touches a ghost, he can eat it if the ghost is hollow. The ghost then is thrown back to the cage again. Otherwise, the ghost eats the Pac-man. An animation of showing a dying Pac-Man appears at this moment. This is in fact a shrinking circle(Arc) which disappears at the end of the animation. The animation is accomplished by the DyingPacMan class.


The below figure depicts the animation process of the dying Pac-man character.

shriking pac-man


The code of DyingPacMan.fx is listed below:

/*
 * DyingPacMan.fx
 *
 * Created on 2009-2-6, 17:52:42
 */
package pacman;

import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.shape.Arc;

/**
 * @author Henry Zhang
 */
public class DyingPacMan extends Arc {
   public var maze : Maze;

   var timeline = Timeline {
      repeatCount: 1
      keyFrames: [
        KeyFrame {
           time: 600ms
           action: function() {
             // hide the pacMan character and ghosts before the animation
              maze.pacMan.visible = false;

              for ( g in maze.ghosts ) {
                 g.hide();
               }

              visible = true;
            }
           values: [ startAngle => 90, length=>360 ];
         },
        KeyFrame {
           time: 1800ms
           action: function() {
              visible = false;
            }
           values: [ startAngle => 270 tween Interpolator.LINEAR,
                     length => 0 tween Interpolator.LINEAR ]
         },
      ]
    }

   public function startAnimation(x: Number, y: Number) : Void {
      startAngle = 90;
      centerX = x;
      centerY = y;

      timeline.play();
    }
}

Interpolations of two variables, startAngle and length, are involved during the animation. To better illustrate this process, the below figure shows the change of the shape against a timeline.

timeline pac-man


Hope you enjoy reading the articles. You can use arrow keys to play the current version of the game. The ghosts are moving randomly which makes the game less challenging. In the next article, I will introduced a better algorithm. Try it by clicking the below screenshot:


click to run

click to run


Related Articles:

Develop Games in JavaFX

My JavaFX Demo Game: Pac-Man

Comments (0) Jun 06 2009

The LinkUp Game in JavaFX

Posted: under linkup.
Tags: , ,

You may have remembered that I introduced a JavaFX implementation of the Frozen Bubble game by Liu Xuan. He finished another JavaFX game, LinkUp, recently. This is a game of fun, especially for kids, and had been implemented in many languages and platform, such as flash and java. It is simple to play the game: look for two identical images and eliminate them. There must be a path connecting the two images and the path must have no more than two turns.

Like in any games, visual effect and algorithms are two key elements. In this game, most of the visual effects can be easily achieved by JavaFX API(like fade in/out, animation). According to Liu, the algorithm to find two conntected cells are the hightlight of the game. A breadth-first search algorithm is used to find the shortest path. Check the code at Model.fx, there are three functions: findNoCross(),
findOneCross() and findTwoCross() implemented for this purpose. The first function finds a set of cells that can be reached by a line(directly). If the target cell is not in this set, the second function is invoked to look for cells reachable by a turn. Similarly, the third function is used for searching a path of two turns.

If you are interested in the details of the game, come take a look at the code (Note: Liu Xuan has the copyright of the source code). Or, you can just enjoy playing this game by clicking the below image:


click to start LinkUP game

click to start LinkUP Game

Comments (1) Jun 01 2009

JavaFX Sample Game: MineSweeper

Posted: under minesweeper.
Tags: , ,

There are lots of talented software developers in China. I saw some of them had written interesting games in JavaFX. As I introduced before, Liu Xuan wrote the Frozen Bubble game in JavaFX. Recently I read a post from blogger mqqqvpppm, who resides in a beautiful city Kun Ming in south western China. He did a good MineSweeper Game with JavaFX. Click on the below screenshot or the “launch” button to play.(JDK1.5 or 1.6U10+)


click to start MineSweeper

click to start MineSweeper

Here are the highlights of the source code:
The cells(Cell.fx) of the broad were implemented by extending the class CustomNode. The interesting part of the code is that four instances of Node are included in the CustomNode, which are used for four possible states of a cell: blank, a bomb, a red flag, or a number. One of the Nodes is visible at a time. The visibility of the nodes are bound to a few instance boolean variables. The main logic then controls these boolean variables to display different states of the cells. We can see the code making good use of JavaFX binding.

In the game, when a cell with a number is clicked, there is an action to uncover adjacent cells like flooding. This is another interesting part of the writing the game. mqqqvpppm implemented the code by indirect recursive functions.

In the current version of the game, a player may hit a mine on the first click. mqqqvpppm said he will modify this soon. Overall, I think the game is well written. Come take a look at the source code here(Note: mqqqvpppm has the copyright of this work), I am sure you will learn something from it.

Comments (1) May 23 2009

How to Write a Pac-Man Game in JavaFX (2)

Posted: under JavaFX Games, pac-man.
Tags: , ,

The 2nd article of the series, “Writing the Pac-Man Game in JavaFX“, is published today.

The first article introduced a data model in Java and the JavaFX drawing logic of the maze. In the 2nd article, the animation part of the Pac-Man character is detailed. When you are reading, you can click on the java web start links to see the Pac-Man opening and closing mouths, and gradually moving inside the maze. The keyboard handling code is illustrated as well.

Some JavaFX features demostrated in these two articles include:

. shapes
. keyboard handling
. animation timeline
. java code integration
. Transfromation

Hope you can enjoy reading the articles. You can use arrow keys to play a no-ghost version of the game. The Pac-Man character now can move around and gobble dots. Try it by clicking the below screenshot:

click to run

click to run
Related Articles:

Develop Games in JavaFX

My JavaFX Demo Game: Pac-Man

Comments (0) May 22 2009

JavaFX Frozen Bubble Game

Posted: under JavaFX Games.
Tags: , ,

Liu Xuan, a Chinese programmer, had written a Frozen Bubble game in JavaFX. He is kind to allow me to share his code here. The game was originally developed in Perl/SDL. A java port is available too. Liu’s program implemented a simplified version of the Frozen Bubble game in JavaFX.


Click on the below button to start the game, use left/right arrow key to aim and space button to fire.








The key handling code is accomplished in the class Container:

public class Container extends CustomNode{
    public var bubbles = new HashMap();
    public var fadeBubbles: Bubble[];
    public var fallBubbles: Bubble[];
    public var gun: Gun = Gun{};
    public var shootAngle: Number;
    public var group: Group = Group {
        content: [
            // backgroud
            Rectangle {
                width: 320
                height: 480
                strokeWidth: 1
                stroke: Color.BLACK
                fill: LinearGradient {
                    startX: 0.0,
                    startY: 0.0,
                    endX: 0.0,
                    endY: 1.0
                    proportional: true
                    stops: [ Stop {
                            offset: 0.0
                            color: Color.YELLOWGREEN },
                        Stop {
                            offset: 1.0
                        color: Color.LIGHTBLUE } ]
                }
                onKeyPressed: function(e: KeyEvent):Void {
                      .......   // keyboard event handling
             }
            }
            gun
            //warning line
            Line {
                startX: 0,
                startY: Config.RED_LINE
                endX: 320
                endY: Config.RED_LINE
                strokeWidth: 1
                stroke: Color.RED
            }
            ImageView {
                  .......
            }
        ]
    };   

    public var inverseX = 1;   

    //semi-transparent layer for game over screen
    var layer = Rectangle {
        width: 320
        height: 480
        fill: Color.BLACK
        opacity: .4
    }
    var text = Text {
        content: "Press Enter To Start"
        font: Font {
            size: 20
        }
        x: 60
        y: 250
    }   

    // status of the game
    // 0 - game start and wait for shooting
    // 1 - bubble is moving
    // 2 - game over, the animation timeLine instanc
   // will stop at this value
    public var state = 2 on replace {
        if(state == 2) {
            timeline.stop();
            insert layer into group.content;
            insert text into group.content;
        }
    }   

    override public function create(): Node {
        group
    }   

   public function getLocation(row: Integer, col: Integer) : Point{
        var locationY = Config.ROW_SPACE * row;
        var locationX;
        if(row mod 2 == 0) {
            locationX = Config.BUBBLE_DIAMETER * col
        } else {
            locationX =
            Config.BUBBLE_DIAMETER * (col + .5) as Integer
        }
        return new Point(locationX, locationY)
    }   

   public function getAround(row: Integer, col: Integer): Bubble[] {
        var bArray: Bubble[] = [];
        var flag: Integer = 0;
        if(row mod 2 == 0) {
            flag = -1;
        }
        var bubble0 = getBubble(row, col - 1);
        var bubble1 = getBubble(row - 1, col + flag);
        var bubble2 = getBubble(row - 1, col + 1 + flag);
        var bubble3 = getBubble(row, col + 1);
        var bubble4 = getBubble(row + 1, col + flag);
        var bubble5 = getBubble(row + 1, col + 1 + flag);
        insert bubble0 into bArray;
        insert bubble1 into bArray;
        insert bubble2 into bArray;
        insert bubble3 into bArray;
        insert bubble4 into bArray;
        insert bubble5 into bArray;
        return bArray;
    }   

    public function getAround(bubble: Bubble): Bubble[] {
        var row: Integer = bubble.index.x;
        var col: Integer = bubble.index.y;
        return getAround(row, col);
    }

   public function getSameBubble(bubble: Bubble): Vector {
     var vector: Vector = new Vector();
     vector.add(bubble);
     var cursor = 0;
     while(
      cursor < vector.size()) {
        var bubbleInVector: Bubble =
        vector.get(cursor++) as Bubble;
        var aroundBubbles:Bubble[]=getAround(bubbleInVector);
        for(aroundBubble in aroundBubbles) {
          if ( aroundBubble != null
               and aroundBubble.color == bubble.color
               and vector.indexOf(aroundBubble) == - 1) {
               vector.add(aroundBubble);
                }
            }
        }
        return vector;
    }   

    public function getConnected(bubble: Bubble): Vector {
      var vector: Vector = new Vector();
      vector.add(bubble);
      var cursor = 0;
      while(cursor < vector.size()) {
        var bubbleInVector: Bubble =
        vector.get(cursor++) as Bubble;
        var aroundBubbles:Bubble[]=getAround(bubbleInVector);
        for(aroundBubble in aroundBubbles) {
            if ( aroundBubble != null
              and vector.indexOf(aroundBubble) == - 1) {
              vector.add(aroundBubble);
              }
            }
        }
        return vector;
    }   

  public function getIsolatedBubble(vector: Vector): Vector {
    var islatedBubble: Vector = new Vector();
      for(object in vector) {
        var sameBubble: Bubble = object as Bubble;
        var aroundBubbles:Bubble[] = getAround(sameBubble);
        for(aroundBubble in aroundBubbles) {
          if(aroundBubble != null) {
            var connectedBubble:Vector=getConnected(aroundBubble);
            var islate = true;
            for(col in [0..= 3) {
              for(object in vector) {
                var sameBubble: Bubble = object as Bubble;
                bubbles.remove(sameBubble.index);
                insert sameBubble into fadeBubbles;
              }
              for(object in getIsolatedBubble(vector)) {
                var islatedBubble: Bubble = object as Bubble;
                bubbles.remove(islatedBubble.index);
                insert islatedBubble into fallBubbles;
              }
            }
            state = 0;
            checkGameOver();
        }
    }   

    var timeCount = 0;   

    //moving, erasing, dropping the bubbles
    def timeline = Timeline {
        repeatCount: Timeline.INDEFINITE
        keyFrames:[
            KeyFrame {
                time: 0.005s
                action: function() {
                    ......
                    checkGameOver();
                    .......
            //reduce the transparency to erase the bubbles
            for(fadeBubble in fadeBubbles) {
                fadeBubble.opacity -= .02;
                if(fadeBubble.opacity <= 0) {
                   fadeBubble.visible = false;
                   delete fadeBubble from fadeBubbles;
                   delete fadeBubble from group.content;
                   }
              }
              //drop those isolated bubbles
              for(fallBubble in fallBubbles) {
                fallBubble.locationY += 5;
                if(fallBubble.locationY >= 428) {
                   fallBubble.visible = false;
                   delete fallBubble from fallBubbles;
                   delete fallBubble from group.content;
                   }
                 }   

                if(state == 1) {
                        ......
                //rebounce and collision handling
                checkCollision(bubble);
                }
              }
            }
        ]
    }   

    // initialization
    public function gameStart():Void {
          .....
          timeline.play();
    }
}


Source code can be downloaded here. Please note that Liu Xuan has the copyright of the code.

Comments (0) Apr 09 2009

My First JavaFX Demo Game: PAC-MAN

Posted: under JavaFX Games.
Tags: , ,

I spent some time to implement the classic game “PAC MAN”. It demos many features of the JavaFX language. Right now, it is a “simplified” PACMAN Game. I am working on the code and hopefully to complete the PACMAN game soon.  Source code is not released yet because I plan to do so when I finish the whole game. A blog of writing this game will be available soon. Stay tuned.

Usage:
Arrow keys to move and control to pac man to eat all beans inside the maze. The big beans are magic beans which allow the pac man to eat ghosts.

JavaFX Features Demostrated:

  • Bindings
  • Animations
  • Effects
  • Transforms
  • Multiple inheritant
  • Java classes integration
  • Declarative statements
  • Sequences, how to map 2D arrays into a 1D Sequence
  • Handling keyboard events

JRE 1.5+ required, JRE1.6 U10 is better because it allows draggable applet to run outside the browser. It will take some time for first time launching JavaFX application …

screenshot


My JavaFX code is compatible with the newly released JavaFX 1.0. JavaFX is for Windows and Mac for now.

Comments (3) Dec 07 2008