A more efficient way to pick and place tiles? - actionscript-3

is it wrong to pick and place my tiles like this?
In other words, whats a better way to place the tiles so removing and adding a different tile is easy. Or what is a good method of doing so with the way i have it set up.
Whats a good way to reference a specific tile on the map?
public class Stage extends MovieClip
{
protected var tilesInWorld:Vector.<MovieClip> = new Vector.<MovieClip>();
public var worldTiles:Sprite;
protected var tile:MovieClip;
protected var TILE_SIZE:int = 5;
protected var map_width:int = 800;
protected var map_height:int = 600;
protected var pmap:BitmapData = new BitmapData(map_width,map_height);
protected var _seed:uint = Math.round(Math.random() * 100);
protected var grid_width:uint = new uint(map_width / TILE_SIZE);
protected var grid_height:uint = new uint(map_height / TILE_SIZE);
protected var heightmap:Array = new Array();
protected var pixelPoint:Point = new Point();
protected var darkest_pixel:Number = 1;
protected var brightest_pixel:Number = 0;
protected var hm :Number;
public function Stage()
{
pmap.perlinNoise(map_width,map_height, 6, _seed, true, false, 1, true);
for (var i:uint=0; i < grid_width; i++)
{
heightmap[i] = new Array();
for (var j:uint=0; j < grid_height; j++)
{
heightmap[i][j] = new uint();
}
}
//Divide the map in to a 7x7 grid and take data at each interval
for (i=0; i < grid_width; i++)
{
for (j=0; j < grid_height; j++)
{
pixelPoint.x = Math.round((i/grid_width) * pmap.width)+1;
pixelPoint.y = Math.round((j/grid_width) * pmap.height)+1;
heightmap[i][j] = pmap.getPixel(pixelPoint.x,pixelPoint.y);
heightmap[i][j] /= 0xffffff;
if (heightmap[i][j] < darkest_pixel)
{
darkest_pixel = heightmap[i][j];
}
}
}
//Adjust values to a min of 0
for (i=0; i < grid_width; i++)
{
for (j=0; j < grid_height; j++)
{
heightmap[i][j] -= darkest_pixel;
if (heightmap[i][j] > brightest_pixel)
{
brightest_pixel = heightmap[i][j];
}
}
}
//Adjust values to highest value of 1
for (i=0; i < grid_width; i++)
{
for (j=0; j < grid_height; j++)
{
heightmap[i][j] /= brightest_pixel;
}
}
worldTiles = new Sprite();
addChild(worldTiles);
placeTile();
}
The part below this is the main part im referring too
protected function placeTile()
{
for (var i=0; i < grid_width; i++)
{
for (var j=0; j < grid_height; j++)
{
hm = heightmap[i][j];
if (hm >= 0.84)
{
tile = new Water();
}
else if (hm >= 0.8 && hm < 0.84)
{
tile = new Shallow();
}
else if (hm >= 0.7 && hm < 0.8)
{
tile = new Sand();
}
else if (hm >= 0.2 && hm < 0.7)
{
tile = new Tile();
}
else
{
tile = new Stone();
}
tile.width = TILE_SIZE;
tile.height = TILE_SIZE;
worldTiles.x = 0;
worldTiles.y = 0;
tile.x = TILE_SIZE * (i % grid_width);
tile.y = TILE_SIZE * (j % grid_height);
tilesInWorld.push(tile);
worldTiles.addChild(tile);
}
}
}
}
is there a more efficient way of picking and placing the tiles?
and while i have your attention, how can i go about deleting and replacing the tiles?

It would be better to not do so many array look-ups.
//..
var hm :Number;
for (var i=0; i < grid_width; i++)
{
for (var j=0; j < grid_height; j++)
{
hm = heightmap[i][j];
if (hm >= 0.84)
{
tile = new Water();
}
else if ( hm >= 0.8 && hm < 0.84)
{
//...
Also you might want to look at object pooling. Here is a nice video tutorial about object pooling.
Apart of that I can't see much more to optimise here. But I am sure others will give you more tips.

Related

Autocomplete search for any word in the sentence

I'm using an autocomplete for my flash app. The autocomplete uses an external text file.
When I'm typing the first word of the sentence, it display all the sentences that begins with this word.
Is it possible to display all sentence that have this word (and not just the begining of the sentence) ?
Exemple :
I've got two phrases : "I'm going to school" and " I'm going to look for him".
I would like to be able to type "school" and that it displays the first sentence.
Do you know how I can do that ?
For now, I have to type "I'm going to s" in order to display the first sentence.
Here's my code :
urlLoader.load(new URLRequest("test.txt"));
urlLoader.addEventListener(Event.COMPLETE, loadComplete);
inputField.addEventListener(KeyboardEvent.KEY_UP, suggest);
function loadComplete(e:Event):void
{
suggestions = e.target.data.split(",");
}
function suggest(e:KeyboardEvent):void
{
suggested = [];
for (var i:int = 0; i < textfields.length; i++)
{
removeChild(textfields[i]);
}
textfields = [];
for (var j:int = 0; j < suggestions.length; j++)
{
if (suggestions[j].indexOf(inputField.text.toLowerCase()) == 0)
{
var term:TextField = new TextField();
term.width = 300;
term.height = 20;
term.x = 70;
term.y = (20 * suggested.length) + 314;
term.border = true;
term.borderColor = 0x353535;
term.background = true;
term.backgroundColor = 0xFF9900;
term.textColor = 0x4C311D;
term.defaultTextFormat = format;
term.addEventListener(MouseEvent.MOUSE_UP, useWord);
term.addEventListener(MouseEvent.MOUSE_OVER, hover);
term.addEventListener(MouseEvent.MOUSE_OUT, out);
term.addEventListener(MouseEvent.CLICK, tellMe);
addChild(term);
textfields.push(term);
suggested.push(suggestions[j]);
term.text = suggestions[j];
}
}
if (inputField.length == 0)
{
suggested = [];
for (var k:int = 0; k < textfields.length; k++)
{
removeChild(textfields[k]);
}
textfields = [];
}
if(e.keyCode == Keyboard.DOWN && currentSelection < textfields.length-1)
{
currentSelection++;
textfields[currentSelection].textColor = 0x4C311D;
}
if(e.keyCode == Keyboard.UP && currentSelection > 0)
{
currentSelection--;
textfields[currentSelection].textColor = 0x4C311D;
}
if(e.keyCode == Keyboard.ENTER)
{
inputField.text = textfields[currentSelection].text;
suggested = [];
for (var l:int = 0; l < textfields.length; l++)
{
removeChild(textfields[l]);
}
textfields = [];
currentSelection = 0;
}
}
function useWord(e:MouseEvent):void
{
inputField.text = e.target.text;
suggested = [];
for (var i:int = 0; i < textfields.length; i++)
{
removeChild(textfields[i]);
}
textfields = [];
}
Thank you
Change the condition from:
suggestions[j].indexOf(inputField.text.toLowerCase()) == 0
to
suggestions[j].indexOf(inputField.text.toLowerCase()) != -1

testHitObject is returning an incorrect "true" value on the first few frames

if I add the else statement I do receive the "not hit" in my output window, so it looks like the statement only returns true on the first few frames, when it is supposed to return false. Now my problem is that the code wants to remove child objects before they've even been created. I just want to achieve basic collision detection between the enemy and the bullet so that i can remove both children when an enemy is shot. Please keep in mind that there will be multiple enemies and bullets on the stage at a given time.
I seemed to narrow down my problem. What was happening was the instance of an enemy would still exist, after i used the removeChild() function. Only now I couldn't see it for some reason. And its x and y properties were equal to 0. Therefore if I moved the ship to x = 0, it would have an "invisible" enemy collide with it. So I think my problem is I am not removing the instance properly? if not could you please help me?
` <pre>
var BulletsArr:Array = new Array();
var EnemysArray:Array = new Array();
for(i = 0; i < 5; i++)
{
BulletsArr[i] = new Bullet();
}
for(i = 0; i < 5; i++)
{
EnemysArray[i] = new Enemy();
//EnemysArray[i].x = 2000; //Fix
//EnemysArray[i].y = 2000; //Fix
}
stage.addChild(Ship);
Ship.addEventListener(Event.ENTER_FRAME, fnEnterFrame);
function fnEnterFrame(event:Event)
{
txtScore.text = "Score: " + score;
//Make Ship follow cursor
Ship.x = stage.mouseX;
//Boundries
if (Ship.x > 500)
{
Ship.x = 550 - 50;
}
if (Ship.x <= 0)
{
Ship.x = 0 ;
}
for(var i = 0; i < 5; i++){
for(var p = 0; p < 5; p++)
{
if(BulletsArr[i].hitTestObject(EnemysArray[p]))
{
remove(BulletsArr[i]);
removeChild(EnemysArray[p]);
}
for(i = 0; i < 5; i++)
{
BulletsArr[i].y -= 15;
}
for(i = 0; i < 5; i++)
{
EnemysArray[i].y += 5;
}
for(var z = 0; z < 5; z++)
{
if(Ship.hitTestObject(EnemysArray[z])
{
Ship.parent.removeChild(Ship);
EnemysArray[z].parent.removeChild(EnemysArray[z]);
trace("Game over");
}
}
for(i = 0; i < 5; i++)
{
var found:Boolean = false;
for(var p = 0; p < 5; p++)
{
if(BulletsArr[i].hitTestObject(EnemysArray[p]))
{
BulletsArr[i].parent.removeChild(BulletsArr[i]);
BulletsArr[i] = new Bullet();
EnemysArray[p].parent.removeChild(EnemysArray[p]);
EnemysArray[p] = new Enemy();
//EnemysArray[p].x = 2000;//fix
//EnemysArray[p].y = 2000;//fix
score += 50;
found = true;
break;
}
if(found)
{
break;
}
}
}
}
var count_Enemys:int = 0;
var Timer_3_sec:Timer = new Timer(3000, 0);
Timer_3_sec.start();
Timer_3_sec.addEventListener(TimerEvent.TIMER,spawn_ship);
function spawn_ship(Event:TimerEvent):void
{
if(count_Enemys >= 5)
{
count_Enemys = 0;
EnemysArray[count_Enemys].x = fl_GenerateRandomNumber(450) + 70;
EnemysArray[count_Enemys].y = 0 - fl_GenerateRandomNumber(250) - 100;
addChild(EnemysArray[count_Enemys]);
count_Enemys++;
}
else
{
EnemysArray[count_Enemys].x = fl_GenerateRandomNumber(450) + 70;
EnemysArray[count_Enemys].y = 0 - fl_GenerateRandomNumber(250) - 100;
addChild(EnemysArray[count_Enemys]);
count_Enemys++;
}
}
`</code>

Why dont i lag on my first chunk load but i do on my last?

I have a procedural generated game using a perlin map. Iv made it where it only loads the tiles of the area your at, and when you leave that area it deletes them and re draws them according to where you walked too. So theoretically it should only load the tiles of the area you are at. But it seems the further into my map you walk the more it begins to lag. Tho im not sure why since it should never be loading a different amount of blocks.
Here is a link to the game as of now.
http://www.fastswf.com/nzpBar0
These are the functions that are adding and deleting the tiles.
//deletes the tiles on the world.
public function deleteTiles()
{
if (tilesInWorld.length > 0)
{
for (var i:int = 0; i < tilesInWorld.length; i++)
{
worldTiles.removeChild(tilesInWorld.pop());
}
generateTile();
}
}
//generates the tiles on the world
public function generateTile()
{
for (var i:int = X/GlobalCode.MAP_SCALE; i < (X + (800/TILE_SIZE)/GlobalCode.MAP_SCALE); i++)
{
for (var j:int = Y/GlobalCode.MAP_SCALE; j < Y + (600/TILE_SIZE)/GlobalCode.MAP_SCALE; j++)
{
hm = heightmap[i][j];
if (hm >= 0.84)
{
tile = new Water();
}
else if (hm >= 0.8 && hm < 0.84)
{
tile = new Shallow();
}
else if (hm >= 0.7 && hm < 0.8)
{
tile = new Sand();
}
else if (hm >= 0.2 && hm < 0.7)
{
tile = new Tile();
}
else
{
tile = new Stone();
}
tile.width = TILE_SIZE;
tile.height = TILE_SIZE;
worldTiles.x = 0;
worldTiles.y = 0;
tile.x = TILE_SIZE * (i % 800);
tile.y = TILE_SIZE * (j % 600);
tilesInWorld.push(tile);
worldTiles.addChild(tile);
}
}
}
This is where the perlin map and the first tile area is created
public function World(parentMC:MovieClip)
{
TILE_SIZE = GlobalCode.TILE_SIZE;
map_width = GlobalCode.MAP_WIDTH;
map_height = GlobalCode.MAP_HEIGHT;
pmap = new BitmapData(map_width,map_height);
grid_width = new uint(map_width / TILE_SIZE);
grid_height = new uint(map_height / TILE_SIZE);
//map_width = GlobalCode.MAP_WIDTH;
//map_height = GlobalCode.MAP_HEIGHT;
pmap.perlinNoise(map_width,map_height, 6, _seed, true, false, 1, true);
for (var i:uint=0; i < grid_width; i++)
{
heightmap[i] = new Array();
for (var j:uint=0; j < grid_height; j++)
{
heightmap[i][j] = new uint();
}
}
//Divide the map in to a 7x7 grid and take data at each interval
for (i = 0; i < grid_width; i++)
{
for (j = 0; j < grid_height; j++)
{
pixelPoint.x = Math.round((i/grid_width) * pmap.width)+1;
pixelPoint.y = Math.round((j/grid_width) * pmap.height)+1;
heightmap[i][j] = pmap.getPixel(pixelPoint.x,pixelPoint.y);
heightmap[i][j] /= 0xffffff;
if (heightmap[i][j] < darkest_pixel)
{
darkest_pixel = heightmap[i][j];
}
}
}
//Adjust values to a min of 0
for (i = 0; i < grid_width; i++)
{
for (j = 0; j < grid_height; j++)
{
heightmap[i][j] -= darkest_pixel;
if (heightmap[i][j] > brightest_pixel)
{
brightest_pixel = heightmap[i][j];
}
}
}
//Adjust values to highest value of 1
for (i = 0; i < grid_width; i++)
{
for (j = 0; j < grid_height; j++)
{
heightmap[i][j] /= brightest_pixel;
}
}
worldTiles = new Sprite();
parentMC.addChild(worldTiles);
generateTile();
}
this is what creats the scroll rect and the X/Y changes when you walk to the edge of a screen.
public function update(e:Event)
{
world.worldTiles.scrollRect = new Rectangle(X,Y,800,600);
if (canMove == true)
{
MovePlayer();
}
player.update();
PlayerOnTile();
}
And for giggles this is what moves my character and the scroll/rect
protected function MovePlayer()
{
if (goin[0] == 1)
{
player.y -= moveSpeed;
if (player.y <= 0 && (Yloc) > 0)
{
world.Y -= int(600/world.TILE_SIZE)/MAP_SCALE;
world.deleteTiles();
Y -= 600 / MAP_SCALE;
Yloc -= 1;
player.y += 600;
}
}
if (goin[1] == 1)
{
player.y += moveSpeed;
if (player.y >= 600 && (Yloc + 1) < MAP_SCALE)
{
world.Y += int(600/world.TILE_SIZE)/MAP_SCALE;
world.deleteTiles();
Y += 600 / MAP_SCALE;
Yloc += 1;
player.y -= 600;
}
//world.worldTiles.y -= moveSpeed;
}
if (goin[2] == 1)
{
player.x -= moveSpeed;
if (player.x <= 0 && (Xloc) > 0 )
{
world.X -= int(800/world.TILE_SIZE)/MAP_SCALE;
world.deleteTiles();
X -= 800 / MAP_SCALE;
Xloc -= 1;
player.x += 800;
}
//world.worldTiles.x += moveSpeed;
}
if (goin[3] == 1)
{
player.x += moveSpeed;
if (player.x >= 800&& (Xloc + 1) < MAP_SCALE)
{
world.X += int(800/world.TILE_SIZE)/MAP_SCALE;
world.deleteTiles();
X += 800 / MAP_SCALE;
Xloc += 1;
player.x -= 800;
}
//world.worldTiles.x -= moveSpeed;
}
}
It's very simple and basic. You are creating new objects constantly and after a while your app can't get enough memory to create new object and also needs to use big chunk of CPU power to clean up. All this produces lag and is a form of memory leak. You need to reuse object instead of creating new ones. This process is called Object Pooling. Once a graphic is not needed, instead of discarding it and creating a new one next time, you keep it and reuse it next time. That way you don't need additional memory and your app doesn't need more and doesn't need to clean up as much (called garbage collection).
public function deleteTiles()
{
if (tilesInWorld.length)
{
for (var i:int = 0; i < tilesInWorld.length; i++)
{
worldTiles.removeChild(tilesInWorld[i]);//no new object creation when deleting
//tilesInWorld.pop() create a new array internally
}
tilesInWorld.length = 0;//empty array
generateTile();//this should be improved too
}
}
if worldTiles holds only titles then worldTiles.removeChildren() will do it as:
worldTiles.removeChildren();
tilesInWorld.length = 0;
generateTile();
I don't remember how Actionscript handles it, but it might be your for loop in the deleteTiles() function. It only continues until i < tilesInWorld.length, and every iteration of the for loop your i increases by one, and your tilesInWorld decreases by one. This means that you'd only be deleting half of the tiles every time you try to remove all the tiles.
Try using a while loop instead, and see if that fixes it. e.g.
public function deleteTiles()
{
if (tilesInWorld.length > 0)
{
while (tilesInWorld.length > 0)
{
worldTiles.removeChild(tilesInWorld.pop());
}
generateTile();
}
}

getting array coordinates (8 queens problem)

I am trying to make a graphic program that solves the 8 queens problem and so far what i have is the chess board
var chessBoard:Array = new Array();
for(var i:int = 0; i < 4; i++)
{
chessBoard.push(new Array(1,0,1,0,1,0,1,0));
chessBoard.push(new Array(0,1,0,1,0,1,0,1));
}
var tileSize:int = 20;
function createChessBoard():void
{
for(var i:int = 0; i < chessBoard.length; i++)
{
for(var j:int = 0; j < chessBoard[i].length; j++)
{
var tile:Sprite = new Sprite();
var tileColor:int = chessBoard[i][j] * 0xffffff;
tile.graphics.beginFill(tileColor);
tile.graphics.drawRect(0, 0, tileSize, tileSize);
tile.graphics.endFill();
tile.x = j * tileSize;
tile.y = i * tileSize;
addChild(tile);
}
}
}
createChessBoard();
(thanks André for this code)
this creates a black and white checkered board for the problem but now i need to be able to place the queens. How am i able to see where the user clicks in order to put the queen in the box that is clicked on?
(sorry if my question isn't fully clear)
I added a very simple example to your question. See below:
var chessBoard:Array = new Array();
for(var i:int = 0; i < 4; i++)
{
chessBoard.push(new Array(1,0,1,0,1,0,1,0));
chessBoard.push(new Array(0,1,0,1,0,1,0,1));
}
var tileSize:int = 20;
function createChessBoard():void
{
for(var i:int = 0; i < chessBoard.length; i++)
{
for(var j:int = 0; j < chessBoard[i].length; j++)
{
var tile:Sprite = new Sprite();
var tileColor:int = chessBoard[i][j] * 0xffffff;
tile.graphics.beginFill(tileColor);
tile.graphics.drawRect(0, 0, tileSize, tileSize);
tile.graphics.endFill();
//I added the name property and a MouseEvent.CLICK event listener
tile.name = "tile__" + i + "_" j + "_sp";
tile.addEventListener(MouseEvent.CLICK, onTileClick);
tile.x = j * tileSize;
tile.y = i * tileSize;
addChild(tile);
}
}
}
function onTileClick(event:MouseEvent):void
{
//This tells you which tile the user clicked on
trace(event.target.name);
};
createChessBoard();
Good luck,
Rob

What's faster: new Vector.<T> OR Vector.<T>.splice(0, Vector.<T>.length)?

What gives the best performance? (edited to include another option)
var myVec:Vector.<Number> = new Vector.<Number>();
for (...)
// Do stuff - example: myVec.push(Math.random());
// Need to empty and repopulate vector
myVec.splice(0, myVec.length);
// OR
myVec = new Vector.<Number>();
// OR
myVec.length = 0;
I have heard that:
myVec.length=0
is the fastest way... but i never verify this point
Using the code below I've found myVec = new Vector.<T>() is the fastest way.
Output (average times in ms to empty a Vector.<Number> with 100000 random entries):
0.679 // using splice()
0.024 // using new Vector.<T>()
0.115 // using .length = 0;
Code:
var myVec:Vector.<Number> = new Vector.<Number>();
var emptyTime:int;
var startTime:int;
var totalEmptyTime:Number = 0;
// Need to empty and repopulate vector
const NUM_TRIALS:int = 1000;
var j:int;
for(j = 0; j < NUM_TRIALS; j++)
{
fillVector();
startTime = getTimer();
myVec.splice(0, myVec.length);
emptyTime = getTimer() - startTime;
totalEmptyTime += emptyTime;
}
trace(totalEmptyTime/NUM_TRIALS);
totalEmptyTime = 0;
// OR
for(j = 0; j < NUM_TRIALS; j++)
{
fillVector();
startTime = getTimer();
myVec = new Vector.<Number>();
emptyTime = getTimer() - startTime;
totalEmptyTime += emptyTime;
}
trace(totalEmptyTime/NUM_TRIALS);
totalEmptyTime = 0;
// OR
for(j = 0; j < NUM_TRIALS; j++)
{
fillVector();
startTime = getTimer();
myVec.length = 0;
emptyTime = getTimer() - startTime;
totalEmptyTime += emptyTime;
}
trace(totalEmptyTime/NUM_TRIALS);
function fillVector():void
{
for (var i:int = 0; i < 100000; i++)
myVec.push(Math.random());
}