Showing posts with label as3. Show all posts
Showing posts with label as3. Show all posts
Thursday, February 5, 2015
AS3 TextFormat
In this lesson, were going to learn how to format the text inside a text field. In order to do that, we are going to learn how to use the ActionScript 3 TextFormat class. This AS3 TextFormat class can be used to apply text formatting properties - such as the font face, font color and the font size - to the contents of a text field.
Lets begin.
Create a new Flash ActionScript 3 document. Then lets go to the Actions Panel and lets start writing some code. Lets start by creating a new AS3 dynamic text field:
If you want to format the text - increase / decrease the size, change the font, etc..., then you can use the AS3 TextFormat class. A TextFormat object will hold the text formatting properties, which you can then apply to a TextField object. Some properties from the TextFormat class are:
Lets go ahead and create a new instance of the TextFormat class. Im going to name it myTextFormat:
One way of assigning a TextFormat object to a TextField object would be by using the setTextFormat() method of the TextField class. This is how you would use it:
Here, you see that the TextFormat object is passed to the setTextFormat() method. So in our example, we would write:
Also, you should know that this line should always come AFTER the text assignment statement (textField.text = "text"). Otherwise, the formatting will not apply. So lets go ahead and add the setTextFormat() statement, making sure that it comes after the text assignment statement:
Here, the TextFormat object is assigned to the defaultTextFormat property. So in our example, if we use an input text field that is initially empty instead, we would write:
As was mentioned previously, this line does NOT need to come after any text assignment statement.
Heres a version of our sample code that uses an input text field instead:
And heres a recap of the difference between setTextFormat() and defaultTextFormat:
Use the setTextFormat() method to apply formatting AFTER text is added to a text field.
Use the defaultTextFormat property to apply formatting BEFORE text is added to a text field.
And that is how you use the AS3 TextFormat class.
Read more »
Lets begin.
Create a new Flash ActionScript 3 document. Then lets go to the Actions Panel and lets start writing some code. Lets start by creating a new AS3 dynamic text field:
var myTextField:TextField = new TextField();
addChild(myTextField);
myTextField.border = true;
myTextField.x = 50;
myTextField.y = 25;
myTextField.autoSize = TextFieldAutoSize.LEFT;
myTextField.text = "Formatting Text in ActionScript 3";
Here, weve created a new text field named myTextField. If you test the movie, the text is just going to have the default text formatting properties applied.If you want to format the text - increase / decrease the size, change the font, etc..., then you can use the AS3 TextFormat class. A TextFormat object will hold the text formatting properties, which you can then apply to a TextField object. Some properties from the TextFormat class are:
- font - to specify the font to be used
- size - to specify the font size in pixels
- color - to specify the font color
- leftMargin - to specify the left margin in pixels
- rightMargin - to specify the right margin in pixels
Lets go ahead and create a new instance of the TextFormat class. Im going to name it myTextFormat:
var myTextField:TextField = new TextField();
var myTextFormat:TextFormat = new TextFormat();
addChild(myTextField);
myTextField.border = true;
myTextField.x = 50;
myTextField.y = 25;
myTextField.autoSize = TextFieldAutoSize.LEFT;
myTextField.text = "Formatting Text in ActionScript 3";
Now that we have a new TextFormat object, lets go ahead and set some of the formatting properties:var myTextField:TextField = new TextField();
var myTextFormat:TextFormat = new TextFormat();
addChild(myTextField);
myTextFormat.font = "Arial";
myTextFormat.size = 15;
myTextFormat.color = 0x0000FF;
myTextFormat.leftMargin = 30;
myTextField.border = true;
myTextField.x = 50;
myTextField.y = 25;
myTextField.autoSize = TextFieldAutoSize.LEFT;
myTextField.text = "Formatting Text in ActionScript 3";
Here, weve set new values for the font, size, color and leftMargin. But if we test the movie, we wont see any of the new text formatting properties applied just yet. Thats because we have yet to apply the formatting properties to the actual text field. Just because weve created the text format object, that does not mean that the text formatting properties will automatically apply to all the text fields in our Flash movie.One way of assigning a TextFormat object to a TextField object would be by using the setTextFormat() method of the TextField class. This is how you would use it:
textFieldObject.setTextFormat(textFormatObject);
Here, you see that the TextFormat object is passed to the setTextFormat() method. So in our example, we would write:
myTextField.setTextFormat(myTextFormat);
Also, you should know that this line should always come AFTER the text assignment statement (textField.text = "text"). Otherwise, the formatting will not apply. So lets go ahead and add the setTextFormat() statement, making sure that it comes after the text assignment statement:
var myTextField:TextField = new TextField();
var myTextFormat:TextFormat = new TextFormat();
addChild(myTextField);
myTextFormat.font = "Arial";
myTextFormat.size = 15;
myTextFormat.color = 0x0000FF;
myTextFormat.leftMargin = 30;
myTextField.border = true;
myTextField.x = 50;
myTextField.y = 25;
myTextField.autoSize = TextFieldAutoSize.LEFT;
myTextField.text = "Formatting Text in ActionScript 3";
myTextField.setTextFormat(myTextFormat);
So if you test the movie now, you should see the text formatting properties applied to the text in our text field.Formatting Input Text Fields
Formatting an input text field would be pretty much the same as the method we just demonstrated above. However, if our input text field is initially empty, then we can NOT use the setTextFormat() method in order to assign the text formatting to the text field. We can not use the setTextFormat() method because if you recall, the setTextFormat() statement must come AFTER the text assignment statement. So if we dont have a text assignment statement, then there would be no appropriate place to write the setTextFormat() statement. So if that is the case, we can use the defaultTextFormat property of the TextField class instead:textFieldObject.defaultTextFormat = textFormatObject;
Here, the TextFormat object is assigned to the defaultTextFormat property. So in our example, if we use an input text field that is initially empty instead, we would write:
myTextField.defaultTextFormat = myTextFormat;
As was mentioned previously, this line does NOT need to come after any text assignment statement.
Heres a version of our sample code that uses an input text field instead:
var myTextField:TextField = new TextField();
var myTextFormat:TextFormat = new TextFormat();
addChild(myTextField);
myTextFormat.font = "Arial";
myTextFormat.size = 15;
myTextFormat.color = 0x0000FF;
myTextFormat.leftMargin = 30;
myTextField.type = "input";
myTextField.border = true;
myTextField.x = 50;
myTextField.y = 25;
myTextField.autoSize = TextFieldAutoSize.LEFT;
myTextField.defaultTextFormat = myTextFormat;
And heres a recap of the difference between setTextFormat() and defaultTextFormat:
setTextFormat()
This is a METHOD of the TextField class.Use the setTextFormat() method to apply formatting AFTER text is added to a text field.
defaultTextFormat
This is a PROPERTY of the TextField class.Use the defaultTextFormat property to apply formatting BEFORE text is added to a text field.
And that is how you use the AS3 TextFormat class.
Wednesday, February 4, 2015
Creating a Pentomino game using AS3 Part 38
Today we will add the ability to browse through the solutions.
Firstly declare a new variable - currentSol. Set its value to 0 by default. This variable represents the id of the solution that is currently displayed.
Open the project in Flash and go to fifth frame (the solutions screen). Add two buttons shaped like arrows with ids btn_prev and btn_next. Add a dynamic text field with id tSol, it will display which solutions is currently is shown.
Go to level_solver.as again and in the solve() function add CLICK event listeners for btn_prev and btn_next. Set handlers to goPrev and goNext functions:
The functions decrease/increase currentSol values and call updateCurrentInfo() function.
The same function is called in nextCycle():
The function updates what is displayed in the tSol text field, disables/enables arrow buttons when needed, draws the preview image:
Update the drawPreview() function so that cells are colored depending on the shape type:
Now we can browse through the solutions!
Full level_solver.as code:
Thanks for reading!
Read more »
Firstly declare a new variable - currentSol. Set its value to 0 by default. This variable represents the id of the solution that is currently displayed.
private var currentSol:int = 0;
Open the project in Flash and go to fifth frame (the solutions screen). Add two buttons shaped like arrows with ids btn_prev and btn_next. Add a dynamic text field with id tSol, it will display which solutions is currently is shown.
Go to level_solver.as again and in the solve() function add CLICK event listeners for btn_prev and btn_next. Set handlers to goPrev and goNext functions:
btn_prev.addEventListener(MouseEvent.CLICK, goPrev);
btn_next.addEventListener(MouseEvent.CLICK, goNext);
The functions decrease/increase currentSol values and call updateCurrentInfo() function.
private function goPrev(evt:MouseEvent):void {
currentSol--;
updateCurrentInfo();
}
private function goNext(evt:MouseEvent):void {
currentSol++;
updateCurrentInfo();
}
The same function is called in nextCycle():
private function nextCycle():void {
cycleNum++;
updateCurrentInfo();
if (cycleNum == givenShapes.length) {
removeEventListener(Event.ENTER_FRAME, onFrame);
// display info
tInfo.text = Finished solving " + levelName + "
Attempts: + attempts + "
Solutions: " + solutions.length;
}
}
The function updates what is displayed in the tSol text field, disables/enables arrow buttons when needed, draws the preview image:
private function updateCurrentInfo():void {
var sol:int = (solutions.length > 0)?(currentSol + 1):(0);
tSol.text = sol + "/" + solutions.length;
if (sol <= 1) {
btn_prev.alpha = 0.5;
btn_prev.mouseEnabled = false;
}else {
btn_prev.alpha = 1;
btn_prev.mouseEnabled = true;
}
if (sol == solutions.length) {
btn_next.alpha = 0.5;
btn_next.mouseEnabled = false;
}else {
btn_next.alpha = 1;
btn_next.mouseEnabled = true;
}
if (solutions.length > 0) drawPreview(levelPreview, solutions[currentSol]);
}
Update the drawPreview() function so that cells are colored depending on the shape type:
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.shape.x = gridStartX;
levelPreview.shape.y = gridStartY;
levelPreview.shape.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.shape);
if (mapGrid[i][u] == 2) drawCell(u, i, 0xff66cc, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 3) drawCell(u, i, 0x00FF66, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 4) drawCell(u, i, 0x99ff00, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 5) drawCell(u, i, 0x00ccff, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 6) drawCell(u, i, 0xffcc66, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 7) drawCell(u, i, 0x66cc33, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 8) drawCell(u, i, 0x9900ff, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 9) drawCell(u, i, 0xff5858, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 10) drawCell(u, i, 0xff6600, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 11) drawCell(u, i, 0x00ffff, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 12) drawCell(u, i, 0xff6b20, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 13) drawCell(u, i, 0xffff66, 1, 0x000000, gridCellWidth, levelPreview.shape);
}
}
}
Now we can browse through the solutions!
Full level_solver.as code:
package
{
import fl.controls.TextInput;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.net.SharedObject;
import flash.text.TextField;
import flash.utils.ByteArray;
/**
* Open-source pentomino game engine
* @author Kirill Poletaev
*/
public class level_solver extends MovieClip
{
private var allCells:Array;
private var tempGrid:Array;
private var tempShapes:Array;
private var givenGrid:Array;
private var givenShapes:Array;
private var cycleNum:int;
private var attempts:int;
private var solutions:Array;
private var levelName:String;
private var shapeValues:Array;
private var currentSol:int = 0;
public function level_solver()
{
}
public function solve(levelItem:Object):void {
drawPreview(levelPreview, levelItem.grid);
tInfo.text = Solving level " + levelItem.name + ";
var shapes:Array = levelItem.shapes;
var i:int
var u:int;
givenShapes = [];
for (i = 1; i <= 12; i++) {
this["tShape" + i].text = shapes[i - 1];
for (u = 0; u < shapes[i - 1]; u++) {
givenShapes.push(i - 1);
}
}
allCells = [];
for (i = 0; i < levelItem.grid.length; i++) {
for (u = 0; u < levelItem.grid.length; u++) {
if (levelItem.grid[i][u] == 1) {
allCells.push(new Point(u, i));
}
}
}
givenGrid = levelItem.grid;
tempGrid = [];
tempShapes = [];
solutions = [];
levelName = levelItem.name;
attempts = 0;
cycleNum = 0;
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]]
];
addEventListener(Event.ENTER_FRAME, onFrame);
btn_prev.addEventListener(MouseEvent.CLICK, goPrev);
btn_next.addEventListener(MouseEvent.CLICK, goNext);
}
private function onFrame(evt:Event):void {
// set temporary values
tempGrid = clone(givenGrid);
tempShapes = clone(givenShapes);
// perform a new cycle
var i:int;
var u:int;
var currentShapeValues:Array;
// Take the first shape in this cycle...
currentShapeValues = clone(shapeValues[tempShapes[cycleNum]]);
// ...try putting it in each available cell...
for (i = 0; i < allCells.length; i++) {
// ...try putting the shape in all 8 possible positions...
for (u = 0; u < 8; u++) {
// ...if successful, continue placing other cells...
if (canPut(currentShapeValues, allCells[i].x, allCells[i].y, tempGrid, u)) {
// ...first put the first shape on the grid...
putHere(currentShapeValues, allCells[i].x, allCells[i].y, tempGrid, u, tempShapes[cycleNum] + 2);
// ...try all remaining shapes...
tryRemainingShapes(tempShapes, tempGrid);
removeHere(currentShapeValues, allCells[i].x, allCells[i].y, tempGrid, u);
}
}
}
// ...remove it from shapes array (so that it isnt used twice)...
tempShapes.splice(cycleNum, 1);
// display info
tInfo.text = Solving level " + levelName + "
Attempts: + attempts + "
Solutions: " + solutions.length;
// next cycle
nextCycle();
}
private function tryRemainingShapes(tShapes:Array, tGrid:Array):void {
var innerTempShapes:Array = clone(tShapes);
var innerTempGrid:Array = clone(tGrid);
var e:int;
var t:int;
// ...take each remaining shape...
while (innerTempShapes.length > 0) {
var currentShapeValues:Array = clone(shapeValues[innerTempShapes[0]]);
// ...try putting it in each cell...
for (e = 0; e < allCells.length; e++) {
// ...rotate the shape...
if(hasSpace(innerTempGrid, allCells[e].x, allCells[e].y)){
for (t = 0; t < 8; t++) {
// ...check if it can be put...
if (canPut(currentShapeValues, allCells[e].x, allCells[e].y, innerTempGrid, t)) {
// ...on success, put this shape and try all remaining shapes...
putHere(currentShapeValues, allCells[e].x, allCells[e].y, innerTempGrid, t, innerTempShapes[0] + 2);
tryRemainingShapes(clone(innerTempShapes).splice(0, 1), innerTempGrid);
// ...if no empty cells remaining, then the puzzle is solved.
if (checkWin(innerTempGrid)) doWin(clone(innerTempGrid));
// ...step backwards...
removeHere(currentShapeValues, allCells[e].x, allCells[e].y, innerTempGrid, t);
}
}
}
}
// ...get rid of the shape and continue...
innerTempShapes.shift();
}
}
private function hasSpace(mapGrid:Array, cX:int, cY:int):Boolean {
if (mapGrid[cY][cX] != 1) return false;
return true;
}
private function checkWin(mapGrid:Array):Boolean {
var i:int;
var u:int;
var didWin:Boolean = true;
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) {didWin = false;}
}
}
return didWin;
}
private function doWin(grid:Array):void {
var exists:Boolean = false;
for (var i:int = 0; i < solutions.length; i++) {
if (compare(solutions[i], grid)) {
exists = true;
break;
}
}
if (!exists) solutions.push(grid);
}
private function compare(arr1:Array, arr2:Array):Boolean {
for (var i:int = 0; i < arr1.length; i++) {
for (var u:int = 0; u < arr1[i].length; u++){
if (arr1[i][u] != arr2[i][u]) {
return false;
break;
}
}
}
return true;
}
private function goPrev(evt:MouseEvent):void {
currentSol--;
updateCurrentInfo();
}
private function goNext(evt:MouseEvent):void {
currentSol++;
updateCurrentInfo();
}
private function updateCurrentInfo():void {
var sol:int = (solutions.length > 0)?(currentSol + 1):(0);
tSol.text = sol + "/" + solutions.length;
if (sol <= 1) {
btn_prev.alpha = 0.5;
btn_prev.mouseEnabled = false;
}else {
btn_prev.alpha = 1;
btn_prev.mouseEnabled = true;
}
if (sol == solutions.length) {
btn_next.alpha = 0.5;
btn_next.mouseEnabled = false;
}else {
btn_next.alpha = 1;
btn_next.mouseEnabled = true;
}
if (solutions.length > 0) drawPreview(levelPreview, solutions[currentSol]);
}
private function nextCycle():void {
cycleNum++;
updateCurrentInfo();
if (cycleNum == givenShapes.length) {
removeEventListener(Event.ENTER_FRAME, onFrame);
// display info
tInfo.text = Finished solving " + levelName + "
Attempts: + attempts + "
Solutions: " + solutions.length;
}
}
private function canPut(cValues:Array, cellX:int, cellY:int, mapGrid:Array, rotation:int):Boolean {
attempts++;
var canPutHere:Boolean = true;
var currentValues:Array = clone(cValues);
updateCurrentValues(currentValues, rotation);
for (var i:int = 0; i < currentValues.length; i++) {
var cX:int = currentValues[i][0] + cellX;
var cY:int = currentValues[i][1] + cellY;
if (cX < 0 || cY < 0 || cX >= mapGrid[0].length || cY >= mapGrid.length || mapGrid[cY][cX]!=1) {
canPutHere = false;
}
}
return canPutHere;
}
private function putHere(currentValues:Array, cellX:int, cellY:int, mapGrid:Array, rotation:int, shape:int):void {
currentValues = clone(currentValues);
updateCurrentValues(currentValues, rotation);
for (var i:int = 0; i < currentValues.length; i++) {
var cX:int = currentValues[i][0] + cellX;
var cY:int = currentValues[i][1] + cellY;
mapGrid[cY][cX] = shape;
}
}
private function removeHere(currentValues:Array, cellX:int, cellY:int, mapGrid:Array, rotation:int):void {
currentValues = clone(currentValues);
updateCurrentValues(currentValues, rotation);
for (var i:int = 0; i < currentValues.length; i++) {
var cX:int = currentValues[i][0] + cellX;
var cY:int = currentValues[i][1] + cellY;
mapGrid[cY][cX] = 1;
}
}
private function updateCurrentValues(currentValues:Array, rot:int):void{
for (var i:int = 0; i < 5; i++) {
// do nothing if rot == 0
if (rot == 1) {
currentValues[i].reverse();
currentValues[i][0] *= -1;
}
if (rot == 2) {
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (rot == 3) {
currentValues[i].reverse();
currentValues[i][1] *= -1;
}
if (rot == 4) {
currentValues[i][0] *= -1;
}
if (rot == 5) {
currentValues[i].reverse();
}
if (rot == 6) {
currentValues[i][1] *= -1;
}
if (rot == 7) {
currentValues[i].reverse();
currentValues[i][1] *= -1;
currentValues[i][0] *= -1;
}
}
}
private function clone(source:Object):*{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}
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.shape.x = gridStartX;
levelPreview.shape.y = gridStartY;
levelPreview.shape.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.shape);
if (mapGrid[i][u] == 2) drawCell(u, i, 0xff66cc, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 3) drawCell(u, i, 0x00FF66, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 4) drawCell(u, i, 0x99ff00, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 5) drawCell(u, i, 0x00ccff, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 6) drawCell(u, i, 0xffcc66, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 7) drawCell(u, i, 0x66cc33, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 8) drawCell(u, i, 0x9900ff, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 9) drawCell(u, i, 0xff5858, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 10) drawCell(u, i, 0xff6600, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 11) drawCell(u, i, 0x00ffff, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 12) drawCell(u, i, 0xff6b20, 1, 0x000000, gridCellWidth, levelPreview.shape);
if (mapGrid[i][u] == 13) drawCell(u, i, 0xffff66, 1, 0x000000, gridCellWidth, levelPreview.shape);
}
}
}
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);
}
}
}
Thanks for reading!
Friday, January 30, 2015
AdvAlert AS3 class Download and How To Use Part 7
Today Ill show you how to create a simple skin set.
By skin set I mean a few skins of the same design, but different functionality and goal.
For example, today well create 3 skins of one design for warning messages, information messages, and confirmation windows.
You can see the final results in the end of this tutorial.
The source code for todays tutorial can be found here.
You can unpack the package now and look at the contents. Youll see that the AdvAlert class is stored in the com folder, and there are 3 pictures and 2 sounds in the rsrc folder. The code of the example is in main.as file of the root folder.
The pictures and sounds from the rsrc folder are embedded into the final swf file. You can see that embedding resources and using them as dynamic content does not require you to use Flash IDE.
I will now explain what each part of main.as code does.
You can see that the first thing we do is declare the essential variables. Theres only one in this case - AlertManager:
Then we embed each resource:
And then we declare 6 variables for 3 skins:
In the init() function we first set AlertManager to a new AdvAlertManager:
And then proceed to stylize the 3 skins:
I also have this bit of code that just displays the alert windows as an example, one after another (with some interaction involved). This code functions called confirm, warn, and info, which we will create shortly.
Then we create 3 functions - warn, info and confirm, which use the 3 different skins:
And thats it! Now we can use these handy alert windows of different types in games and applications with minimum number of lines.
Using this principle, you can create your own skin sets and introduce new window types.
Thanks for reading!
Read more »
By skin set I mean a few skins of the same design, but different functionality and goal.
For example, today well create 3 skins of one design for warning messages, information messages, and confirmation windows.
You can see the final results in the end of this tutorial.
The source code for todays tutorial can be found here.
You can unpack the package now and look at the contents. Youll see that the AdvAlert class is stored in the com folder, and there are 3 pictures and 2 sounds in the rsrc folder. The code of the example is in main.as file of the root folder.
The pictures and sounds from the rsrc folder are embedded into the final swf file. You can see that embedding resources and using them as dynamic content does not require you to use Flash IDE.
I will now explain what each part of main.as code does.
You can see that the first thing we do is declare the essential variables. Theres only one in this case - AlertManager:
// Essential variables
private var AlertManager:AdvAlertManager;
Then we embed each resource:
// Embedded resources
[Embed(source = "rsrc/warning.png")]
private var warning_icon:Class;
[Embed(source = "rsrc/info.png")]
private var info_icon:Class;
[Embed(source = "rsrc/confirm.png")]
private var confirm_icon:Class;
[Embed(source = "rsrc/critical.mp3")]
private var warning_sound:Class;
[Embed(source = "rsrc/info.mp3")]
private var info_sound:Class;
And then we declare 6 variables for 3 skins:
// Skins
private var skin_warning:AdvAlertSkin = new AdvAlertSkin();
private var buttonskin_warning:AdvAlertButtonSkin = new AdvAlertButtonSkin();
private var skin_info:AdvAlertSkin = new AdvAlertSkin();
private var buttonskin_info:AdvAlertButtonSkin = new AdvAlertButtonSkin();
private var skin_confirm:AdvAlertSkin = new AdvAlertSkin();
private var buttonskin_confirm:AdvAlertButtonSkin = new AdvAlertButtonSkin();
In the init() function we first set AlertManager to a new AdvAlertManager:
// Declare AlertManager
AlertManager = new AdvAlertManager(this, stage, null, null, null, true, 300, 0, 160);
And then proceed to stylize the 3 skins:
// Warning skin
skin_warning.bgRect = new SolidColorRect(0xe2e2e2, [5, 5, 5, 5], 1);
skin_warning.bgStroke = new SolidColorStroke(1, 0x000000, 0);
skin_warning.headerHeight = 34;
skin_warning.headerRect = new SolidColorRect(0x99ccff, [5, 5, 0, 0], 1);
skin_warning.headerPadding = new TextPadding(0, 0, 0, 0);
skin_warning.titleFormat = new TextFormat("Verdana", 22, 0xffffff);
skin_warning.titleFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
skin_warning.textFormat = new TextFormat("Verdana", 15, 0x111111);
skin_warning.blurColor = 0x000000;
skin_warning.buttonbarPadding.bottom = 0;
skin_warning.addContent(warning_icon, new Point(5, 40));
skin_warning.textPadding = new TextPadding(5, 5, 5, 125);
buttonskin_warning.bgRect = new SolidColorRect(0x99ccff, [5, 5, 0, 0], 1);
buttonskin_warning.bgStroke = new SolidColorStroke(1, 0x000000, 0);
buttonskin_warning.textFormat = new TextFormat("Verdana", 17, 0xffffff, null, null, null, null, null, "center" );
buttonskin_warning.textFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
buttonskin_warning.hover_bgRect = new SolidColorRect(0x7799bb, [5, 5, 0, 0], 1);
buttonskin_warning.hover_bgStroke = new SolidColorStroke(1, 0x000000, 0);
buttonskin_warning.hover_textFormat = new TextFormat("Verdana", 17, 0xffffff, null, null, null, null, null, "center" );
buttonskin_warning.hover_textFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
// Information skin
skin_info.bgRect = new SolidColorRect(0xe2e2e2, [5, 5, 5, 5], 1);
skin_info.bgStroke = new SolidColorStroke(1, 0x000000, 0);
skin_info.headerHeight = 34;
skin_info.headerRect = new SolidColorRect(0x99ccff, [5, 5, 0, 0], 1);
skin_info.headerPadding = new TextPadding(0, 0, 0, 0);
skin_info.titleFormat = new TextFormat("Verdana", 22, 0xffffff);
skin_info.titleFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
skin_info.textFormat = new TextFormat("Verdana", 15, 0x111111);
skin_info.blurColor = 0x000000;
skin_info.buttonbarPadding.bottom = 0;
skin_info.addContent(info_icon, new Point(5, 40));
skin_info.textPadding = new TextPadding(5, 5, 5, 125);
buttonskin_info.bgRect = new SolidColorRect(0x99ccff, [5, 5, 0, 0], 1);
buttonskin_info.bgStroke = new SolidColorStroke(1, 0x000000, 0);
buttonskin_info.textFormat = new TextFormat("Verdana", 17, 0xffffff, null, null, null, null, null, "center" );
buttonskin_info.textFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
buttonskin_info.hover_bgRect = new SolidColorRect(0x7799bb, [5, 5, 0, 0], 1);
buttonskin_info.hover_bgStroke = new SolidColorStroke(1, 0x000000, 0);
buttonskin_info.hover_textFormat = new TextFormat("Verdana", 17, 0xffffff, null, null, null, null, null, "center" );
buttonskin_info.hover_textFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
// Confirmation skin
skin_confirm.bgRect = new SolidColorRect(0xe2e2e2, [5, 5, 5, 5], 1);
skin_confirm.bgStroke = new SolidColorStroke(1, 0x000000, 0);
skin_confirm.headerHeight = 34;
skin_confirm.headerRect = new SolidColorRect(0x99ccff, [5, 5, 0, 0], 1);
skin_confirm.headerPadding = new TextPadding(0, 0, 0, 0);
skin_confirm.titleFormat = new TextFormat("Verdana", 22, 0xffffff);
skin_confirm.titleFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
skin_confirm.textFormat = new TextFormat("Verdana", 15, 0x111111);
skin_confirm.blurColor = 0x000000;
skin_confirm.buttonbarPadding.bottom = 0;
skin_confirm.addContent(confirm_icon, new Point(5, 40));
skin_confirm.textPadding = new TextPadding(5, 5, 5, 125);
buttonskin_confirm.bgRect = new SolidColorRect(0x99ccff, [5, 5, 0, 0], 1);
buttonskin_confirm.bgStroke = new SolidColorStroke(1, 0x000000, 0);
buttonskin_confirm.textFormat = new TextFormat("Verdana", 17, 0xffffff, null, null, null, null, null, "center" );
buttonskin_confirm.textFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
buttonskin_confirm.hover_bgRect = new SolidColorRect(0x7799bb, [5, 5, 0, 0], 1);
buttonskin_confirm.hover_bgStroke = new SolidColorStroke(1, 0x000000, 0);
buttonskin_confirm.hover_textFormat = new TextFormat("Verdana", 17, 0xffffff, null, null, null, null, null, "center" );
buttonskin_confirm.hover_textFilters = [new DropShadowFilter(1, 45, 0x000000, 0.7, 0, 0)];
I also have this bit of code that just displays the alert windows as an example, one after another (with some interaction involved). This code functions called confirm, warn, and info, which we will create shortly.
// Test different window types
var timer:Timer = new Timer(1000, 1);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
warn("The specified E-mail address is not valid!", "Warning!", closeWarn);
}
function closeWarn(response:String):void {
confirm("Are you sure you want to exit?", "Confirmation", onYes, onNo);
}
function onYes():void {
info("You answered Yes!", "Notice!");
}
function onNo():void {
info("You answered No!", "Notice!");
}
Then we create 3 functions - warn, info and confirm, which use the 3 different skins:
public function warn(message:String, title:String, handler:Function = null):void {
var warnSound:Sound = new warning_sound();
AlertManager.reset(this, stage, skin_warning, buttonskin_warning, warnSound, true, 300, 0, 160);
AlertManager.alert(message, title, null, 0, handler);
}
public function info(message:String, title:String):void {
var infoSound:Sound = new info_sound();
AlertManager.reset(this, stage, skin_info, buttonskin_info, infoSound, true, 300, 0, 160);
AlertManager.alert(message, title);
}
public function confirm(message:String, title:String, handlerYes:Function = null, handlerNo:Function = null):void {
var infoSound:Sound = new info_sound();
AlertManager.reset(this, stage, skin_confirm, buttonskin_confirm, infoSound, true, 300, 0, 160);
AlertManager.alert(message, title, [new AdvAlertButton("Yes"), new AdvAlertButton("No")], 0, handler);
function handler(response:String):void {
if (response == "Yes") handlerYes.call();
if (response == "No") handlerNo.call();
}
}
And thats it! Now we can use these handy alert windows of different types in games and applications with minimum number of lines.
Using this principle, you can create your own skin sets and introduce new window types.
Thanks for reading!
Thursday, January 29, 2015
Creating a Pentomino game using AS3 Part 9
Today well improve our shape placing code, as well as make all shapes available for placing.
First of all, well create a function that draws a preview of where the shape will be placed exactly. We need to declare a new Sprite object for this - canPutShape:
And add it to the stage after creating the grid:
The function uses some of the already used code in the script, slightly modified. We simply loop through the currentValues and draw each cell:
Now go to mouseMove() and instead of the selectedShape if statement call a function called checkPut():
The functions code is the same, but has a few modifications. We check if canPutHere is tru or false and set the cursors alpha to represent whether its possible or not to place the selected shape here. We also clear canPutShapes graphics if canPutHere is false.
Where is drawCanPut() called if not in checkPut(), you ask? Thats in updateCurrentValues(), but only after weve added a few modifications to the code inside updateCurrentValues(). These bugs were found after I implemented the "shadow" feature thats drawn by drawCanPut(), and you can see the updated code below:
Call checkPut() instead of updateCurrentValues() in mouseWheel() and keyDown():
Now we can update the shapeValues() array. Simply add each shape cells relative coordinates:
And full code so far:
Thanks for reading!
The results:
Read more »
First of all, well create a function that draws a preview of where the shape will be placed exactly. We need to declare a new Sprite object for this - canPutShape:
private var canPutShape:Sprite = new Sprite();
And add it to the stage after creating the grid:
// can put shape
addChild(canPutShape);
canPutShape.x = gridStartX;
canPutShape.y = gridStartY;
The function uses some of the already used code in the script, slightly modified. We simply loop through the currentValues and draw each cell:
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.1);
canPutShape.graphics.drawRect(cellX, cellY, gridCellWidth, gridCellWidth);
}
}
Now go to mouseMove() and instead of the selectedShape if statement call a function called checkPut():
private function mouseMove(evt:MouseEvent):void {
cursor.x = mouseX;
cursor.y = mouseY;
checkPut();
}
The functions code is the same, but has a few modifications. We check if canPutHere is tru or false and set the cursors alpha to represent whether its possible or not to place the selected shape here. We also clear canPutShapes graphics if canPutHere is false.
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;
}
}
}
Where is drawCanPut() called if not in checkPut(), you ask? Thats in updateCurrentValues(), but only after weve added a few modifications to the code inside updateCurrentValues(). These bugs were found after I implemented the "shadow" feature thats drawn by drawCanPut(), and you can see the updated code below:
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();
}
Call checkPut() instead of updateCurrentValues() in mouseWheel() and keyDown():
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();
}
Now we can update the shapeValues() array. Simply add each shape cells relative coordinates:
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]]
];
And full code so far:
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);
}
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.1);
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):void {
cursor.rotation = 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);
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;
}
}
}
}
}
Thanks for reading!
The results:
Wednesday, January 28, 2015
EasyTooltip free AS3 tooltip library HOW TO CUSTOMIZE
Today Ill explain how to style tooltips made using EasyTooltip class.
To change the appearance of tooltips, create a new TooltipStyle object, set its properties and then apply it to the EasyTooltip instance using EasyTooltips setStyle() method.
To edit the text appearance, use the textAlpha, textFormat and textPadding properties of the TooltipStyle object.
To change the position of the tooltip relative to mouse, use the offsetX and offsetY properties.
You can use minWidth, maxWidth, minHeight and maxHeight to set size limits for your tooltips.
You can apply an array of filters to the filters property of the TooltipStyle object.
The remaining 3 properties are backgroundRect, backgroundStroke and dynamicContent. Lets start with dynamic content.
Normally, you wouldnt need to edit contents of the dynamicContent array manually. Theres a method that lets you add a visual element to the tooltip (such as an icon) without having to touch the array. The method is called addContent().
There are 3 parameters in the function - class of the visual object, x and y offsets. The class is passed instead of a class instance, because objects of that class will be created automatically. In the example below, myIcon is the name of a class that extends MovieClip and just contains a small image.

There are 3 background fill types for the tooltip box, and 3 background stroke types. Those are solid color fill and stroke, gradient color fill and stroke, and bitmap fill and stroke. Any fill type can be used in combination with any stroke.
You can set the backgroundRect property value to an instance of SolidColorRect, GradientColorRect or BitmapColorRect class. Similarly, youcan set the backgroundStroke property value to an instance of SolidColorStroke, GradientColorStroke or BitmapColorStroke class.
Use the properties of these classes to customize your tooltip appearances. Below are 3 examples using all of the classes:



Using all these tools you can customize your tooltips to look the way you want them to.
Thanks for reading!
Read more »
To change the appearance of tooltips, create a new TooltipStyle object, set its properties and then apply it to the EasyTooltip instance using EasyTooltips setStyle() method.
To edit the text appearance, use the textAlpha, textFormat and textPadding properties of the TooltipStyle object.
To change the position of the tooltip relative to mouse, use the offsetX and offsetY properties.
You can use minWidth, maxWidth, minHeight and maxHeight to set size limits for your tooltips.
You can apply an array of filters to the filters property of the TooltipStyle object.
The remaining 3 properties are backgroundRect, backgroundStroke and dynamicContent. Lets start with dynamic content.
Normally, you wouldnt need to edit contents of the dynamicContent array manually. Theres a method that lets you add a visual element to the tooltip (such as an icon) without having to touch the array. The method is called addContent().
There are 3 parameters in the function - class of the visual object, x and y offsets. The class is passed instead of a class instance, because objects of that class will be created automatically. In the example below, myIcon is the name of a class that extends MovieClip and just contains a small image.
var myStyle:TooltipStyle = new TooltipStyle();
myStyle.maxWidth = 0;
myStyle.offsetX = -10;
myStyle.offsetY = -10;
myStyle.addContent(myIcon, 5, 5);
tooltip = new EasyTooltip(stage, stage.stageWidth, stage.stageHeight);
tooltip.setStyle(myStyle);
tooltip.addListener(object1, " Example of dynamic content usage. ");

There are 3 background fill types for the tooltip box, and 3 background stroke types. Those are solid color fill and stroke, gradient color fill and stroke, and bitmap fill and stroke. Any fill type can be used in combination with any stroke.
You can set the backgroundRect property value to an instance of SolidColorRect, GradientColorRect or BitmapColorRect class. Similarly, youcan set the backgroundStroke property value to an instance of SolidColorStroke, GradientColorStroke or BitmapColorStroke class.
Use the properties of these classes to customize your tooltip appearances. Below are 3 examples using all of the classes:
var myStyle:TooltipStyle = new TooltipStyle();
myStyle.backgroundRect = new SolidColorRect(0x880000, [10, 0, 10, 0], 1);
myStyle.backgroundStroke = new SolidColorStroke(2, 0xcc0000, 1);
myStyle.offsetX = -10;
myStyle.offsetY = -10;
tooltip = new EasyTooltip(stage, stage.stageWidth, stage.stageHeight);
tooltip.setStyle(myStyle);
tooltip.addListener(object1, "Example of using solid color fill and stroke. ");

var myStyle:TooltipStyle = new TooltipStyle();
myStyle.minWidth = 240;
myStyle.maxWidth = 0;
myStyle.offsetX = -10;
myStyle.offsetY = -10;
var matrix1:Matrix = new Matrix();
matrix1.createGradientBox(240, 200, 1.57);
myStyle.backgroundRect = new GradientColorRect(GradientType.LINEAR, [0, 0], [0.2, 1], [0, 255], matrix1, "pad", "rgb", 0, [0, 0, 0, 0]);
var matrix2:Matrix = new Matrix();
matrix2.createGradientBox(240, 200, 1.57);
myStyle.backgroundStroke = new GradientColorStroke(2, GradientType.LINEAR, [0x000000, 0xffffff], [1, 1], [0, 255], matrix2, "pad", "rgb", 0);
tooltip = new EasyTooltip(stage, stage.stageWidth, stage.stageHeight);
tooltip.setStyle(myStyle);
tooltip.addListener(object1, "Example of gradient fill and stroke usage. ");

var myStyle:TooltipStyle = new TooltipStyle();
myStyle.minWidth = 240;
myStyle.maxWidth = 0;
myStyle.offsetX = -10;
myStyle.offsetY = -10;
var myBitmap:BitmapData = new BitmapData(200, 100, false, 0x000000);
myBitmap.perlinNoise(200, 100, 3, 7, true, true);
var myBitmap2:BitmapData = new BitmapData(200, 100, false, 0x000000);
myBitmap2.perlinNoise(100, 100, 3, 11, true, true);
myStyle.backgroundRect = new BitmapRect(myBitmap, new Matrix(), true, true, [10, 10, 10, 10]);
myStyle.backgroundStroke = new BitmapStroke(2, myBitmap2, new Matrix(), true, true);
tooltip = new EasyTooltip(stage, stage.stageWidth, stage.stageHeight);
tooltip.setStyle(myStyle);
tooltip.addListener(object1, "Example of bitmap fill and stroke usage. ");

Using all these tools you can customize your tooltips to look the way you want them to.
Thanks for reading!
Creating a Pentomino game using AS3 Part 8
In this part we will add the ability to place rotated shape on the grid.
In the previous tutorials we used internal (temporary) variable inside several functions named currentValues. Lets make this a private variable available in all functions:
So in mouseMove(), instead of setting value to this variable we call updateCurrentValues():
Call the same function in mouseWheel and keyDown if there is a shape thats selected:
Go to mouseUp() and add a line that sets the new shapes rotation to cursor.rotation:
Now create a new function called clone(). This will be used to clone an array with arrays inside of it properly:
Add updateCurrentValues() function. Here in the first line we clone the respective element in shapeValues and apply that value to currentValues.
Then we loop through all the 5 elemenst of it, and go through a few if statements to modify the values. If cursors rotation is 0, we dont need to change anything, since the values in currentValues apply here. If the shape has rotation 90, we need to reverse the values of each element (x becomes y and y becomes x). If the rotation is 180 or -180, we inverse the values by multiplying them by -1. If the rotation is -90, inverse and reverse the values. If scaleX of cursor is less than 0, inverse the X values:
Now we can drag, rotate, and drop the first shape properly.
Full code so far:
Next time well implement more shapes.
Thanks for reading!
Read more »
In the previous tutorials we used internal (temporary) variable inside several functions named currentValues. Lets make this a private variable available in all functions:
private var currentValues:Array = [];
So in mouseMove(), instead of setting value to this variable we call updateCurrentValues():
private function mouseMove(evt:MouseEvent):void {
cursor.x = mouseX;
cursor.y = mouseY;
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;
}
}
}
}
Call the same function in mouseWheel and keyDown if there is a shape thats selected:
private function mouseWheel(evt:MouseEvent):void {
if (evt.delta > 0) cursor.rotation += 90;
if (evt.delta < 0) cursor.rotation -= 90;
if (selectedShape > 0) updateCurrentValues();
}
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) updateCurrentValues();
}
Go to mouseUp() and add a line that sets the new shapes rotation to cursor.rotation:
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);
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;
}
}
}
Now create a new function called clone(). This will be used to clone an array with arrays inside of it properly:
private function clone(source:Object):*{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}
Add updateCurrentValues() function. Here in the first line we clone the respective element in shapeValues and apply that value to currentValues.
Then we loop through all the 5 elemenst of it, and go through a few if statements to modify the values. If cursors rotation is 0, we dont need to change anything, since the values in currentValues apply here. If the shape has rotation 90, we need to reverse the values of each element (x becomes y and y becomes x). If the rotation is 180 or -180, we inverse the values by multiplying them by -1. If the rotation is -90, inverse and reverse the values. If scaleX of cursor is less than 0, inverse the X values:
private function updateCurrentValues():void {
currentValues = clone(shapeValues[selectedShape-1]);
for (var i:int = 0; i < 5; i++) {
if (cursor.rotation == 90) currentValues[i].reverse();
if (cursor.rotation == 180 || cursor.rotation == -180) {
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.rotation == -90) {
currentValues[i].reverse();
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.scaleX < 0) {
currentValues[i][0] *= -1;
}
}
}
Now we can drag, rotate, and drop the first shape properly.
Full code so far:
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 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, 0, 0, 1, 1, 1, 1],
[1, 1, 1, 1, 0, 0, 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 = [20, 2, 1, 2, 1, 2, 2, 3, 4, 1, 2, 2];
shapeValues = [[[0,0],[0,1],[0,2],[0,-1],[0,-2]]];
// grid settings
calculateGrid();
addChild(gridShape);
gridShape.x = gridStartX;
gridShape.y = gridStartY;
// draw tiles
drawGrid();
// 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);
}
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;
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;
}
}
}
}
private function mouseWheel(evt:MouseEvent):void {
if (evt.delta > 0) cursor.rotation += 90;
if (evt.delta < 0) cursor.rotation -= 90;
if (selectedShape > 0) updateCurrentValues();
}
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) updateCurrentValues();
}
private function updateCurrentValues():void {
currentValues = clone(shapeValues[selectedShape-1]);
for (var i:int = 0; i < 5; i++) {
if (cursor.rotation == 90) currentValues[i].reverse();
if (cursor.rotation == 180 || cursor.rotation == -180) {
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.rotation == -90) {
currentValues[i].reverse();
currentValues[i][0] *= -1;
currentValues[i][1] *= -1;
}
if (cursor.scaleX < 0) {
currentValues[i][0] *= -1;
}
}
}
private function clone(source:Object):*{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}
private function startDragShape(shapeType:int):void {
cursor.rotation = 0;
cursor.scaleX = gridCellWidth / 100;
cursor.scaleY = gridCellWidth / 100;
cursor.alpha = 0.75;
cursor.visible = true;
cursor.gotoAndStop(shapeType);
}
private function stopDragShape():void {
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);
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;
}
}
}
}
}
Next time well implement more shapes.
Thanks for reading!
Monday, January 26, 2015
The AS3 this keyword and how to randomize the size of a MovieClip instance on the stage
In this lesson, well learn about the this keyword. But before I explain what the this keyword is, lets first recall that we can put some ActionScript code on keyframes in the main timeline. But we can also put some ActionScript code on the keyframes inside MovieClip symbols as well. If we place ActionScript code on the keyframes inside of MovieClip symbols, then it means that the code is inside the MovieClip symbols timeline, instead of being on the main timeline.
Its important to take note of this, because the this keyword references whichever object the this keyword is placed in. Is it within the code in the main timeline, or is it within the code nested inside a MovieClip symbol instead? If we placed the this keyword within the code inside one of the keyframes in the main timeline, then the this keyword will refer to the main timeline. If the this keyword was placed inside the timeline of a MovieClip symbol, then the this keyword will refer to the instance of the MovieClip symbol instead (or instances, if there are more than one). So what this is, depends on where this is.
To hopefully make things a little more clear, lets create an example.
Step 1
Create a new Flash ActionScript 3.0 document.
Step 2
Select the first keyframe of the main timeline, then open up the Actions Panel.
Step 3
In the script pane, type:
Lets try to trace this to see what it will output. Basically, when we create this trace statement, its like we are asking: what is this?
And in this example, the answer to that is: the main timeline. Since we selected the first keyframe of the main timeline when we added the code, this means that our code is on the main timeline. So in this example, this refers to the main timeline.
Step 4
Test the movie.
In the output window, you should see the message: [object MainTimeline]. So the message in the output window confirms that the this keyword in our example refers to the main timeline. It refers to the main timeline since the this keyword was placed in one of the frames of the main timeline. When using the this keyword, this refers to wherever this is.
Step 5
Now lets try placing the this keyword inside a MovieClip symbol instead. But first, go back to the actions panel and erase the trace statement that we placed on the main timeline so as not to make things confusing when were trying to trace the MovieClip objects were about to create.
Step 6
Next, lets create the symbol.
Using the oval tool, draw a circle on the stage and convert it into a MovieClip symbol named Circle.
Step 7
Then give the MovieClip instance an instance name.
Make sure that the instance on the stage is selected (do NOT double-click on it because that will bring you inside the MovieClip symbols timeline). Once its selected, go to the properties inspector and type circle1_mc in the instance name input field.
Step 8
Now, lets add some ActionScript inside the MovieClip symbols timeline.
This time, you can double-click on the MovieClip instance on the stage in order to go inside the MovieClip symbols timeline.
Step 9
Double-check that you are, in fact, inside the MovieClip symbols timeline by looking at the edit bar. The edit bar should say Scene 1 > Circle.
Step 10
Once youve confirmed that you are inside the MovieClip symbol, add another layer on the timeline, and name it Actions.
Remember: this timeline belongs to the MovieClip symbol. We are not on the main timeline.
Step 11
Then select the first keyframe of the Actions layer, then go to the actions panel and type:
Here, we are not simply referencing this, but specifically, we are tracing its name. You can use the this keyword to access properties and methods of objects as well. Basically, in this example, our trace statement is trying to ask: what is the name of this object?
Since the statement that contains the this keyword is inside the MovieClip symbols timeline, its going to output circle1_mc in the output window. The this keyword references objects or instances that contain it. So the this keyword is going to refer to the specific instance of the MovieClip symbol on the stage, and not the symbol in the library. This is why its going to output circle1_mc (which is the instance name), and not Circle (which is the symbols name).
Step 12
Now lets go back to the main timeline by clicking on the scene 1 link.
Step 13
Once we are back on the main timeline, go to the library and drag another instance of the Circle symbol onto the stage. Give this one circle2_mc for its instance name. Since this is also an instance of the Circle symbol, its also going to have the same trace statement inside it already. Whatever code you have inside the timeline of a MovieClip symbol will also be inside any of its instances that are on the stage. There is no need for us to add it again. So when we test the movie, Flash is also going to output the name of this second instance in the output window.
Step 14
Go ahead and test the movie.
You should see the names circle1_mc and circle2_mc displayed in the output window.
Why would we want to use the this keyword?
Sometimes, you might want to place some code inside a MovieClip symbols timeline. That way, every single instance of the MovieClip symbol will have the same code inside it when you place it on the stage. This saves you some time, because you wont have to repeatedly type the same code over and over. You just have to put it inside the MovieClip symbols timeline once, and then every instance of that MovieClip symbol will have the code already placed inside it. The this keyword lets us explicitly tell Flash that our code is meant for that specific instance that contains the this keyword.
Lets see this in action by adding more code to our example.
Step 15
Go back inside the Circle MovieClips timeline by double-clicking any of the instances on the stage. Then select the first keyframe of the Actions layer and open up the Actions Panel.
Step 16
Remove the trace statement, and add these lines of code in its place:
Here, weve added some lines of code that will increase the size of each instance of the MovieClip symbol that we place on the stage. Basically, the code is saying: scale this to 3 times its original size. So when the Flash movie runs, the MovieClip instances on the stage will be 3 times larger.
Step 17
Go back to the main timeline, by clicking on the scene 1 link.
Step 18
From the library, add more instances of the Circle MovieClip symbol until you have ten instances on the stage.
Step 19
Test the movie.
You should see that each circle is now three times larger than its original size. Since we placed the code inside the MovieClip symbols timeline, instead of the main timeline, then we only had to write those lines once, and each instance will have the same lines of code built-in. This is why every single instance of the Circle MovieClip became bigger.
Step 20
In this next step, well modify the code so that each instance of the Circle MovieClip will have a different, randomized size.
Go back to the Circle MovieClip symbols timeline by double-clicking any of the instances on the stage. Then select the first keyframe of the Actions layer and open up the Actions Panel.
Step 21
In the script pane, replace the current code with this:
The first line of code creates a variable named nScale. And then to this variable, we are assigning the expression Math.random() * 3. What this does is it generates a random number that can be anywhere between 0 and 3. If you want Flash to choose from a higher range of values, then simply replace 3 with a higher value (but dont make it too high since we are going to use it to scale the size of the objects). Then once that random number is generated, the number is assigned to the scaleX and scaleY properties. This is going to happen for each instance of the Circle MovieClip that we have on the stage. So chances are, theyll each generate a different value, thus making them all have different sizes once the Flash movie runs.
Step 22
Test the movie and you should see your circles all in different sizes. And since the values are generated randomly, youll see different sizes every time you test the movie.
Read more »
Its important to take note of this, because the this keyword references whichever object the this keyword is placed in. Is it within the code in the main timeline, or is it within the code nested inside a MovieClip symbol instead? If we placed the this keyword within the code inside one of the keyframes in the main timeline, then the this keyword will refer to the main timeline. If the this keyword was placed inside the timeline of a MovieClip symbol, then the this keyword will refer to the instance of the MovieClip symbol instead (or instances, if there are more than one). So what this is, depends on where this is.
To hopefully make things a little more clear, lets create an example.
Step 1
Create a new Flash ActionScript 3.0 document.
Step 2
Select the first keyframe of the main timeline, then open up the Actions Panel.
Step 3
In the script pane, type:
trace(this);
Lets try to trace this to see what it will output. Basically, when we create this trace statement, its like we are asking: what is this?
And in this example, the answer to that is: the main timeline. Since we selected the first keyframe of the main timeline when we added the code, this means that our code is on the main timeline. So in this example, this refers to the main timeline.
Step 4
Test the movie.
In the output window, you should see the message: [object MainTimeline]. So the message in the output window confirms that the this keyword in our example refers to the main timeline. It refers to the main timeline since the this keyword was placed in one of the frames of the main timeline. When using the this keyword, this refers to wherever this is.
Step 5
Now lets try placing the this keyword inside a MovieClip symbol instead. But first, go back to the actions panel and erase the trace statement that we placed on the main timeline so as not to make things confusing when were trying to trace the MovieClip objects were about to create.
Step 6
Next, lets create the symbol.
Using the oval tool, draw a circle on the stage and convert it into a MovieClip symbol named Circle.
Step 7
Then give the MovieClip instance an instance name.
Make sure that the instance on the stage is selected (do NOT double-click on it because that will bring you inside the MovieClip symbols timeline). Once its selected, go to the properties inspector and type circle1_mc in the instance name input field.
Step 8
Now, lets add some ActionScript inside the MovieClip symbols timeline.
This time, you can double-click on the MovieClip instance on the stage in order to go inside the MovieClip symbols timeline.
Step 9
Double-check that you are, in fact, inside the MovieClip symbols timeline by looking at the edit bar. The edit bar should say Scene 1 > Circle.
Step 10
Once youve confirmed that you are inside the MovieClip symbol, add another layer on the timeline, and name it Actions.
Remember: this timeline belongs to the MovieClip symbol. We are not on the main timeline.
Step 11
Then select the first keyframe of the Actions layer, then go to the actions panel and type:
trace(this.name);
Here, we are not simply referencing this, but specifically, we are tracing its name. You can use the this keyword to access properties and methods of objects as well. Basically, in this example, our trace statement is trying to ask: what is the name of this object?
Since the statement that contains the this keyword is inside the MovieClip symbols timeline, its going to output circle1_mc in the output window. The this keyword references objects or instances that contain it. So the this keyword is going to refer to the specific instance of the MovieClip symbol on the stage, and not the symbol in the library. This is why its going to output circle1_mc (which is the instance name), and not Circle (which is the symbols name).
Step 12
Now lets go back to the main timeline by clicking on the scene 1 link.
Step 13
Once we are back on the main timeline, go to the library and drag another instance of the Circle symbol onto the stage. Give this one circle2_mc for its instance name. Since this is also an instance of the Circle symbol, its also going to have the same trace statement inside it already. Whatever code you have inside the timeline of a MovieClip symbol will also be inside any of its instances that are on the stage. There is no need for us to add it again. So when we test the movie, Flash is also going to output the name of this second instance in the output window.
Step 14
Go ahead and test the movie.
You should see the names circle1_mc and circle2_mc displayed in the output window.
Why would we want to use the this keyword?
Sometimes, you might want to place some code inside a MovieClip symbols timeline. That way, every single instance of the MovieClip symbol will have the same code inside it when you place it on the stage. This saves you some time, because you wont have to repeatedly type the same code over and over. You just have to put it inside the MovieClip symbols timeline once, and then every instance of that MovieClip symbol will have the code already placed inside it. The this keyword lets us explicitly tell Flash that our code is meant for that specific instance that contains the this keyword.
Lets see this in action by adding more code to our example.
Step 15
Go back inside the Circle MovieClips timeline by double-clicking any of the instances on the stage. Then select the first keyframe of the Actions layer and open up the Actions Panel.
Step 16
Remove the trace statement, and add these lines of code in its place:
this.scaleX = 3;
this.scaleY = 3;
Here, weve added some lines of code that will increase the size of each instance of the MovieClip symbol that we place on the stage. Basically, the code is saying: scale this to 3 times its original size. So when the Flash movie runs, the MovieClip instances on the stage will be 3 times larger.
Step 17
Go back to the main timeline, by clicking on the scene 1 link.
Step 18
From the library, add more instances of the Circle MovieClip symbol until you have ten instances on the stage.
Step 19
Test the movie.
You should see that each circle is now three times larger than its original size. Since we placed the code inside the MovieClip symbols timeline, instead of the main timeline, then we only had to write those lines once, and each instance will have the same lines of code built-in. This is why every single instance of the Circle MovieClip became bigger.
Step 20
In this next step, well modify the code so that each instance of the Circle MovieClip will have a different, randomized size.
Go back to the Circle MovieClip symbols timeline by double-clicking any of the instances on the stage. Then select the first keyframe of the Actions layer and open up the Actions Panel.
Step 21
In the script pane, replace the current code with this:
var nScale:Number = Math.random() * 3;
this.scaleX = nScale;
this.scaleY = nScale;
The first line of code creates a variable named nScale. And then to this variable, we are assigning the expression Math.random() * 3. What this does is it generates a random number that can be anywhere between 0 and 3. If you want Flash to choose from a higher range of values, then simply replace 3 with a higher value (but dont make it too high since we are going to use it to scale the size of the objects). Then once that random number is generated, the number is assigned to the scaleX and scaleY properties. This is going to happen for each instance of the Circle MovieClip that we have on the stage. So chances are, theyll each generate a different value, thus making them all have different sizes once the Flash movie runs.
Step 22
Test the movie and you should see your circles all in different sizes. And since the values are generated randomly, youll see different sizes every time you test the movie.
Subscribe to:
Posts (Atom)