Monday, August 6, 2012

Creating a Pentomino game using AS3: Part 19

In this tutorial we'll add a custom alert system to our game.

With this alert system we'll be able to pop up a window with an error/warning message to the user, with an "OK" button that closes the window.

Start by opening your Flash project and creating a new MovieClip. You can do it by duplicating any of the _screen movieclips you have so far (win_screen or new_edit_screen). Draw a window box, add a button with an id "btn_continue" and a dynamic text field with id "tAlert". Give this MovieClip a class path of alert_screen.

Create the alert_screen.as file. Make the class extend MovieClip, and declare 1 variable - onClose, typed Function.

The constructor shall have two paramaters - one required and one optional. The required one is message:String, which value we apply to tAlert's text property in the constructor. The optional one is closeHandler:Function, set it to null by default. This will be the reference to a function that will be called when the user closes the alert window. In the constructor set onClose to reference this value and also add a CLICK event listener for btn_continue. In the handler function, remove the event listener, remove the movie clip and call onClose function:

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

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

public class alert_screen extends MovieClip
{

var onClose:Function;

public function alert_screen(message:String, closeHandler:Function = null) 
{
onClose = closeHandler;
tAlert.text = message;
btn_continue.addEventListener(MouseEvent.CLICK, onContinue);
}

private function onContinue(evt:MouseEvent):void {
btn_continue.removeEventListener(MouseEvent.CLICK, onContinue);
this.parent.removeChild(this);
onClose.call();
}

}

}

Now go to pentomino_editor.as script file. Here we will add 2 new functions - alert() and alertClose(). The first function has 1 parameter message:String, the function simply creates an instance of alertWindow and sets canDraw to false. When creating the alertWindow object, set its message value to the function's parameter, and its closeFunction parameter to alertClose.

The alertClose() function sets canDraw to true:

private function alert(message:String):void {
var alertWindow:MovieClip = new alert_screen(message, alertClose);
addChild(alertWindow);
canDraw = false;
}

private function alertClose():void {
canDraw = true;
}

We can now use alert() instead of trace() in checkSave() function to display errors:

private function checkSave():Boolean {
// count total cells
var totalCells:int = 0;
var i:int;
var u:int;
var width:int = mapGrid[0].length;
var height:int = mapGrid.length;

for (i = 0; i < height; i++) {
for (u = 0; u < width; u++) {
if (mapGrid[i][u] == 1) totalCells++;
}
}

// check if total cells can be divided by 5
if (totalCells / 5 != Math.round(totalCells / 5)) {
alert("Error!\nIncorrect cell count: " + totalCells + ", number must be divideable by 5.");
return false;
}

// count total available shape count
var totalShapes:int = 0;

for (i = 0; i < shapeButtons.length; i++) {
totalShapes += shapeButtons[i].count.value;
}

// check if there are enough shapes available
if (totalCells > totalShapes * 5) {
alert("Error!\nNot enough shapes available: " + totalShapes + " out of " + totalCells/5);
return false;
}

return true;
}

Full pentomino_editor.as code:

package  
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.sampler.NewObjectSample;
import flash.utils.ByteArray;

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

public class pentomino_editor extends MovieClip
{
private var mapGrid:Array = [];
private var shapeButtons:Array = [];

private var gridShape:Sprite = new Sprite();
private var canPutShape:Sprite = new Sprite();
private var gridStartX:int;
private var gridStartY:int;
private var gridCellWidth:int;
private var canDraw:Boolean = false;
private var mouseDown:Boolean = false;
private var currentCell:Point = new Point(-1, -1);

public function pentomino_editor() 
{
addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
addEventListener(Event.ENTER_FRAME, onEnterFrame);

// add shape buttons
for (var i:int = 0; i < 4; i++) {
for (var u:int = 0; u < 3; u++) {
var shapeButton:MovieClip = new edit_shape();
shapeButton.x = 528 + u * 62;
shapeButton.y = 15 + i * 84;
addChild(shapeButton);
shapeButton.bg.alpha = 0.3;
shapeButton.shape.gotoAndStop(3 * i + u + 1);
shapeButtons.push(shapeButton);
shapeButton.addEventListener(MouseEvent.ROLL_OVER, buttonOver);
shapeButton.addEventListener(MouseEvent.ROLL_OUT, buttonOut);
}
}

// buttons
btn_mainmenu.addEventListener(MouseEvent.CLICK, doMainmenu);
btn_reset.addEventListener(MouseEvent.CLICK, function():void { newLevel() } );
btn_save.addEventListener(MouseEvent.CLICK, doSave);

// new level
newLevel();

addChild(gridShape);
addChild(canPutShape);
canPutShape.mouseEnabled = false;
canPutShape.mouseChildren = false;
}

private function onMouseMove(evt:MouseEvent):void {
if(mapGrid.length>0){
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
canPutShape.x = mousePos.x * gridCellWidth + gridStartX;
canPutShape.y = mousePos.y * gridCellWidth + gridStartY;
if (mousePos.x < mapGrid[0].length && mousePos.y < mapGrid.length && mousePos.x >= 0 && mousePos.y >= 0) {
canPutShape.alpha = 1;
}else {
canPutShape.alpha = 0;
}
}
}

private function newLevel():void {
canDraw = false;

var newScreen:MovieClip = new new_edit_screen();
addChild(newScreen);
newScreen.tWidth.restrict = "0-9";
newScreen.tHeight.restrict = "0-9";
newScreen.tWidth.text = 10;
newScreen.tHeight.text = 6;
newScreen.incorrect.alpha = 0;
newScreen.btn_continue.addEventListener(MouseEvent.CLICK, editContinue);

function editContinue(evt:MouseEvent):void {
if (newScreen.tWidth.text == "" || newScreen.tHeight.text == "" || newScreen.tWidth.text == "0" || newScreen.tHeight.text == "0") {
newScreen.incorrect.alpha = 1;
return;
}
newScreen.parent.removeChild(newScreen);
mapGrid = [];
var width:int = newScreen.tWidth.text;
var height:int = newScreen.tHeight.text;
for (var i:int = 0; i < height; i++) {
mapGrid[i] = [];
for (var u:int = 0; u < width; u++) {
mapGrid[i][u] = 1;
}
}
// grid settings
calculateGrid();
gridShape.x = gridStartX;
gridShape.y = gridStartY;

// draw tiles
drawGrid();

// canPutShape settings
canPutShape.graphics.clear();
canPutShape.graphics.lineStyle(2, 0xff0000);
canPutShape.graphics.drawRect(0, 0, gridCellWidth, gridCellWidth);
canPutShape.alpha = 0;

canDraw = true;
}
}

private function calculateGrid():void {
var columns:int = mapGrid[0].length;
var rows:int = mapGrid.length;

// free size: 520x460
// fit in: 510x450

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

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

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

if (height < 450) {
gridStartY = (450 - height) / 2;
}
if (height >= 450) {
gridCellWidth = Math.round(450 / rows);
height = rows * gridCellWidth;
width = columns * gridCellWidth;
gridStartY = (460 - height) / 2;
gridStartX = (520 - width) / 2;
}
}

private function drawGrid():void {
gridShape.graphics.clear();
var width:int = mapGrid[0].length;
var height:int = mapGrid.length;

var i:int;
var u:int;

// draw background
for (i = 0; i < height; i++) {
for (u = 0; u < width; u++) {
if (mapGrid[i][u] == 1) drawCell(u, i, 0xffffff, 1, 0x999999);
}
}
}

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

private function buttonOver(evt:MouseEvent):void {
evt.currentTarget.bg.alpha = 1;
}

private function buttonOut(evt:MouseEvent):void {
evt.currentTarget.bg.alpha = 0.3;
}

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

private function onMouseDown(evt:MouseEvent):void {
mouseDown = true;
}

private function onMouseUp(evt:MouseEvent):void {
mouseDown = false;
currentCell = new Point(-1, -1)
}

private function onEnterFrame(evt:Event):void {
// if drawing is allowed and mouse is down
if (canDraw && mouseDown) {
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
// if valid coordinates
if (mousePos.x < mapGrid[0].length && mousePos.y < mapGrid.length && mousePos.x >= 0 && mousePos.y >= 0) {
// if the cell is not "current cell"
if (mousePos.x != currentCell.x || mousePos.y != currentCell.y) {
currentCell.x = mousePos.x;
currentCell.y = mousePos.y;
if (mapGrid[mousePos.y][mousePos.x] == 1) {
mapGrid[mousePos.y][mousePos.x] = 0;
}else {
mapGrid[mousePos.y][mousePos.x] = 1;
}
drawGrid();
}
}
}
}

private function doSave(evt:MouseEvent):void {
if (checkSave()) {
trace("Save!");
}
}

private function checkSave():Boolean {
// count total cells
var totalCells:int = 0;
var i:int;
var u:int;
var width:int = mapGrid[0].length;
var height:int = mapGrid.length;

for (i = 0; i < height; i++) {
for (u = 0; u < width; u++) {
if (mapGrid[i][u] == 1) totalCells++;
}
}

// check if total cells can be divided by 5
if (totalCells / 5 != Math.round(totalCells / 5)) {
alert("Error!\nIncorrect cell count: " + totalCells + ", number must be divideable by 5.");
return false;
}

// count total available shape count
var totalShapes:int = 0;

for (i = 0; i < shapeButtons.length; i++) {
totalShapes += shapeButtons[i].count.value;
}

// check if there are enough shapes available
if (totalCells > totalShapes * 5) {
alert("Error!\nNot enough shapes available: " + totalShapes + " out of " + totalCells/5);
return false;
}

return true;
}

private function alert(message:String):void {
var alertWindow:MovieClip = new alert_screen(message, alertClose);
addChild(alertWindow);
canDraw = false;
}

private function alertClose():void {
canDraw = true;
}

}

}

Thanks for reading!

Results:


No comments:

Post a Comment