From the fantastic tutorial / example here: http://gamedev.tutsplus.com/tutorials/implementation/coding-destructible-pixel-terrain/
Most of the original comments left intact
Arrow keys to move left and right and jump.
Mouse down to shoot
@author Devon O.
/**
* Copyright ps35200 ( http://wonderfl.net/user/ps35200 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/b2FQ
*/
// forked from devon_o's Destructible Terrain
package
{
import com.bit101.components.Label;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.LoaderContext;
/**
* From the fantastic tutorial / example here: http://gamedev.tutsplus.com/tutorials/implementation/coding-destructible-pixel-terrain/
* Most of the original comments left intact
*
*
* Arrow keys to move left and right and jump.
* Mouse down to shoot
*
*
* @author Devon O.
*/
[SWF(width='465', height='465', backgroundColor='#000000', frameRate='60')]
public class Main extends Sprite
{
private var player:Player;
private var canvas:BitmapData;
private var terrain:Bitmap;
private var background:Bitmap;
private var numItemsToLoad:int = 2;
private var numItemsLoaded:int = 0;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.showDefaultContextMenu = false;
loadTextures();
}
private var loadText:Label;
private function loadTextures():void
{
loadText = new Label(this, 50, 50, "Loading Textures...");
var tLoader:Loader = new Loader();
tLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTerrainLoaded);
tLoader.load(new URLRequest("http://assets.wonderfl.net/images/related_images/5/50/50ed/50ed97ea2bf41c27aa797fb95b12d239d1d89905"), new LoaderContext(true));
var bgLoader:Loader = new Loader();
bgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onBgLoaded);
bgLoader.load(new URLRequest("http://assets.wonderfl.net/images/related_images/e/e2/e248/e24840461132639617484f03fad8f40cc45e26b0"), new LoaderContext(true));
}
private function onTerrainLoaded(event:Event):void
{
var l:Loader = (event.currentTarget as LoaderInfo).loader;
l.contentLoaderInfo.removeEventListener(Event.COMPLETE, onTerrainLoaded);
terrain = l.content as Bitmap;
numItemsLoaded++;
if (numItemsLoaded == numItemsToLoad)
{
start();
}
}
private function onBgLoaded(event:Event):void
{
var l:Loader = (event.currentTarget as LoaderInfo).loader;
l.contentLoaderInfo.removeEventListener(Event.COMPLETE, onBgLoaded);
background = l.content as Bitmap;
numItemsLoaded++;
if (numItemsLoaded == numItemsToLoad)
{
start();
}
}
private function start():void
{
removeChild(loadText);
canvas = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
addChild(new Bitmap(canvas));
player = new Player(100, 100);
Controls.init(player, stage, terrain);
addEventListener(Event.ENTER_FRAME, draw);
}
private function draw(event:Event):void
{
// update physics
Controls.getPhysics().update();
// load changes into the terrain
Controls.getTerrain().update();
/* Rendering */
canvas.lock();
canvas.fillRect(canvas.rect, 0x000000);
// draw the background
canvas.draw(background);
// draw the terrain
Controls.getTerrain().draw(canvas);
// draw everything else
Controls.getRenderer().draw(canvas);
canvas.unlock();
}
}
}
interface IPhysicsObj
{
function getX():Number;
function getY():Number;
function getVX():Number;
function getVY():Number;
function setX(value:Number):void;
function setY(value:Number):void;
function setVX(value:Number):void;
function setVY(value:Number):void;
function checkConstraints():void;
}
import flash.display.BitmapData;
interface IRenderObj
{
function draw(canvas:BitmapData):void;
}
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.display.Bitmap;
import flash.display.Stage;
import flash.ui.Keyboard;
class Controls {
private static var player:Player;
private static var stage:Stage;
private static var terrain:Terrain;
private static var physics:Physics;
private static var renderer:Renderer;
public static function init(p:Player, s:Stage, tImg:Bitmap):void
{
player = p;
stage = s;
terrain = new Terrain(tImg, 4);
physics = new Physics();
renderer = new Renderer();
physics.add(player);
renderer.add(player);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private static function onKeyDown(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.UP)
player.jump();
if (event.keyCode == Keyboard.LEFT)
player.moveLeft();
if (event.keyCode == Keyboard.RIGHT)
player.moveRight();
}
private static function onKeyUp(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.LEFT)
player.stopLeft();
if (event.keyCode == Keyboard.RIGHT)
player.stopRight();
}
private static function onMouseDown(event:MouseEvent):void
{
player.shoot();
}
private static function onMouseUp(event:MouseEvent):void
{
player.stopShooting();
}
public static function getMouseX():Number
{
return stage.mouseX;
}
public static function getMouseY():Number
{
return stage.mouseY;
}
public static function getTerrain():Terrain
{
return terrain;
}
public static function getPhysics():Physics
{
return physics;
}
public static function getRenderer():Renderer
{
return renderer;
}
/* RayCast */
// Uses Bresenham's line algorithm to efficiently loop between two points, and find the first solid pixel
// This particular variation always starts from the first point, so collisions don't happen at the wrong end.
// returns an int array
// ||| x = ([0],[1]) point in empty space before collision point
// ||| o = ([2],[3]) collision point
//(end)--||ox------- (start)
// |||
// using http://www.gamedev.net/page/resources/_/reference/programming/sweet-snippets/line-drawing-algorithm-explained-r1275
public static function rayCast(startX:int, startY:int, lastX:int, lastY:int):Vector.<int>
{
var deltax:int = int(Math.abs(lastX - startX)); // The difference between the x's
var deltay:int = int(Math.abs(lastY - startY)); // The difference between the y's
var x:int = startX; // Start x off at the first pixel
var y:int = startY; // Start y off at the first pixel
var xinc1:int, xinc2:int, yinc1:int, yinc2:int;
if (lastX >= startX) // The x-values are increasing
{
xinc1 = 1;
xinc2 = 1;
} else { // The x-values are decreasing
xinc1 = -1;
xinc2 = -1;
}
if (lastY >= startY) // The y-values are increasing
{
yinc1 = 1;
yinc2 = 1;
} else { // The y-values are decreasing
yinc1 = -1;
yinc2 = -1;
}
var den:int, num:int, numadd:int, numpixels:int;
if (deltax >= deltay) // There is at least one x-value for every y-value
{
xinc1 = 0; // Don't change the x when numerator >= denominator
yinc2 = 0; // Don't change the y for every iteration
den = deltax;
num = deltax / 2;
numadd = deltay;
numpixels = deltax; // There are more x-values than y-values
} else { // There is at least one y-value for every x-value
xinc2 = 0; // Don't change the x for every iteration
yinc1 = 0; // Don't change the y when numerator >= denominator
den = deltay;
num = deltay / 2;
numadd = deltax;
numpixels = deltay; // There are more y-values than x-values
}
var prevX:int = startX;
var prevY:int = startY;
for (var curpixel:int = 0; curpixel <= numpixels; curpixel++)
{
if (terrain.isPixelSolid(x, y))
return new <int>[ prevX, prevY, x, y ];
prevX = x;
prevY = y;
num += numadd; // Increase the numerator by the top of the fraction
if (num >= den) // Check if numerator >= denominator
{
num -= den; // Calculate the new numerator value
x += xinc1; // Change the x as appropriate
y += yinc1; // Change the y as appropriate
}
x += xinc2; // Change the x as appropriate
y += yinc2; // Change the y as appropriate
}
return new <int>[];
}
public static function randRange(max:Number, min:Number = 0, decimals:int = 0):Number {
if (min > max) return NaN;
var rand:Number = Math.random() * (max - min) + min;
var d:Number = Math.pow(10, decimals);
return ~~((d * rand) + 0.5) / d;
}
public static function explode(xPos:int, yPos:int, radius:Number):void
{
var radiusSq:Number = radius * radius;
// loop through every x from xPos-radius to xPos+radius
for (var x:int = xPos - int(radius); x < xPos + int(radius); x += terrain.destructionRes)
{
// first make sure that the x is within terrain's boundaries
if (x > 0 && x < terrain.width())
{
// next loop through every y pos in this x column
for (var y:int = yPos - int(radius); y < yPos + int(radius); y += terrain.destructionRes)
{
if (y > 0 && y < terrain.height())
{
// first determine if this pixel (or if any contained within its square area) is solid
var solidX:int = 0;
var solidY:int = 0;
var solid:Boolean = false;
// loop through every pixel from (xPos,yPos) to (xPos + destructionRes, yPos + destructionRes)
// to find whether this area is solid or not
for (var i:int = 0; i < terrain.destructionRes && !solid; i++)
{
for (var j:int = 0; j < terrain.destructionRes && !solid; j++)
{
if (terrain.isPixelSolid(x + i, y + j))
{
solid = true;
solidX = x + i;
solidY = y + j;
}
}
}
if (solid) { // we know this pixel is solid, now we need to find out if it's close enough
var xDiff:Number = x - xPos;
var yDiff:Number = y - yPos;
var distSq:Number = xDiff * xDiff + yDiff * yDiff;
if (distSq < radiusSq)
{
// finally calculate the distance
var distance:Number = Math.sqrt(distSq);
// the speed will be based on how far the pixel is from the center
var speed:Number = 800 * (1 - distance / radius);
if (distance == 0)
distance = .001; // prevent divide by zero in next two statements
// velocity
var velX:Number = speed * (xDiff + randRange(10, -10, 2)) / distance;
var velY:Number = speed * (yDiff + randRange(10, -10, 2)) / distance;
// create the dynamic pixel
var pixel:PhysicsPixel = new PhysicsPixel(terrain.getColor(solidX, solidY), x, y, velX, velY, terrain.destructionRes);
pixel.stickiness = 800;
physics.add(pixel);
renderer.add(pixel);
// remove the static pixels
for (i = 0; i < terrain.destructionRes; i++)
{
for (j = 0; j < terrain.destructionRes; j++)
{
terrain.removePixel(x + i, y + j);
}
}
}
}
}
}
}
}
}
}
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.geom.Rectangle;
class Bullet implements IPhysicsObj, IRenderObj
{
private var x:Number;
private var y:Number;
private var lastX:Number;
private var lastY:Number;
private var velX:Number;
private var velY:Number;
public function Bullet(xpos:Number, ypos:Number, vx:Number, vy:Number)
{
x = xpos;
y = ypos;
velX = vx;
velY = vy;
}
/* INTERFACE IPhysicsObj */
public function getX():Number {return x;}
public function getY():Number {return y;}
public function getVX():Number {return velX;}
public function getVY():Number {return velY;}
public function setX(value:Number):void {x = value;}
public function setY(value:Number):void {y = value;}
public function setVX(value:Number):void {velX = value;}
public function setVY(value:Number):void {velY = value;}
public function checkConstraints():void
{
var collision:Vector.<int> = Controls.rayCast(int(lastX), int(lastY), int(x), int(y));
if (collision.length > 0)
{
Controls.getRenderer().remove(this);
Controls.getPhysics().remove(this);
Controls.explode(collision[2], collision[3], 30);
}
if (x > Controls.getTerrain().width() || x < 0 || y > Controls.getTerrain().height())
{
Controls.getRenderer().remove(this);
Controls.getPhysics().remove(this);
}
lastX = x;
lastY = y;
}
/* INTERFACE IRenderObj */
private var r:Rectangle = new Rectangle();
public function draw(canvas:BitmapData):void
{
r.height = 8;
r.width = 8;
r.x = x - 4;
r.y = y - 4;
canvas.fillRect(r, 0xFF0000);
}
}
import flash.utils.getTimer;
class Physics
{
private var previousTime:Number;
private var currentTime:Number;
private var fixedDeltaTime:int = 16;
private var fixedDeltaTimeSeconds:Number = Number(fixedDeltaTime) / 1000.00;
private var leftOverDeltaTime:int = 0;
private var objects:Vector.<IPhysicsObj>;
public function Physics()
{
objects = new Vector.<IPhysicsObj>();
}
public function add(obj:IPhysicsObj):void
{
objects[objects.length] = obj;
}
public function remove(obj:IPhysicsObj):void
{
var idx:int = objects.indexOf(obj);
if (idx < 0) return;
objects.splice(idx, 1);
}
public function update():void
{
// This game uses fixed-sized timesteps.
// The amount of time elapsed since last update is split up into units of 16 ms
// any left over is pushed over to the next update
// we take those units of 16 ms and update the simulation that many times.
// a fixed timestep will make collision detection and handling (in the Player class, esp.) a lot simpler
// A low framerate will not compromise any collision detections, while it'll still run at a consistent speed.
currentTime = getTimer();
var deltaTimeMS:Number = currentTime - previousTime; // how much time has elapsed since the last update
previousTime = currentTime; // reset previousTime
// Find out how many timesteps we can fit inside the elapsed time
var timeStepAmt:int = int(Number(deltaTimeMS + leftOverDeltaTime) / Number(fixedDeltaTime));
// Limit the timestep amount to prevent freezing
timeStepAmt = Math.min(timeStepAmt, 1);
// store left over time for the next frame
leftOverDeltaTime = int(deltaTimeMS) - (timeStepAmt * fixedDeltaTime);
for (var iteration:int = 1; iteration <= timeStepAmt; iteration++) {
for (var i:int = 0; i < objects.length; i++) { // loop through every PhysicsObj
var obj:IPhysicsObj = objects[i];
// get their velocity
var velX:Number = obj.getVX();
var velY:Number = obj.getVY();
// add gravity
velY += 980 * fixedDeltaTimeSeconds;
obj.setVY(velY);
// Always add x velocity
obj.setX(obj.getX() + velX * fixedDeltaTimeSeconds);
// if it's a player, only add y velocity if he's not on the ground.
if (obj is Player) {
if (!(Player(obj).onGround && velY > 0))
obj.setY(obj.getY() + velY * fixedDeltaTimeSeconds);
} else {
obj.setY(obj.getY() + velY * fixedDeltaTimeSeconds);
}
// allow the object to do collision detection and other things
obj.checkConstraints();
}
}
}
}
import flash.display.BitmapData;
import flash.geom.Rectangle;
class PhysicsPixel implements IPhysicsObj, IRenderObj
{
private var x:Number;
private var y:Number;
private var lastX:Number;
private var lastY:Number;
private var velX:Number;
private var velY:Number;
private var bounceFriction:Number = 0.50;
private var col:uint;
private var size:int = 1;
public var stickiness:Number = 1500;
public function PhysicsPixel(c:uint, xx:Number, yy:Number, vX:Number, vY:Number, s:int)
{
col = c;
x = xx;
y = yy;
velX = vX;
velY = vY;
size = s;
}
/* INTERFACE IPhysicsObj */
public function getX():Number {return x;}
public function getY():Number {return y;}
public function getVX():Number { return velX; }
public function getVY():Number {return velY;}
public function setX(value:Number):void {x = value;}
public function setY(value:Number):void {y = value;}
public function setVX(value:Number):void {velX = value;}
public function setVY(value:Number):void {velY = value;}
public function checkConstraints():void
{
// Find if there's a collision between the current and last points
var collision:Vector.<int> = Controls.rayCast(int(lastX), int(lastY), int(x), int(y));
if (collision.length > 0)
collide(collision[0], collision[1], collision[2], collision[3]);
// reset last positions
lastX = x;
lastY = y;
// Boundary constraints... only remove the pixel if it exits the sides or bottom of the map
if (x > Controls.getTerrain().width() || x < 0 || y > Controls.getTerrain().height())
{
Controls.getRenderer().remove(this);
Controls.getPhysics().remove(this);
}
}
/* INTERFACE IRenderObj */
private var r:Rectangle = new Rectangle();
public function draw(canvas:BitmapData):void
{
r.width = size;
r.height = size;
r.x = x - (size >> 1);
r.y = y - (size >> 1);
canvas.fillRect(r, col);
}
/* Collide */
// called whenever checkConstraints() detects a solid pixel in the way
public function collide(thisX:int, thisY:int, thatX:int, thatY:int):void
{
// first determine if we should stick or if we should bounce
if (velX * velX + velY * velY < stickiness * stickiness) // if the velocity's length is less than our stickiness property, add the pixel
{
// remove this dynamic pixel
Controls.getRenderer().remove(this);
Controls.getPhysics().remove(this);
} else { // otherwise, bounce
// find the normal at the collision point
// to do this, we need to reflect the velocity across the edge normal at the collision point
// this is done using a 2D vector reflection formula ( http://en.wikipedia.org/wiki/Reflection_(mathematics) )
var pixelNormal:Vector.<Number> = Controls.getTerrain().getNormal(int(thatX), int(thatY));
var d:Number = 2 * (velX * pixelNormal[0] + velY * pixelNormal[1]);
velX -= pixelNormal[0] * d;
velY -= pixelNormal[1] * d;
// apply bounce friction
velX *= bounceFriction;
velY *= bounceFriction;
// reset x and y so that the pixel starts at point of collision
x = thisX;
y = thisY;
}
}
}
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.utils.getTimer;
class Player implements IPhysicsObj, IRenderObj
{
private var x:Number;
private var y:Number;
private var velX:Number;
private var velY:Number;
private var goLeft:Boolean;
private var goRight:Boolean;
private var shooting:Boolean;
private var lastShot:Number;
private var topBlocked:Boolean;
private var playerWidth:int;
private var playerHeight:int;
public var onGround:Boolean;
public function Player(xx:int, yy:int)
{
x = xx;
y = yy;
velX = 0;
velY = 0; // set the initial velocity to 0
// initialize the player as a 15x15 px box
playerWidth = 15;
playerHeight = 15;
}
// Shooting toggles
public function shoot():void
{
shooting = true;
}
public function stopShooting():void
{
shooting = false;
}
// jump
public function jump():void
{
if (onGround && !topBlocked && velY > -500)
velY -= 500;
}
// moving toggles
public function moveLeft():void
{
goLeft = true;
}
public function moveRight():void
{
goRight = true;
}
public function stopLeft():void
{
goLeft = false;
}
public function stopRight():void
{
goRight = false;
}
/* INTERFACE IRenderObj */
private var r:Rectangle = new Rectangle();
public function draw(canvas:BitmapData):void
{
r.width = playerWidth;
r.height = playerHeight;
r.x = x - (playerWidth >> 1);
r.y = y - (playerWidth >> 1);
canvas.fillRect(r, 0x232323);
}
/* INTERFACE IPhysicsObj */
public function getX():Number {return x;}
public function getY():Number {return y;}
public function getVX():Number {return velX;}
public function getVY():Number {return velY;}
public function setX(value:Number):void {x = value;}
public function setY(value:Number):void {y = value;}
public function setVX(value:Number):void {velX = value;}
public function setVY(value:Number):void {velY = value;}
public function checkConstraints():void
{
// shooting
if (shooting) {
// Primary fire happens every 200 ms
var now:int = getTimer();
if (!(now - lastShot < 200))
{
// Create a vector between the player and the mouse, then normalize that vector (to change its length to 1)
// after multiplying by the desired bullet speed, we get how fast along each axis we want the bullet to be traveling
var diffX:Number = Controls.getMouseX() - x;
var diffY:Number = Controls.getMouseY() - y;
var len:Number = Math.sqrt(diffX * diffX + diffY * diffY);
var bullet:Bullet = new Bullet(x, y, 1500 * diffX / len, 1500 * diffY / len);
Controls.getPhysics().add(bullet);
Controls.getRenderer().add(bullet);
lastShot = now;
}
}
// movement
if (goLeft)
{
if (velX > -500)
velX -= 40;
} else if (velX < 0) {
velX *= 0.8; // slow down side-ways velocity if we're not moving left
}
if (goRight) {
if (velX < 500)
velX += 40;
} else if (velX > 0) {
velX *= 0.8;
}
// Collision detection/handling
// Loop along each edge of the square until we find a solid pixel
// if there is one, we find out if there's any adjacent to it (loop perpendicular from that pixel into the box)
// Once we hit empty space, we move the box to that empty space
onGround = false;
for (var bottomX:int = int(x - (playerWidth >> 1)); bottomX <= int(x + (playerWidth >> 1)); bottomX++)
{
if (Controls.getTerrain().isPixelSolid(bottomX, int(y + (playerHeight >> 1) + 1)) && (velY > 0))
{
onGround = true;
for (var yCheck:int = int(y + playerHeight / 4); yCheck < int(y + (playerHeight >> 1)); yCheck++)
{
if (Controls.getTerrain().isPixelSolid(bottomX, yCheck))
{
y = yCheck - (playerHeight >> 1);
break;
}
}
if (velY > 0)
{
velY *= -0.5; // bounce up
}
}
}
topBlocked = false;
// start with the top edge
for (var topX:int = int(x - (playerWidth >> 1)); topX <= int(x + (playerWidth >> 1)); topX++)
{
if (Controls.getTerrain().isPixelSolid(topX, int(y - (playerHeight >> 1) - 1)))
{ // if the pixel is solid
topBlocked = true;
if (velY < 0)
{
velY *= -0.5;
}
}
}
var xCheck:int;
// loop left edge
if (velX < 0)
{
for (var leftY:int = int(y - (playerHeight >> 1)); leftY <= int(y + (playerHeight >> 1)); leftY++)
{
if (Controls.getTerrain().isPixelSolid(int(x - (playerWidth >> 1)), leftY))
{
// next move from the edge to the right, inside the box (stop it at 1/4th the player width)
for (xCheck = int(x - playerWidth / 4); xCheck < int(x - (playerWidth >> 1)); xCheck--)
{
if (Controls.getTerrain().isPixelSolid(xCheck, leftY))
{
x = xCheck + (playerWidth >> 1); // push the block over
break;
}
}
if (leftY > y && !topBlocked)
{
y -= 1;
} else {
velX *= -0.4;
x += 2;
}
}
}
}
// do the same for the right edge
if (velX > 0)
{
for (var rightY:int = int(y - (playerHeight >> 1)); rightY <= int(y + (playerHeight >> 1)); rightY++)
{
if (Controls.getTerrain().isPixelSolid(int(x + (playerWidth >> 1)), rightY))
{
for (xCheck = int(x + playerWidth / 4); xCheck < int(x + (playerWidth >> 1) + 1); xCheck++)
{
if (Controls.getTerrain().isPixelSolid(xCheck, rightY))
{
x = xCheck - (playerWidth >> 1);
break;
}
}
if (rightY > y && !topBlocked)
{
y -= 1;
} else {
velX *= -0.4;
x -= 2;
}
}
}
}
// Boundary Checks
if (x < 0 && velX < 0)
{
x -= x;
velX *= -1;
}
if (y < 0 && velY < 0)
{
y -= y;
velY *= -1;
}
if (x > Controls.getTerrain().width() && velX > 0)
{
x += Controls.getTerrain().width() - x;
velX *= -1;
}
if (y + (playerHeight >>1) > Controls.getTerrain().height() && velY > 0)
{
y += Controls.getTerrain().height() - y - (playerHeight >> 1);
velY = 0;
onGround = true;
}
}
}
import flash.display.BitmapData;
class Renderer
{
private var objects:Vector.<IRenderObj>;
public function Renderer()
{
objects = new Vector.<IRenderObj>();
}
public function draw(canvas:BitmapData):void
{
for (var i:int = 0; i < objects.length; i++)
{
var obj:IRenderObj = objects[i];
obj.draw(canvas);
}
}
public function add(obj:IRenderObj):void
{
objects[objects.length] = obj;
}
public function remove(obj:IRenderObj):void
{
var idx:int = objects.indexOf(obj);
if (idx < 0) return;
objects.splice(idx, 1);
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
class Terrain
{
private var img:BitmapData;
public var destructionRes:int;
private var pixels:Vector.<uint>;
public function Terrain(bmp:Bitmap, dr:int)
{
img = new BitmapData(bmp.width, bmp.height, true, 0x00000000);
img.draw(bmp);
pixels = img.getVector(img.rect);
bmp.bitmapData.dispose();
bmp = null;
destructionRes = dr;
}
public function width():int
{
return img.width;
}
public function height():int
{
return img.height;
}
public function draw(c:BitmapData):void
{
c.draw(img);
}
public function update():void
{
img.lock();
img.setVector(img.rect, pixels);
img.unlock();
}
// Determine if a pixel is solid based on whether or not it's transparent
public function isPixelSolid(x:int, y:int):Boolean
{
if (x > 0 && x < img.width && y > 0 && y < img.height)
{
return (pixels[x + y * img.width] >> 24) != 0x0;
}
return false;
}
// Make a pixel non-solid
public function removePixel(x:int, y:int):void
{
if (x > 0 && x < img.width && y > 0 && y < img.height)
pixels[x + y * img.width] = 0x00000000;
}
// Get a pixel's color
public function getColor(x:int, y:int):uint
{
if (x > 0 && x < img.width && y > 0 && y < img.height)
return pixels[x + y * img.width];
return 0;
}
// Find a normal at a position
public function getNormal(x:int, y:int):Vector.<Number>
{
// First find all nearby solid pixels, and create a vector to the average solid pixel from (x,y)
var avgX:Number = 0;
var avgY:Number = 0;
for (var w:int = -3; w <= 3; w++)
{
for (var h:int = -3; h <= 3; h++)
{
if (isPixelSolid(x + w, y + h))
{
avgX -= w;
avgY -= h;
}
}
}
var len:Number = Math.sqrt(avgX * avgX + avgY * avgY); // get the distance from (x,y)
return new <Number>[ avgX / len, avgY / len ]; // normalize the vector by dividing by that distance
}
}