Jun 18

Pac-Man Widget for JavaFX 1.2

Posted: under JavaFX, JavaFX Coding, Javafx Games.
Tags: , , , , , June 18th, 2009

To fully take advantage of the power of JavaFX 1.2, Stephen Chin had just released the WidgetFX 1.2 API beta version. There is even a widget contest running until the end of July.

Just before the JavaOne 2009, Jim Weaver asked me to write a widget for my JavaFX Pac-man game. The WidgetFX API was quite simple to use, so I finished it pretty soon. The game widget later got demo-ed on Jim and Steve’s JavaOne sessions. There was a small problem of the pac-man widget: it run relatively slow due to the performance issue of JavaFX 1.1. Since JavaFX 1.2 and WidgetFX 1.2 are ready now, I am modifying the code to see the improvement on performance.

First, the code of the Pac-Man game needs to be modified a little bit for JavaFX 1.2 . Since multi-inheritance is gone, we need to use mixin classes now. You can refer to my articles on insideRIA.com for details of the code. Changes for JavaFX 1.2 were given on comments of Article 4 by Patrick Webster. I also added in a pausing key(“P” button) handling for the game. I compiled the game into a pacman.jar file.

The next step is to write the widget. Actually, the code is quite simple if you do not have stuffs like configuartion etc. Let’s take a look at the code below:

/*
 * PacManWidget.fx
 * http://www.javafxgame.com
 */

package pacmanwidget;

import org.widgetfx.Widget;

/**
 * @author Henry Zhang
 */

def defaultWidth = 528.0;
def defaultHeight = 576.0;

def maze =  pacman.Maze {};

var widget:Widget = Widget {
    width: defaultWidth
    height: defaultHeight
    aspectRatio: defaultWidth / defaultHeight
    resizable: false
    content: maze          

    onDock: function():Void {
       maze.pauseGame();
    }
}

return widget;

In the standalone game, an instance of the Maze class was put into the content variable of a Stage. Now, instead of putting it into a Stage, we add it into a Widget. To do this, we can just set the content variable of a Widget instance. Other attributes of the Widget class are quite straightforward, mostly for resizing purposes. The next thing is to write a onDock() function to pause the game when the widget gets docked. The game can be resumed after pressing the “p” button when it is undocked.

The last thing is to deploy it on a web server. We need a JNLP file. Be sure to write the jnlp file of the JavaFX 1.2 style. Netbeans can generate the JNLP file which we can modify for deployment. I listed below part of my jnlp file. Besides the widget code PacManWidget.jar, there are supporting jar files( pacman.jar and WidgetFX-API.jar) under the /lib folder as well. Notice that there is a bug in the generated JNLP file by NetBeans 6.5.1: the <update> tag is missing a slash(/) at the end of the tag.

 . . . . . .
 <resources>
     <j2se version="1.5+" />
     <extension name="JavaFX Runtime"
        href="http://dl.javafx.com/1.2/javafx-rt.jnlp"/>
     <jar href="PacManWidget.jar" main="true"/>
     <jar href="lib/pacman.jar"/>
     <jar href="lib/WidgetFX-API.jar"/>
 </resources>
 <application-desc main-class="com.sun.javafx.runtime.main.Main">
    <argument>MainJavaFXScript=pacmanwidget.PacManWidget</argument>
 </application-desc>
 <update check="background" />
 . . . . . .

Now, you can click on the below button to start the Pac-man widget for JavaFX 1.2. Enjoy!


Pac-Man Widget 1.2

Pac-Man Game Widget for WidgetFX 1.2

Comments (0)

Jun 13

JavaFX Pac-Man Game Article 5

Posted: under JavaFX, JavaFX Coding, Javafx Games.
Tags: , , June 13th, 2009

On java.net, there was a post by kfarnham about my articles on writing Pac-man game in JavaFX. He wondered whether there would be a fifth article. The answer is positive. 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 run very smoothly.


click to run

click to run

Related Articles:

JavaFX Pac Man Part 5
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

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 06

Writing the Pac-Man Game Part 4

Posted: under JavaFX, JavaFX Coding, Javafx Games.
Tags: , , , June 6th, 2009

My latest article of a series, “Writing the Pac-Man Game in JavaFX Part 4“, was out on June 4. As we continue on bulding the game, the articles are getting more and more interesting now.

In this fourth article, the interaction between Pac-Man character and the four ghosts was implemented. The article showed how to determine whether the Pac-man character and a ghost touched each other. A simplified equation 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, at this moment, an animation of showing a dying Pac-Man appears. This is in fact a shrinking circle 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:

public class DyingPacMan extends Arc {
 
  var timeline = Timeline {
     repeatCount: 1
     keyFrames: [
        KeyFrame {
           time: 600ms
           action: function() {
           // hide the pacMan character & 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 ]
         },
      ]
    }
 
 ... code omitted ...
}

As you can see it in the code, there are two keyframes of the animation. Interpolations of two instance 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. The animation started with a pause of 600ms and then the first key frame appears, which is a full circle. After that, the full circle will gradually turns into nothing(empty circle). This effect is done by the interpolation provided by JavaFX API.

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:

More articles on JavaFX Games: Developing Games in JavaFX
Source of the Pac-Man Game
JavaFX Demo Game: MineSweeper
JavaFX Demo Game: LinkUP
Game Widgets for WidgetFX: Pac-Man
My JavaFX Demo Game: Pac-Man

Comments (0)

May 31

Writing the JavaFX Pac-Man Game Part 3

Posted: under JavaFX, JavaFX Coding, Javafx Games.
Tags: , , , , May 31st, 2009

The latest JavaFX article of writing the Pac-Man game is published on May 28. It is the third out of a five-article series: “Writing the Pac-Man Game in JavaFX – Part 3“.

In this article, the ghosts are introduced. The animation of the ghosts are almost the same as the Pac-Man character. It is basically moving a CustomNode and switching the images. There are three sets of images for the animation: normal, hollow and flashing. The moving algorithm of the ghosts is the heart of the code. In this article, for simplicity, a random moving algorithm is applied. A more complicated moving behavior of the ghosts will be introduced in Article 5. Click on the below image to try the program so far. The Pac-man and ghosts cannot eat each other even they meet in the maze. The feature of eating each other will be illustrated in next article. As before, you can try this semi-completed program by clicking Java Web Start button below:


Related Articles:

My JavaFX Demo Game: Pac-Man

Articles on Writing the JavaFX Pac-Man Game

Pac-Man Game WidgetFX Widget

JavaFX Game Example Code: MineSweeper

JavaFX Game Demo Example: Pac-Man


Other unrelated links:

Applicant of American Citizenship Information, software and guide.

Guide American Citizenship in United States

Canadian Citizenship Application and Practice Exam

Comments (0)

May 21

The 2nd Article on Pac-Man Game with JavaFX

Posted: under JavaFX, JavaFX Coding, Javafx Games.
Tags: , May 21st, 2009

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 16

Articles on Writing the JavaFX Pac-Man Game

Posted: under JavaFX, JavaFX Coding, Javafx Games.
Tags: , , , May 16th, 2009

After the release of JavaFX 1.0, I wrote a Pac-Man game using the JavaFX API. Many people were quite interested in the game. They either enjoyed playing it or asked me for the details fo the JavaFX code. JavaFX expert Jim Weaver invited me to write some articles about the process of building the game. After a few weeks’ hard work, with Jim’s constructive ideas and great help in proofreading, I finished the articles. Now they are published on insideRIA.com, an O’Reilly’s web site, as featured articles. There will be 5 articles in total and they will run through the coming 5 weeks.

In each article, there are a few web start links that you can click on and start a JavaFX program to see how it works. If you follow the 5 articles, you will find out how the game is built bit by bit. I hope the articles can help people who want to learn JavaFX or want to develop games in JavaFX. Thanks Jim for making these articles possible. My thanks also goes to Rich, the editor of insideRIA.com.

Here is the links for the articles:

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

Related articles:
Answer: Blinky, Pinky, Inky and Clyde from Jim Weaver’s Blog

My JavaFX Demo Game: PACMAN

JavaFX Demo Game: PAC-MAN

Comments (0)

Apr 08

JavaFX Game: Frozen Bubble

Posted: under JavaFX, Javafx Games.
Tags: , , April 8th, 2009

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)

Nov 30

My JavaFX Demo Game: PAC-MAN

Posted: under JavaFX, Javafx Games.
Tags: , , , November 30th, 2008

UPDATE: June 30, 2009
If you are interested in how to write the Pac-Man game in JavaFX, the source code of this game, please check out this article:

How to Write the Pac-Man Game with JavaFX
or Articles on Writing the JavaFX Pac-Man Game

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 dots inside the maze. The big dots are magic dots 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, it will take some time for first time launching the game …

screenshot

My JavaFX code is compatible with the newly released JavaFX 1.0. JavaFX is for Windows and Mac for now. The unofficial JavaFX SDK of Linux can be found here:
http://silveiraneto.net/2008/12/06/javafx-sdk-10-on-linux/

If you cannot play now, you can watch the video:






Unrelated links:

JavaFX Articles

Collection: NASCAR Diecast Model Cars


Canadian Citizenship Practice Test

NASCAR Car Diecast

NASCAR Drivers

British Citizenship Test for United Kingdom

Comments (32)