Wednesday, August 29, 2012

Creating a Pentomino game using AS3: Part 42

Today we'll start working on level selection.

The autosolving algorithm we have is not perfect, but it works well for small puzzles. We've optimized it, and even though it is still slower than I would like it to be, but we're pretty much done with it. Starting today, we'll be working on level selection.

The level selection screen will contain premade levels for the players to play. The window will open when the user hits "Play" in main menu.

Go to main_menu.as and in doPlay() function direct the user to frame 6 instead of 2:

package  
{
import flash.display.MovieClip;
import flash.events.MouseEvent;

/**
 * Open-source pentomino game engine
 * @author Kirill Poletaev
 */

public class main_menu extends MovieClip
{

public function main_menu() 
{
(root as MovieClip).stop();
btn_play.addEventListener(MouseEvent.CLICK, doPlay);
btn_editor.addEventListener(MouseEvent.CLICK, doEditor);
btn_saved.addEventListener(MouseEvent.CLICK, doSaved);
}

private function doPlay(evt:MouseEvent):void {
(root as MovieClip).gotoAndStop(6);
}

private function doEditor(evt:MouseEvent):void {
(root as MovieClip).gotoAndStop(3);
}

private function doSaved(evt:MouseEvent):void {
(root as MovieClip).gotoAndStop(4);
}
}

}

Now open your game in Flash, go to frame 6 and add a new MovieClip there. Set its class path to level_select.

Inside, add a button with id "btn_back", it will redirect the player back to main menu.

Now create a new MovieClip (in the library, don't add it to stage). It will be the preview block for the level, which will also act as a button. Make it rather big, around 360x200 pixels. Draw a rectangle of the same size and turn it into a MovieClip - set its id to levelPreview. Add a dynamic text field on top of the level preview, give it an id of tTitle. Add a transparent button on top of it all, give it an id of "btn".

Give that MovieClip class path "level_item".

Now create a new class level_select.as.

In the constructor, add a click event listener for the Back button, in the handle direct the user to frame 1.

In the constructor, create a new Object called "level1", make it an instance of level_item class (which we will also create shortly).

When declaring the variable, add 3 values as parameters: map grid data array, shapes array and title string. Add the object to stage and center it:

package  
{
import flash.display.MovieClip;
import flash.events.MouseEvent;

/**
 * Open-source pentomino game engine
 * @author Kirill Poletaev
 */

public class level_select extends MovieClip
{

public function level_select() 
{
(root as MovieClip).stop();
btn_back.addEventListener(MouseEvent.CLICK, doBack);

var level1:MovieClip = new level_item(
[
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
"Standard 10x6"
);
addChild(level1);
level1.x = 180;
level1.y = 100;
}

private function doBack(evt:MouseEvent):void {
(root as MovieClip).gotoAndStop(1);
}
}

}

Now create a new class level_item.as.

In the constructor we receive the 3 parameters, set the title string to tTitle's text value. Draw the preview using drawPreview() method (which works the same way as the method with the same name in saved_levels and level_solver), pass the levelPreview MC and map grid array as parameters. Add a click event listener for the "btn" button, in the handler call root's playLevel() method using map data and shapes arrays.

The drawPreview() and drawCell() functions are similar to the ones we used before, but a little bit modified:

package  
{
import flash.display.MovieClip;
import flash.events.MouseEvent;

/**
 * Open-source pentomino game engine
 * @author Kirill Poletaev
 */

public class level_item extends MovieClip
{

public function level_item(mapGrid:Array, shapes:Array, title:String) 
{
tTitle.text = title;
levelPreview.mouseEnabled = false;
drawPreview(levelPreview, mapGrid);
btn.addEventListener(MouseEvent.CLICK, function() { (root as MovieClip).playLevel(mapGrid, shapes); } );
}

private function drawPreview(levelPreview:MovieClip, mapGrid:Array):void {
var columns:int = mapGrid[0].length;
var rows:int = mapGrid.length;
var frameWidth:int = levelPreview.width;
var frameHeight:int = levelPreview.height;
var padding:int = 5;
var fitWidth:int = levelPreview.width - (padding * 2);
var fitHeight:int = levelPreview.height - (padding * 2);
var gridStartX:int;
var gridStartY:int;

// calculate width of a cell:
var gridCellWidth:int = Math.round(fitWidth / columns);

var width:int = columns * gridCellWidth;
var height:int = rows * gridCellWidth;

// calculate side margin
gridStartX = (frameWidth - width) / 2;

if (height < fitHeight) {
gridStartY = (fitHeight - height) / 2;
}
if (height >= fitHeight) {
gridCellWidth = Math.round(fitHeight / rows);
height = rows * gridCellWidth;
width = columns * gridCellWidth;
gridStartY = (frameHeight - height) / 2;
gridStartX = (frameWidth - width) / 2;
}

// draw map
levelPreview.x = gridStartX;
levelPreview.y = gridStartY;
levelPreview.graphics.clear();
var i:int;
var u:int;

for (i = 0; i < rows; i++) {
for (u = 0; u < columns; u++) {
if (mapGrid[i][u] == 1) drawCell(u, i, 0xffffff, 1, 0x999999, gridCellWidth, levelPreview);
}
}
}

private function drawCell(width:int, height:int, fill:uint, thick:Number, line:uint, gridCellWidth:int, gridShape:MovieClip):void {
gridShape.graphics.beginFill(fill);
gridShape.graphics.lineStyle(thick, line);
gridShape.graphics.drawRect(width * gridCellWidth, height * gridCellWidth, gridCellWidth, gridCellWidth);
}
}

}

And that is all so far.

Thanks for reading!

Results:


No comments:

Post a Comment