PDA

View Full Version : Tonypa's AS3 Tutorial help



gregmax
January 3rd, 2008, 10:45 PM
I just finished the animated sprites tutorial from Tonypa's latest tutorial on AS3 Tile base d games, and am thrilled to be learning it. AS3 is a though, challenging, learning curve from the other Actionscripts - for me (fyi :/ ). And now I want to move forward by having my hero walk around the tile based world scrolling, but Tonypa has yet to come this far on his tutorials :'( . And right now I am little anxious and impatient and a little too very excited to wait for him to post the tutorial.

So I am asking if anyone would have an idea on how to have the tile based world scroll with the hero walking around the stage. I really appreciate ANY help or pointers on how to accomplish this. I am fairly new to this bitmap data stuff, and very new to AS3... so please try to explain it to me carefully, hehe.

Many thanks in advance


ps - or do I have to wait... :'(

gregmax
January 4th, 2008, 07:24 PM
It seems that I would have to wait.... :/

Sirisian
January 4th, 2008, 09:18 PM
I'm guessing this is tile based?

I could explain it, but you should try this on your own. Okay to help you, you'll have a camera that is viewing the tiles. The camera has a position as a Flash.Geom.Point and a width and height.

You have a double for loop I imagine to create your tile grid. Something like:


var tiles:Array = new Array();
for(var x:uint = 0; x < gridWidth; ++x)
{
tiles.push(new Array());
for(var y:uint = 0; y < gridHeight; ++y)
{
tiles[x].push(new Tile());
}
}

right now you are just iterating through all of the tiles and rendering I imagine.

In order to render the translated grid you need to calculate where to start rendering on the x and y and where to end


var startX:int = camera.X/tileWidth;
var endX:int = (camera.X+camera.Width)/tileWidth;
var startY:int = camera.Y/tileHeight;
var endY:int = (camera.Y+camera.Height)/tileHeight;
//I left out the if statements to check for the bounds. startX can't be < 0 and such startY can't be greater than gridWidth and such. Do something here.
for(var x:uint = 0; x < gridWidth; ++x)
{
tiles.push(new Array());
for(var y:uint = 0; y < gridHeight; ++y)
{
//Render using the offset (x*tileWidth - camera.X, y*tileHeight - camera.Y)
}
}


Okay that should get you to think about the problem. Draw pictures of the camera over a grid using graph paper and try to understand how to do this. Understanding how everything works in the end will help you a lot.

gregmax
January 5th, 2008, 12:00 AM
Alright, this is what I have, and I know its not proper, but so far I am not experiencing any slow down. :/ I am basically moving the huge bitmap around the stage.



var offset_x:Number = (obj.x + obj.ts) - (i_parent.stage.stageWidth / 2);
var offset_y:Number = (obj.y + obj.ts) - (i_parent.stage.stageHeight / 2);

if(offset_x < 0) { offset_x = 0; }
if(offset_y < 0) { offset_y = 0; }
if(offset_x > (map_bmp.width - i_parent.stage.stageWidth)) { offset_x = map_bmp.width - i_parent.stage.stageWidth; }
if(offset_y > (map_bmp.height - i_parent.stage.stageHeight)) { offset_y = map_bmp.height - i_parent.stage.stageHeight; }


i_parent.x = -offset_x;
i_parent.y = -offset_y;



am I almost there?

Sirisian
January 5th, 2008, 03:37 AM
The bitmap never moves. Try again.

hatu
January 5th, 2008, 12:15 PM
Here's my Render class. Hope this helps you.

map_drawx and map_drawy that you give to the method as parameters are the x and y coordinates of the 0,0 point of the camera.
So if you have a 100x100 tiles map and each tile is 32x32 pixels then the size in pixels of this map is 3200x3200

This way you can draw the map where the camera is anywhere with 1 pixel precision.
Render.map_draw(569, 295, .....)

sizeX and sizeY are pretty clear. Just the width and height of the showGrid which is basically the size of the camera in TILES

What you need the map_drawx and drawy for is:
-mapx and mapy: these are the camera coordinates conversed into tiles instead of pixels
-map_xoff and _yoff: knowing which TILES to draw is not enough so these to variables take care of the offset. Like you remember you use this whole method with pixel coordinates so this counts how many pixels in what direction do you need to move the tiles you just figured out


Then for the loop which is pretty basic stuff.
Get the current tile. It's a Tile object inside the map array.
Get the current texture. This uses the texturemanager posted earlier
Get newX and newY aka the place where to put this tile on screen.
x= tilenumber * tileWidth - xoffset

copypixels.

That's basically all you need to know. The rest of the parameters aren't that important as I stripped some functionality to make this simpler.

you can see it in action here http://hatu.biz/as31/tile.htm

ActionScript Code:

package {

import flash.geom.Point;
import flash.geom.Rectangle;
import flash.display.BitmapData;

public class Render {


public var sizeX:int;
public var sizeY:int;

public var newX:int = 0;
public var newY:int = 0;

public var f:int = 0;
public var g:int = 0;

public var tex:Texture;

public var tileWidth:int = 64;
public var tileHeight:int = 64;


public function Render() {

}

public function map_draw(map_drawx:int, map_drawy:int, mm:MapManager, tm:TextureManager, hero:Char, screen:BitmapData):void {

//do..while ~5fps nopeempi kuin for
//for f=0;f<size;f++

sizeX = mm.showGrid.length;
sizeY = mm.showGrid[0].length;


//CHANGE TO WHILE LOOPS WHEN WORKING


//current tile
//var currentTile:Tile = mm.getShowMap(f,g);

var mapx:int = map_drawx / tileWidth;
var mapy:int = map_drawy / tileHeight;

var map_xoff:int = map_drawx & tileWidth-1;
var map_yoff:int = map_drawy & tileHeight-1;




//trace(mapTexture.xPos + mapTexture.yPos +""+ mapTexture.walkable)


//tile_draw(map[mapy + i][mapx + j].tile_number, j * 16 - map_xoff, i * 16 - map_yoff);



f = 0;
do {
g = 0;
do {


var currentTile:Tile = mm.getMap(mapx + f, mapy + g);
tex = tm.GetTexture(currentTile.tileTexture);

newX = f * tileWidth - map_xoff;
newY = g * tileHeight - map_yoff;


//rendering the pixels on the screen
screen.copyPixels(tex.bitmap, new Rectangle(0, 0, tex.bitmap.width, tex.bitmap.height), new Point(newX, newY));


g++;
}while (g < sizeY);
f++;
}while (f < sizeX);


}
}

}

Sirisian
January 5th, 2008, 04:20 PM
hatu:


for(var f:uint = 0; f < sizeX; ++f){
for(var g:uint = 0; g < sizeY; ++g){

}
}

equivalent to your do while.

tex = tm.GetTexture(currentTile.tileTexture);

NO!!!

tileTexture should be a BitmapData reference not a string.
tex = currentTile.tileTexture;

string look ups are totally unnecessary.

Read my first post both of you.

Hatu, I'm curious why are you rendering the tiles outside of the screen? What if your map is 50,000 by 50,000. If done correctly the size of the map never changes the speed at which the map is drawn.

hatu
January 5th, 2008, 04:43 PM
hatu:


for(var f:uint = 0; f < sizeX; ++f){
for(var g:uint = 0; g < sizeY; ++g){

}
}
equivalent to your do while.

tex = tm.GetTexture(currentTile.tileTexture);

NO!!!

tileTexture should be a BitmapData reference not a string.
tex = currentTile.tileTexture;

string look ups are totally unnecessary.

Read my first post both of you.

Hatu, I'm curious why are you rendering the tiles outside of the screen? What if your map is 50,000 by 50,000. If done correctly the size of the map never changes the speed at which the map is drawn.

I did have it as a for loop but do..while seemed to be about 5fps faster so I went with it here.

Good point on the strings. I'll look into it. The map generator I have is really primitive at the moment so I think it was easier that way.

Hmm I'm not rendering tiles outside the screen. Not much anyways, just enough to not get trailing on the edges.

sizeX is 9 and sizeY 6 here. showGrid is the "camera-array", actually it's pretty useless now as it just gives the size of the view so I'm just gonna change it to variables.
Besides in the example it's 32000x32000 in pixels

Maybe my code is a bit confusing
var currentTile:Tile = mm.getMap(mapx + f, mapy + g);

See here, mapx is calculated from your position in the 32000x32000 pixel map into a tile position. X pixels / tileWidth

+ f, and f runs from 0 to sizeX which is 9 now

gregmax
January 5th, 2008, 05:26 PM
This is so neat hatu and Sirisian, thanks. I am going to give this a shot tonight after work. This AS3 is just SO different from the others, and I am the kind of programmer who won't know how to do it unless I have a complete example at hand. So I am really am in the dark on this... :'(

thanks again, I will post if I need help

gregmax
January 6th, 2008, 09:48 PM
Hey hatu, I forgot to ask a question the other day looking at your example that I didn't think of asking until now... how are you able to generate a 32,000x32,000 size map when I thought the max size for a bitmap data is 2880?

Sirisian
January 6th, 2008, 10:53 PM
Maybe my code is a bit confusing
var currentTile:Tile = mm.getMap(mapx + f, mapy + g);

See here, mapx is calculated from your position in the 32000x32000 pixel map into a tile position. X pixels / tileWidth

+ f, and f runs from 0 to sizeX which is 9 now
ah didn't look at that line. For some reason I thought you were iterating through your whole map.

hatu
January 7th, 2008, 04:17 AM
Hey hatu, I forgot to ask a question the other day looking at your example that I didn't think of asking until now... how are you able to generate a 32,000x32,000 size map when I thought the max size for a bitmap data is 2880?

The 32000x32000(px) bitmap never actually exists anywhere.

There's the 500x500 tile array which holds the coordinates for the map.
Then there are the 64x64 pixel Tiles (from the Tile class)

Now when you point at a place in the 32000x32000 map, the function calculates a small area of it and draws it on screen (9x6 Tiles or 576x384 pixels)

gregmax
January 9th, 2008, 02:15 AM
I am so lost... I tried several examples and I am just stumped... :'(

here is what I have...(thanks in advance for your patience)

- I have two textures right now, one for the map and the other for the character
- I figured out how to display the surrounding tiles around the character from anywhere within the map without display the whole map
- I have the character walking around the map, rendering the tiles and placing them in place as well

but here is my problem and question (to help me better understand)
- how do I just have the map it self appear to be moving with the hero without literally having the map itself move?
I believe it has something to do with the Point() function, but I am having trouble how to utilize it :(

do I have to create a third bitmap or something? here is the code right now to render and draw the map around the hero...



// loop through map
for(var i:Number = offset_y; i < (offset_y + 9); i++) {
for(var j:Number = offset_x; j < (offset_x + 11); j++) {

tile_nm = "t_" + i + "_" + j;
tiles[tile_nm] = new Object();
tiles[tile_nm].sheet = map_texture;
tiles[tile_nm].ts = ts;
tiles[tile_nm].i = i;
tiles[tile_nm].j = j;
tiles[tile_nm].walk = map_walk[i][j];
tiles[tile_nm].img = map_tile[i][j];

// create a bitmap to place the tile
var bmp:Bitmap = new Bitmap();
bmp = get_texture(tiles[tile_nm], tiles[tile_nm].img);
draw_tile(tiles[tile_nm], bmp);


} // end for j loop
} // end for i loop


// gets the tile from the texture page
public function get_texture(tile:Object, img:Number):Bitmap {

var texture_columns:Number = tile.sheet.width / tile.ts;
var col:Number = img % texture_columns;
var row:Number = Math.floor(img / texture_columns);

// rectangle to define tile image
var rec:Rectangle = new Rectangle( (col * tile.ts), (row * tile.ts), (tile.ts), (tile.ts) );
var point:Point = new Point(0, 0);
var bmp:Bitmap = new Bitmap(new BitmapData(ts, ts, true, 0));
bmp.bitmapData.copyPixels(tile.sheet, rec, point, null, null, true);

return bmp;

} // end get_tile


// draws the tile to the screen
public function draw_tile(tile:Object, bmp:Bitmap) {

// rectangle has size and it starts from 0, 0
var rec:Rectangle = new Rectangle(0, 0, tile.ts, tile.ts);
// point on screen where to go
var point:Point = new Point( (tile.j * tile.ts), (tile.i * tile.ts) );
// copy tile bmp to main bmp
map_bmp.bitmapData.copyPixels(bmp.bitmapData, rec, point, null, null, true);

} // end draw_tile



many thanks in advance... :)

hatu
January 9th, 2008, 03:46 AM
- how do I just have the map it self appear to be moving with the hero without literally having the map itself move?

Umm what's the difference between the map appear to be moving and the map actually moving?

I'm not sure what you mean by this but you have to move the whole map to give the illusion that it's the character moving in the map and a camera following him.

Like when you jump you're not jumping up, you're pushing the earth down. :thumb2:

-Z-
January 9th, 2008, 07:59 AM
He means, moving the image that holds the map, or keeping the image still, and just redrawing the map in a slightly moved position... Keeping the image still is the best option.


How do you have the hero stay still, simple, just draw him in the middle :) have the map 'redrawn' under him.

hatu
January 9th, 2008, 10:36 AM
He means, moving the image that holds the map, or keeping the image still, and just redrawing the map in a slightly moved position... Keeping the image still is the best option.


Moving the "canvas" then? The bitmap where you copy the pixels to.
You can move it by changing the Point(x,y).

gregmax
January 9th, 2008, 11:44 AM
Yes -Z-, you got it. I guess what I am trying to do is move the canvas and redraw the map under the hero. This is my existing grab the texture and draw it to the map...



// gets the tile from the texture page
public function get_texture(tile:Object, img:Number):Bitmap {

var texture_columns:Number = tile.sheet.width / tile.ts;
var col:Number = img % texture_columns;
var row:Number = Math.floor(img / texture_columns);

// rectangle to define tile image
var rec:Rectangle = new Rectangle( (col * tile.ts), (row * tile.ts), (tile.ts), (tile.ts) );
var point:Point = new Point(0, 0);
var bmp:Bitmap = new Bitmap(new BitmapData(ts, ts, true, 0));
bmp.bitmapData.copyPixels(tile.sheet, rec, point, null, null, true);

return bmp;

} // end get_tile


// draws the tile to the screen
public function draw_tile(tile:Object, bmp:Bitmap) {

// rectangle has size and it starts from 0, 0
var rec:Rectangle = new Rectangle(0, 0, tile.ts, tile.ts);
// point on screen where to go
var point:Point = new Point( (tile.j * tile.ts), (tile.i * tile.ts) );
// copy tile bmp to main bmp
map_bmp.bitmapData.copyPixels(bmp.bitmapData, rec, point, null, null, true);

} // end draw_tile




So in the draw_tile function is where I would change the Points, correct? Since right now it when display/renders the tile it will be placed on the map_bmp by its i and j position times the tile.ts ... correct? So if "i" were 400 and tile.ts is 32 the tile would be placed way off the screen (12,800 pixels off).

Is this the function I would do it in...am getting the right idea? Do I have to recreate a new function for this? Either way, the map_bmp is already being set with a set width and max, the map_array.length (for height) and map_array[0].length (for width). So I would have to change this to be the desired screen size, and not the total size of the map_array, correct?

(I am no code head)

gregmax
January 12th, 2008, 06:50 PM
anyone? :(

hatu
January 13th, 2008, 06:25 AM
I still don't get what you're trying to do here. Can you explain what kind of functionality are you trying to achieve

gregmax
January 13th, 2008, 03:52 PM
I am really trying to accomplish what you have going on your example, really. BUT I don't know how to utilize it into my structure because I am no code head. :( (that is why I am asking for help on this)

I love how in your example you are able to keep the hero centered in the middle of the stage/screen and have a HUGE tiled map. That is very impressive and I want to do how to do that.

omgnoseat
February 2nd, 2010, 02:06 PM
Hello,

I've been reading tonypa's tutorials for self education, I can recommend it to everyone, even if your not interested in game design.

I have a very simple problem, but I'm too stupid to see what is causing it.
I'm simply trying to retrieve the tile location [x][y] from the hero character. But for some reason it is throwing an error at some tiles.

The code:


//imports
import KeyObject;

//vars
var Key:KeyObject = new KeyObject(stage);
var mapW:Number = 0;
var mapH:Number = 0;
var tileSize:Number = 50;
var curMap:Array;
var square:Sprite;
var canWalk:Boolean = true;
var xDirection:Number;
var yDirection:Number;
var newX:Number = 0;
var newY:Number = 0;

var mapArray:Array = new Array(
[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 1, 1, 1, 1, 1, 1, 0],
[ 0, 1, 0, 1, 1, 1, 1, 0],
[ 0, 1, 1, 1, 1, 0, 1, 0],
[ 0, 1, 1, 1, 1, 1, 1, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0]
);
curMap = mapArray;
buildMap();

function buildMap():void {
//get map dimensions
mapW = curMap[0].length;
mapH = curMap.length;
//main bitmap to be shown on screen
//loop to place tiles on stage
for (var yt:int = 0; yt < mapH; yt++) {
for (var xt:int = 0; xt < mapW; xt++) {

var s:int = curMap[yt][xt];

if (s == 0) {
drawTile (s, xt, yt);
}
}
}
}

function drawTile (s:Number, xt:int, yt:int):void {
//rectangle has size of tile and it starts from 0,0
var rect:Rectangle = new Rectangle(0, 0, tileSize, tileSize);
//point on screen where the tile goes
var pt:Point = new Point(xt * tileSize, yt * tileSize);

square = new Sprite();
square.graphics.beginFill(Math.random() * 0x000000);
square.graphics.drawRect(xt * tileSize, yt * tileSize, tileSize, tileSize);
square.graphics.endFill();

addChild(square);
//copy tile bitmap to main bitmap

}

//make hero
var hero:Hero = new Hero(2, 2, tileSize);
addChild(hero);

addEventListener(Event.ENTER_FRAME,moveChar);

function moveChar(e:Event):void{

checkWalkable();

hero.x = hero.xTile * tileSize;
hero.y = hero.yTile * tileSize;


if(Key.isDown(Key.LEFT) && canWalk){

hero.xTile -= 1;
xDirection = -1;


} else if(Key.isDown(Key.RIGHT) && canWalk ){

hero.xTile += 1;
xDirection = +1



}else if(Key.isDown(Key.UP) && canWalk){

hero.yTile -= 1;
yDirection = -1;


}else if(Key.isDown(Key.DOWN) && canWalk){

hero.yTile += 1;
yDirection = +1;

}else{

xDirection = 0;
yDirection = 0;

}
}

function checkWalkable():void{
//current location - throws an error
trace(mapArray[hero.xTile][hero.yTile]);

}


tracing the current location (trace(mapArray[hero.xTile][hero.yTile]); ) throws the following error:

TypeError: Error #1010: A term is undefined and has no properties.
at array_fla::MainTimeline/checkWalkable()
at array_fla::MainTimeline/moveChar()


I have absolutely no idea whats causing this on some tiles. It even throws the same error when using tonypa's unchanged source file.
Any thoughts?

_kp
February 2nd, 2010, 02:39 PM
tracing the current location (trace(mapArray[hero.xTile][hero.yTile]); ) throws the following error:


try mapArray[hero.yTile][hero.xTile]

omgnoseat
February 2nd, 2010, 03:05 PM
try mapArray[hero.yTile][hero.xTile]

still get the error :(
Anyone else?

_kp
February 2nd, 2010, 04:00 PM
still get the error :(
Anyone else?

It will throw an error because there is nothing to prevent from walking outside the map. So at some point you trying to access for example mapArray[2][-1] when just walking left.

omgnoseat
February 2nd, 2010, 04:03 PM
It will throw an error because there is nothing to prevent from walking outside the map. So at some point you trying to access for example mapArray[2][-1] when just walking left.

It throws the error when it is still inside the map, the problem is that I cannot make boundaries because of that error haha. When the hero moves outside of the map the trace returns "undefined" and doesn't display an error.
Clueless on this one.

omgnoseat
February 2nd, 2010, 05:54 PM
I noticed that I get the error when I try to trace(curMap[hero.xTile][hero.yTile]); when the hero is on xTile 5 and yTile 2.
But when I try to trace curMap[5][2] it responds and gives me the tileinfo without an error. Very strange since that are exactly the values that hero.xTile and hero.yTile give.

therobot
February 3rd, 2010, 01:53 AM
I noticed that I get the error when I try to trace(curMap[hero.xTile][hero.yTile]); when the hero is on xTile 5 and yTile 2.
But when I try to trace curMap[5][2] it responds and gives me the tileinfo without an error. Very strange since that are exactly the values that hero.xTile and hero.yTile give.

run it through the debugger, you'll have this solved in < 1 minute.

if you don't have access to the debugger, consider the following:

make sure hero is a valid object.
make sure hero.xTile is in a valid range ( >= 0, <= mapWidth - 1 )
make sure hero.yTile is in a valid range ( >= 0, <= mapHeight -1 )
make sure curMap is a valid reference to your map array.
make sure curMap is a valid array that contains many other arrays that are also not messed up.

like i said, run it through the debugger. it's a good habit to get into

omgnoseat
February 3rd, 2010, 01:42 PM
run it through the debugger, you'll have this solved in < 1 minute.

if you don't have access to the debugger, consider the following:

make sure hero is a valid object.
make sure hero.xTile is in a valid range ( >= 0, <= mapWidth - 1 )
make sure hero.yTile is in a valid range ( >= 0, <= mapHeight -1 )
make sure curMap is a valid reference to your map array.
make sure curMap is a valid array that contains many other arrays that are also not messed up.

like i said, run it through the debugger. it's a good habit to get into

Thanks for the advice, never really used the debugger but it does seem very handy. Unforunatly in this case it won't bring me any new information. It points out what line is creating the error, but I already was aware if that.

I have adjusted the trace statement to the conditions:


if(hero.xTile >= 0 && hero.xTile < mapW && hero.yTile >=0 && hero.yTile < mapH ){
trace(curMap[hero.xTile][hero.yTile]);
}


Absolutely clueless.
I have uploaded the file if anyone is kind enough to take a look, because I have no idea whats going wrong.

http://stap.iam.hva.nl/~wullem01/TileSystem.rar

Thanks for your reply therobot, I appreciate it :)

therobot
February 3rd, 2010, 02:12 PM
Thanks for the advice, never really used the debugger but it does seem very handy. Unforunatly in this case it won't bring me any new information. It points out what line is creating the error, but I already was aware if that.

Are you using the debugger correctly? If you place a breakpoint in your code, it will halt your program when it reaches that line of code during execution. Further, it allows you to see the values of EVERY SINGLE VARIABLE in your program at that point in time. It also lets you step through the code line by line and (again) inspect the value of every variable.

Of course, if you don't place a breakpoint in your code, the debugger will just run and you'll see the error just like you would if you hadn't run the debugger.

omgnoseat
February 3rd, 2010, 02:32 PM
Are you using the debugger correctly? If you place a breakpoint in your code, it will halt your program when it reaches that line of code during execution. Further, it allows you to see the values of EVERY SINGLE VARIABLE in your program at that point in time. It also lets you step through the code line by line and (again) inspect the value of every variable.

Of course, if you don't place a breakpoint in your code, the debugger will just run and you'll see the error just like you would if you hadn't run the debugger.
There are no breakpoints. The variable section shows 2 variables:
- name: this
value: array_fla.MainTimeline
- name: e
value: flash.events.Event

I'm not using "this" anywhere in the code, so that seems pretty strange. Still doesn't seem like anything usefull. Can't stand it when something this simple consumes so much time.

therobot
February 3rd, 2010, 06:11 PM
I don't think you're using the debugger right.

I'm saying, add the breakpoint in your function, a line before the problem (sometimes flash gives you problems if you add a breakpoint on the problem line). something like this:



function myProblemFunction():void
{
trace("I want a line before this so I can check out the var values before Flash starts tweaking out!" );// put your break point here.
myProblemLine[potentialIssue.y][potentialIssue.x];
}


Also, make sure "Permit Debugging" is checked in your .fla's publish settings (might be 'debugging permitted' - not in front of it right now) .

omgnoseat
February 8th, 2010, 09:15 AM
Fixed this, _kp was right al along. I was running an other trace with wrong code which I forgot to remove. Still pretty good that you teached me about the debugger tho :)