<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" 
      xmlns:thr="http://purl.org/syndication/thread/1.0">
  <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html" />
  <link rel="self" type="application/atom+xml" href="http://www.insideria.com/atom.xml" />
  <id>tag:www.insideria.com,2009://34/tag:www.insideria.com,2009://34.35762-</id>
  <updated>2009-11-16T15:01:49Z</updated>
  <title>Comments for Writing the Pac-Man Game in JavaFX - Part 1 (http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html)</title>
  <generator uri="http://www.sixapart.com/movabletype/">Movable Type 4.21-en</generator>
  <entry>
    <id>tag:www.insideria.com,2009://34.35762</id>
    <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blogs.oreilly.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=34/entry_id=35762" title="Writing the Pac-Man Game in JavaFX - Part 1" />
    <published>2009-05-14T13:00:00Z</published>
    <updated>2009-06-08T15:28:27Z</updated>
    <title>Writing the Pac-Man Game in JavaFX - Part 1</title>
    <summary>When I was young I was fascinated by arcade games. One of my favorites was the Pac-Man game.&#160; Recently, when I was learning the JavaFX language, I decided to write the game in JavaFX.&#160; Based on my experience in other programming languages, I assumed there would be some amount of work in building a game such as Pac-Man, giving me a good feel for RIA development in JavaFX.</summary>
    <author>
      <name>Haining Henry Zhang</name>
      
    </author>
    
    <category term="Features" />
    
    <content type="html" xml:lang="en" xml:base="http://www.insideria.com/">
      <![CDATA[<p><b>Haining Henry Zhang with <a href="http://learnjavafx.typepad.com" target="_blank">James L. Weaver</a></b></p>

<p>When I was young I was fascinated by arcade games. One of my favorites was the Pac-Man game.&#160; Recently, when I was learning the JavaFX language, I decided to write the game in JavaFX.&#160; Based on my experience in other programming languages, I assumed there would be some amount of work in building a game such as Pac-Man, giving me a good feel for RIA development in JavaFX.</p>

<h3>Data Model of the Maze</h3> 

<p>Before writing the JavaFX code, it is first necessary to design the data model. A data model is a way to represent physical objects with data structures. The functional programming style of JavaFX makes it easy to bind the UI to the model. When designing a data model, I usually consider two aspects: performance and space. Performance means that the data should be accessed via an efficient approach. For example, a hash table is usually faster than a linked list when a keyword-based search is performed. Performance is an important consideration for games that are constantly taking a player's input and updating graphical objects. Games like Pac-Man or Space Invaders fall into this category.&#160; The other design consideration for a data model is memory space. I still remember the time when I was programming on an APPLE II with only 48KB RAM. Much effort was spent on minimizing memory consumption. Fortunately, our Pac-Man game is not data-intensive, so I wasn't very concerned about the space issue. </p>

<p>Keeping the above analysis in mind, we will now start building the data model. We can treat the board as an NxN grid. The wall of the maze can be drawn by lines connecting the points of the grid. Naturally, a 2-dimensional array is the best way to model this grid. Each point of the grid may have one of the following four types: </p>

<p>
<table border=1>
<tr><td><b>Type</b></td><td><b>Value</b></td><td><b>Explanation</b></td></tr>
<tr><td>BLOCK</td><td>1</td><td>The point is part of a "wall" of the maze</td></tr>
<tr><td> NORMAL_DOT </td><td>2</td><td> The point contains a normal dot </td></tr>
<tr><td> MAGIC_DOT </td><td>3</td><td> The point contains a magic dot </td></tr>
<tr><td> EMPTY </td><td>0</td><td> The point does not have any of the above objects at it </td></tr>
</table>

<p>For example, the picture shown below is the upper-left corner of the maze, and the corresponding data in the array is shown on the right:</p>

<p>
<table>
<tr><td>
<img src="http://www.insideria.com/upload/2009/04/corner.jpg">
</td>
<td width=50></td>
<td>

<table border=1>
<tr><td width=30>&nbsp;</td><td width=30><b>0</b></td><td width=30><b>1</b></td><td width=30><b>2</b></td><td width=30><b>3</b></td><td width=30><b>4</b></td><td width=30><b>5</b></td><td width=30><b>6</b></td></tr>
<tr><td><b>0</b></td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
<tr><td><b>1</b></td><td>1</td><td>2</td><td>2</td><td>2</td><td>2</td><td>2</td><td>2</td></tr>
<tr><td><b>2</b></td><td>1</td><td>2</td><td>1</td><td>1</td><td>1</td><td>1</td><td>2</td></tr>
<tr><td><b>3</b></td><td>1</td><td>3</td><td>1</td><td>0</td><td>0</td><td>1</td><td>2</td></tr>
<tr><td><b>4</b></td><td>1</td><td>2</td><td>1</td><td>1</td><td>1</td><td>1</td><td>2</td></tr>
<tr><td><b>5</b></td><td>1</td><td>2</td><td>2</td><td>2</td><td>2</td><td>2</td><td>2</td></tr>
<tr><td><b>6</b></td><td>1</td><td>2</td><td>1</td><td>1</td><td>1</td><td>1</td><td>2</td></tr>
<tr><td><b>7</b></td><td>1</td><td>2</td><td>1</td><td>1</td><td>1</td><td>1</td><td>2</td></tr>
<tr><td><b>8</b></td><td>1</td><td>2</td><td>2</td><td>2</td><td>2</td><td>2</td><td>2</td></tr>
<tr><td><b>9</b></td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td><td>2</td></tr>

</table>
</td>
</tr>
</table>


<p>This data model will be accessed very frequently when the Pac-Man character and ghosts are moving inside the maze, therefore, an efficient data structure should be used. A 2-dimensional (2D) array is a good choice because its access time is almost a constant. Though it is common in modern programming languages to have 2D arrays, JavaFX sequences (an array-like structure in JavaFX) are single dimensional. For this reason, I decided to use a Java class to hold this array. I created a few methods for accessing the model or converting the grid data into drawing coordinates.&#160; Note that the ability to leverage Java from within JavaFX is one of the very powerful features of JavaFX.</p>


<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
package pacman;

<span class="blockcomment">/**
 * MazeData.java
 *
 * @author Henry Zhang
 *
 * a 2D array for data model of the maze
 *
 */</span>

<span class="category1">public</span> <span class="category1">class</span> MazeData {
 
   <span class="category1">public</span> final <span class="category1">static</span> <span class="category1">int</span> GRID_SIZE = 29;
   <span class="category1">public</span> final <span class="category1">static</span> <span class="category1">int</span> EMPTY = 0;
   <span class="category1">public</span> final <span class="category1">static</span> <span class="category1">int</span> BLOCK = 1;
   <span class="category1">public</span> final <span class="category1">static</span> <span class="category1">int</span> NORMAL_DOT = 2;
   <span class="category1">public</span> final <span class="category1">static</span> <span class="category1">int</span> MAGIC_DOT = 3;
 
   <span class="category1">public</span> <span class="category1">static</span> <span class="category1">int</span> DOT_TOTAL = 0;
   <span class="category1">public</span> <span class="category1">static</span> <span class="category1">int</span> mazeData[][] = <span class="category1">new</span> <span class="category1">int</span>[GRID_SIZE + 1][GRID_SIZE + 1];
 
   <span class="category1">public</span> final <span class="category1">static</span> <span class="category1">int</span> GRID_GAP = 16;
   <span class="category1">public</span> final <span class="category1">static</span> <span class="category1">int</span> GRID_STROKE = 2;
   final <span class="category1">static</span> <span class="category1">int</span> xoffset = GRID_GAP * 2;
   final <span class="category1">static</span> <span class="category1">int</span> yoffset = GRID_GAP * 2;
 
   <span class="category1">public</span> <span class="category1">static</span> <span class="category1">int</span> makeInRange(<span class="category1">int</span> a) {
      <span class="category1">if</span> (a &lt; 0) {
         a = 0;
       } <span class="category1">else</span> <span class="category1">if</span> (a &gt; GRID_SIZE) {
         a = GRID_SIZE;
       }
  
      <span class="category1">return</span> a;
    }
 
   <span class="linecomment">// set the grid of maze data to be BLOCK</span>
   <span class="category1">public</span> <span class="category1">static</span> <span class="category1">void</span> setBlockMazeData(<span class="category1">int</span> x1, <span class="category1">int</span> y1, <span class="category1">int</span> x2, <span class="category1">int</span> y2) {
      x1 = makeInRange(x1);
      y1 = makeInRange(y1);
      x2 = makeInRange(x2);
      y2 = makeInRange(y2);
  
      <span class="category1">for</span> (<span class="category1">int</span> i = x1; i &lt;= x2; i++) {
         mazeData[i][y1] = BLOCK;
         mazeData[i][y2] = BLOCK;
       }
  
      <span class="category1">for</span> (<span class="category1">int</span> i = y1; i &lt;= y2; i++) {
         mazeData[x1][i] = BLOCK;
         mazeData[x2][i] = BLOCK;
       }
    }
 
   <span class="category1">public</span> <span class="category1">static</span> double calcGridX(double <span class="category2">x</span>) {
      <span class="category1">return</span> GRID_GAP * <span class="category2">x</span> + xoffset;
    }
 
   <span class="category1">public</span> <span class="category1">static</span> double calcGridY(double <span class="category2">y</span>) {
      <span class="category1">return</span> GRID_GAP * <span class="category2">y</span> + yoffset;
    }
 
   <span class="category1">public</span> <span class="category1">static</span> <span class="category1">int</span> <span class="category2">getData</span>(<span class="category1">int</span> <span class="category2">x</span>, <span class="category1">int</span> <span class="category2">y</span>) {
      <span class="category1">return</span> mazeData[<span class="category2">x</span>][<span class="category2">y</span>];
    }
 
   <span class="category1">public</span> <span class="category1">static</span> <span class="category1">void</span> <span class="category2">setData</span>(<span class="category1">int</span> <span class="category2">x</span>, <span class="category1">int</span> <span class="category2">y</span>, <span class="category1">int</span> value) {
      mazeData[<span class="category2">x</span>][<span class="category2">y</span>] = value;
  
      <span class="category1">if</span> ((value == MAGIC_DOT) || (value == NORMAL_DOT)) {
         DOT_TOTAL++;
       }
    } <span class="linecomment">// end setData</span>
}</pre>
</code>

</div></div> 


<h3>Drawing the Maze</h3>

<p>Once we have the data model of the maze, we can start drawing the maze based on this model. There are basically two approaches for drawing the maze. One approach is to draw the maze directly with JavaFX code.&#160; Another approach is use an image file such as a PNG or JPG. The image could then be used as a background picture in our Pac-Man game. I choose the first approach because it is easier to link the GUI to our data model.</p>

<p>JavaFX provides some standard APIs for basic shapes such as lines, circles and rectangles. 
We can use <tt><b>Line</b></tt> and <tt><b>Rectangle</b></tt> classes as building blocks for 
most parts of the maze. First, we write a class named <tt><b>WallRectangle</b></tt> to draw the walls of the maze. In the code shown below, (x1,y1) and (x2,y2) are the coordinates of the upper-left and bottom-right corner of a rectangle. Here is the code: </p>


<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="blockcomment">/*
 * WallRectangle.fx
 *
 * Created on 2008-12-25, 16:08:28
 */</span>

package pacman;

<span class="category1">import</span> javafx.scene.CustomNode;
<span class="category1">import</span> javafx.scene.Node;
<span class="category1">import</span> javafx.scene.paint.<span class="category2">Color</span>;
<span class="category1">import</span> javafx.scene.shape.Rectangle;
<span class="category1">import</span> pacman.MazeData;

<span class="blockcomment">/**
 * @author Henry Zhang
 */</span>

<span class="category1">public</span> <span class="category1">class</span> WallRectangle <span class="category1">extends</span> CustomNode {
 
   <span class="category1">public</span> <span class="category1">var</span> x1: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> y1: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> x2: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> y2: <span class="category2">Number</span>;
 
   <span class="category1">public</span> override <span class="category1">function</span> create(): Node {
      Rectangle {
         <span class="category2">x</span>: MazeData.calcGridX(x1)
         <span class="category2">y</span>: MazeData.calcGridY(y1)
         <span class="category2">width</span>: MazeData.calcGridX(x2) - MazeData.calcGridX(x1)
         <span class="category2">height</span>: MazeData.calcGridY(y2) - MazeData.calcGridY(y1)
         strokeWidth: MazeData.GRID_STROKE
         stroke: <span class="category2">Color</span>.BLUE
         arcWidth: 12
         arcHeight: 12
       } 
    }    
}</pre>
</code>

</div></div> 


<p>Next, we start to work on the <tt><b>Maze</b></tt> class. The <tt><b>Maze</b></tt> class 
extends <tt><B>CustomNode</b></tt> class and 
overrides the <tt><b>create()</b></tt> function. In the <tt><b>create()</b></tt> function, 
we place <tt><b>WallRectangle</b></tt> and 
<tt><b>Line</b></tt> instances to construct the maze. Here is the source code of the <tt><b>Maze</b></tt> class: </p>

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="blockcomment">/*
 * Maze.fx
 *
 * Created on 2008-12-20, 20:22:15
 */</span>

package pacman;

<span class="category1">import</span> javafx.scene.CustomNode;
<span class="category1">import</span> javafx.scene.Group;
<span class="category1">import</span> javafx.scene.Node;
<span class="category1">import</span> javafx.scene.paint.<span class="category2">Color</span>;
<span class="category1">import</span> javafx.scene.shape.Line;
<span class="category1">import</span> javafx.scene.shape.Rectangle;
<span class="category1">import</span> pacman.MazeData;

<span class="blockcomment">/**
 * @author Henry Zhang
 */</span>

<span class="category1">public</span> <span class="category1">class</span> Maze <span class="category1">extends</span> CustomNode {
     
   <span class="category1">var</span> group : Group =
   Group {
      content: [
        Rectangle {
           <span class="category2">x</span>:0
           <span class="category2">y</span>:0
           <span class="category2">width</span>: MazeData.calcGridX(MazeData.GRID_SIZE + 2)
           <span class="category2">height</span>: MazeData.calcGridY(MazeData.GRID_SIZE + 3)
           fill: <span class="category2">Color</span>.BLACK
         },
  
        WallRectangle{ x1:0 y1:0 x2:MazeData.GRID_SIZE y2:MazeData.GRID_SIZE },
  
        WallRectangle { x1:14 y1:-0.5 x2:15 y2:4 },
  
        WallRectangle { x1:2 y1:2 x2:5 y2:4 },
        WallRectangle { x1:7 y1:2 x2:12 y2:4 },
        WallRectangle { x1:17 y1:2 x2:22 y2:4 },
        WallRectangle { x1:24 y1:2 x2:27 y2:4 },
        WallRectangle { x1:2 y1:6 x2:5 y2:7 },
  
        WallRectangle { x1:14 y1:6.2 x2:15 y2:10 },
        WallRectangle { x1:10 y1:6 x2:19 y2:7 },
  
        WallRectangle { x1:7.5 y1:9 x2:12 y2:10 },
        WallRectangle { x1:7 y1:6 x2:8 y2:13 },
        WallBlackLine { x1:8 y1:9 x2:8 y2:10 },
  
        WallRectangle { x1:17 y1:9 x2:21.5 y2:10 },
        WallRectangle { x1:21 y1:6 x2:22 y2:13 },
  
        WallRectangle { x1:24 y1:6 x2:27 y2:7 },
  
        WallRectangle { x1:-1 y1:9 x2:5 y2:13 },
        WallRectangle { x1:24 y1:9 x2:MazeData.GRID_SIZE + 1 y2:13 },
  
        <span class="linecomment">//cage and the gate</span>
        WallRectangle { x1:10 y1:12 x2:19 y2:17 },
        WallRectangle { x1:10.5 y1:12.5 x2:18.5 y2:16.5 },
        Rectangle {
           <span class="category2">x</span>: MazeData.calcGridX(13)
           <span class="category2">width</span>: MazeData.GRID_GAP * 3
           <span class="category2">y</span>: MazeData.calcGridY(12)
           <span class="category2">height</span>: MazeData.GRID_GAP / 2
           stroke: <span class="category2">Color</span>.GREY
           fill: <span class="category2">Color</span>.GREY
         },
  
        WallRectangle { x1:7.5 y1:19 x2:12 y2:20 },
        WallRectangle { x1:7 y1:15 x2:8 y2:23 },
  
        WallRectangle { x1:17 y1:19 x2:21.5 y2:20 },
        WallRectangle { x1:21 y1:15 x2:22 y2:23 },
  
        WallRectangle { x1:14 y1:19 x2:15 y2:27 },
        WallRectangle { x1:10 y1:22 x2:19 y2:23 },
  
        WallRectangle { x1:2 y1:25 x2:5 y2:27 },
        WallRectangle { x1:17 y1:25 x2:22 y2:27 },
  
        WallRectangle { x1:7 y1:25 x2:12 y2:27 },
        WallRectangle { x1:24 y1:25 x2:27 y2:27 },
  
        WallRectangle { x1:-1 y1:15 x2:5 y2:17 },
        WallRectangle { x1:4 y1:19 x2:5 y2:23 },
        WallRectangle { x1:2 y1:19 x2:4.5 y2:20 },
        WallRectangle { x1:-1 y1:22 x2:2 y2:23 },
  
        WallRectangle { x1:24 y1:15 x2:MazeData.GRID_SIZE + 1 y2:17 },
        WallRectangle { x1:24 y1:19 x2:25 y2:23 },
        WallRectangle { x1:24.5 y1:19 x2:27 y2:20 },
        WallRectangle { x1:27 y1:22 x2:MazeData.GRID_SIZE + 1 y2:23 },
  
        WallBlackRectangle { x1:-2 y1:8 x2:0 y2:MazeData.GRID_SIZE },
        WallBlackRectangle {
            x1:MazeData.GRID_SIZE
            y1:8
            x2:MazeData.GRID_SIZE + 2
            y2:MazeData.GRID_SIZE
         },
  
        Rectangle {
           <span class="category2">x</span>: MazeData.calcGridX(-0.5)
           <span class="category2">y</span>: MazeData.calcGridY(-0.5)
           <span class="category2">width</span>: (MazeData.GRID_SIZE + 1) * MazeData.GRID_GAP
           <span class="category2">height</span>: (MazeData.GRID_SIZE + 1) * MazeData.GRID_GAP
           strokeWidth: MazeData.GRID_STROKE
           stroke: <span class="category2">Color</span>.BLUE
           fill: <span class="category1">null</span>
           arcWidth: 12
           arcHeight: 12
         },
        Line {
           startX: MazeData.calcGridX(-0.5)
           endX: MazeData.calcGridX(-0.5)
           startY: MazeData.calcGridY(13)
           endY: MazeData.calcGridY(15)
           stroke: <span class="category2">Color</span>.BLACK
           strokeWidth: MazeData.GRID_STROKE + 1
         },
        Line {
           startX: MazeData.calcGridX(MazeData.GRID_SIZE + 0.5)
           endX: MazeData.calcGridX(MazeData.GRID_SIZE + 0.5)
           startY: MazeData.calcGridY(13)
           endY: MazeData.calcGridY(15)
           stroke: <span class="category2">Color</span>.BLACK
           strokeWidth: MazeData.GRID_STROKE + 1
         },
        Line {
           startX: MazeData.calcGridX(-0.5)
           endX: MazeData.calcGridX(0)
           startY: MazeData.calcGridY(13)
           endY: MazeData.calcGridY(13)
           stroke: <span class="category2">Color</span>.BLUE
           strokeWidth: MazeData.GRID_STROKE
         },
        Line {
           startX: MazeData.calcGridX(-0.5)
           endX: MazeData.calcGridX(0)
           startY: MazeData.calcGridY(15)
           endY: MazeData.calcGridY(15)
           stroke: <span class="category2">Color</span>.BLUE
           strokeWidth: MazeData.GRID_STROKE
         },
        Line {
           startX: MazeData.calcGridX(MazeData.GRID_SIZE + 0.5)
           endX: MazeData.calcGridX(MazeData.GRID_SIZE)
           startY: MazeData.calcGridY(13)
           endY: MazeData.calcGridY(13)
           stroke: <span class="category2">Color</span>.BLUE
           strokeWidth: MazeData.GRID_STROKE
         },
        Line {
           startX: MazeData.calcGridX(MazeData.GRID_SIZE + 0.5)
           endX: MazeData.calcGridX(MazeData.GRID_SIZE)
           startY: MazeData.calcGridY(15)
           endY: MazeData.calcGridY(15)
           stroke: <span class="category2">Color</span>.BLUE
           strokeWidth: MazeData.GRID_STROKE
         },
  
      ]
    }; <span class="linecomment">// end Group</span>
 
   <span class="category1">public</span> override <span class="category1">function</span> create(): Node {
         <span class="category1">return</span> group;
    } <span class="linecomment">// end create()</span>
 
}</pre>
</code>

</div></div> 

<p>Now we'll write a Main class to put the maze onto the stage and display it:</p>


<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="blockcomment">/*
 * Main.fx
 *
 * Created on 2008-12-20, 12:02:26
 */</span>

package pacman;

<span class="category1">import</span> javafx.scene.Scene;
<span class="category1">import</span> javafx.stage.<span class="category2">Stage</span>;

<span class="blockcomment">/**
 * @author Henry Zhang
 */</span>

<span class="category2">Stage</span>{
     title: "<span class="quote">PACMAN</span>"
     <span class="category2">width</span>: MazeData.calcGridX(MazeData.GRID_SIZE + 2)
     <span class="category2">height</span>: MazeData.calcGridY(MazeData.GRID_SIZE + 5)
     scene: Scene{
              content: [ Maze {}
              ]
             }
}</pre>
</code>

</div></div> 


<p>Run the program and we have the first version of our maze: </p>
<p>
<img src="http://www.insideria.com/upload/2009/04/maze1.jpg"><BR><BR>
<strong><a href="http://www.javafxgame.com/v1/pacman.jnlp">Launch</a></strong>
</P>

<p>The maze is almost done except that some lines are overlapping each other. 
This is not a problem, because we can put some black lines and rectangles to hide 
those overlapping areas so that the maze looks nice. Let's write two classes, 
<tt><b>WallBlackRectangle</b></tt> and <tt><b>WallBlackLine</b></tt>, for this purpose. The <tt><b>WallBlackRectangle</b></tt> 
class covers a rectangular area. The <tt><b>WallBlackLine</b></tt> class draws a black line in the 
maze. In the previous section, our <tt><b>WallRectangle</b></tt> class extends the <tt><b>CustomNode</b></tt> class. 
We do the same thing for the <tt><b>WallBlackRectangle</b></tt> here. The <tt><b>WallBlackLine</b></tt> class demonstrates 
another way to achieve the same functionality. We subclass the JavaFX <tt><b>Line</b></tt> class and use 
a <tt><b>postinit</b></tt> block (which is invoked upon instantiation after the instance variables have been 
assigned values) to change the details of the <tt><b>Line</b></tt> object.</p>


<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="blockcomment">/*
 * WallBlackRectangle.fx
 *
 * Created on 2008-12-27, 16:35:42
 */</span>

package pacman;

<span class="category1">import</span> javafx.scene.CustomNode;
<span class="category1">import</span> javafx.scene.Node;
<span class="category1">import</span> javafx.scene.paint.<span class="category2">Color</span>;
<span class="category1">import</span> javafx.scene.shape.Rectangle;
<span class="category1">import</span> pacman.MazeData;

<span class="blockcomment">/**
 * @author Henry Zhang
 */</span>

<span class="category1">public</span> <span class="category1">class</span> WallBlackRectangle <span class="category1">extends</span> CustomNode {
 
   <span class="category1">public</span> <span class="category1">var</span> x1: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> y1: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> x2: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> y2: <span class="category2">Number</span>;
 
   <span class="category1">public</span> override <span class="category1">function</span> create(): Node {
      Rectangle {
         <span class="category2">x</span>: MazeData.calcGridX(x1) + MazeData.GRID_STROKE
         <span class="category2">y</span>: MazeData.calcGridY(y1) + MazeData.GRID_STROKE
         <span class="category2">width</span>: MazeData.GRID_GAP * (x2-x1) - MazeData.GRID_STROKE * 2
         <span class="category2">height</span>: MazeData.GRID_GAP * (y2-y1) - MazeData.GRID_STROKE * 2
         strokeWidth: MazeData.GRID_STROKE
         stroke: <span class="category2">Color</span>.BLACK
         arcWidth: 3
         arcHeight: 3
       }  
    }
}</pre>
</code>

</div></div> 

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="blockcomment">/*
 * WallBlackLine.fx
 *
 * Created on 2008-12-27, 17:52:58
 */</span>

package pacman;

<span class="category1">import</span> javafx.scene.paint.<span class="category2">Color</span>;
<span class="category1">import</span> javafx.scene.shape.Line;
<span class="category1">import</span> pacman.MazeData;

<span class="blockcomment">/**
 * @author Henry Zhang
 */</span>

<span class="category1">public</span> <span class="category1">class</span> WallBlackLine <span class="category1">extends</span> Line {
 
   <span class="category1">public</span> <span class="category1">var</span> x1: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> y1: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> x2: <span class="category2">Number</span>;
   <span class="category1">public</span> <span class="category1">var</span> y2: <span class="category2">Number</span>;
 
   postinit {
      strokeWidth = MazeData.GRID_STROKE + 1;
      stroke = <span class="category2">Color</span>.BLACK;
          
      <span class="category1">if</span> ( x1 == x2 ) { <span class="linecomment">// vertically line</span>
         startX = MazeData.calcGridX(x1);
         startY = MazeData.calcGridY(y1) + MazeData.GRID_STROKE;
         endX = MazeData.calcGridX(x2);
         endY = MazeData.calcGridY(y2) - MazeData.GRID_STROKE;
       }
      <span class="category1">else</span>  { <span class="linecomment">// horizontal line</span>
         startX = MazeData.calcGridX(x1) + MazeData.GRID_STROKE;
         startY = MazeData.calcGridY(y1);
         endX = MazeData.calcGridX(x2) - MazeData.GRID_STROKE;
         endY = MazeData.calcGridY(y2);
       }
    } <span class="linecomment">// end postinit</span>
}</pre>
</code>

</div></div> 

<p>In the <tt><b>Maze</b></tt> class, we put in some instances of the above classes into the group variable:</p>

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre>   
<span class="category1">var</span> group : Group =
  Group {
     content: [
       Rectangle {
          <span class="category2">x</span>:0
          <span class="category2">y</span>:0
          <span class="category2">width</span>: MazeData.calcGridX(MazeData.GRID_SIZE + 2)
          <span class="category2">height</span>: MazeData.calcGridY(MazeData.GRID_SIZE + 3)
          fill: <span class="category2">Color</span>.BLACK
        },
 
       WallRectangle{ x1:0 y1:0 x2:MazeData.GRID_SIZE y2:MazeData.GRID_SIZE },
 
       WallRectangle { x1:14 y1:-0.5 x2:15 y2:4 },
       WallBlackRectangle { x1:13.8 y1:-1 x2:15.3 y2:0 },
 
       WallRectangle { x1:2 y1:2 x2:5 y2:4 },
       WallRectangle { x1:7 y1:2 x2:12 y2:4 },
       WallRectangle { x1:17 y1:2 x2:22 y2:4 },
       WallRectangle { x1:24 y1:2 x2:27 y2:4 },
       WallRectangle { x1:2 y1:6 x2:5 y2:7 },
 
       WallRectangle { x1:14 y1:6.2 x2:15 y2:10 },
       WallRectangle { x1:10 y1:6 x2:19 y2:7 },
       WallBlackLine { x1:14 y1:7 x2:15 y2:7 },
 
       WallRectangle { x1:7.5 y1:9 x2:12 y2:10 },
       WallRectangle { x1:7 y1:6 x2:8 y2:13 },
       WallBlackLine { x1:8 y1:9 x2:8 y2:10 },
 
       WallRectangle { x1:17 y1:9 x2:21.5 y2:10 },
       WallRectangle { x1:21 y1:6 x2:22 y2:13 },
       WallBlackLine { x1:21 y1:9 x2:21 y2:10 },
 
       WallRectangle { x1:24 y1:6 x2:27 y2:7 },
 
       WallRectangle { x1:-1 y1:9 x2:5 y2:13 },
       WallRectangle { x1:24 y1:9 x2:MazeData.GRID_SIZE + 1 y2:13 },
       WallBlackLine { x1:0 y1:13 x2:0 y2:15  },
       WallBlackLine { x1:MazeData.GRID_SIZE y1:13 x2:MazeData.GRID_SIZE y2:15},
 
       <span class="linecomment">//cage and the gate</span>
       WallRectangle { x1:10 y1:12 x2:19 y2:17 },
       WallRectangle { x1:10.5 y1:12.5 x2:18.5 y2:16.5 },
       Rectangle {
          <span class="category2">x</span>: MazeData.calcGridX(13)
          <span class="category2">width</span>: MazeData.GRID_GAP * 3
          <span class="category2">y</span>: MazeData.calcGridY(12)
          <span class="category2">height</span>: MazeData.GRID_GAP / 2
          stroke: <span class="category2">Color</span>.GREY
          fill: <span class="category2">Color</span>.GREY
        },
 
       WallRectangle { x1:7.5 y1:19 x2:12 y2:20 },
       WallRectangle { x1:7 y1:15 x2:8 y2:23 },
       WallBlackLine { x1:8 y1:19 x2:8 y2:20 },
 
       WallRectangle { x1:17 y1:19 x2:21.5 y2:20 },
       WallRectangle { x1:21 y1:15 x2:22 y2:23 },
       WallBlackLine { x1:21 y1:19 x2:21 y2:20 },
 
       WallRectangle { x1:14 y1:19 x2:15 y2:27 },
       WallRectangle { x1:10 y1:22 x2:19 y2:23 },
       WallBlackLine { x1:14 y1:22 x2:15 y2:22 },
       WallBlackLine { x1:14 y1:23 x2:15 y2:23 },
 
       WallRectangle { x1:2 y1:25 x2:5 y2:27 },
       WallRectangle { x1:17 y1:25 x2:22 y2:27 },
 
       WallRectangle { x1:7 y1:25 x2:12 y2:27 },
       WallRectangle { x1:24 y1:25 x2:27 y2:27 },
 
       WallRectangle { x1:-1 y1:15 x2:5 y2:17 },
       WallRectangle { x1:4 y1:19 x2:5 y2:23 },
       WallRectangle { x1:2 y1:19 x2:4.5 y2:20 },
       WallBlackRectangle { x1:4 y1:19.05 x2:5 y2:20.2 },
       WallRectangle { x1:-1 y1:22 x2:2 y2:23 },
 
       WallRectangle { x1:24 y1:15 x2:MazeData.GRID_SIZE + 1 y2:17 },
       WallRectangle { x1:24 y1:19 x2:25 y2:23 },
       WallRectangle { x1:24.5 y1:19 x2:27 y2:20 },
       WallBlackRectangle { x1:24 y1:19.05 x2:25 y2:20.2 },
       WallRectangle { x1:27 y1:22 x2:MazeData.GRID_SIZE + 1 y2:23 },
 
       WallBlackRectangle { x1:-2 y1:8 x2:0 y2:MazeData.GRID_SIZE },
       WallBlackRectangle {
           x1:MazeData.GRID_SIZE
           y1:8
           x2:MazeData.GRID_SIZE + 2
           y2:MazeData.GRID_SIZE
        },
 
       Rectangle {
          <span class="category2">x</span>: MazeData.calcGridX(-0.5)
          <span class="category2">y</span>: MazeData.calcGridY(-0.5)
          <span class="category2">width</span>: (MazeData.GRID_SIZE + 1) * MazeData.GRID_GAP
          <span class="category2">height</span>: (MazeData.GRID_SIZE + 1) * MazeData.GRID_GAP
          strokeWidth: MazeData.GRID_STROKE
          stroke: <span class="category2">Color</span>.BLUE
          fill: <span class="category1">null</span>
          arcWidth: 12
          arcHeight: 12
        },
       Line {
          startX: MazeData.calcGridX(-0.5)
          endX: MazeData.calcGridX(-0.5)
          startY: MazeData.calcGridY(13)
          endY: MazeData.calcGridY(15)
          stroke: <span class="category2">Color</span>.BLACK
          strokeWidth: MazeData.GRID_STROKE + 1
        },
       Line {
          startX: MazeData.calcGridX(MazeData.GRID_SIZE + 0.5)
          endX: MazeData.calcGridX(MazeData.GRID_SIZE + 0.5)
          startY: MazeData.calcGridY(13)
          endY: MazeData.calcGridY(15)
          stroke: <span class="category2">Color</span>.BLACK
          strokeWidth: MazeData.GRID_STROKE + 1
        },
       Line {
          startX: MazeData.calcGridX(-0.5)
          endX: MazeData.calcGridX(0)
          startY: MazeData.calcGridY(13)
          endY: MazeData.calcGridY(13)
          stroke: <span class="category2">Color</span>.BLUE
          strokeWidth: MazeData.GRID_STROKE
        },
       Line {
          startX: MazeData.calcGridX(-0.5)
          endX: MazeData.calcGridX(0)
          startY: MazeData.calcGridY(15)
          endY: MazeData.calcGridY(15)
          stroke: <span class="category2">Color</span>.BLUE
          strokeWidth: MazeData.GRID_STROKE
        },
       Line {
          startX: MazeData.calcGridX(MazeData.GRID_SIZE + 0.5)
          endX: MazeData.calcGridX(MazeData.GRID_SIZE)
          startY: MazeData.calcGridY(13)
          endY: MazeData.calcGridY(13)
          stroke: <span class="category2">Color</span>.BLUE
          strokeWidth: MazeData.GRID_STROKE
        },
       Line {
          startX: MazeData.calcGridX(MazeData.GRID_SIZE + 0.5)
          endX: MazeData.calcGridX(MazeData.GRID_SIZE)
          startY: MazeData.calcGridY(15)
          endY: MazeData.calcGridY(15)
          stroke: <span class="category2">Color</span>.BLUE
          strokeWidth: MazeData.GRID_STROKE
        },
 
     ]
   }; <span class="linecomment">// end Group</span></span></pre>
</code>

</div></div> 

<p>After these adjustments, we have the Pac-Man maze shown below:</p>
<p><img src="http://www.insideria.com/upload/2009/04/maze2.jpg">
<BR><BR><strong><a href="http://www.javafxgame.com/v1.5/pacman.jnlp">Launch</a></strong>
</p>


<p>Before moving forward to the next step, I would like to initialize the data model as we draw the maze, i.e. to set the points related to a wall to a value of <tt><b>BLOCK</b></tt>. One of the benefits of doing 
so is that the data model is always in sync with the GUI. If you want to modify the layout of the 
maze later, you can just change the drawing code and the data model is adjusted automatically. 
This can be achieved by adding a <tt><b>postinit</b></tt> block into the <tt><b>WallRectangle</b></tt> class:</p>

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="category1">public</span> <span class="category1">class</span> WallRectangle <span class="category1">extends</span> CustomNode {
 
   . . . . . .
 
   postinit {
      <span class="linecomment">// initialize the data model while drawing the maze</span>
      MazeData.setBlockMazeData(x1, y1, x2, y2);
    }
 
   . . . . . .
 
}</pre>
</code>

</div></div> 

<p>The method <tt>MazeData.setBlockMazeData(x1,y1,x2,y2)</tt> updates the data model by 
setting all of the points of a rectangle to the value <tt><b>BLOCK</b></tt>. The values 
<tt><b>x1</b></tt>, <tt><b>y1</b></tt> and <tt><b>x2</b></tt>, <tt><b>y2</b></tt> are the coordinates of the rectangle's two corners.</p>


<h3>Drawing the Dots</h3>

<p>Now that the maze is drawn, let's work on the dots. There are two types of dots in the game: 
normal dots and magic dots. The magic dots are bigger in size and they continually flash. 
If the Pac-Man character gobbles the magic dots, he has the power to eat ghosts for a short 
period of time. Our <tt><b>Dot</b></tt> class extends the <tt><b>CustomNode</b></tt> class, a
nd adds functionality to achieve the desired behavior. Take a look at the source code in <b>Dot.fx</b>, 
shown below: </p>

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="blockcomment">/*
 * Dot.fx
 *
 * Created on 2008-12-21, 21:59:45
 */</span>

package pacman;

<span class="blockcomment">/**
 * @author Henry Zhang
 */</span>

<span class="category1">import</span> java.lang.<span class="category2">Math</span>;
<span class="category1">import</span> javafx.animation.KeyFrame;
<span class="category1">import</span> javafx.animation.Timeline;
<span class="category1">import</span> javafx.scene.CustomNode;
<span class="category1">import</span> javafx.scene.Node;
<span class="category1">import</span> javafx.scene.paint.<span class="category2">Color</span>;
<span class="category1">import</span> javafx.scene.shape.Circle;

<span class="category1">public</span> <span class="category1">class</span> Dot <span class="category1">extends</span> CustomNode {
 
   <span class="category1">public</span> <span class="category1">var</span> dotType: Integer;
 
   <span class="linecomment">// location of the dot</span>
   <span class="category1">public</span> <span class="category1">var</span> <span class="category2">x</span> : <span class="category2">Number</span> ;
   <span class="category1">public</span> <span class="category1">var</span> <span class="category2">y</span> : <span class="category2">Number</span> ;
 
   <span class="linecomment">// radius of the dot</span>
   <span class="category1">public</span> <span class="category1">var</span> r: <span class="category2">Number</span> =
   <span class="category1">if</span> ( dotType == MazeData.MAGIC_DOT ) 5 <span class="category1">else</span> 1;
 
   <span class="linecomment">// the dot</span>
   <span class="category1">var</span> circle = Circle{
      centerX: <span class="category2">x</span>
      centerY: <span class="category2">y</span>
      radius: bind r
      fill: <span class="category2">Color</span>.YELLOW
      <span class="category2">visible</span>: bind <span class="category2">visible</span>   <span class="linecomment">// bind to Dot.visible</span>
      } ;
 
   <span class="linecomment">// variables for magic dot's growing/shrinking animation</span>
   <span class="category1">public</span> <span class="category1">var</span> animationRadius: <span class="category2">Number</span> = 3;
   <span class="category1">public</span> <span class="category1">var</span> delta: <span class="category2">Number</span> = -1;
   <span class="category1">var</span> timeline: Timeline;
 
   <span class="linecomment">// create the animation timeline for magic dot</span>
   <span class="category1">public</span> <span class="category1">function</span> createTimeline(): Timeline {
      Timeline {
         repeatCount: Timeline.INDEFINITE
         keyFrames: [
           KeyFrame {
              <span class="category2">time</span>: 250ms
              action: <span class="category1">function</span>() {
                 doOneTick();
               }
            }
         ]
           }
    }
 
   <span class="category1">public</span> <span class="category1">function</span> playTimeline() {
      <span class="category1">if</span> ( timeline == <span class="category1">null</span> )
      timeline = createTimeline();
  
      timeline.<span class="category2">play</span>();
    }
 
   <span class="linecomment">// do the animation</span>
   <span class="category1">public</span> <span class="category1">function</span> doOneTick () {
  
      <span class="category1">if</span> ( <span class="category2">visible</span> == <span class="category1">false</span> )
      <span class="category1">return</span>;
  
      animationRadius += delta;
      <span class="category1">var</span> <span class="category2">x</span> = <span class="category2">Math</span>.<span class="category2">abs</span>(animationRadius) + 3;
  
      <span class="category1">if</span> ( <span class="category2">x</span> &gt; 5 ) {
         delta = -delta;
       }
  
      r = <span class="category2">x</span>;
    }
 
   <span class="category1">public</span> override <span class="category1">function</span> create(): Node {
          <span class="category1">return</span> circle;
    }
}</pre>
</code>

</div></div> 


<p>The <tt><b>Circle</b></tt> class is used to display the dots. For a magic dot, 
we continually change its radius to create the blinking effect. A <tt><b>Timeline</b></tt> instance generates 
an animation frame every 250ms. The <tt><b>doOneTick()</b></tt> function is invoked each time 
to adjust the value of the radius. Binding, an important feature of JavaFX, is used in 
the <tt><b>Circle</b></tt> object to link it with the data model. During an animation 
process, there is no need to manually update the GUI object because its radius is bound 
to the model. </p>


<p>Now that the <tt><b>Dot</b></tt> class is ready, we can put dots into the maze. 
To accomplish this, we'll add the three functions shown below to the <tt><b>Maze</b></tt> class:</p>


<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="linecomment">// create a Dot GUI object</span>
  <span class="category1">public</span> <span class="category1">function</span> createDot(  x1: <span class="category2">Number</span>,  y1:<span class="category2">Number</span>, <span class="category2">type</span>:Integer ): Dot {
     <span class="category1">var</span> d = Dot {
        <span class="category2">x</span>: MazeData.calcGridX(x1)
        <span class="category2">y</span>: MazeData.calcGridY(y1)
        dotType: <span class="category2">type</span>
        <span class="category2">visible</span>: <span class="category1">true</span>
        }
 
     <span class="category1">if</span> ( d.dotType == MazeData.MAGIC_DOT )
       d.playTimeline();
 
 <span class="linecomment">// set the dot type in data model</span>
 MazeData.<span class="category2">setData</span>( <span class="category2">x</span>, <span class="category2">y</span>, dotType ) ;
 
     <span class="category1">return</span> d;
   }

  <span class="linecomment">// put dots into the maze as a horizontal line</span>
  <span class="category1">public</span> <span class="category1">function</span> putDotHorizontally(x1: Integer, x2: Integer, <span class="category2">y</span>: <span class="category2">Number</span> ) {
 
     <span class="category1">var</span> dots =
     <span class="category1">for</span> ( <span class="category2">x</span> <span class="category1">in</span> [ x1..x2] )
       <span class="category1">if</span> ( MazeData.<span class="category2">getData</span>(<span class="category2">x</span>,<span class="category2">y</span>) == MazeData.EMPTY ) {
          <span class="category1">var</span> dotType: Integer;
  
          <span class="category1">if</span> ( (<span class="category2">x</span> == 28 <span class="category1">or</span> <span class="category2">x</span> == 1) <span class="category1">and</span> (<span class="category2">y</span> == 3 <span class="category1">or</span> <span class="category2">y</span> == 26) )
            dotType = MazeData.MAGIC_DOT
          <span class="category1">else</span>
            dotType = MazeData.NORMAL_DOT;
  
          createDot( <span class="category2">x</span>, <span class="category2">y</span>, dotType )
        }
       <span class="category1">else</span>   [] ;
 
     insert dots into group.content;
   }

  <span class="linecomment">// put dots into the maze as a vertical line</span>
  <span class="category1">public</span> <span class="category1">function</span> putDotVertically(<span class="category2">x</span>: Integer, y1: Integer, y2: <span class="category2">Number</span> ) {
 
     <span class="category1">var</span> dots =
     <span class="category1">for</span> ( <span class="category2">y</span> <span class="category1">in</span> [ y1..y2] )
       <span class="category1">if</span> ( MazeData.<span class="category2">getData</span>(<span class="category2">x</span>,<span class="category2">y</span>) == MazeData.EMPTY ) {
          <span class="category1">var</span> dotType: Integer;
  
          <span class="category1">if</span> ( (<span class="category2">x</span> == 28 <span class="category1">or</span> <span class="category2">x</span> == 1) <span class="category1">and</span> (<span class="category2">y</span> == 3 <span class="category1">or</span> <span class="category2">y</span> == 26) )
            dotType = MazeData.MAGIC_DOT
          <span class="category1">else</span>
            dotType = MazeData.NORMAL_DOT;
  
          createDot( <span class="category2">x</span>, <span class="category2">y</span>, dotType )
        }
       <span class="category1">else</span>  [];
 
     insert dots into group.content;
   }</pre>
</code>

</div></div> 



<p>The <tt><b>createDot()</b></tt> function creates a <tt><b>Dot</b></tt> object based on its 
coordinates (x,y) and its type (<tt><b>NORMAL_DOT</b></tt> or <tt><b>MAGIC_DOT</b></tt>). Again, while we are creating the dots, 
we bind the dot status to our data model with the following statement:</p>
<pre>
  // set the dot type to data model 
  MazeData.setData( x, y, dotType ) ;</pre>


<p>The <tt><b>putDotHorizontally()</b></tt> function places a horizontal line of dots into the maze and makes four of them the magic dots. </p>

<p>The <tt><b>putDotVertically()</b></tt> function is almost the same as <tt><b>putDotHorizontally()</b></tt> except that it puts dots in a vertical fashion.</p>

<p>Last thing is to put all the dots into the maze. We add some code to the <tt><b>postinit</b></tt> block of the <tt><b>Maze</b></tt> class:</p>

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="category1">public</span> <span class="category1">class</span> Maze <span class="category1">extends</span> CustomNode {
 
    . . . . . . 
 
   <span class="linecomment">// put dots into the maze </span>
   postinit { 
      putDotHorizontally(2,13,1); 
      putDotHorizontally(16,27,1); 
      putDotHorizontally(2,27,5); 
      putDotHorizontally(2,27,28);
  
      putDotHorizontally(2,13,24); 
      putDotHorizontally(16,27,24);
  
      putDotHorizontally(2,5,8); 
      putDotHorizontally(9,13,8); 
      putDotHorizontally(16,20,8); 
      putDotHorizontally(24,27,8);
  
      putDotHorizontally(2,5,18); 
      putDotHorizontally(9,13,21); 
      putDotHorizontally(16,20,21); 
      putDotHorizontally(24,27,18);
  
      putDotHorizontally(2,3,21); 
      putDotHorizontally(26,27,21);
  
      putDotVertically(1,1,8); 
      putDotVertically(1,18,21); 
      putDotVertically(1,24,28);
  
      putDotVertically(28,1,8); 
      putDotVertically(28,18,21); 
      putDotVertically(28,24,28);
  
      putDotVertically(6,2,27); 
      putDotVertically(23,2,27);
  
      putDotVertically(3,22,23); 
      putDotVertically(9,22,23); 
      putDotVertically(20,22,23); 
      putDotVertically(26,22,23);
  
      putDotVertically(13,25,27); 
      putDotVertically(16,25,27);
  
      putDotVertically(9,6,7); 
      putDotVertically(20,6,7);
  
      putDotVertically(13,2,4); 
      putDotVertically(16,2,4);
    } 
   . . . . . 
}</pre>
</code>

</div></div> 

<p>If you'd like to see the result so far, run the program and you'll get a maze populated with dots, four of which are the flashing magic dots: </p>
<p><img src="http://www.insideria.com/upload/2009/04/maze3.jpg">
<BR><BR>
<strong><a href="http://www.javafxgame.com/v2/pacman.jnlp">Launch</a></strong>
</p>

<h3>Building an Index of Dot References</h3>

<p>In preparation for the next phase,we need to do one more thing. During the game, we need a fast way to get the reference of a <tt><b>Dot</b></tt> object at point (x, y). Our current code does not support an efficient reference. So in <b>MazeData.java</b>, 
we define a 2D array dotPointers to store the references to these dots. 
Two accessor methods are added as well, using the <tt><b>Object</b></tt> type to store the references to JavaFX Dot instances.</p>

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="category1">public</span> <span class="category1">class</span> MazeData {
 
   . . . . . . 
 
   <span class="category1">static</span> <span class="category2">Object</span> dotPointers[][] = <span class="category1">new</span> <span class="category2">Object</span>[GRID_SIZE + 1][GRID_SIZE + 1];
 
   . . . . . .
 
   <span class="category1">public</span> <span class="category1">static</span> <span class="category2">Object</span> getDot(<span class="category1">int</span> <span class="category2">x</span>, <span class="category1">int</span> <span class="category2">y</span>) { 
      <span class="category1">return</span> dotPointers[<span class="category2">x</span>][<span class="category2">y</span>]; 
    }
 
   <span class="category1">public</span> <span class="category1">static</span> <span class="category1">void</span> setDot(<span class="category1">int</span> <span class="category2">x</span>, <span class="category1">int</span> <span class="category2">y</span>, <span class="category2">Object</span> dot) { 
      dotPointers[<span class="category2">x</span>][<span class="category2">y</span>] = dot; 
    }
 
   . . . . . .
}</pre>
</code>

</div></div>
 
<p>In <tt><b>Maze.createDot()</b></tt>, we add a line to update the pointer in the data model:</p>

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre> 
<span class="category1">public</span> <span class="category1">function</span> createDot( x1: <span class="category2">Number</span>, y1:<span class="category2">Number</span>, <span class="category2">type</span>:Integer ): Dot { 
     <span class="category1">var</span> d = Dot { 
        <span class="category2">x</span>: MazeData.calcGridX(x1) 
        <span class="category2">y</span>: MazeData.calcGridY(y1) 
        dotType: <span class="category2">type</span> 
        <span class="category2">visible</span>: <span class="category1">true</span> 
        }
 
     <span class="category1">if</span> ( d.dotType == MazeData.MAGIC_DOT ) 
       d.playTimeline();
 
     <span class="linecomment">// set the dot type in data model </span>
     MazeData.<span class="category2">setData</span>( x1, y1, <span class="category2">type</span> );
 
     &lt;b&gt;<span class="linecomment">// set dot reference </span>
     MazeData.setDot( x1, y1, d );&lt;/b&gt;
 
     <span class="category1">return</span> d; 
   } </pre>
</code>

</div></div>

<p>Congratulations! You've completed the first phase of the Pac-Man game in which a maze and its dots are drawn. In subsequent articles, we will introduce the Pac-Man character and ghosts.</p>

<p><a href="http://www.insideria.com/upload/2009/05/javafxsource1.zip">Download Source Code</a></p>

<p><strong>Related Features</strong></p>
<ul><li><a href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja-1.html">Writing the Pac-Man Game in JavaFX - Part 2</a></li></ul>]]>
      
    </content>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.35762-comment:2059612</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.35762" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html"/>
    <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html#comment-2059612" />
    <title>Comment from Won C. Lee on 2009-05-14</title>
    <author>
        <name>Won C. Lee</name>
        <uri></uri>
    </author>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[<p>JavaFX syntax looks like Actionscript3. very resembles. I hope Oracle not let JavaFX dying. Good share!</p>]]>
    </content>
    <published>2009-05-14T13:51:28Z</published>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.35762-comment:2059738</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.35762" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html"/>
    <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html#comment-2059738" />
    <title>Comment from Priya on 2009-05-15</title>
    <author>
        <name>Priya</name>
        <uri>http://java-interview-faqs.blogspot.com</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="http://java-interview-faqs.blogspot.com">
        <![CDATA[<p>I did pacman 10 years back as my college project...but it was in C and we really worked hard to come up with something which looked like pacman...but it was a great pleasure playing that game we developed ourselves...even though it used to hang once in a while :)</p>

<p>nice post...</p>]]>
    </content>
    <published>2009-05-16T00:28:32Z</published>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.35762-comment:2059763</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.35762" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html"/>
    <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html#comment-2059763" />
    <title>Comment from Dmitri Trembovetski on 2009-05-16</title>
    <author>
        <name>Dmitri Trembovetski</name>
        <uri></uri>
    </author>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[<p>Very nice!</p>

<p>One comment regarding the timeline for dot animation: you can just animate the radius of the circle directly instead of calculating it yourself.<br />
Something like this:<br />
Timeline {<br />
    repeatCount : Timeline.INDEFINITE<br />
    autoReverse: true<br />
    keyFrames: [<br />
        at (0s) { circle.radius => 3 }<br />
        at (0.5s) { circle.radius => 5 }<br />
    ]<br />
};<br />
</p>]]>
    </content>
    <published>2009-05-16T21:59:45Z</published>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.35762-comment:2060567</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.35762" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html"/>
    <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html#comment-2060567" />
    <title>Comment from Mariusz on 2009-05-28</title>
    <author>
        <name>Mariusz</name>
        <uri>http://www.sizeof.pl</uri>
    </author>
    <content type="html" xml:lang="en" xml:base="http://www.sizeof.pl">
        <![CDATA[<p>Great work! </p>]]>
    </content>
    <published>2009-05-28T13:28:27Z</published>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.35762-comment:2068855</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.35762" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html"/>
    <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html#comment-2068855" />
    <title>Comment from Liang on 2009-07-23</title>
    <author>
        <name>Liang</name>
        <uri></uri>
    </author>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[<p>I followed your process step by step, it is a great tutorial!</p>]]>
    </content>
    <published>2009-07-23T09:02:49Z</published>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.35762-comment:2069317</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.35762" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html"/>
    <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html#comment-2069317" />
    <title>Comment from Neeraj on 2009-07-28</title>
    <author>
        <name>Neeraj</name>
        <uri></uri>
    </author>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[<p><b>Pretty good example.</b> </p>]]>
    </content>
    <published>2009-07-28T16:37:50Z</published>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.35762-comment:2088492</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.35762" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html"/>
    <link rel="alternate" type="text/html" href="http://www.insideria.com/2009/05/writing-the-pac-man-game-in-ja.html#comment-2088492" />
    <title>Comment from Anonymous on 2009-09-08</title>
    <author>
        <name>Anonymous</name>
        <uri></uri>
    </author>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[<p>super!</p>]]>
    </content>
    <published>2009-09-08T13:50:44Z</published>
  </entry>

</feed
