forked from: BallBlast
BallBlast.as
- Blast while balls.
- When the red ball is blasted or the ball reaches the celling,
the game is over.
<Control>
Mouse: Move the red ring.
Click: Blast balls in the red ring.
// forked from ABA's BallBlast
// BallBlast.as
// - Blast while balls.
// - When the red ball is blasted or the ball reaches the celling,
// the game is over.
// <Control>
// Mouse: Move the red ring.
// Click: Blast balls in the red ring.
package
{
import flash.display.Sprite;
import flash.events.Event;
[SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")]
public class BallBlast extends Sprite
{
public function BallBlast()
{
main = this;
initialize();
addEventListener(Event.ENTER_FRAME, update);
}
}
}
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.events.Event;
import flash.events.MouseEvent;
const SCREEN_WIDTH:int = 465;
const SCREEN_HEIGHT:int = 465;
var main:Sprite;
var buffer:BitmapData = new BitmapData(SCREEN_WIDTH, SCREEN_HEIGHT, false, 0);
var isMouseClicked:Boolean;
var mousePos:Vector3D = new Vector3D;
var rect:Rectangle = new Rectangle;
var offset:Vector3D = new Vector3D;
var ticks:int;
var scoreField:TextField = new TextField;
var score:int;
const GAME_OVER_DURATION:int = 150;
const NO_START_DURATION:int = 30;
var gameOverTicks:int;
var lastBall:Ball;
var messageField:TextField = new TextField;
function initialize():void
{
main.addChild(new Bitmap(buffer));
main.stage.addEventListener(MouseEvent.CLICK, function(e:Event):void { isMouseClicked = true; } );
Field.initialize();
initializeBlurs();
scoreField = createTextField(SCREEN_WIDTH - 100, 0, 100, 24, 0xff6666, TextFormatAlign.RIGHT);
main.addChild(scoreField);
messageField = createTextField(SCREEN_WIDTH - 256, 0, 256, 36, 0xff6666);
main.addChild(messageField);
startTitle();
}
function update(event:Event):void
{
mousePos.x = main.stage.mouseX - SCREEN_WIDTH / 2;
mousePos.y = main.stage.mouseY - SCREEN_HEIGHT / 2;
if (isMouseClicked)
{
isMouseClicked = false;
if (gameOverTicks < 0) blastBalls(mousePos);
else if (gameOverTicks > NO_START_DURATION) startGame();
}
buffer.lock();
buffer.fillRect(buffer.rect, 0);
updateBlurs();
updateSparks();
var b:Ball;
for each (b in balls) b.update();
for each (b in balls) if (!b.isRed) b.draw();
for each (b in balls) if (b.isRed) b.draw();
if (gameOverTicks < 0) drawBlastCircle();
buffer.unlock();
if (gameOverTicks < 0 || gameOverTicks > GAME_OVER_DURATION)
{
if (ticks % 5 == 0) balls.push(new Ball(false));
if (ticks % 256 == 50) balls.push(new Ball(true));
}
ticks++;
if (gameOverTicks >= 0)
{
if (gameOverTicks == GAME_OVER_DURATION) startTitle();
gameOverTicks++;
if (lastBall != null) lastBall.draw();
}
}
function drawBlastCircle():void
{
offset.x = 0; offset.y = BALL_BLAST_RADIUS;
rotation(offset, ticks * 0.05);
for (var i:int = 0; i < 16; i++)
{
drawBox(mousePos.x + offset.x, mousePos.y + offset.y, 7, 0xee0000,
13, 200, 100, 100);
rotation(offset, Math.PI * 2 / 16);
}
}
function startTitle():void
{
ticks = 0;
lastBall = null;
gameOverTicks = GAME_OVER_DURATION;
messageField.y = SCREEN_HEIGHT / 3;
messageField.text = "BallBlast";
}
function startGame():void
{
blastAllBalls();
lastBall = null;
score = 0;
scoreField.text = String(score);
ticks = 0;
gameOverTicks = -1;
messageField.text = "";
}
function startGameOver():void
{
blastAllBalls();
if (gameOverTicks >= 0) return;
gameOverTicks = 0;
messageField.y = SCREEN_HEIGHT / 2;
messageField.text = "GAME OVER";
}
// Balls.
const BALL_BLAST_RADIUS:Number = 64.0;
var balls:Vector.<Ball> = new Vector.<Ball>;
class Ball
{
private const GRAVITY_FORCE:Number = 0.5;
private const MAX_VELOCITY_CHANGE:Number = 0.5;
public var pos:Vector3D = new Vector3D;
public var vel:Vector3D = new Vector3D;
public var radius:Number;
public var angle:Number, angleVel:Number;
public var isInAppearance:Boolean;
public var isRed:Boolean;
public function Ball(isRed:Boolean):void
{
if (!isRed)
{
radius = 20.0 + Math.random() * 20.0;
pos.x = (Math.random() * 2 - 1) * (Field.size.x - radius * 1.1);
pos.y = Field.size.y + radius * 1.1;
isInAppearance = true;
}
else
{
radius = 20.0;
pos.x = 0; pos.y = -Field.size.y * 0.8;
isInAppearance = false;
}
vel.x = vel.y = 0;
angle = Math.random() * Math.PI * 2;
angleVel = (Math.random() * 2 - 1) * 0.1;
this.isRed = isRed;
}
public function update():void
{
if (isInAppearance)
{
vel.y -= GRAVITY_FORCE * 2;
if (pos.y < Field.size.y - radius) isInAppearance = false;
}
else
{
if (pos.y > Field.size.y - radius) hit(pos.y - (Field.size.y - radius), 0);
vel.y += GRAVITY_FORCE;
}
vel.scaleBy(0.95);
pos.incrementBy(vel);
if (pos.y < -Field.size.y + radius)
{
if (gameOverTicks < 0) lastBall = this;
startGameOver();
return;
}
if (pos.x > Field.size.x - radius) hit(pos.x - (Field.size.x - radius), Math.PI / 2);
if (pos.x < -Field.size.x + radius) hit((-Field.size.y + radius) - pos.x, Math.PI / 2 * 3);
for each (var b:Ball in balls)
{
if (b == this) continue;
var d:Number = radius + b.radius - Vector3D.distance(pos, b.pos);
if (d > 0) hit(d, Math.atan2(b.pos.x - pos.x, b.pos.y - pos.y));
}
angle += angleVel; angleVel *= 0.95;
}
public function draw():void
{
offset.x = 0; offset.y = radius / 2;
rotation(offset, angle);
var color:int , r:int ,g:int, b:int;
if (!isRed)
{
color = 0xeeeeee; r = 200; g = 200; b = 200;
}
else
{
color = 0xff0000; r = 250; g = 200; b = 200;
}
for (var i:int = 0; i < 3; i++)
{
drawBox(pos.x + offset.x, pos.y + offset.y, radius, color, radius, r, g, b);
rotation(offset, Math.PI * 2 / 3);
}
}
private function hit(d:Number, a:Number):void
{
if (isInAppearance) return;
var sv:Number = Math.sin(a) * d, cv:Number = Math.cos(a) * d;
pos.x -= sv / 2; pos.y -= cv / 2;
angleVel += (-sv * 0.1 - angleVel) * 0.1;
sv *= 0.1 * vel.x; cv *= 0.1 * vel.y;
if (sv > MAX_VELOCITY_CHANGE) sv = MAX_VELOCITY_CHANGE;
if (sv < -MAX_VELOCITY_CHANGE) sv = -MAX_VELOCITY_CHANGE;
if (cv > MAX_VELOCITY_CHANGE) cv = MAX_VELOCITY_CHANGE;
if (cv < -MAX_VELOCITY_CHANGE) cv = -MAX_VELOCITY_CHANGE;
vel.x -= sv; vel.y -= cv;
}
}
function blastBalls(p:Vector3D):void
{
var bc:int = 0;
var b:Ball;
for each (b in balls)
{
if (Vector3D.distance(b.pos, p) < BALL_BLAST_RADIUS + b.radius)
{
if (b.isRed)
{
lastBall = b;
startGameOver();
return;
}
bc++;
}
}
score += bc;
scoreField.text = String(score);
for (var i:int = 0; i < balls.length; i++)
{
b = balls[i];
if (Vector3D.distance(b.pos, p) < BALL_BLAST_RADIUS + b.radius)
{
addSparks(10, b.pos, 20.0, 20.0, 250, 250, 250);
balls.splice(i, 1); i--;
}
}
}
function blastAllBalls():void
{
for each (var b:Ball in balls) addSparks(5, b.pos, 20.0, 20.0, 250, 0, 0);
balls = new Vector.<Ball>;
}
// Sparks.
var sparks:Vector.<Spark> = new Vector.<Spark>;
class Spark
{
public var pos:Vector3D = new Vector3D;
public var vel:Vector3D = new Vector3D;
public var size:Number;
public var r:int, g:int, b:int;
public var ticks:int;
public function update():Boolean
{
pos.incrementBy(vel);
vel.y += 0.5;
vel.scaleBy(0.99);
size *= 0.95;
r *= 0.98; g *= 0.98; b *= 0.98;
if (pos.x < -Field.size.x && vel.x < 0 ||
pos.x > Field.size.x && vel.x > 0) vel.x *= -1;
if (pos.y < -Field.size.y && vel.y < 0 ||
pos.y > Field.size.y && vel.y > 0) vel.y *= -1;
var cr:Number = Math.random();
drawBox(pos.x, pos.y, size, r * 0x10000 + g * 0x100 + b, size, r * cr, g * cr, b * cr);
ticks--;
return (ticks >= 0);
}
}
function addSparks(count:int, p:Vector3D, speed:Number, size:Number, r:int, g:int ,b:int):void
{
for (var i:int = 0; i < count; i++)
{
var s:Spark = new Spark;
s.pos.x = p.x; s.pos.y = p.y;
var a:Number = Math.random() * Math.PI * 2;
var sp:Number = speed * (0.5 + Math.random());
s.vel.x = Math.sin(a) * sp; s.vel.y = Math.cos(a) * sp;
s.size = size;
s.r = r; s.g = g; s.b = b;
s.ticks = 15 + 15 * Math.random();
sparks.push(s);
}
}
function updateSparks():void
{
for (var i:int = 0; i < sparks.length; i++)
{
if (!sparks[i].update())
{
sparks.splice(i, 1); i--;
}
}
}
// Game field.
class Field
{
public static var size:Vector3D = new Vector3D;
public static function initialize():void
{
size.x = SCREEN_WIDTH * 0.9 / 2; size.y = SCREEN_HEIGHT * 0.9 / 2;
}
public static function contains(p:Vector3D):Boolean
{
return (p.x >= -size.x && p.x <= size.x && p.y >= -size.y && p.y <= size.y);
}
}
// Blur effect.
const BLUR_MAX_COUNT:int = 512;
const BLUR_HISTORY_COUNT:int = 6;
var blurs:Vector.<Vector.<Blur>> = new Vector.<Vector.<Blur>>(BLUR_HISTORY_COUNT, true);
var blurCounts:Vector.<int> = new Vector.<int>(BLUR_HISTORY_COUNT, true);
var blurIndex:int;
class Blur
{
public var pos:Vector3D = new Vector3D;
public var width:Number, height:Number;
public var r:int, g:int, b:int;
public function update():void
{
rect.x = pos.x - width / 2;
rect.y = pos.y - height / 2;
rect.width = width; rect.height = height;
buffer.fillRect(rect, r * 0x10000 + g * 0x100 + b);
width *= 1.2; height *= 1.2;
var a:int = (r + g + b) / 3;
r += (a - r) * 0.25;
g += (a - g) * 0.25;
b += (a - b) * 0.25;
r *= 0.65; g *= 0.65; b *= 0.65;
}
}
function drawBox(x:Number, y:Number, size:int, color:int,
bsize:int, br:int, bg:int, bb:int):void
{
rect.x = x - size / 2 + SCREEN_WIDTH / 2;
rect.y = y - size / 2 + SCREEN_HEIGHT / 2;
rect.width = rect.height = size;
buffer.fillRect(rect, color);
addBlur(x, y, bsize, bsize, br, bg, bb);
}
function addBlur(x:Number, y:Number, w:Number, h:Number,
r:int, g:int, b:int):void
{
if (blurCounts[blurIndex] >= BLUR_MAX_COUNT) return;
var bl:Blur = blurs[blurIndex][blurCounts[blurIndex]];
bl.pos.x = x + SCREEN_WIDTH / 2; bl.pos.y = y + SCREEN_HEIGHT / 2;
bl.width = w; bl.height = h;
bl.r = r; bl.g = g; bl.b = b;
blurCounts[blurIndex]++;
}
function updateBlurs():void
{
var bi:int = blurIndex + 1;
for (var i:int = 0; i < BLUR_HISTORY_COUNT; i++)
{
if (bi >= BLUR_HISTORY_COUNT) bi = 0;
for (var j:int = 0; j < blurCounts[bi]; j++) blurs[bi][j].update();
bi++;
}
blurIndex++;
if (blurIndex >= BLUR_HISTORY_COUNT) blurIndex = 0;
blurCounts[blurIndex] = 0;
}
function initializeBlurs():void
{
for (var i:int = 0; i < BLUR_HISTORY_COUNT; i++)
{
var bs:Vector.<Blur> = new Vector.<Blur>(BLUR_MAX_COUNT, true);
for (var j:int = 0; j < BLUR_MAX_COUNT; j++)
{
bs[j] = new Blur;
}
blurs[i] = bs;
blurCounts[i] = 0;
}
blurIndex = 0;
}
// Utility functions.
function rotation(v:Vector3D, a:Number):void
{
var sv:Number = Math.sin(a);
var cv:Number = Math.cos(a);
var rx:Number = cv * v.x - sv * v.y;
v.y = sv * v.x + cv * v.y;
v.x = rx;
}
function normalizeAngle(v:Number):Number
{
if (v > Math.PI) return v - Math.PI * 2;
else if (v < -Math.PI) return v + Math.PI * 2;
else return v;
}
function createTextField(x:int, y:int, width:int, size:int, color:int,
align:String = TextFormatAlign.LEFT):TextField
{
var fm:TextFormat = new TextFormat;
fm.font = "_typewriter"; fm.bold = true;
fm.size = size; fm.color = color;
fm.align = align;
var fi:TextField = new TextField;
fi.defaultTextFormat = fm;
fi.x = x; fi.y = y; fi.width = width;
fi.selectable = false;
return fi;
}