Saturday, May 16, 2020

Making Pent Up Anger Part 3 of 5

Making Pent Up Anger part 3 of 5 - Head to Head version of the Game



This is the third part of a five part series on building Pent Up Anger using AnimateCC. The game can be found at http://spelchan.com/games/y2018/PentUpAnger.php .

Preparing the Pieces

The first thing we are going to need to do to get a head-to-head (hot seat) game going is to have more than a single piece on the board. This means that all the pieces for all five potential players will need to be tracked. An array to hold this information is easy enough to build. The big question is where should this information live? It could be a good idea to keep the board separate from the game state and instead have a separate class that manages the game play. This would have the advantage that you would be able to have different variants of the game and that controller would handle the game logic. This is going a bit too overboard and is something that could be done through refactoring at a later point if such a thing ended up being desired so we will follow the principle of only writing the code that we actually need to accomplish our task and put the game logic into the board class.

This means adding a bit of code to the board initialization class to set up the 25 game pieces that are necessary. While I don't like getting ahead of myself, we know that we are going to need to be able to click on the pieces and that this will require a listener so we will set up the click listener right now even though we won't be implementing it until later. The handler for clicking is going to be called choosePiece and we will need. A dummy version of the choosePiece method should be created with a simple console log warning you that the method has not yet been implemented.

As I stated earlier, to use a method as a callback function, you need to bind the method to the class being used. This has a rather strange behavior of being considered a different function call each time it is created which means that while you can add event listeners, removing those listeners is not possible unless you keep a copy of the originally bound function. This information is stored in the pieceListeners field and is used as the event listener.


this.boardMovie = movie;
this.pieces = [
 [movie.p1, movie.p2, movie.p3, movie.p4, movie.p5],
 [movie.r1, movie.r2, movie.r3, movie.r4, movie.r5],
 [movie.y1, movie.y2, movie.y3, movie.y4, movie.y5],
 [movie.g1, movie.g2, movie.g3, movie.g4, movie.g5],
 [movie.b1, movie.b2, movie.b3, movie.b4, movie.b5]
];
this.pieceListeners = [];
this.player = 23;
for (var cntrPlayer = 0; cntrPlayer < 5; ++cntrPlayer) {
this.pieceListeners[cntrPlayer] = [];
for (var cntr = 0; cntr < 5; ++cntr) {
this.pieces[cntrPlayer][cntr].playerID.text = ""+(cntr+1);
this.pieces[cntrPlayer][cntr].useHighlight.visible = false;
this.pieces[cntrPlayer][cntr].playerID = cntrPlayer;
this.pieces[cntrPlayer][cntr].pieceID = cntr;
this.pieceListeners[cntrPlayer][cntr] = this.choosePiece.bind(this, cntrPlayer, cntr);
this.pieces[cntrPlayer][cntr].addEventListener("click", this.pieceListeners[cntrPlayer][cntr]);
}
this.pieces[cntrPlayer][cntrPlayer].useHighlight.visible = true;
}

At the end of initialization, the pieces used by the player exist in a two dimensional array named pieces. This holds the pieces and relevant information about the pieces but does not set up the pieces. This needs to be done to start the game, and as we want to let the player play the game more than once without having to reload the game it makes sense to have a separate method for setting up the starting board. This we will call layoutPieces. This simply loops through the players and their pieces  and then places those pieces on the board in the appropriate starting location. It then loops through all the locations on the board and makes sure that the piece currently assigned to that location is null. This is important as only one piece is allowed on each board location so tracking the piece on a location is required to determine if a piece is going to be sent home.

spelchan.Board.prototype.layoutPieces = function() {
var temp;
for (var cntrcol = 0; cntrcol < 5; ++cntrcol)
{
for (var cntr = 0; cntr < 5; ++cntr)
{
temp = this.BOARD_LAYOUT[cntrcol][this.BOARD_START_INDEX] + cntr;
this.pieces[cntrcol][cntr].x = this.layout_x[temp];
this.pieces[cntrcol][cntr].y = this.layout_y[temp];
this.pieces[cntrcol][cntr].location = temp;
console.log("placing piece on " + temp);
}
}
for (cntr = 0; cntr < 105; ++cntr)
this.layout_piece[cntr] = null;
}

Now that we have the pieces able to appear on the board, the next step is to actually handle the player's turn.


Turn Handling

With the player pieces laid out on the board, we are ready to get the game under way. But for the players to do anything they need to be able to move. This requires a way for the player to roll the die. This can be combined with our solution for determining which players’ turn it is by simply having a roll button for each player. This means that we need to create a roll button for each of the five players. Instead of creating five buttons, we can ``cheat'' by only create one grayscale version of the play button and tint it. The states for this button can be seen in the image below. In addition to the roll button, we will need a skip button for skipping your turn which is set up the same as the roll button.

To make use of this button, it actually has on the screen. This is done in animate by adding the buttons to the Only the player who currently is playing can use this button, only the roll button for the current player needs to be on the screen. This is done by manually positioning the components on the game screen symbol as shown in the image below. Note that this screen shot is from later in development so there are messages and a button that would not normally be on the screen at this time.



This, in my opinion, is user interface related and is kept in the main Pent class.  This is set up allows us to set up in the startGameScreen which is the method we use for initializing the main game screen when the game is being set up. This simply sets up the pieces of the board and then sets up all of the roll and skip buttons.

spelchan.Pent.prototype.startGameScreen = function(movie) {
this.gameMovie = movie;
this.currentPlayer = 0;
this.board = new spelchan.Board(movie.board_movie);
this.board.layoutPieces();
this.die = new spelchan.Die(movie.die_movie);
movie.stop();
this.rollBtns = [movie.proll_btn, movie.rroll_btn, movie.yroll_btn, movie.groll_btn, movie.broll_btn];
this.skipBtns = [movie.pskip_btn, movie.rskip_btn, movie.yskip_btn, movie.gskip_btn, movie.bskip_btn];
this.rollHandler = this.startRoll.bind(this);
this.skipHandler = this.skip.bind(this);
for (var cntr = 0; cntr < 5; ++cntr) {
this.rollBtns[cntr].addEventListener("click", this.rollHandler);
this.rollBtns[cntr].addEventListener("mousedown", spelchan.tintedButtonEvent);
this.rollBtns[cntr].addEventListener("pressup", spelchan.tintedButtonEvent);
this.rollBtns[cntr].addEventListener("rollover", spelchan.tintedButtonEvent);
this.rollBtns[cntr].addEventListener("rollout", spelchan.tintedButtonEvent);
this.skipBtns[cntr].addEventListener("click", this.skipHandler);
this.skipBtns[cntr].addEventListener("mousedown", spelchan.tintedButtonEvent);
this.skipBtns[cntr].addEventListener("pressup", spelchan.tintedButtonEvent);
this.skipBtns[cntr].addEventListener("rollover", spelchan.tintedButtonEvent);
this.skipBtns[cntr].addEventListener("rollout", spelchan.tintedButtonEvent);
}
this.winText = movie.win_txt;
this.continueHandler = this.endGame.bind(this);
this.continueBtn = movie.continue_btn;
this.continueBtn.addEventListener("click", this.continueHandler);
this.UI_ROLL = 0;
this.UI_SKIP = 1;
this.UI_WIN = 2;
this.UI_HIDE = 3;
this.ROLL_TIME = 50;
this.PLAYER_LABELS = ["Purple", "Red", "Yellow", "Green", "Blue"];
this.PLAYER_COLORS = [0xFF8800AA", 0xFFAA0000", 0xFFAAAA00", 0xFF00AA00", 0xFF0000AA"];
this.updateGameUI(0, this.UI_ROLL);
}

As you can see by looking at the code, when the player's button is visible, clicking on the button calls the rollHandler function which is the startRoll method bound to the class instance. It simply sets up the roll and then updates the user interface using our updateGameUP which is called whenever the system needs to update the UI with the caller providing the current player and the state the game is in. Note that we defined four states as follows:

  • UI_ROLL Waits for player to start rolling the dice.
  • UI_SKIP Waiting for user to make move or skips the rest of their turn.
  • UI_WIN Indicates that the game has been won.
  • UI_HIDE Turns off all the user interface.

spelchan.Pent.prototype.updateGameUI = function(player, state) {
// hide all buttons
for (var cntr = 0; cntr < 5; ++cntr) {
this.rollBtns[cntr].visible = false;
this.skipBtns[cntr].visible = false;
}
// reveal stuff based on state
switch (state) {
case this.UI_ROLL:
this.rollBtns[player].visible = true;
break;
case this.UI_SKIP:
this.skipBtns[player].visible = true;
break;
}
}

spelchan.Pent.prototype.startRoll = function() {
this.die.startRoll(this, this.ROLL_TIME);
this.updateGameUI(this.currentPlayer, this.UI_HIDE);
}

The roll function calls the roll result method once the roll is finished. This method makes use of the board class to determine which pieces that the player can move based on the results of the roll.

spelchan.Pent.prototype.rollResult = function(n) {
this.updateGameUI(this.currentPlayer, this.UI_SKIP);
console.log("the roll was " + n);
this.board.prepareMove(this.currentPlayer, n, this);
}


The prepareMove function is a bit more complicated task so I am going to break the function up into several sections and comment on each section. The first thing this function does is determine where the relevant board locations for that player are. This is a simple lookup in our BOARD\_LAYOUT array.

spelchan.Board.prototype.prepareMove = function(player, roll, listener) {
var moveCount, cntr, loc, zonedist;
this.lastRoll = roll;
this.clearActions();
this.move_listener = listener;
var home = this.BOARD_LAYOUT[player][this.BOARD_HOME_INDEX];
var start = this.BOARD_LAYOUT[player][this.BOARD_START_INDEX];
var end = this.BOARD_LAYOUT[player][this.BOARD_END_INDEX];
this.player = player;
movecount = 0;

Next we loop through the five pieces that belong to the player. We then determine how far from home that particular piece is. The board is circular (sort of) which means that we have the extra complexity of dealing with the possibility that the target home location is actually less than the actual location. As this will result in a negative distance from home, we simply need to add the size of the board (55 locations) to this value to get the correct distance.

Knowing how far the piece has to travel to get to the home location is the way we determine if the piece is located in the loading zone. It is also going to prove to be a very useful bit of information when we get around to writing the AI next chapter.

for (cntr = 0; cntr < 5; ++cntr)
{
this.pieces[player][cntr].weight = 0;
loc = this.pieces[player][cntr].location;
console.log("piece " + cntr + "is at " + loc);
zonedist = home - loc;
if (zonedist <= 0)
zonedist += 55;
console.log ("piece " + cntr + " is " + zonedist + " from reaching home");

As you have probably already realized, not all pieces are going to be on the board.  In fact, some of the pieces will be in the starting gates. It is important that those pieces be able to move, otherwise we won’t have a game at all since there would never be any pieces on the board. To start a piece on the board, the roll must match that piece. Likewise, to remove the piece the roll must match the piece. This is simply enough to check. If the piece matches the roll, we then see if the piece is in the loading zone or in the starting gate. If the piece is in one of these area’s then the piece is movable.

// see if piece can be launched or finished
if ((cntr+1) == roll)
{
if ( (loc == (start+cntr)) || (zonedist < 5) )
{
console.log("Piece is launchable/exitable");
this.pieces[player][cntr].useHighlight.visible = true;
++movecount;
}
}

We then see if the piece is on the board and can move without going past the loading zone. We know the board starts at location 50, so that check is simply a location check.  The distance from home can be used to see if the roll will take you past home, as if the distance is less than the roll of the die, then the move will take you past the starting position!

if ( (loc >= 50) && (zonedist > roll) )
{
console.log("piece movable on board");
this.pieces[player][cntr].useHighlight.visible = true;
var targetloc = loc + roll;
if (targetloc > 104)
targetloc -= 55;
++movecount;
}
}

Finally, we see if there is actually a move available for the player. As all the moves are counted above, we simply see if there are moves. If not, we are done the move!

if (movecount == 0)
listener.doneMove();
}


When the player chooses a piece by clicking on it, the choosePiece function gets called. 
The choosePiece function will start the animation so it will be covered next.

Moving the Piece

The choosePiece function within the board movie is called when a piece is clicked. The first thing the function does is determine if the piece that was selected belongs to the player and if not simply returns. It then figures out where the piece is going to be moving to. If the piece is waiting to be put on the board, the target location is the player's starting spot on the board. If the piece is already on the board, it moves it the die roll's number of slots. This is not as easy as it sounds as the board is a circle but in memory is treated as a line. To make the board act circular, if a piece goes of the end of the line, it wraps to the beginning of the line.  Finally, it sets up the movingPiece, movingTargetX and movingTargetY variables so the updateMove method will know that it has a piece to move and where to move that piece to.

spelchan.Board.prototype.choosePiece = function(player, piece) {
console.log("***choosePiece called***");
if (player != this.player) {
console.log("Piece does not belong to player! " + player + "!=" + this.player);
console.log("speed = " + this.SPEED);
return;
}
console.log ("Piece was selected to be moved: " + player + "," + piece);
var loc = this.pieces[player][piece].location;
console.log ("Piece was on " + loc);
this.clearActions();
if (loc == (this.BOARD_LAYOUT[player][this.BOARD_START_INDEX]+piece) )
this.movingTargetLoc = this.BOARD_LAYOUT[player][this.BOARD_HOME_INDEX];
else if ( (loc >= this.BOARD_LAYOUT[player][this.BOARD_LOADING_START]) &&
(loc <= this.BOARD_LAYOUT[player][this.BOARD_LOADING_END]) &&
(this.lastRoll == (piece+1)) )
this.movingTargetLoc = this.BOARD_LAYOUT[player][this.BOARD_END_INDEX]+piece;
else
{
this.movingTargetLoc = loc + this.lastRoll;
if (this.movingTargetLoc > 104)
this.movingTargetLoc -= 55;
}
this.movingPiece = this.pieces[player][piece];
this.movingTargetX = this.layout_x[this.movingTargetLoc];
this.movingTargetY = this.layout_y[this.movingTargetLoc];
this.layout_piece[loc] = null;
console.log("Moving from " + loc + " to " + this.movingTargetLoc);
// onEnterFrame = updateMove;
}

The updateMove function is called every frame whether there is something to be animated or not. It knows that it has work to do if the movingPiece variable is not null. The first thing the function does is figures out if the current frame of motion will be the last frame of motion. This is simply a check of the distance from the current position to the ending position. If the distance remaining to move is less than the speed then we obviously are on the last frame of animation. Or at least the last frame of that pieces’ animation.

If the piece has reached the target location we need to determine if there is already a piece on the target location. If there is, we determine the original start-block location for that piece and create a new animation moving that piece back to start. If there is not a piece on the target location we call the doneMove handler assigned to the board which is used for managing what happens once the animation is finished. This handler will be written shortly.

If the piece being animated has not reached the end of it's animation, then we simply move towards the target location by applying the time adjusted movement speed to the piece.

spelchan.Board.prototype.updateMove = function() {
if (this.movingPiece == null)
return;
console.log("moving piece...");
var curX = this.movingPiece.x;
var curY = this.movingPiece.y;
var deltaX = this.movingTargetX - curX;
var deltaY = this.movingTargetY - curY;
var distance = Math.abs(deltaX) + Math.abs(deltaY);
if (distance <= this.SPEED)

this.movingPiece.x = this.movingTargetX;
this.movingPiece.y = this.movingTargetY;
// movingPiece.changeState(0);
this.movingPiece.location = this.movingTargetLoc;
if (this.layout_piece[this.movingTargetLoc] != null)
{
var temp = this.layout_piece[this.movingTargetLoc];
this.layout_piece[this.movingTargetLoc] = this.movingPiece;
this.movingTargetLoc = this.BOARD_LAYOUT[temp.playerID][this.BOARD_START_INDEX]+temp.pieceID;
this.movingTargetX = this.layout_x[this.movingTargetLoc];
this.movingTargetY = this.layout_y[this.movingTargetLoc];
this.movingPiece = temp;
}
else
{
this.layout_piece[this.movingTargetLoc] = this.movingPiece;
this.movingPiece = null;
// onEnterFrame = null;
this.move_listener.doneMove();
}
}
else

var moveX = deltaX * this.SPEED / distance;
var moveY = deltaY * this.SPEED / distance;
this.movingPiece.x += moveX;
this.movingPiece.y += moveY;
}
}

We are finished with the board movie for the moment, so the last thing we need to do is in the main movie. This task is simply a function to handle the game once the move is done. This function simply checks to see if the game has been won and if so ends the game. If the game is not over it goes to the next player.

spelchan.Pent.prototype.doneMove = function() {
if (this.board.checkWin(this.currentPlayer) == true) {
this.updateGameUI(this.currentPlayer, this.UI_WIN);
return;
}
this.currentPlayer = (this.currentPlayer + 1) % 5;
this.updateGameUI(this.currentPlayer, this.UI_ROLL);
}

The method for determining if the game has been won will be developed in the next section.

Winning the Game

As shown last section, whenever a player finishes a move, we need to determine if the game has been won. This is a fairly simple function as we simply need to check to see if all the pieces are in their end positions. A simple function in the board movie can handle this.

spelchan.Board.prototype.checkWin = function(player) {
var cntr, result = true;
var end = this.BOARD_LAYOUT[player][this.BOARD_END_INDEX];
for (cntr = 0; cntr < 5; ++cntr)
{
if (this.pieces[player][cntr].location != (end + cntr))
{
result = false;
break;
}
}
return result;
}

If the player has won, however, the state for showing the win message has not been handled. To do this we are going to need to create a continue button to add to the screen as well as a large text message that displays the win message. The continue button is a simple gray button with the states as shown in the image below.


The Pent class updateGameUI now needs to be updated to handle the win message and showing the win button. At the top of this method add the following:

this.winText.visible = false;
this.continueBtn.visible = false;

The actual code within the switch statement add the case for the UI\_WIN state as follows:

case this.UI_WIN:
    this.winText.color = this.PLAYER_COLORS[this.currentPlayer];
    this.winText.text = this.PLAYER_LABELS[this.currentPlayer] + "\nPlayer\nWINS!";
    this.winText.visible = true;
    this.continueBtn.visible = true;
    break;

Now the game is playable in hotseat, but it would be sure nice to be able to control the number of players, and be able to play against the computer. To do that we are going to need to delve into the world of Artificial Intelligence, at least as it applies to games. See you next month!


Saturday, April 18, 2020

Making Pent Up Anger part 2 of 5

Pieces of the Game


This is the second part of a five part series on building Pent Up Anger using AnimateCC. The game can be found at http://spelchan.com/games/y2018/PentUpAnger.php .

Building the Board


A hexagon board. Sounds complicated, but building it really isn't. First, we want 12 squares per side, with the corner square shared so we need to figure out how big to make a square. Whatever length we decide, needs to be divisible by 12. Making squares 25x25 would make a line that was 300 pixels long. If we draw a horizontal line 300 pixels long and then rotate it 72 degrees and rotate another 300 pixel horizontal line 144 degrees, we see that the combined lines go outside our 640x480 boundaries, but barely. If we use 288 pixel lines, the lines fit within the area of the screen. This means that 24x24 boxes will work.

I draw a 24x24 box. I then copy the box a couple of times and join them together. I then copy the three boxes and past the copy together. One final copy and past and we end up with a row of 12 boxes. Copy that row and rotate it 72 degrees. Join at the corner. Next past the copied boxes again and rotate 144 and place at the second corner. Again at 228 and 288 and you end up with a completed pentagon board.

Do a bit of colouring so the starting points and the exit zones are distinct. Then grab a 5 block chunk of boxes and create the inside exit zones by rotating the box as you did above.
Finally create starting zones. Turn the whole group into a movie clip.


Building the Player


Building a piece sounds simple, but there is a bit of a logistics problem. First, we need to know if the piece is movable and if it is the current piece selected. This means that a piece is going to have three states associated with it. Next, we are going to have to have a number on the piece indicating which of the five pieces the piece is. Three states with five pieces is 15. There are also five colors. fifteen times five is 75. Drawing 75 pieces is not a pleasant task so we are going to have to come up with a better solution.

In Animate CC, buttons are a special case of a movie clip which simply contain four frames. There is nothing stopping you from using multiple layers on a movie clip, and you can programmatically control objects. By using multiple layers on a button, we can one highlight silhouette indicating which pieces can be moved, a selected silhouette for each color and a piece for each color reduces this down to just 11 shapes which really are just 2 shapes that are colored differently.

Tinting could be used to get different coloring. Create.js does behave a bit strangely with tinting but there is an easy fix for that. We will use tinting with our buttons later to demonstrate this. I chose to do multiple versions of the pieces as this gives you a bit more control over the colors and I really didn’t like the tinted version in my original Flash version of this game. For this reason I have gone with the button technique.


As you can see by looking at the figure above, the button has 4 layers in it. The ``ID'' layer holds a textfield which will allow the changing of the label assigned to that player. This easily allows us to change the number of the piece meaning that we only need to have one piece per color. This is set when the board is initialized, which we will be covering later.

The ``base'' is the actual image for the player. It is second from the top so that it remains clearly visible even when there is indication that the piece is select-able or the piece is highlighted. Each color has their own image which has color choices made to make the piece look nice for that particular color.

The third layer is the ``use'' layer of the button and is simply a movie clip of a white silhouette of the game piece. This movie clip is used by all five variations of the player button. The purpose of this layer is to give the user an indication as to which pieces they are able to move. By having this as a movie clip, you can simply set the visibility of the movie clip to false if the piece is not usable and set it to true when it is usable. This will be done when we implement the game play portion of the game later.

The final layer is not in the button's up state as it is used as a highlight for when the player moves their mouse over the piece. This is shown in the figure below.


Building the Die


This game uses an eight sided die. For those of you unfamiliar with such a die, the die looks sort of like a diamond. You use it like you would a normal six sided die, but it has eight possible results instead of six. The primary reason for going with an eight sided die instead of the standard die was three-fold. First it adds something unique to the game. Second, it speeds up the game as the average roll will be 4.5 as opposed to 3.5 for a six sided die. Finally, they are cool.

Building the die movie is fairly simple. First we start with an image of the die in a finished position. On a separate layer we have text for the eight different values (labelled r1 to r8). We also have mid-roll die images. Figure \ref{fig:die}  shows the frames that make up the die.

This is the minimal die animation approach. A more ambitious approach would have numbers on the between frames with the values shown. This would require many more frames of animation as you would have the 8 result frames and would need 18 additional between frames for 26 frames.  Granted, the animation would look better so if you have the artist time to do this then it may be a good option. 

As with our other movies, we treat the die movie as a class. To handle the rolling of the die we will need some functions, but first we will initialize the die. This simply sets the value of the die to 8 and resets the listener and roll variables, which we will explain shortly.

spelchan.Die = function(movie) {
this.dieMovie = movie;
this.cutoff = 300;
this.frameListener = this.handleTick.bind(this);
this.clickListener = this.handleClick.bind(this);
this.dieMovie.addEventListener("tick", this.frameListener);
this.dieMovie.addEventListener("click", this.clickListener);
this.rolling = false;
this.value = 0;
this.rollListener = null;
}

Rolling the die presents us with a bit of a problem. We want the roll to be animated but if the roll is animated, then it can’t return a value right away. To solve this problem we have the startRoll function to get the die rolling and then once the animation is finished we will have the die class call the handleClick function to stop the movie. The end of roll handler is called handleClick as it is handling the result from the player clicking on a roll button, though this is a poor name but at the time of development it seemed to make sense. This is why code reviews are a good thing to have.

The actual die roll will consist of 5 to 20 spins (the number determined at random) with a spin consisting of an in between shape (the three shapes at the bottom of figure 4) followed by a number. To start the roll the number of spins is determined followed by a call to the handleTick function.

spelchan.Die.prototype.startRoll = function(listener, cutoff) {
this.rollListener = listener;
this.rolling = true;
this.cutoff = cutoff;
}

The handleTick function gets called every frame. It simply looks to see if the die is being rolled and if it is selects a new frame to be displayed. When the die is being rolled, there is a cutoff variable that determines when we are ready to end the roll at which point we call the clickListener function which handles the results of the click on the roll button.

spelchan.Die.prototype.handleTick = function(e) {
if (this.rolling) {
this.dieMovie.gotoAndPlay(Math.floor(Math.random()*17)+1);
--this.cutoff;
if (this.cutoff <= 0)
this.clickListener(null);
}
}

Once the roll is finished, the handleClick function makes sure that the die is actually in the process of being rolled and if so will pick the final value, setting the movie clip to that particular frame. It then makes a call to the roll listener that is currently attached to the die. The roll listener is set by the game as appropriate using a setRollListener method that simply specifies the function to call when the roll is finished.

spelchan.Die.prototype.handleClick = function(e) {
if (this.rolling) {
this.rolling = false;
this.value = Math.floor(Math.random()*8)+1;
this.dieMovie.gotoAndStop(this.value);
if (this.rollListener != null)
this.rollListener.rollResult(this.value);
}
}
spelchan.Die.prototype.setRollLister = function(listener) {
this.rollListener = listener;
}

Board Layout


We already have a board but the player pieces need to be placed on the board. We need to know the coordinates of every square that the player can land on so we can place the pieces on the proper screen locations.  We could create a table by hand that contains all the coordinates of the locations. This is time consuming and very prone to errors but could be simplified by writing a utility to help do this. Such a utility would only need to display the board and then record all the clicks on the board that the user made and storing them in a table that can be saved. For board games that have complex layouts, this may be required. As our board is laid out in a fairly linear and has a regular pattern to it so we should be able to algorithmically create the table.

We still need some coordinates, which lead to a strange problem. Despite dragging an instance of the piece movie to the desired starting locations and using those coordinates, Animate seems to shift the location. After a little investigation, I found out that the properties panel is actually giving you the top-left location of the object, not the location of the anchor. This is something you will need to keep in mind when using coordinates provided to you by Animate.

All the pieces and code to handle the pieces are going to be placed in our board movie. To handle this, a couple additional layers are going to be needed. The layers are “code,” “pieces,” and our original layer which we will name “back.” On the pieces label we drag a copy of our piece movie which we will name “piece\_movie.”

The board initialization will be done in the constructor which is also where we set up all of our ``constants''.  When you think about it, the board really can be broken into three sections for each of the five colors. Every color has a side of the board starting at their starting gate, they also have an ending block of squares where the pieces go when they have finished. Finally, there is the starting - or home - block. All of these things can have simple coordinates attached to them and then, if we know the angle, we can determine the location of the other locations within that group. For the game, we are going to also need the board id’s of these locations as well as the starting and ending ids for the loading zone.

All of this information results in seventy different pieces of information. To handle all of this information without having too many variables to worry about, I have created an array of five arrays with the five arrays containing the fourteen items we are interested in. Each of the items is assigned an index value within the array, with a constant being created so in the future I don’t need to remember the particular index value.

Once this array is created, two layout arrays are created to hold the x and y positions of all of the locations on the board.  Some simple trigonometry is used to generate all the positions within that array. A lot of work, but all this initialization will make developing the rest of the game a bit easier.

var degree = Math.PI / 180;
// set up constants for array indexes
this.BOARD_START_INDEX = 0;
this.BOARD_START_X_INDEX = 1;
this.BOARD_START_Y_INDEX = 2;
this.BOARD_START_ANGLE_INDEX = 3;
this.BOARD_END_INDEX = 4;
this.BOARD_END_X_INDEX = 5;
this.BOARD_END_Y_INDEX = 6;
this.BOARD_END_ANGLE_INDEX = 7;
this.BOARD_HOME_INDEX = 8;
this.BOARD_HOME_X_INDEX = 9;
this.BOARD_HOME_Y_INDEX = 10;
this.BOARD_HOME_ANGLE_INDEX = 11;
this.BOARD_LOADING_START = 12;
this.BOARD_LOADING_END = 13;
this.BOARD_LAYOUT = [
//si,  sx,  sy, sa, ei,  ex,  ey, ea, hi,  hx,  hy, ha, ls, le
[  0, -39,-236,180,  5, -11,-179,270, 50,  -9,-219,324,101,104],
[ 10, 231, -84, 90, 15, 169, -48,198, 61, 205, -58,252, 57, 60],
[ 20, 151, 212,  0, 25, 101, 162,126, 72, 121, 198,180, 68, 71],
[ 30,-175, 212,180, 35,-123, 161, 54, 83,-145, 192,108, 79, 82],
[ 40,-254, -42,270, 45,-185, -48,342, 94,-227, -64, 36, 90, 93] 
];
this.layout_x = new Array(105);
this.layout_y = new Array(105);
this.layout_piece = new Array(105);
for (var cntrcol = 0; cntrcol < 5; ++cntrcol)
{
for (var cntr = 0; cntr < 5; ++cntr)
{
this.layout_x[this.BOARD_LAYOUT[cntrcol][this.BOARD_START_INDEX]+cntr] =
24 * cntr * Math.cos(this.BOARD_LAYOUT[cntrcol][this.BOARD_START_ANGLE_INDEX] * degree) +
this.BOARD_LAYOUT[cntrcol][this.BOARD_START_X_INDEX];
this.layout_y[this.BOARD_LAYOUT[cntrcol][this.BOARD_START_INDEX]+cntr] =
24 * cntr * -Math.sin(this.BOARD_LAYOUT[cntrcol][this.BOARD_START_ANGLE_INDEX] * degree) +
this.BOARD_LAYOUT[cntrcol][this.BOARD_START_Y_INDEX];
this.layout_x[this.BOARD_LAYOUT[cntrcol][this.BOARD_END_INDEX]+cntr] =
24 * cntr * Math.cos(this.BOARD_LAYOUT[cntrcol][this.BOARD_END_ANGLE_INDEX] * degree) +
this.BOARD_LAYOUT[cntrcol][this.BOARD_END_X_INDEX];
this.layout_y[this.BOARD_LAYOUT[cntrcol][this.BOARD_END_INDEX]+cntr] =
24 * cntr * -Math.sin(this.BOARD_LAYOUT[cntrcol][this.BOARD_END_ANGLE_INDEX] * degree) +
this.BOARD_LAYOUT[cntrcol][this.BOARD_END_Y_INDEX];
}
}
for (cntrcol = 0; cntrcol < 5; ++cntrcol)
{
for (cntr = 0; cntr < 11; ++cntr)
{
this.layout_x[this.BOARD_LAYOUT[cntrcol][this.BOARD_HOME_INDEX]+cntr] =
24 * cntr * Math.cos(this.BOARD_LAYOUT[cntrcol][this.BOARD_HOME_ANGLE_INDEX] * degree) +
this.BOARD_LAYOUT[cntrcol][this.BOARD_HOME_X_INDEX];
this.layout_y[this.BOARD_LAYOUT[cntrcol][this.BOARD_HOME_INDEX]+cntr] =
24 * cntr * -Math.sin(this.BOARD_LAYOUT[cntrcol][this.BOARD_HOME_ANGLE_INDEX] * degree) +
this.BOARD_LAYOUT[cntrcol][this.BOARD_HOME_Y_INDEX];
}
}

For testing, we create a copy of our applet specifically for testing the board positions. It will do this by using a loop where a piece goes through every location on the board. This is done in two frames. In the frame we label “Test” we place the following code:

this.test_movie._x = this.layout_x[test_spot]; 
this.test_movie._y = this.layout_y[test_spot]; 

and ten to fifteen frames after the label (so we have time to see the position) we place this code:

++this.test_spot; 
if (this.test_spot >= 105) 
this.test_spot = 0; 
this.gotoAndPlay("Test");

So now we have a board that the players can place pieces on. The next task is to get a head-to-head version of the game working which is what we will be starting on next.


Sunday, March 15, 2020

Making Pent Up Anger Part 1 of 5

Introduction to Board Game

Pent Up Anger is a rather large project so the making of this game will be broken down into 5 different parts (two in the ebook). The game can be found in the Spelchan.com archives at http://spelchan.com/games/y2018/PentUpAnger.php with the source code eventually planned to be posted as part of my https://github.com/BillySpelchan/BlazingPorts repository.

This part will be an overview of board games, why you would want to create a digital version of them, and what the game Pent Up Anger is. This will be followed by Part 2 where we put together the components that make up the game. In part 3 we assemble the parts to create a head to head version of the game. As we want to play the game without other people around, part 4 takes a look at AI and prepares a rudimentary AI opponent. Finally, part 5 builds the user interface components necessary to select the players and whether they are AI or Human players.

I will be releasing a part each month so those of you who are not patient may simply wish to come back after all the parts have been posted, but each part is fairly large so it may be best to follow along as the parts are posted.

Why Digitize Board Games?

Board games are games that are played on a board. There are a huge variety of board games. While card games could be placed in this category, I have placed them in the previous part of the book. In this part of the book we will be looking at games that have some type of playing board.

Some board games are strictly based on luck. These tend to be the track based board games where the goal is to reach an end point by following a track. Players move along the track by rolling a die or dice and moving the amount they rolled along the boards path. Sometimes points on the track will have actions associated with them, such as move back three places. Some may even have spots that require the player to pick up a card, with the card having instructions on it.

Another way luck based games try to make it look like there is more than luck involved in winning is by having points or cash determine the winner. Essentially these types of games are the same as the track game but add some type of points to the game. Points are awarded or lost by entering a specific location or by getting a certain card.

On the opposite end are games that are won strictly by skill. These games start the players off on an even playing field and through the rules of the game determine the winner. The best examples of this type of game is chess, with checkers and go being other good examples.

My favourite category of game is the game that fits in between the two. These are games that have some type of playing field and turn rules as with the skill game, but also deploy some type of random element. The best example of this type of game is backgammon.

Why is the middle ground my favourite? Simply put it allows two players of different skill levels to have fun while still allowing the less skilled player a chance at winning the game. Why is this important? Most people hate losing. What is the point of playing a game if I know I am always going to lose?

One question that a lot of people have is why even bother to create a computer version of a board game when you can just play the game using the board? There are five main reasons that I would do a computerized version of a board game.

The simple answer is ``Convenience''. Let's face it, when you want to play a board game you have to dig out the board, then sort out all the pieces and set up the board before you can even start playing. With the computer, all you have to do is run the software. All the setup work is done for you. For an online game, you have the additional task of going to the website that the game is on, but that is easily book marked.

No lost pieces. Losing a games piece is a fairly common occurrence. I am sure that most people have played a board game using coins or some other non-game object to represent missing pieces. In computer versions, all the pieces are created by the computer and therefore can never be lost.

Fair Referee. Cheating in board games is more common than it should be. Even if no one does cheat, arguments over the rules still happen. On the computer, the rules are coded into the game so there can be no arguments over fair play. Likewise, because the computer controls all the movement of the pieces and exchange of money, no one has to worry about someone ``miscounting'' or making ``adding mistakes''.

Solo Play. It is not always possible to find someone to play a game with. Computers have the ability to take the roll of missing people. While the computer may not be as fun as playing against a real human, it can still be an enjoyable time. More to the point, if you are not familiar with the rules to a particular game, playing a computer version of that game can teach you how to play that game without looking dumb in front of your friends.

Enhancements. In some ways, computers can do things that are not possible with normal board games. Animated sequences are one example. Handling complex rules with ease is another. Strategy board games in particular can benefit by being converted into computer games as most of those games have really complicated rules.

What is Pent Up Anger

Pent up Anger is an original board game that I designed which is loosely based on other board games that I have seen. The figure below shows the game in progress. This game is a traditional track based board game, but with a bit of a twist. First, the players each have five pieces, with the ability to move any piece in a given turn. Second, the game uses an eight sided die. The game's board is pentagon shaped. Each point of the pentagon is a player's starting location. Each player is assigned a different color and has five playing pieces numbered one through five. Their starting gate also has the numbers one through five on it. . The goal is to get all of your pieces from their starting location to their ending location. 



Turns revolve clockwise around the board. Players start their turn by rolling an eight sided dice. Before the player can move, they need to get one of their pieces out of the starting gate. This is done by rolling the number that is assigned to the piece. Players can only move or start once piece a turn. Any piece that is on the board can be moved as long as the move will not take the piece past the player's loading zone. By giving the player control over multiple pieces and adding the ability to send opponents back to the start a bit of skill is added to the game. If the player lands on an opponent's piece, the opponent's piece gets taken back to that player's starting gate.

The loading zone is a special four square line on the board that starts just before the players starting point. Once the player's piece has landed in the loading zone, the piece is able to leave the board. To leave the board, the number of the piece must be rolled.

Players win the game once all their pieces have been removed from the board.

Next month I will take al look at how to build the different pieces that make up the game.

Saturday, February 15, 2020

Leap Year Leap Postmortem

My post last month somehow ended up on the wrong blog (probably too much of a rush and I didn't pay attention when posting). The link to part 2 of making video poker is at http://blazinggames.blogspot.com/2020/01/video-poker-part-2.html.

The source code for Leap Year Leap is at https://github.com/BillySpelchan/BlazingPorts if anybody is interested. I am planning on slowly adding code of the other games I port to that repository.

Leap Year Leap is a leap year game that I originally released back in 2008 (on February 29th), and then released as open source in 2012. This is a Flash game using the really old version of ActionScript but this was not necessarily a bad thing.  The game used an old-school technique for faking 3D. Namely taking advantage of sprite scaling to draw sprites larger as they approach you.
For the HTML5 port I decided that I would do the game using my own library. Interesting, the BGLayers library I wrote was written in 2010 so if I would have had a bit more foresight, I could have done the HTML5 version way back in 2012. Granted, back then HTML5 was not a completed standard so support for the canvas tag and API was limited.

I was expecting the port to only take a few hours, but it ended up taking about twice as long as I anticipated. This may be partially due to not having that much free time to work on the game so one quickly loses track of things when there is long periods of time between work sessions.

What Went Right - Software 3D code ported easily.


While a large amount of the game was re-written from scratch, the main scaling logic and the jumping logic for the game was pretty much ported verbatim.  This allowed me to get the game up and running quickly and the development time would have been substantially longer without this tiny amount of code.

 The 3D logic is somewhat correct with the scale of the object being determined based on how far away from the player it is. The jump logic is physics based using momentum and gravity to simulate the jump. Interestingly, the code breaks the physics into micro-intervals and performs many calculations to keep the jump as consistent as possible even with delta-based timing. The idea here is that the physics is calculated independently of the image so the time between the last physics event and the current one result in several discrete steps that the program processes. By having physics in intervals, you get a bit more consistency with the motion. While the number of steps I used was probably way more than necessary, it is interesting that more modern engines use a similar approach to physics.

What Went Wrong - No bi-linear or Tri-linear filtering

The Flash player did a surprisingly good job of scaling images, though this probably had more to do with use of vector-based images than the actual image scaling. As I am using images and scaling them using the canvas default scaling methods, the results are not as good. The canvas scaling results in images that tend to flicker in the distance as different scanlines of the image are drawn at different scales. This use to be a big problem for 3D graphics cards, with the solution being bi-linear and tri-linear filtering. The idea behind these rendering approaches is that instead of having a single image, you have multiple images at different distances. Finding the appropriate image and swapping it in results in much better scaling. Of course, you can’t have every possible size of image unless you want to use a lot of storage space for the many versions of the image that would be needed so instead you rely on filtering techniques. These take samples of the images that are near the correct size and set the color of each pixel of the scaled image based on the average of these samples.

Software filtering is certainly possible but would take far too long to do.  A better solution, which I will likely implement in the 2024 version of the game, is to use the 3D card to do the scaling work for you. While I have done some work with OpenGL and Direct-3D, I have not really done much with WebGL. I suspect that it would be fairly like the work that I have already done but simply don’t have the time to learn a new graphics API. This is something that I will be doing eventually, when I actually start having more free time.

Mixed Blessings – BGLayers

While I certainly could have used Create.js for the library, for such a simple game it is way too much overkill. Using software libraries that are multiple times the size of the project when you are only using a tiny fraction of the libraries features seems wasteful. More to the point, I like writing my own code and still want to develop my own engine so why not use my own library. The advantage is that it is significantly smaller than the Create.js libraries and I have total control of everything. The disadvantage is that it was written back in 2012 when ECMAScript 6 was non-existent. 

While there are clunky aspects to my BGLayers library, it works surprisingly good for its size. When I have the time, I am going to have to re-write this library using the proper class syntax that ECMAScript 6 uses to make for working with the library and development of games much nicer. I would probably divide this into a canvas and a WebGL version.

Conclusion - Plans for 2024

Overall, this is not too bad of a port for the game. As mentioned above, for the next leap year, I will want to do an even better version of the game. At a minimum, this would switch to a more modern version of JavaScript so rewriting, or more likely a re-imagining, will be in order. It would also probably use the WebGL API so that I can properly handle 3D elements and be able to take advantage of the filtering options to result in smoother looking results. A lot more background objects, and possibly a road that goes from a dirt trail to a modern highway as you advance through the years would be nice but that is probably pushing it unless those things happen to be developed in a different project.