views:

227

answers:

3

I am making a Sim City like game. There are lots of tiles. When I first started. I was just using a tilesheet. I was copying the necessary pieaces from the tilesheet. on to a blank bitMapData. I then took the bitMapData and put it into a bitMap which I then put into a DisplayObject. It worked great!

tileSheet:BitMapData  <----- data is already in
loop { loop through and tiled
bg:bitMapData= new bitMapData();
bg.copyPixel(tileSheet,rect,point);
}

canvas.BitMap(bg);
addChild(canvas);

Only problem was I needed to make my tiles interactive. I needed to highlight them and change colors and stuff. So I used the Sprite object. It works great but I can only have so many on the stage at once. or else it moves slow when I scroll. I need something Lighter then a sprite, but yet I can still turn into a object to make interactive. Anyone have any ideas ???

+1  A: 

If you have a lot of tiles, that will impact performance because Flash needs to update the transformations of a lot of display objects (which internally means a lot of matrix calculations, and subsequent redraws of big areas of the screen.)

There is another way to achieve interactivity, if you find that you must use a single bitmap data for performance. Keep an "abstract" (i.e. not graphical) data model in memory, that stores your game state. Make sure that you are able to read from your store where a certain element is positioned in the game world. Then you can use a flat bitmap data to render the game world, because the individual positions are stored elsewhere.

When the user clicks the DisplayObject containing the bitmap data (a Sprite in which the bitmap is drawn using a bitmap fill, or that wraps a Bitmap), look in your model which of your game elements was hit by that click.

// myTileSprite is a Sprite with a bitmap fill
myTileSprite.addEventListener(MouseEvent.CLICK, handleWorldClick);

function handleWorldClick(ev : MouseEvent) : void
{
  var i : int;

  // Loop through all game element data models
  for (i=0; i<myGameElements.length; i++) {
    // Test the mouse position against the element model
    if (myGameElements[i].hitTest(myTileSprite.mouseX, myTileSprite.mouseY)) {
      trace('this was the element that was clicked: '+myGameElements[i].toString());
    }
  }
}

Here, whenever the player clicks the world graphics, the loop tries to find that element which was directly under the mouse position. You will need to implement a hitTest() method on all your game element data models, of course. Such a method simply checks the supplied world space position against the tile's area:

// GameElement.hitTest():
/**
* Tests a world position against the position and area of this game
* element tile. Returns a boolean indicating whether this tile was hit.
*/
public function hitTest(mouseX : Number, mouseY : Number) : void
{
  var rect : Rectangle = new Rectangle(this.worldX, this.worldY, this.width, this.height);

  if (mouseX > rect.left && mouseX < rect.right
    && mouseY > rect.top && mouseY < rect.top) {
    return true;
  }
  else return false;
}

The GameElement class is not an display object, but has worldX and worldY properties indicating where it is located in the world. It's width and height properties define it's dimensions.

The trick from hereon is to make sure that the rendered bitmap and your model storage is synchronized, so that a tile's position on the bitmap really corresponds to it's worldX/worldY properties in the data model.

richardolsson
I am one step ahead of you. please see my response above you.
numerical25
I kinda understand what you are saying. myTileSprite is my tile holder which will hold all my tiles to create the world. when I click on it, it does a check to see where at on the map I clicked. I am guessing myGameElements[i] represents each indivual data representation of my tiles. which would probably have to be some invisible display object for me to do a hittest(); like a sprite with no bitMap fill. I would render that the exact way I would render my bitmap image. Im not sure if I am going the right direction.
numerical25
Your data models should not be display objects. That's the entire idea with separating the data from your view (a single bitmap data.) The idea was that you would create your own hitTest() method on your models, that contains the logic that applies to your particular case. You can call it whatever you want, like myHitTest() or isUnderPoint() or whatever.
richardolsson
A: 

I am one step ahead of you. And that is a great idea. Its alot easier to keep a data representation of the world when the tiles are squared. I therefore can take my mouseX/tileWidth, and thats hw many columns I moved from left to right. same with the Y axis.

Not only that but coordinates start at top left corner.

But issue I have is that my tiles are Isometric. So instead of the X axis start off like...

  012345678
0
1
2
3
4
5
6
7
8

My tiles are aligned like...

                       00
                      1  1
                     2     2
                    3       3
                   4         4
                  5            6

its a little sloppy. but the right side represents the y axis and the left represents the x axis. and the center origin is in the center of the screen. not on the top left. I am trying to figure out how to measure where my mouse is from the center and out on both sides. This sounds extremely difficult. I am not sure if its possible. The game is suppose to be like a sim city like game. The first sim city was squares not isometric. I dont think they went isometric until they started using 3d. I wonder if its possible to create a illusion of isometric on a square tile.

numerical25
A: 

Ive been reading this great book on isometrics. They show to calculate tiles in 3d space. and even calculate your mouse in 3d space as well. here is the code. Its alot, but I hope someone else understands it more then I. The book was written by jobe makar on building multiplayer worlds. I wanted to share it because the code it is pretty simple as far as amount of code put into it. only 2 classes needed. I am not that good with trigonometry. so I cant really interpret how the math is getting the results. hopefully someone can explain that for me :D.

Y coordinates are not given because the width is = to height. The coordinates method is just a custom made Point class which holds x, y and z.

package com.gamebook.grid {
    import com.gamebook.utils.geom.Coordinate;
    import com.gamebook.utils.Isometric;
    import flash.display.MovieClip;
    import flash.events.MouseEvent;

    /**
     * ...
     * @author Jobe Makar - [email protected]
     */
    public class Map extends MovieClip{

        private var _grid:Array;
        private var _iso:Isometric;
        private var _tileWidthOnScreen:int;
        private var _tileHeightOnScreen:int;
        private var _tileWidth:Number;
        private var _tileHeight:Number;
        private var _cols:int;
        private var _rows:int;

        private var _lastTile:Tile;

        public function Map() {
            initialize();
        }

        private function initialize():void{
            _iso = new Isometric();


            //when mapped to the screen the tile makes a diamond of these dimensions
            _tileWidthOnScreen = 64;
            _tileHeightOnScreen = 32;

            //figure out the width of the tile in 3D space
            _tileWidth = _iso.mapToIsoWorld(64, 0).x;

            //the tile is a square in 3D space so the height matches the width
            _tileHeight = _tileWidth;

            buildGrid();

            addEventListener(MouseEvent.MOUSE_MOVE, mouseMoved);
        }

        private function mouseMoved(e:MouseEvent):void {
            if (_lastTile != null) {
                _lastTile.alpha = 1;
                _lastTile = null;
            }

            var coord:Coordinate = _iso.mapToIsoWorld(mouseX, mouseY);
            var col:int = Math.floor(coord.x / _tileWidth);
            var row:int = Math.floor(Math.abs(coord.z / _tileHeight));

            if (col < _cols && row < _rows) {
                var tile:Tile = getTile(col, row);
                tile.alpha = .5;
                _lastTile = tile;
            }
        }

        private function buildGrid():void{
            _grid = [];
            _cols = 10;
            _rows = 10;
            for (var i:int = 0; i < _cols;++i) {
                _grid[i] = [];
                for (var j:int = 0; j < _rows;++j) {
                    var t:Tile = new Tile();

                    var tx:Number = i * _tileWidth;
                    var tz:Number = -j * _tileHeight;

                    var coord:Coordinate = _iso.mapToScreen(tx, 0, tz);

                    t.x = coord.x;
                    t.y = coord.y;

                    _grid[i][j] = t;

                    addChild(t);
                }
            }
        }

        private function getTile(col:int, row:int):Tile {
            return _grid[col][row];
        }

    }

}

Then we have the isometric class that calculates 3d space.

package com.gamebook.utils { import com.gamebook.utils.geom.Coordinate;

/**
 * @author Jobe Makar - [email protected]
 */
public class Isometric {

    //trigonometric values stored for later use
    private var _sinTheta:Number;
    private var _cosTheta:Number;
    private var _sinAlpha:Number;
    private var _cosAlpha:Number;

    /**
     * Isometric class contrustor.
     * @param   declination value. Defaults to the most common value, which is 30.
     */
    public function Isometric() {
        var theta:Number = 30;//even though the tiles are already isometric, you still have to put the degrees the tiles will be turned.
        var alpha:Number = 45;//45 degrees on y axis, 30 dgrees on x axis
        theta *= Math.PI/180; // then you translate to radians
        alpha *= Math.PI/180;
        _sinTheta = Math.sin(theta);
        _cosTheta = Math.cos(theta);
        _sinAlpha = Math.sin(alpha);
        _cosAlpha = Math.cos(alpha);
    }

    /**
     * Maps 3D coordinates to the 2D screen
     * @param   x coordinate
     * @param   y coordinate
     * @param   z coordinate
     * @return  Coordinate instance containig screen x and screen y
     */
    public function mapToScreen(xpp:Number, ypp:Number, zpp:Number):Coordinate {
        var yp:Number = ypp;
        var xp:Number = xpp*_cosAlpha+zpp*_sinAlpha;
        var zp:Number = zpp*_cosAlpha-xpp*_sinAlpha;
        var x:Number = xp;
        var y:Number = yp*_cosTheta-zp*_sinTheta;
        return new Coordinate(x, y, 0);
    }

    /**
     * Maps 2D screen coordinates into 3D coordinates. It is assumed that the target 3D y coordinate is 0.
     * @param   screen x coordinate
     * @param   screen y coordinate
     * @return  Coordinate instance containig 3D x, y, and z
     */
    public function mapToIsoWorld(screenX:Number, screenY:Number):Coordinate {
        var z:Number = (screenX/_cosAlpha-screenY/(_sinAlpha*_sinTheta))*(1/(_cosAlpha/_sinAlpha+_sinAlpha/_cosAlpha));
        var x:Number = (1/_cosAlpha)*(screenX-z*_sinAlpha);
        return new Coordinate(x, 0, z);
    }

}

}

numerical25