Skip to content

Instantly share code, notes, and snippets.

@prettymuchbryce
Last active December 10, 2015 10:39
Show Gist options
  • Save prettymuchbryce/4422628 to your computer and use it in GitHub Desktop.
Save prettymuchbryce/4422628 to your computer and use it in GitHub Desktop.
Performant tile rendering with Starling
//How to accomplish perfomant tile rendering with the Starling engine.
package {
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.ui.Keyboard;
import starling.display.Image;
import starling.display.Sprite;
import starling.events.EnterFrameEvent;
import starling.events.Event;
import starling.events.KeyboardEvent;
import starling.textures.Texture;
import world.WorldData;
public class Game extends Sprite {
[Embed (source= "../assets/tileset.png")]
private var TileSet:Class;
public static var TILE_SIZE:uint = 32;
public static var WINDOW_WIDTH:uint = Global.STAGE_WIDTH;
public static var WINDOW_HEIGHT:uint = Global.STAGE_HEIGHT;
//This represents our TileMap. It is defined outside of this class.
private var _tiles:Vector.<Vector.<uint>> = WorldData.tiles;
//This is a 2D Vector roughly the size of the screen, that holds our Starling Image objects.
private var _visibleTiles:Vector.<Vector.<Image>> = new Vector.<Vector.<Image>>();
private var _cameraX:Number = 0;
private var _cameraY:Number = 0;
private var _tileSetBitmapData:BitmapData;
private var _keysDown:Array = new Array();
public function Game() {
addEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
}
private function onAddedToStage(event:Event):void {
removeEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
//Scale tiles up 2x using normal BitmapData.
_tileSetBitmapData = new BitmapData(new TileSet().width*2, new TileSet().height*2,false);
var m:Matrix = new Matrix();
m.scale(2,2);
_tileSetBitmapData.draw(new TileSet(),m);
//Create a Starling texture object from the BitmapData.
var sheetTexture:Texture = Texture.fromBitmapData(_tileSetBitmapData,false,false,1);
//Instaniate, and store our Starling Image objects.
for (var y:uint = 0; y < Global.STAGE_HEIGHT/TILE_SIZE+2; y++) {
_visibleTiles.push(new Vector.<Image>());
for (var x:uint = 0; x < Global.STAGE_WIDTH/TILE_SIZE+2; x++) {
var image:Image = new Image(sheetTexture);
image.width = image.height = TILE_SIZE;
addChild(image);
_visibleTiles[y].push(image);
}
}
//Add some event listeners
addEventListener(EnterFrameEvent.ENTER_FRAME,onEnterFrame);
stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP,onKeyUp);
}
//Key down and up for camera movement.
private function onKeyDown(event:KeyboardEvent):void {
for (var i:uint = 0; i < _keysDown.length; i++) {
if (_keysDown[i] == event.keyCode) {
return;
}
}
_keysDown.push(event.keyCode);
}
private function onKeyUp(event:KeyboardEvent):void {
for (var i:uint = 0; i < _keysDown.length; i++) {
if (_keysDown[i] == event.keyCode) {
_keysDown.splice(i,1);
return;
}
}
}
//Render loop
private function onEnterFrame(event:EnterFrameEvent):void {
for (var y:uint = 0; y < Global.STAGE_HEIGHT/TILE_SIZE+2; y++) {
for (var x:uint = 0; x < Global.STAGE_WIDTH/TILE_SIZE+2; x++) {
_visibleTiles[y][x].x = Math.floor(TILE_SIZE * (Math.ceil(_cameraX/TILE_SIZE) - _cameraX/TILE_SIZE) + x*TILE_SIZE - TILE_SIZE);
_visibleTiles[y][x].y = Math.floor(TILE_SIZE * (Math.ceil(_cameraY/TILE_SIZE) - _cameraY/TILE_SIZE) + y*TILE_SIZE - TILE_SIZE);
var curTile:uint = _tiles[y + Math.ceil(_cameraY/TILE_SIZE)][x + Math.ceil(_cameraX/TILE_SIZE)];
//Our tile sheet is a long strip of tiles
//where the height is TILE_SIZE and the width is TILE_SIZE * Number of Tiles.
//In order to visually change each tile,
//we set the texture coordinates on each Starling Image object.
//This prevents us from having to remove and add new Images with
//new Textures every frame.
//Starling sets Texture Coordinates on a 0-1 scale.
//This means that even though our
//strip may be 320 x 32 (10x1),
//We need to do some math to figure out the
//proper coordinates for each Vertex.
//The math is:
// Upper left vertex: new Point(desiredTile*(1/(textureWidth/TILE_SIZE)),0)
// Lower left vertex: new Point(desiredTile*(1/(textureWidth/TILE_SIZE)),1)
//Upper right vertex: new Point(1+desiredTile*(1/(textureWidth/TILE_SIZE)),0)
//Lower right vertex: new Point(1+desiredTile*(1/(textureWidth/TILE_SIZE)),1)
//Upper Left
_visibleTiles[y][x].setTexCoords(0,new Point(curTile*(1/(_visibleTiles[0][0].texture.width/TILE_SIZE)),0));
//Lower Left
_visibleTiles[y][x].setTexCoords(2,new Point(curTile*(1/(_visibleTiles[0][0].texture.width/TILE_SIZE)),1));
//Upper Right
_visibleTiles[y][x].setTexCoords(1,new Point((1+curTile)*(1/(_visibleTiles[0][0].texture.width/TILE_SIZE)),0));
//Lower Right
_visibleTiles[y][x].setTexCoords(3,new Point((1+curTile)*(1/(_visibleTiles[0][0].texture.width/TILE_SIZE)),1));
}
}
for (var i:uint =0; i < _keysDown.length; i++) {
if (_keysDown[i] == Keyboard.LEFT) {
_cameraX -= 1000 * event.passedTime;
}
if (_keysDown[i] == Keyboard.RIGHT) {
_cameraX += 1000 * event.passedTime;
}
if (_keysDown[i] == Keyboard.UP) {
_cameraY -= 1000 * event.passedTime;
}
if (_keysDown[i] == Keyboard.DOWN) {
_cameraY += 1000 * event.passedTime;
}
}
//Check camera bounds
if (_cameraX < 0) {
_cameraX = 0;
}
if (_cameraY < 0) {
_cameraY = 0;
}
if (_cameraX > -Global.STAGE_WIDTH + (_tiles[0].length-2)*TILE_SIZE) {
_cameraX = -Global.STAGE_WIDTH + (_tiles[0].length-2)*TILE_SIZE;
}
if (_cameraY > -Global.STAGE_HEIGHT + (_tiles.length-2)*TILE_SIZE) {
_cameraY = -Global.STAGE_HEIGHT + (_tiles.length-2)*TILE_SIZE;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment