pixel perfect collisions
click to create a hole
Terrain generation, pixel perfect collisions and physics
UPDATE:
Fast PPC with lots of objects!!
http://fav.me/d63gqlv
/**
* Copyright Jacky.Riawan ( http://wonderfl.net/user/Jacky.Riawan )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/9WHW
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
/**
* ...
* @author Jacky Riawan
*/
public class Main extends Sprite
{
public var collisionBitmap:BitmapData;
private const objects:Vector.<physicObject> = new Vector.<physicObject>();
private var display:Sprite = new Sprite();
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);
addEventListener(Event.ENTER_FRAME, update);
stage.addEventListener(MouseEvent.CLICK, clickHandler);
generateGroundBitmap();
addChild(display);
for (var i:int = 0; i <200;i++){
addObject(stage.stageWidth*Math.random(),stage.stageHeight*Math.random()*.1);
}
}
private function clickHandler(e:MouseEvent):void
{
makeHole(mouseX, mouseY, 30);
}
private function addObject(posx:Number, posy:Number):void {
objects.push(new physicObject(posx, posy,this));
}
private function update(e:Event):void
{
updateForces();
}
private function updateForces():void
{
display.graphics.clear();
var le:int = objects.length;
for (var i:int = 0; i < le; i++) {
objects[i].updateForce();
objects[i].updatePosition();
display.graphics.beginFill(0xFF0000);
display.graphics.drawCircle(objects[i]._x, objects[i]._y, 2);
display.graphics.endFill();
}
}
private function makeHole(x0:int, y0:int, radius:int):void {
if (radius == 0) {
collisionBitmap.setPixel(x0, y0, 0xFFFFFF);
}else{
var error:int = 0;
var x:int = radius;
var y:int = 0;
var xChange:int = 1 - (radius << 1);
var yChange:int = 0;
while (x >= y) {
for (var i:int = x0 - x; i <= x0 + x; i++) {
collisionBitmap.setPixel(i, y0 + y,0);
collisionBitmap.setPixel(i, y0 - y, 0);
}
for (i = x0 - y; i <= x0 + y; i++) {
collisionBitmap.setPixel(i, y0 + x,0);
collisionBitmap.setPixel(i, y0 - x, 0);
}
y++;
error += yChange;
yChange += 2;
if (((error << 1) + xChange) > 0) {
x--;
error += xChange;
xChange += 2;
}
}
}
}
private function generateGroundBitmap():void
{
collisionBitmap = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0);
var currentPoint:Number = collisionBitmap.height * .4;
var topLimit:int = collisionBitmap.height * .35;
var bottomLimit:int = collisionBitmap.height*.9;
var timer:int = 0;
var smoothing:Number = 30;
var range:Number = 8;
var speedClimbTarget:Number = (Math.random() - .5) * range;
var speedClimb:Number = speedClimbTarget
for (var _x:int = 0; _x < collisionBitmap.width; _x++) {
var depth:Number = collisionBitmap.height - currentPoint;
for (var _y:int = currentPoint; _y < collisionBitmap.height; _y++) {
var color:uint;
var depth_percent:Number = (_y-currentPoint) / depth;
if (depth_percent < .005) {
color = 0x80FF00
}else if (depth_percent < .01) {
color = 0x00FF00;
}else if (depth_percent < .03) {
color = 0x008400;
}else if (depth_percent < .04) {
color = 0x804000;
}else if (depth_percent < .1) {
color = 0x623100;
}else if (depth_percent < .15) {
color = 0x824100;
}else if (depth_percent < .35) {
color = 0x4A2500;
}else if (depth_percent < .36) {
color = 0xCEFFC1;
}else if (depth_percent < .57) {
color = 0x000000;
}else if (depth_percent < .59) {
color = 0x80FFFF;
}else if (depth_percent < .62) {
color = 0xC0C0C0;
}else if (depth_percent < .68) {
color = 0x707070;
}else if (depth_percent < .75) {
color = 0x3B3B3B;
}else if (depth_percent < .76) {
color=0xFFFF00
}else if (depth_percent < .85) {
color = 0xFF0000;
}else {
color=0x1D1D1D
}
collisionBitmap.setPixel(_x, _y, color);
}
currentPoint += speedClimb;
speedClimb += (speedClimbTarget - speedClimb) / smoothing;
if (currentPoint <= topLimit) {
currentPoint = topLimit;
speedClimbTarget = Math.abs(speedClimbTarget);
speedClimb = Math.abs(speedClimb);
}else if (currentPoint >= bottomLimit) {
currentPoint = bottomLimit;
speedClimbTarget = -Math.abs(speedClimbTarget);
speedClimb = -Math.abs(speedClimb);
}
if (timer == 0) {
speedClimbTarget = (Math.random() - .5) * range;
timer = 3 + Math.random() * 5;
}else {
timer--;
}
}
addChild(new Bitmap(collisionBitmap));
}
}
}
class physicObject {
public static const GRAV:Number =.5;
private var px:int;
private var py:int;
private var vx:Number=(Math.random()-.5)*5;
private var vy:Number=10;
private var main:Main;
public var _x:int;
public var _y:int;
private var applyGravTimer:int = 0;
public function physicObject(_x:Number,_y:Number,main:Main)
{
this.main = main;
this._y = py=_y;
this._x = px=_x;
}
public function updateForce():void {
var nx:Number = _x + vx;
var ny:Number = _y + vy;
px = _x;
py = _y;
_x = nx;
_y = ny;
}
public function updatePosition():void {
var dist:int = Math.max(Math.abs(vx), Math.abs(vy));
var stepDone:int = 1+Math.min(dist,4);
var divx:Number = vx / stepDone;
var divy:Number = vy / stepDone;
for (var i:int = 1; i < stepDone; i++) {
var checkX:int = px + i * divx;
var checkY:int = py + i * divy;
var col:uint = main.collisionBitmap.getPixel(checkX,checkY);
if (col !== 0x000000) {
// if (main.collisionBitmap.getPixel(checkX, checkY) !== 0x000000) {
var force:int = Math.sqrt(Math.pow(vx, 2) + Math.pow(vy, 2));
var forceX:int = 0;
var forceY:int = 0;
if (main.collisionBitmap.getPixel(checkX-2, checkY) !== 0x000000) {
forceX = 1;
}else if (main.collisionBitmap.getPixel(checkX + 2, checkY) !== 0x000000) {
forceX = -1;
}
if (main.collisionBitmap.getPixel(checkX, checkY-2) !== 0x000000) {
forceY = 1;
}else if (main.collisionBitmap.getPixel(checkX, checkY + 2) !== 0x000000) {
forceY = -1;
}
if (i == 1) {
force *= .9;
}
var angle:Number = Math.atan2(forceY, forceX);
vx = Math.cos(angle) * force * i / stepDone;
vy = Math.sin(angle) * force*i/stepDone;
_x = checkX;
_y = checkY;
break;
}
//}
}
vx *= .94;
vy *= .94;
vy += GRAV * i / stepDone;
}
}