ぷよんぷよんな俺様
ぷよんぷよんな俺様
「一応」モーションにあわせてぷよんぷよんと動くようにした「つもり」です。
ですが実装がプアすぎてガクガクです。
もっとモーションに合わせて滑らかにぷよんぷよんすれば面白いモノになりそうですが・・・
だれか実装してください!
元ネタ
ぷよんぷよんな栗様
http://wonderfl.net/code/b08396be6cc4efbf8a1b0c741bd316cb01a1ee29
動体検知
http://wonderfl.net/code/016a3cf5ba82c4386f2107b90ca92e2d0f4e7f0b
/**
* Copyright fumix ( http://wonderfl.net/user/fumix )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/kxbK
*/
/*
ぷよんぷよんな俺様
「一応」モーションにあわせてぷよんぷよんと動くようにした「つもり」です。
ですが実装がプアすぎてガクガクです。
もっとモーションに合わせて滑らかにぷよんぷよんすれば面白いモノになりそうですが・・・
だれか実装してください!
元ネタ
ぷよんぷよんな栗様
http://wonderfl.net/code/b08396be6cc4efbf8a1b0c741bd316cb01a1ee29
動体検知
http://wonderfl.net/code/016a3cf5ba82c4386f2107b90ca92e2d0f4e7f0b
*/
package
{
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.ConvolutionFilter;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Camera;
import flash.media.Video;
import flash.text.TextField;
//import net.hires.debug.Stats;
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "0x000000")]
public class motionPuyon extends Sprite
{
private var _container:Sprite;
private var _canvas:Sprite;
private var _bmdCurrent:BitmapData;
private var _stageW:Number = 465;
private var _stageH:Number = 465;
private var _w:Number;
private var _cP0:CirclePoint;
private var _cP1:CirclePoint;
private var _cP2:CirclePoint;
private var _cP3:CirclePoint;
private var _matrixArrayCurrent:Array;
private var _pointsArray:Array;
//分割数。増やせば細かくなるが、画像変換がうまくいってないので、増やすとヒビが大きくなるだけ・・・
private var Pice:int = 15;
private var Reaction:uint = 120;//変化する範囲(半径)
private var spring:Number = 0.75;//びよーんってなる数(振動数??
private var friction:Number = 0.85;//びよーんってなる大きさ
private var camera:Camera;
private var video:Video;
private var rect:Rectangle;
private var pt:Point;
private var motion:MotionDetector;
private var noiseReduction:ConvolutionFilter;
private var now:BitmapData;
private var before:BitmapData;
private var nextButton:Sprite;
private var mouseSp:Sprite;
public function motionPuyon()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// take a capture after 10 sec
//Wonderfl.capture_delay( 120 );
noiseReduction = new ConvolutionFilter(3, 3);
noiseReduction.bias = -(0x1000 + 0x100 * 6);
noiseReduction.matrix = [
1, 1, 1,
1, 16, 1,
1, 1, 1
];
camera = Camera.getCamera();
//カメラあり
if(camera != null){
setUpCamera();
//カメラ無し
}else{
var txt:TextField = new TextField();
txt.text ='カメラ無し';
addChild(txt);
}
}
//画像読み込み後の処理
private function setUpCamera():void
{
//カメラセットアップ
camera.setMode(240,240,12);
video = new Video(camera.width,camera.height);
video.attachCamera(camera);
//動体検知クラス
motion = new MotionDetector();
//動体検知用セットアップ
now = new BitmapData(camera.width,camera.height,false);
before = new BitmapData(camera.width,camera.height,false);
rect = new Rectangle(0, 0, camera.width,camera.height);
pt = new Point;
//取得した画像を切り取って_bmdCurrentに入れる
_bmdCurrent = new BitmapData (465, 465, true, 0xFFFFFF);
//キャンバスを用意してコンテナに入れる(重要じゃないので無視無視)
_canvas = new Sprite();
_container = new Sprite();
_container.addChild(_canvas);
addChild(_container);
_container.x = 0;//(_stageW - _bmdCurrent.width)/2;
_container.y = 0;//(_stageH - _bmdCurrent.height)/2;
//画像の大きさに合わせ、ポイントを7x7の格子に配置する(配列に入れる)
//CirclePointのコンストラクタの中で初期状態のポイント位置を保持してます。(initPoint)
//その初期ポイントと、マウスドラッグで移動したポイントを比較してるのが、_drawの中。
_w = _bmdCurrent.width / Pice;
_matrixArrayCurrent = new Array ();
_pointsArray = new Array();
//x軸とy軸の2重ループ(配列に入れる
for (var j:int = 0; j <= _bmdCurrent.width; j += _w) {
for (var i:int = 0; i <= _bmdCurrent.height; i += _w) {
var cp1:CirclePoint = new CirclePoint (i, j);
_matrixArrayCurrent.push (cp1);
_container.addChild (cp1);
var _points:Points = new Points(i,j);
_pointsArray.push(_points);
//cp1.addEventListener(MouseEvent.MOUSE_DOWN , _cPMouseDownHandler );
}
}
//_draw ();
addEventListener(Event.ENTER_FRAME,onEnterFrameHandler);
//速度とかメモリとかチェック(重要じゃないので無視無視)
//addChild(new Stats());
}
private function onEnterFrameHandler(e:Event):void{
now.draw(video);
now.draw(before,new Matrix(), new ColorTransform(),BlendMode.DIFFERENCE);
var ret:uint = now.threshold(now,rect,pt,">",0xff111111,0xffffffff);
//ノイズリダクション
var ret2 = now.applyFilter(now, rect, pt, noiseReduction);
before.draw(video);
var motionArray:Object = motion.Check(now);
var mousePoint:Point = new Point(-100,-100);
for(var idx:int=0; idx<motionArray.w.length; idx++){
var tgt_rec:Rectangle = motionArray.w[idx].rect;
if(tgt_rec.width * tgt_rec.height > 250){
mousePoint = new Point(2*(tgt_rec.x+tgt_rec.width/2), 2*(tgt_rec.y+tgt_rec.height/2));
break;
}
}
var i:int = 0;
for each (var _point:Points in _pointsArray) {
_point.update(mousePoint, Reaction, spring, friction);
_matrixArrayCurrent[i].x = _point.x;
_matrixArrayCurrent[i].y = _point.y;
i++;
}
_bmdCurrent.draw (video,new Matrix(2,0,0,2));
_draw();
}
private function _draw():void {
/**
* ポイントの番号は↓な感じ。
* 0--------1
* ----------
* ----------
* 2--------3
*/
//マトリックスを適応
_canvas.graphics.clear();
//配列のループ
for (var k:String in _matrixArrayCurrent) {
//
if (int(k) > _matrixArrayCurrent.length - (Pice+3)) break;
if ((int (k) + 1) % (Pice+1) == 0) continue;
//初期状態のポイント取得
_cP0 = _matrixArrayCurrent[int (k)];
_cP1 = _matrixArrayCurrent[int (k) + 1];
_cP2 = _matrixArrayCurrent[int (k) + (Pice+1)];
_cP3 = _matrixArrayCurrent[int (k) + (Pice+2)];
var aP0:Point = _cP0.initPoint;
var aP1:Point = _cP1.initPoint;
var aP2:Point = _cP2.initPoint;
var aP3:Point = _cP3.initPoint;
//初期状態のポイントよりマトリックス取得
//var initMatrix1:Matrix = _getTransformMatrix( aP0 , aP1 , aP2 );
//var initMatrix2:Matrix = _getTransformMatrix( aP3 , aP2 , aP1 );
//変更状態のポイント取得
var bP0:Point = new Point(_cP0.x , _cP0.y );
var bP1:Point = new Point(_cP1.x , _cP1.y );
var bP2:Point = new Point(_cP2.x , _cP2.y );
var bP3:Point = new Point (_cP3.x , _cP3.y );
//Graphics.drawTriangles()による描画にしてみる
var indices:Vector.<int> = new Vector.<int>();
var uvtData:Vector.<Number> = new Vector.<Number>();
// 三角形の頂点番号の組合わせ(第2引数)
indices.push(0,1,2);
indices.push(1,3,2);
// テクスチャマッピングのuv座標(第3引数)
uvtData.push(aP0.x/_stageW,aP0.y/_stageH);
uvtData.push(aP1.x/_stageW,aP1.y/_stageH);
uvtData.push(aP2.x/_stageW,aP2.y/_stageH);
uvtData.push(aP3.x/_stageW,aP3.y/_stageH);
// 三角形の頂点座標(第1引数)
var vertices:Vector.<Number> = new Vector.<Number>();
vertices.push(bP0.x, bP0.y);
vertices.push(bP1.x, bP1.y);
vertices.push(bP2.x, bP2.y);
vertices.push(bP3.x, bP3.y);
_canvas.graphics.beginBitmapFill(_bmdCurrent,null,true,true);
_canvas.graphics.drawTriangles( vertices, indices, uvtData);
}
}
private function _getTransformMatrix($pt0:Point, $pt1:Point, $pt2:Point):Matrix
{
/*
http://www.adobe.com/jp/devnet/flash/articles/matrix_class.html
a: 水平方向の伸縮率 = 変換後の幅/もとの幅
b: 垂直方向の傾斜率 = 垂直方向の傾斜/もとの幅
c: 水平方向の傾斜率 = 水平方向の傾斜/もとの高さ
d: 垂直方向の伸縮率 = 変換後の高さ/もとの高さ
tx: 水平方向の移動ピクセル数
ty: 垂直方向の移動ピクセル数
*/
var w:Number = _w;
var h:Number = _w;
var mat:Matrix = new Matrix();
mat.a = ($pt1.x - $pt0.x) / w;
mat.b = ($pt1.y - $pt0.y) / w;
mat.c = ($pt2.x - $pt0.x) / h;
mat.d = ($pt2.y - $pt0.y) / h;
mat.tx = $pt0.x;
mat.ty = $pt0.y;
return mat;
}
//こっからマウスのドラッグとかの設定(無視無視)
private function _cPMouseDownHandler(e:MouseEvent):void {
CirclePoint(e.target).startDrag();
CirclePoint(e.target).addEventListener(MouseEvent.MOUSE_MOVE , _cPMouseMoveHandler );
stage.addEventListener(MouseEvent.MOUSE_UP , _cPMouseUpHandler );
}
private function _cPMouseUpHandler(e:MouseEvent):void {
CirclePoint(e.target).stopDrag();
CirclePoint(e.target).removeEventListener(MouseEvent.MOUSE_MOVE , _cPMouseMoveHandler );
stage.removeEventListener(MouseEvent.MOUSE_UP , _cPMouseUpHandler );
}
private function _cPMouseMoveHandler(e:MouseEvent):void {
_draw();
}
}
}
import flash.display.Bitmap;
import flash.display.Shape;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Point;
/**
* 赤い点を書いてる。
* それと初期状態のポイントをinitPointに保持してる。
*/
class CirclePoint extends Sprite {
public var initPoint:Point;
public function CirclePoint($x:Number , $y:Number) {
x = $x;
y = $y;
this.initPoint = new Point( $x , $y );
//this.graphics.beginFill(0xFF0000,1);
//this.graphics.drawCircle(0,0,5);
//this.graphics.endFill();
//this.buttonMode = true;
}
}
import flash.geom.Point;
class Points {
private var localX:Number;
private var localY:Number;
private var vx:Number = 0;
private var vy:Number = 0;
private var _x:Number;
private var _y:Number;
public function Points(x:Number, y:Number) {
_x = localX = x;
_y = localY = y;
}
public function update(mousePoint:Point, Reaction:uint, spring:Number, friction:Number):void {
var dx:Number;
var dy:Number;
var distance:Number = Point.distance(mousePoint, new Point(localX, localY));
if (distance < Reaction) {
var diff:Number = distance * -1 * (Reaction - distance) / Reaction;
var radian:Number = Math.atan2(mousePoint.y - localY, mousePoint.x - localX);
var diffPoint:Point = Point.polar(diff, radian);
dx = localX + diffPoint.x;
dy = localY + diffPoint.y;
} else{
dx = localX;
dy = localY;
}
vx += (dx - _x) * spring;
vy += (dy - _y) * spring;
vx *= friction;
vy *= friction;
_x += vx;
_y += vy;
}
public function get x():Number { return _x; }
public function get y():Number { return _y; }
}
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Point;
class MotionDetector
{
private var _watchingRectArray:Array;
private var _nowRectArray:Array;
private var idCounter:int;
/**
*コンストラクタ
*
*/
public function MotionDetector()
{
_watchingRectArray = new Array();
idCounter = 0;
}
/**
* 動体の矩形の入った配列を返す
* @param bmd チェックしたい画像(フィルタリング済み)
* @return 矩形情報の配列
*
*/
public function Check(bmd:BitmapData):Object
{
var dst:BitmapData = bmd.clone();
var temp:BitmapData = new BitmapData(dst.width,1,false,0x000000);
var lno:int = 0;
var zero:Point = new Point();
_nowRectArray = new Array();
var _tempRectArray:Array = new Array();
//ブロック化
var rect:Rectangle = dst.getColorBoundsRect(0xffffff,0xffffff,true);
var area:Rectangle = new Rectangle(0,0,dst.width,1);
while( !rect.isEmpty()){
area.y = rect.top;
temp.copyPixels(dst,area,zero);
rect = temp.getColorBoundsRect(0xffffff,0xffffff,true);
dst.floodFill(rect.x,area.y,0xff00ff);
var br:Rectangle = dst.getColorBoundsRect(0xFFFFFF, 0xFF00FF);
br.inflate(4,4);
dst.fillRect(br, 0x0000ff);
rect = dst.getColorBoundsRect( 0xffffff, 0xffffff, true );
_nowRectArray.push(new ExRectangle(br));
}
//rectangleが重なってたら、同じものとみなして、数を絞る
for(var idx:int=0; idx<_nowRectArray.length; idx++){
var idx_rec:Rectangle = _nowRectArray[idx].rect;
for(var i:int=idx+1; i<_nowRectArray.length; i++){
var tgt_rec:Rectangle = _nowRectArray[i].rect;
if( idx_rec!=tgt_rec && idx_rec.intersects(tgt_rec) ){
_nowRectArray.push(new ExRectangle(idx_rec.union(tgt_rec)));
_nowRectArray.splice(Number(i), 1);
_nowRectArray.splice(Number(idx), 1);
idx --;
break;
}
}
}
//今回検出分とwatchしてる分の重なりを検証
for(var i:int=0; i<_nowRectArray.length; i++){
var rec_ref:ExRectangle = _nowRectArray[i];
if(rec_ref.rect.width*rec_ref.rect.height >= bmd.width*bmd.height*0.7){
//画面全体が変化している場合(カメラの自動露出調整、オートホワイトバランス調整など)は無視。
_nowRectArray.splice(Number(i), 1);
i--;
}else{
rec_ref.isects = new Array();
for(var j in _watchingRectArray){
var rec_watch:ExRectangle = _watchingRectArray[j];
if(rec_watch.rect.intersects(rec_ref.rect)){
rec_watch.isects.push(rec_ref);
rec_ref.isects.push(rec_watch);
}
}
}
}
//今回検出分がそれぞれ、いくつのwatch分に重なってるか
for(i=0; i<_nowRectArray.length; i++){
var rec_ref:ExRectangle = _nowRectArray[i];
var isectsNum:int = rec_ref.isects.length;
//1つもwatch中のrectとintersectしてない → 新規追加
if(isectsNum == 0){
if(rec_ref.rect.width*rec_ref.rect.height <= 50){
//結構小さい → 廃棄
//trace("tooSmall"+(rec_ref.width*rec_ref.height)+":"+(original_width*original_height*resolution_scale*_min_size_new_rect_rate));
_nowRectArray.splice(Number(i), 1);
i--;
}else{
//新規生成
rec_ref.event = "create";
rec_ref.life = 5;
rec_ref.id = ++idCounter;
_tempRectArray.push(rec_ref);
}
//複数のwatch中のrectとintersectしている → watch中のrectと最もたくさんintersectしているものに統合
}
else if(isectsNum >= 2){
var rect_integrate:ExRectangle = null;
for(var j=0; j<isectsNum-1; j++){
var rec_now:ExRectangle = rec_ref.isects[j];
var rec_next:ExRectangle = rec_ref.isects[j+1];
rect_integrate = (rec_now.isects.length >= rec_next.isects.length) ? rec_now : rec_next;
}
for(var j=0; j<isectsNum; j++){
if(rect_integrate != rec_ref.isects[j]){
var rec_disappear:ExRectangle = rec_ref.isects[j];
//rec_disappear.event = "integrate";
//rec_disappear.integarateTarget = rect_integrate;
//recs_disappeared.push(rec_disappear);
//recs_watchingから統合されたrectを削除
for(var k=0; k<_watchingRectArray.length; k++){
if(_watchingRectArray[k] == rec_disappear){
_watchingRectArray.splice(k, 1);
break;
}
}
}
}
}
}
//重なりデータを下に、新規watch候補を選定
for(var k=0; k<_watchingRectArray.length; k++){
var rec_watch:ExRectangle = _watchingRectArray[k];
var isectsNum = rec_watch.isects.length;
if(isectsNum == 0){
//0個の場合 → LIFE分継続
//trace("watch_未intersect: id:" + rec_watch.id + " life:"+ rec_watch.life + " event: " + rec_watch.event);
rec_watch.life -- ;
rec_watch.event = "stay";
_tempRectArray.push(rec_watch);
}else if(isectsNum == 1){
//一つの場合 → 維持
//trace("watch_1つintersect: " + rec_watch.id + " life:" + rec_watch.life);
rec_watch.isects[0].id = rec_watch.id;
rec_watch.isects[0].event = "move";
rec_watch.isects[0].life = 5;
rec_watch.isects[0].isects = new Array();
_tempRectArray.push(rec_watch.isects[0]);
}else{
//複数個の場合 → union
//それぞれの距離を測ったほうがいいかも
//あるいは、一つのrectの面積の上限を決めるとか
//(2つのrectが一旦unionされると以降つながり続ける→すれ違った人が、ずっと同じ人間だと認識されてしまうだろう)
//計算面倒だなあ
//trace("watch_複数にintersect: id:" + rec_watch.id + " 個数:" + rec_watch.isects.length + " life:" + rec_watch.life);
while(rec_watch.isects.length > 1){
rec_watch.isects[0].rect = rec_watch.isects[0].rect.union(rec_watch.isects[1].rect);
rec_watch.isects.splice(1, 1);
}
rec_watch.isects[0].id = rec_watch.id;
rec_watch.isects[0].event = "move";
rec_watch.isects[0].life = 5;
_tempRectArray.push(rec_watch.isects[0]);
}
}
_watchingRectArray = new Array();
//候補から、判定
for(var i=0; i<_tempRectArray.length; i++){
var rec_item:ExRectangle = _tempRectArray[i];
var infratex = (rec_item.rect.width - 4 <= 4) ? 0 : 4;
var infratey = (rec_item.rect.height - 4 <= 4) ? 0 : 4;
rec_item.rect.inflate(-infratex, -infratey);
//誰ともintersectしてない寿命切れのrectを削除
if(rec_item.event == "stay"){
if(rec_item.life <= 0){
//trace("disappear : " + rec_item.id);
//rec_item.event = "disappear";
//recs_disappeared.push(rec_item);
}else{
_watchingRectArray.push(rec_item);
}
//移動、統合物
}else if(rec_item.event == "move"){
_watchingRectArray.push(rec_item);
//新規
}else if(rec_item.event == "create"){
_watchingRectArray.push(rec_item);
}
}
//trace("item:"+_watchingRectArray.length);
return {w:_watchingRectArray,n:_nowRectArray};
}
public function get watchingRectArray():Array
{
return _watchingRectArray;
}
public function get nowRectArray():Array
{
return _nowRectArray;
}
}
class ExRectangle
{
public var rect:Rectangle;
public var isects:Array;
public var event:String;
public var life:int;
public var id:int;
public function ExRectangle(r:Rectangle)
{
isects = new Array();
rect = r;
event = '';
life = 0;
id = 0;
}
}