Sunday, July 29, 2012

Creating a Pentomino game using AS3: Part 11

Today we'll add a Reset button to the game!

First of all, add a Button component to your game. If you're using flash, just create a button graphic, save it as Button and set its id to "btn_reset". If you only want to use AS3, add a button dynamically. In the end, it doesn't matter how you add the button - it just has to be on the stage with an id "btn_reset".

Add a line in the constructor of your class that adds a MouseEvent CLICK event listener to the button with a handler function doReset():

// buttons
btn_reset.addEventListener(MouseEvent.CLICK, doReset);

Full constructor code:

public function pentomino_game() 
{
// default map
mapGrid = [
[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]
];

availableShapes = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2];

shapeValues = [
[[0, 0], [0, 1], [0, 2], [0, -1], [0, -2]],
[[0, 0], [0, -1], [0, 1], [1, 0], [1, -1]],
[[0, 0], [0, 1], [0, 2], [0, -1], [ -1, -1]],
[[0, 0], [0, 1], [0, -1], [ -1, 0], [1, -1]],
[[0, 0], [ -1, 0], [ -2, 0], [0, -1], [1, -1]],
[[0, 0], [0, 1], [0, -1], [1, -1], [ -1, -1]],
[[0, 0], [1, 0], [ -1, 0], [1, -1], [ -1, -1]],
[[0, 0], [ -1, 0], [ -2, 0], [0, -1], [0, -2]],
[[0, 0], [0, 1], [ -1, 1], [1, 0], [1, -1]],
[[0, 0], [0, 1], [0, -1], [1, 0], [ -1, 0]],
[[0, 0], [ -1, 0], [ -2, 0], [1, 0], [0, -1]],
[[0, 0], [0, 1], [1, 1], [0, -1], [-1, -1]]
];

// grid settings
calculateGrid();
addChild(gridShape);
gridShape.x = gridStartX;
gridShape.y = gridStartY;

// draw tiles
drawGrid();

// can put shape
addChild(canPutShape);
canPutShape.x = gridStartX;
canPutShape.y = gridStartY;

// add shape buttons
for (var i:int = 0; i < 4; i++) {
for (var u:int = 0; u < 3; u++) {
var shapeButton:MovieClip = new select_shape();
shapeButton.x = 528 + u * 62;
shapeButton.y = 55 + i * 62;
addChild(shapeButton);
shapeButton.bg.alpha = 0.3;
shapeButton.count.text = String(availableShapes[3 * i + u]);
shapeButton.shape.gotoAndStop(3 * i + u + 1);
shapeButtons.push(shapeButton);
shapeButton.addEventListener(MouseEvent.ROLL_OVER, buttonOver);
shapeButton.addEventListener(MouseEvent.ROLL_OUT, buttonOut);
shapeButton.addEventListener(MouseEvent.MOUSE_DOWN, buttonDown);
}
}

// cursor
cursor = new game_shape();
addChild(cursor);
cursor.mouseEnabled = false;
cursor.mouseChildren = false;
cursor.visible = false;
addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheel);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
addEventListener(Event.ENTER_FRAME, everyFrame);
addEventListener(MouseEvent.MOUSE_UP, mouseUp);

// buttons
btn_reset.addEventListener(MouseEvent.CLICK, doReset);
}

In the beginning of the doReset() function, declare 4 variables - i, u, width and height:

var i:int;
var u:int;
var width:int = mapGrid[0].length;
var height:int = mapGrid.length;

Loop through all cells in mapGrid and set their values to 1 if they are currently 2:

// change values in array
for (i = 0; i < height; i++) {
for (u = 0; u < width; u++) {
if (mapGrid[i][u] == 2) mapGrid[i][u] = 1;
}
}

Next, loop through all placed shapes in the placedShapes array. We need to reset the available shapes count for each shape, update the count text field for each shape and then actually remove each shape from the stage and the array.

// delete each shape
for (i = placedShapes.length-1; i >= 0; i--) {
selectedShape = placedShapes[i].currentFrame;
availableShapes[selectedShape-1]++;
shapeButtons[selectedShape-1].count.text = availableShapes[selectedShape-1];
placedShapes[i].parent.removeChild(placedShapes[i]);
placedShapes.splice(i, 1);
}

selectedShape = 0;

Full function:

private function doReset(evt:MouseEvent):void {
var i:int;
var u:int;
var width:int = mapGrid[0].length;
var height:int = mapGrid.length;

// change values in array
for (i = 0; i < height; i++) {
for (u = 0; u < width; u++) {
if (mapGrid[i][u] == 2) mapGrid[i][u] = 1;
}
}

// delete each shape
for (i = placedShapes.length-1; i >= 0; i--) {
selectedShape = placedShapes[i].currentFrame;
availableShapes[selectedShape-1]++;
shapeButtons[selectedShape-1].count.text = availableShapes[selectedShape-1];
placedShapes[i].parent.removeChild(placedShapes[i]);
placedShapes.pop();
}
placedShapes = [];

selectedShape = 0;
}

We also need to delete a shape from placedShapes array when the user clicks on it to drag it from the position, so add a for loop that goes through all placedShapes elements in placedShapeDown() and compares them to the evt.currentTarget shape. If they match, delete the item from the array:

private function placedShapeDown(evt:MouseEvent):void {
selectedShape = evt.currentTarget.currentFrame;

// start dragging
startDragShape(evt.currentTarget.currentFrame, evt.currentTarget.rotation, evt.currentTarget.scaleX, evt.currentTarget.scaleY);

// update grid values
updateCurrentValues();
var shapePos:Point = new Point(Math.floor((evt.currentTarget.x - gridStartX) / gridCellWidth), Math.floor((evt.currentTarget.y - gridStartY) / gridCellWidth));
for (var i:int = 0; i < 5; i++) {
mapGrid[shapePos.y + currentValues[i][1]][shapePos.x + currentValues[i][0]] = 1;
}

// remove shape
for (var u:int = 0; u < placedShapes.length; u++) {
if (placedShapes[u] == evt.currentTarget) {
placedShapes.splice(u, 1);
break;
}
}
evt.currentTarget.removeEventListener(MouseEvent.MOUSE_DOWN, placedShapeDown);
evt.currentTarget.parent.removeChild(evt.currentTarget);

checkPut();
}

And now we can reset the level!

Full 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.utils.ByteArray;

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

public class pentomino_game extends MovieClip
{
private var mapGrid:Array = [];
private var availableShapes:Array = [];
private var shapeButtons:Array = [];
private var shapeValues:Array = [];
private var placedShapes: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 cursor:MovieClip;
private var rolledShape:MovieClip;
private var selectedShape:int = 0;
private var canPutHere:Boolean = false;
private var currentValues:Array = [];

public function pentomino_game() 
{
// default map
mapGrid = [
[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]
];

availableShapes = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2];

shapeValues = [
[[0, 0], [0, 1], [0, 2], [0, -1], [0, -2]],
[[0, 0], [0, -1], [0, 1], [1, 0], [1, -1]],
[[0, 0], [0, 1], [0, 2], [0, -1], [ -1, -1]],
[[0, 0], [0, 1], [0, -1], [ -1, 0], [1, -1]],
[[0, 0], [ -1, 0], [ -2, 0], [0, -1], [1, -1]],
[[0, 0], [0, 1], [0, -1], [1, -1], [ -1, -1]],
[[0, 0], [1, 0], [ -1, 0], [1, -1], [ -1, -1]],
[[0, 0], [ -1, 0], [ -2, 0], [0, -1], [0, -2]],
[[0, 0], [0, 1], [ -1, 1], [1, 0], [1, -1]],
[[0, 0], [0, 1], [0, -1], [1, 0], [ -1, 0]],
[[0, 0], [ -1, 0], [ -2, 0], [1, 0], [0, -1]],
[[0, 0], [0, 1], [1, 1], [0, -1], [-1, -1]]
];

// grid settings
calculateGrid();
addChild(gridShape);
gridShape.x = gridStartX;
gridShape.y = gridStartY;

// draw tiles
drawGrid();

// can put shape
addChild(canPutShape);
canPutShape.x = gridStartX;
canPutShape.y = gridStartY;

// add shape buttons
for (var i:int = 0; i < 4; i++) {
for (var u:int = 0; u < 3; u++) {
var shapeButton:MovieClip = new select_shape();
shapeButton.x = 528 + u * 62;
shapeButton.y = 55 + i * 62;
addChild(shapeButton);
shapeButton.bg.alpha = 0.3;
shapeButton.count.text = String(availableShapes[3 * i + u]);
shapeButton.shape.gotoAndStop(3 * i + u + 1);
shapeButtons.push(shapeButton);
shapeButton.addEventListener(MouseEvent.ROLL_OVER, buttonOver);
shapeButton.addEventListener(MouseEvent.ROLL_OUT, buttonOut);
shapeButton.addEventListener(MouseEvent.MOUSE_DOWN, buttonDown);
}
}

// cursor
cursor = new game_shape();
addChild(cursor);
cursor.mouseEnabled = false;
cursor.mouseChildren = false;
cursor.visible = false;
addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheel);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
addEventListener(Event.ENTER_FRAME, everyFrame);
addEventListener(MouseEvent.MOUSE_UP, mouseUp);

// buttons
btn_reset.addEventListener(MouseEvent.CLICK, doReset);
}

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 mouseMove(evt:MouseEvent):void {
cursor.x = mouseX;
cursor.y = mouseY;
checkPut();
}

private function checkPut():void {
if (selectedShape > 0) {
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
canPutHere = true;
updateCurrentValues();
for (var i:int = 0; i < currentValues.length; i++) {
var cellX:int = currentValues[i][0] + mousePos.x;
var cellY:int = currentValues[i][1] + mousePos.y;
if (cellX < 0 || cellY < 0 || cellX >= mapGrid[0].length || cellY >= mapGrid.length || mapGrid[cellY][cellX]!=1) {
canPutHere = false;
}
}
if (canPutHere) {
cursor.alpha = 0.8;
}
if (!canPutHere) {
canPutShape.graphics.clear();
cursor.alpha = 0.4;
}
}
}

private function drawCanPut():void {
canPutShape.graphics.clear();
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
for (var i:int = 0; i < currentValues.length; i++) {
var cellX:int = (currentValues[i][0] + mousePos.x) * gridCellWidth;
var cellY:int = (currentValues[i][1] + mousePos.y) * gridCellWidth;
canPutShape.graphics.beginFill(0x00ff00, 0.2);
canPutShape.graphics.drawRect(cellX, cellY, gridCellWidth, gridCellWidth);
}
}

private function mouseWheel(evt:MouseEvent):void {
if (evt.delta > 0) cursor.rotation += 90;
if (evt.delta < 0) cursor.rotation -= 90;
if (selectedShape > 0) checkPut();
}

private function keyDown(evt:KeyboardEvent):void {
if (evt.keyCode == 68) cursor.rotation += 90;
if (evt.keyCode == 65) cursor.rotation -= 90;
if (evt.keyCode == 32) cursor.scaleX *= -1;
if (selectedShape > 0) checkPut();
}

private function updateCurrentValues():void {
currentValues = clone(shapeValues[selectedShape-1]);
for (var i:int = 0; i < 5; i++) {
if (cursor.rotation == 90) {
currentValues[i].reverse();
currentValues[i][0] *= -1;
}
if (cursor.rotation == 180 || cursor.rotation == -180) {
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.rotation == -90) {
currentValues[i].reverse();
currentValues[i][1] *= -1;
}
if (cursor.scaleX < 0 && (cursor.rotation != -90 || cursor.rotation != 90)) {
currentValues[i][0] *= -1;
}
if (cursor.scaleX < 0 && (cursor.rotation == -90 || cursor.rotation == 90)) {
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
}
drawCanPut();
}

private function clone(source:Object):*{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}

private function startDragShape(shapeType:int, rot:int = 0, scalex:Number = 0, scaley:Number = 0):void {
cursor.rotation = rot;
cursor.scaleX = scalex;
cursor.scaleY = scaley;
if (scalex == 0) {
cursor.scaleX = gridCellWidth / 100;
cursor.scaleY = gridCellWidth / 100;
}
cursor.alpha = 0.75;
cursor.visible = true;
cursor.gotoAndStop(shapeType);
}

private function stopDragShape():void {
canPutShape.graphics.clear();
cursor.alpha = 0;
cursor.visible = false;
}

private function everyFrame(evt:Event):void {
if (rolledShape != null) {
rolledShape.rotation += 4;
}
}

private function buttonOver(evt:MouseEvent):void {
if(selectedShape==0){
evt.currentTarget.bg.alpha = 1;
rolledShape = evt.target.shape;
}
}

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

private function buttonDown(evt:MouseEvent):void {
selectedShape = evt.currentTarget.shape.currentFrame;
if(availableShapes[selectedShape-1]>0){
startDragShape(selectedShape);
availableShapes[selectedShape-1]--;
evt.currentTarget.count.text = availableShapes[selectedShape-1];
evt.currentTarget.bg.alpha = 0.3;
rolledShape = null;
}else {
selectedShape = 0;
}
}

private function mouseUp(evt:MouseEvent):void {
stopDragShape();
if (selectedShape > 0) {
if (!canPutHere) {
availableShapes[selectedShape-1]++;
shapeButtons[selectedShape-1].count.text = availableShapes[selectedShape-1];
selectedShape = 0;
}
if (canPutHere) {
var mousePos:Point = new Point(Math.floor((mouseX - gridStartX) / gridCellWidth), Math.floor((mouseY - gridStartY) / gridCellWidth));
var newShape:MovieClip = new game_shape();
addChild(newShape);
newShape.x = gridStartX + (mousePos.x+1) * gridCellWidth - gridCellWidth / 2;
newShape.y = gridStartY + (mousePos.y + 1) * gridCellWidth - gridCellWidth / 2;
newShape.rotation = cursor.rotation;
newShape.scaleX = cursor.scaleX;
newShape.scaleY = cursor.scaleY;
newShape.gotoAndStop(selectedShape);
newShape.addEventListener(MouseEvent.MOUSE_DOWN, placedShapeDown);
placedShapes.push(newShape);
for (var i:int = 0; i < currentValues.length; i++) {
var cellX:int = currentValues[i][0] + mousePos.x;
var cellY:int = currentValues[i][1] + mousePos.y;
mapGrid[cellY][cellX] = 2;
}
cursor.parent.setChildIndex(cursor, cursor.parent.numChildren - 1);
selectedShape = 0;
}
}
}

private function placedShapeDown(evt:MouseEvent):void {
selectedShape = evt.currentTarget.currentFrame;

// start dragging
startDragShape(evt.currentTarget.currentFrame, evt.currentTarget.rotation, evt.currentTarget.scaleX, evt.currentTarget.scaleY);

// update grid values
updateCurrentValues();
var shapePos:Point = new Point(Math.floor((evt.currentTarget.x - gridStartX) / gridCellWidth), Math.floor((evt.currentTarget.y - gridStartY) / gridCellWidth));
for (var i:int = 0; i < 5; i++) {
mapGrid[shapePos.y + currentValues[i][1]][shapePos.x + currentValues[i][0]] = 1;
}

// remove shape
for (var u:int = 0; u < placedShapes.length; u++) {
if (placedShapes[u] == evt.currentTarget) {
placedShapes.splice(u, 1);
break;
}
}
evt.currentTarget.removeEventListener(MouseEvent.MOUSE_DOWN, placedShapeDown);
evt.currentTarget.parent.removeChild(evt.currentTarget);

checkPut();
}

private function doReset(evt:MouseEvent):void {
var i:int;
var u:int;
var width:int = mapGrid[0].length;
var height:int = mapGrid.length;

// change values in array
for (i = 0; i < height; i++) {
for (u = 0; u < width; u++) {
if (mapGrid[i][u] == 2) mapGrid[i][u] = 1;
}
}

// delete each shape
for (i = placedShapes.length-1; i >= 0; i--) {
selectedShape = placedShapes[i].currentFrame;
availableShapes[selectedShape-1]++;
shapeButtons[selectedShape-1].count.text = availableShapes[selectedShape-1];
placedShapes[i].parent.removeChild(placedShapes[i]);
placedShapes.pop();
}
placedShapes = [];

selectedShape = 0;
}
}

}

Thanks for reading!

Here are the results:


No comments:

Post a Comment