forked from: Saqoosha challenge for professionals(ぱらぱら)
砂粒の落下
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/hOWJ
*/
// forked from checkmate's Saqoosha challenge for professionals
// 砂粒の落下
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
public class Main1 extends Sprite {
// 定数
private const NUM_OF_FALL:uint = 10; // 同時に落ちる粒子の数
private const GRAVITY:Number = 0.98; // 重力
private const BOUNCE:Number = -0.38; // 跳ね返り
private const BEND:Number = 4.5; // 跳ね返るときに曲がる率
private const FRICTION:Number = 0.98; // 空気抵抗
private const FLOOR_FRICTION:Number = 0.9; // 摩擦抵抗
private const LOCAL_FRICTION_BOOLEAN:Boolean = false;
private const BOTTOM:uint = stage.stageHeight - 1;
private const LETTER:String = "ぱらぱら"; // 表示文字列
// パーティクルの色の設定
private function setParticleColor():uint {
var c:uint = Math.random() * 0x99 + 0x66;
return rgbToHex(c, c, c);
}
// 変数
private var particleArray:Array; // パーティクル格納配列(繰り返し使うために待避)
private var copyParticleArray:Array; // パーティクル格納配列(実際に使用する)
private var canvasBitmapData:BitmapData; // 描画 BitmapData
private var pixelizer:Pixelizer; // ピクセル化クラス
// コンストラクタ
public function Main1() {
// Particle クラスの初期化
Particle.gravity = GRAVITY;
Particle.bounce = BOUNCE
Particle.bend = BEND;
Particle.friction = FRICTION;
Particle.floorFriction = FLOOR_FRICTION;
Particle.localFrictionBoolrean = LOCAL_FRICTION_BOOLEAN;
Particle.top = 0;
Particle.bottom = BOTTOM;
Particle.left = 0;
Particle.right = stage.stageWidth;
// 描画 BitmapData 生成
canvasBitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
addChild(new Bitmap(canvasBitmapData));
// 案内表示
var signField:TextField = new TextField();
signField.text = "Stage を Click すると motion を再演します。";
signField.autoSize = TextFieldAutoSize.LEFT;
signField.selectable = false;
signField.blendMode = BlendMode.INVERT;
addChild(signField);
// テキストピクセライズ
var pTextField:PixelizerTextField = new PixelizerTextField();
pTextField.text = LETTER;
var textFormat:TextFormat = new TextFormat(null, 120);
textFormat.letterSpacing = -5;
pTextField.setTextFormat(textFormat);
pixelizer = new Pixelizer();
pixelizer.addEventListener(Event.COMPLETE, scanCompleteHandler);
pixelizer.scan(pTextField.bitmapData);
}
// ピクセライズ完了後の処理
// (ピクセル格納配列の生成)
private function scanCompleteHandler(event:Event):void {
// イテレータ生成
var iter:PixelizerIterator = pixelizer.iterator;
pixelizer = null;
// 配置オフセット計算
var offsetX:Number = (stage.stageWidth - iter.width) / 2;
var offsetY:Number = 40;// (stage.stageHeight - iter.height) / 2;
// イテレーション
particleArray = [];
while (iter.hasNext()) {
var color:uint = iter.next();
var alpha:uint = getAlpha(color);
if (alpha > 0x7F) {
particleArray.push(new Particle(iter.x + offsetX, iter.y + offsetY, setParticleColor()));
}
}
// イベント登録
stage.addEventListener(MouseEvent.CLICK, clickHandler);
motion();
}
// 実処理(モーションタイポ)
private function motion():void {
// 待避配列から使用配列を生成
copyParticleArray = [];
for each (var original:Particle in particleArray) {
copyParticleArray.push(original.clone());
}
// Particle 格納配列をシャッフル
shuffle(copyParticleArray);
// 各 particle に動作開始時限値
var n:uint = copyParticleArray.length;
for (var i:int = 0; i < n; i++) {
copyParticleArray[i].counter = i / NUM_OF_FALL;
}
// イベント登録
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
// フレームイベント
private function enterFrameHandler(event:Event):void {
canvasBitmapData.lock();
canvasBitmapData.fillRect(canvasBitmapData.rect, 0x000000);
for each (var particle:Particle in copyParticleArray) {
particle.update();
canvasBitmapData.setPixel(particle.x, particle.y, particle.color);
}
canvasBitmapData.unlock();
}
// マウスイベント
private function clickHandler(event:MouseEvent):void {
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
motion();
}
// シャッフル
private function shuffle(a:Array):void {
var n:int;
var t:*;
var l:uint = a.length;
while (l--) {
n = Math.floor(Math.random() * (l+1));
t = a[l];
a[l] = a[n];
a[n] = t;
}
}
// RGB → 0xNNNNNN
private function rgbToHex(r:uint, g:uint, b:uint):uint {
r = adjust(r);
g = adjust(g);
b = adjust(b);
return r << 16 | g << 8 | b;
}
// 255 を超えていた場合は 255 に切り捨てる
private function adjust(val:uint):uint {
return Math.min(val, 0xFF);
}
// 32 bit color のアルファ値を取得
private function getAlpha(color:uint):uint {
return (color >> 24) & 0xFF;
}
}
}
// パーティクルクラス
class Particle {
// static 変数
// 物理変数
public static var gravity:Number = 0.98; // 重力
public static var bounce:Number = -0.45; // 跳ね返り
public static var bend:Number = 4.5; // 跳ね返るときに曲がる率
public static var friction:Number = 0.98; // 空気抵抗
public static var floorFriction:Number = 0.9; // 摩擦抵抗
public static var localFrictionBoolrean:Boolean = false;
// ステージ領域
public static var top:uint;
public static var bottom:uint;
public static var left:uint;
public static var right:uint;
// 現在座標
public function get x():Number { return _x; }
private var _x:Number;
public function get y():Number { return _y; }
private var _y:Number;
// 色
public function get color():uint { return _color; }
private var _color:uint;
// 動作開始時限値
public function set counter(value:uint):void {
_counter = value;
}
private var _counter:uint = 0;
// ローカルな物理変数
private var localBounce:Number; // 跳ね返り
private var localBend:Number; // 跳ね返るときに曲がる率
private var localFriction:Number; // 空気抵抗
// 速度
private var vx:Number = 0;
private var vy:Number = 0;
public function Particle(x:Number, y:Number, color:uint) {
_x = x;
_y = y;
_color = color;
// bounce、bend、friction のローカル補正
localBounce = bounce + (Math.random() * 2 - 1) / 4;
localBend = (Math.random() < 0.5) ? bend : -bend;
localBend += (Math.random() * 2 - 1);
localFriction = (localFrictionBoolrean) ? friction - Math.random() : friction - Math.random() / 20;
}
public function clone():Particle {
return new Particle(_x, _y, _color);
}
public function update():void {
if (_counter > 0) {
_counter--;
} else {
// 壁処理
if (_x > right) {
_x = right;
vx *= localBounce;
vx *= floorFriction;
localBend *= floorFriction;
vy = localBend;
} else if (_x < left) {
_x = left;
vx *= localBounce;
vx *= floorFriction;
localBend *= floorFriction;
vy = localBend;
}
if (_y > bottom) {
_y = bottom;
vy *= localBounce;
vy *= floorFriction;
localBend *= floorFriction;
vx = localBend;
} else if (_y < top) {
_y = top;
vy *= localBounce;
vy *= floorFriction;
localBend *= floorFriction;
vx = localBend;
}
// 座標更新
vx *= localFriction;
vy *= localFriction;
vy += gravity;
_x += vx;
_y += vy;
}
}
}
// ピクセル化クラス
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
class Pixelizer extends EventDispatcher {
private var width:uint = 0;
private var height:uint = 0;
private var data:Vector.<uint>;
// イテレータ
public function get iterator():PixelizerIterator {
return new PixelizerIterator(width, height, data);
}
// コンストラクタ
public function Pixelizer() {}
// スキャン
public function scan(bmd:BitmapData):void {
width = bmd.width;
height = bmd.height;
data = bmd.getVector(bmd.rect);
// イベント発行
dispatchEvent(new Event(Event.COMPLETE));
}
}
// ピクセル化したデータのイテレータ
class PixelizerIterator {
// モードフラグ
public static const NEXT:String = "next"; // 正順
public static const PREV:String = "prev"; // 逆順
// next()、nextCol() で取得したデータのX座標
public function get x():int { return positionX; }
private var positionX:int = 0;
// next()、nextRow() で取得したデータのY座標
public function get y():int { return positionY; }
private var positionY:int = 0;
// スキャンサイズ(幅)
public function get width():uint { return _width; }
private var _width:uint;
// スキャンサイズ(高)
public function get height():uint { return _height; }
private var _height:uint;
// データ格納 Vector
private var data:Vector.<uint>;
// 現在のイテレートモード
private var mode:String = "next";
// イテレーションカウンター
private var position:int = 0;
public function PixelizerIterator(width:uint, height:uint, data:Vector.<uint>) {
_width = width;
_height = height;
this.data = data;
}
// データの操作(1個ずつデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNext():Boolean {
return (mode == "prev") ? _hasPrev() : _hasNext();
}
// 外部から呼び出せる next
public function next():uint {
return (mode == "prev") ? _prev() : _next();
}
// データの同一列(横)の操作(同一列の全てのデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNextRow():Boolean {
return (mode == "prev") ? _hasPrevRow() : _hasNextRow();
}
// 外部から呼び出せる next
public function nextRow():Vector.<uint> {
return (mode == "prev") ? _prevRow() : _nextRow();
}
// データの同一行(縦)の操作(同一行のすべてのデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNextCol():Boolean {
return (mode == "next") ? _hasNextCol() : _hasPrevCol();
}
// 外部から呼び出せる next
public function nextCol():Vector.<uint> {
return (mode == "next") ? _nextCol() : _prevCol();
}
// リセット
public function reset(mode:String = "next"):void {
this.mode = mode;
var offset:uint = (mode == "next") ? 0 : 1;
position = (data.length - 1) * offset;
positionX = (_width - 1) * offset;
positionY = (_height - 1) * offset;
}
// データの操作(1個ずつデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNext():Boolean {
return position < data.length;
}
// mode = "prev" 時の hasNext
private function _hasPrev():Boolean {
return position > 0;
}
// mode = "next" 時の next
private function _next():uint {
positionX = position % _width;
positionY = position / _width;
return getData(position++);
}
// mode = "prev" 時の next
private function _prev():uint {
positionX = position % _width;
positionY = position / _width;
return getData(position--);
}
private function getData(idx:uint):uint {
if (idx >= data.length) {
throw new Error("getData#配列範囲外:" + idx + " length:" + data.length);
}
return data[idx];
}
// データの同一列(横)の操作(同一列の全てのデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNextRow():Boolean {
return positionY < _height;
}
// mode = "prev" 時の hasNext
private function _hasPrevRow():Boolean {
return positionY > -1;
}
// mode = "next" 時の next
private function _nextRow():Vector.<uint> {
return getRow(positionY++);
}
// mode = "prev" 時の next
private function _prevRow():Vector.<uint> {
return getRow(positionY--);
}
// next の実体
private function getRow(val:uint):Vector.<uint> {
if (val >= _height) {
throw new Error("getRow#配列範囲外:" + val + " _height:" + _height);
}
return data.slice(val * _width, (val + 1) * _width);
}
// データの同一行(縦)の操作(同一行のすべてのデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNextCol():Boolean {
return positionX < _width;
}
// mode = "prev" 時の hasNext
private function _hasPrevCol():Boolean {
return positionX > -1;
}
// mode = "next" 時の next
private function _nextCol():Vector.<uint> {
return getCol(positionX++);
}
// mode = "prev" 時の next
private function _prevCol():Vector.<uint> {
return getCol(positionX--);
}
// next の実体
private function getCol(idx:uint):Vector.<uint> {
if (idx >= _width) {
throw new Error("getCol#配列範囲外:" + idx + " _width:" + _width);
}
var vector:Vector.<uint> = new Vector.<uint>(_height, true);
for (var i:uint = 0; i < _height; i++) {
vector[i] = data[_width * i + idx];
}
return vector;
}
}
// 文字列をピクセル化クラスへ投げ込むためのテキストフィールド
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
class PixelizerTextField extends TextField {
// 透明
private const TRANSPARENT:uint = 0x00000000;
public function PixelizerTextField() {
autoSize = TextFieldAutoSize.LEFT;
}
public function get bitmapData():BitmapData {
// 普通に TextField を BitmapData に draw
var bmd1:BitmapData = new BitmapData(textWidth, textHeight, true, TRANSPARENT);
bmd1.draw(this, new Matrix(1, 0, 0, 1, -2, -2));
// 上記 BitmapData のうち、文字である範囲を Rectangle として取得
var rect:Rectangle = bmd1.getColorBoundsRect(0xFF000000, TRANSPARENT, false);
// 上記 Rectangle 部分のみの BitmapData を生成
var bmd2:BitmapData = new BitmapData(rect.width, rect.height, true, TRANSPARENT);
bmd2.draw(bmd1, new Matrix(1, 0, 0, 1, -rect.x, -rect.y));
bmd1.dispose();
return bmd2;
}
}