トランプタワー破壊 ボタン - forked from: Button
Alternativa3DおよびBox2Dの習作。
余計な事をせずに「なるべく自立するトランプタワー」を目指してみました。
/**
* Copyright AtuyL ( http://wonderfl.net/user/AtuyL )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/qVmD
*/
// forked from Event's Button
/** トランプタワー破壊 ボタン
*
* @author AtuyL
*
* トランプタワーって崩したくなるよね(´・ω・)
*
* 今日は思う存分やってね。
* トランプは勝手に積み上がっていくからね。
* PUTボタンを使えば自分で積み上げる事も出来るけど、タイミングを見計らってカードを置かないと崩れちゃうよ。
* 後片付けはCLEARボタンを押してね。
*
* なるべく軽くしようとコリジョン形状を工夫したけど、
* それでもあんまり段数多くすると超重いよ。
*
* 最後あたり突貫工事でソース汚くしちゃってごめんね J(’ー`)し
* アップロード直前にwonderflのAlternativa3Dのバージョンが最新になってるのに気が付いて超焦った。
* 全くの別物になっててびっくりしたよ。
* だれか7.5にて両面に別々のUV張る方法おしえてください(´・ω・)以前は簡単にできたのに…
*
* ※:@narutohyper 氏にアドバイスいただき、チラつきを抑えたバージョンに変更しました。
*/
package{
import alternativ7.engine3d.containers.BSPContainer;
import alternativ7.engine3d.containers.ConflictContainer;
import alternativ7.engine3d.containers.DistanceSortContainer;
import alternativ7.engine3d.containers.KDContainer;
import alternativ7.engine3d.controllers.SimpleObjectController;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Geometry;
import alternativ7.engine3d.core.Object3DContainer;
import alternativ7.engine3d.core.Sorting;
import alternativ7.engine3d.core.Transform3D;
import alternativ7.engine3d.core.View;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.primitives.Plane;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.materials.TextureMaterial;
import com.bit101.components.*;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Vector3D;
import flash.net.URLRequest;
import flash.utils.setTimeout;
import flash.system.LoaderContext;
[SWF(width=465,height=465,backgroundColor=0xFFFFFF,frameRate=60)]
public class Main extends Sprite{
private var content:Sprite;
private var ui:Sprite;
public function Main(){
this.preload();
}
private var loadlist:Array = [
{url:"http://assets.wonderfl.net/images/related_images/6/68/68fc/68fc116ad5920aa4dc69540835e854581794e8f3",key:"floorTexture"},
{url:"http://assets.wonderfl.net/images/related_images/a/a1/a151/a151b4db9de05b302de3d6316bc20b4c0d6b173e",key:"cardTexture"},
];
// asset image の読み込み
public function preload():void{
var target:Main = this;
var current:Object = loadlist.shift();
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(event:Event):void{
event.currentTarget.removeEventListener(event.type,arguments.callee);
target[current.key] = Bitmap(loader.content).bitmapData.clone();
if(loadlist.length) preload();
else init();
});
loader.load(new URLRequest(current.url),new LoaderContext(true));
}
public function init():void{
this.addChild(this.content = new Sprite);
this.addChild(this.ui = new Sprite);
this.init3D();
this.initCardTower();
// 各種 ui を設置
var offset:Number = 24;
var x:Number = (this.stage.stageWidth - offset * 2.0) >> 2;
var y:Number = this.stage.stageHeight - 64;
this.stage.addEventListener(Event.ENTER_FRAME,enterframe);
var button:PushButton;
button = new PushButton(this.ui,0,0,"BREAK",function(event:MouseEvent):void{
// 頂点位置モデル
// A B
// D C
var poly:Polygon = new Polygon(Vector.<Vertex>([
new Vertex(-0.3,-0.3),// A
new Vertex( 0.3,-0.3),// B
new Vertex( 0.3, 0.3),// C
new Vertex(-0.3, 0.3) // D
]));
poly.x = (Math.random() - 0.5) * 0.5;
poly.y = -cardtower.rowLength * 0.5;
poly.rotation = Math.PI * Math.random();
poly.restitution = 1.0;
box2d.addChild(poly);
});
button.width = 128;
button.height = 128;
var slider:HUISlider = new HUISlider(this.ui,0,0,"ROW");
slider.maximum = 8;
slider.value = 4;
slider.addEventListener(Event.CHANGE,function(event:Event):void{
init3D();
initCardTower(slider.value);
});
slider.x = 138;
slider.y = 128 - offset * 1;
new PushButton(this.ui,138,128 - offset * 2,"PUT",function(event:MouseEvent):void{
putCard();
framecount = 0;
})
new PushButton(this.ui,138,128 - offset * 3,"RESET",function(event:MouseEvent):void{
init3D();
initCardTower(slider.value);
while(cardtower.cardlist.length > numCards) putCard();
})
new PushButton(this.ui,138,128 - offset * 4,"CLEAR",function(event:MouseEvent):void{
init3D();
initCardTower(slider.value);
framecount = 0;
});
this.ui.x = 10;
this.ui.y = this.stage.stageHeight - 138;
while(cardtower.cardlist.length > numCards) putCard();
}
private var numCards:int;
public function putCard():void{
if(this.cardtower.cardlist.length <= this.numCards) return;
this.numCards++;
var size:Number = 0.1618;
var thickness:Number = size * 0.05;
var card:Card = this.cardtower.cardlist[this.cardtower.cardlist.length - this.numCards];
// create 2D-CardTower
var verticles:Vector.<Vertex>;
if(!card.isBridge){
var cos:Number = Math.cos(Math.abs(card.radian)) * thickness * 2.0;
var sin:Number = Math.sin(Math.abs(card.radian)) * thickness * 2.0;
// offset1,offset2
var o1:Number,o2:Number;
if(card.radian < 0){
o1 = sin;
o2 = cos;
}else{
o1 = cos;
o2 = sin;
}
// 頂点位置モデル
// A
// F B
// E C
// D
verticles = Vector.<Vertex>([
new Vertex( 0.0 , -size ),// A
new Vertex( o1 , -size + o2 ),// B
new Vertex( o2 , size - o1 ),// C
new Vertex( 0.0 , size ),// D
new Vertex( -o1 , size - o2 ),// E
new Vertex( -o2 , -size + o1 ) // F
]);
}else{
// 頂点位置モデル
// A B
// D C
verticles = Vector.<Vertex>([
new Vertex( -thickness , -size ),// A
new Vertex( thickness , -size ),// B
new Vertex( thickness , size ),// C
new Vertex( -thickness , size ),// D
]);
}
var polygon:Polygon = new Polygon(verticles);
polygon.x = card.x * size * 2.0;
polygon.y = card.y * size * 2.0 - 0.02;
//if(card.isBridge && card.isOdd) polygon.y += 0.01;
polygon.rotation = -card.radian;
polygon.density = 0.01;
polygon.friction = 0.4;
polygon.restitution = 0.0;
this.box2d.addChild(polygon,false);
this.cardlist2D.push(polygon);
// create 3D-CardTower from 2D-CardTower
var rotationAxis:Vector3D = new Vector3D(0,1,0);
var rotationMatrix:Matrix3D = new Matrix3D();
//
// for @narutohyper
// thanks for advise !!
//
var card3D:Plane = new Plane(100.0,161.8,1,1,true);
card3D.z = Math.random();
var vertices:Vector.<alternativa.engine3d.core.Vertex>;
var vertex:alternativa.engine3d.core.Vertex;
var position:Vector3D;
var normal:Vector3D;
var rotation:Matrix3D = new Matrix3D();
rotation.appendRotation(!card.isBridge && card.isOdd ? 90 : -90,Vector3D.Y_AXIS);
var geometry:Geometry = card3D.geometry;
geometry.transform(rotation);
card3D.geometry = geometry;
var frontFace:Face = card3D.faces[0];
var backFace:Face = card3D.faces[1];
frontFace.material = backFace.material = new TextureMaterial(this.cardTexture,false,true);
// texture consts
const textureWidth:Number = 392;
const textureHeight:Number = 640;
const spriteWidth:Number = 56;
const spriteHeight:Number = 80;
var uvX:Number = 336 / textureWidth;
var uvY:Number = spriteHeight / textureHeight;
var uvW:Number = spriteWidth / textureWidth;
var uvH:Number = spriteHeight / textureHeight;
// frontface uv
vertex = frontFace.vertices[0];
vertex.u = uvX + uvW;
vertex.v = uvY;
vertex = frontFace.vertices[1];
vertex.u = uvX + uvW;
vertex.v = uvY + uvH;
vertex = frontFace.vertices[2];
vertex.u = uvX;
vertex.v = uvY + uvH;
vertex = frontFace.vertices[3];
vertex.u = uvX;
vertex.v = uvY;
// backface uv
var xIndex:int = (Math.random() * 7) >> 0;
var yIndex:int = (Math.random() * 8) >> 0;
uvX = spriteWidth * xIndex / textureWidth;
uvY = spriteHeight * yIndex / textureHeight;
vertex = backFace.vertices[0];
vertex.u = uvX + uvW;
vertex.v = uvY + uvH;
vertex = backFace.vertices[1];
vertex.u = uvX;
vertex.v = uvY + uvH;
vertex = backFace.vertices[2];
vertex.u = uvX;
vertex.v = uvY;
vertex = backFace.vertices[3];
vertex.u = uvX + uvW;
vertex.v = uvY;
card3D.sorting = Sorting.DYNAMIC_BSP;
card3D.optimizeForDynamicBSP(2);
this.cardlist3D.push(this.world.addChild(card3D));
if(!card.isBridge && !card.isOdd) putCard(); // ペア設置
}
private var box2d:Box2D;
private var box2dCanvas:Sprite;
private var cardtower:CardTower;
private var cardlist2D:Vector.<Polygon>;
private var cardlist3D:Vector.<Plane>;
public function initCardTower(row:int = 4):void{
this.cardtower = new CardTower(row);
// init box2d and debugdraw
var calcSize:Number = 10.0;
this.box2d = new Box2D(-calcSize,-calcSize,calcSize,calcSize);
// 頂点位置モデル
// A B
// D C
var floor:Polygon = new Polygon(
Vector.<Vertex>([
new Vertex(-calcSize,-calcSize * 0.5),// A
new Vertex( calcSize,-calcSize * 0.5),// B
new Vertex( calcSize, calcSize * 0.5),// C
new Vertex(-calcSize, calcSize * 0.5),// D
])
);
floor.x = 0.0;
floor.y = calcSize * 0.5;
floor.rotation = 0.0;
floor.density = 0.0;
floor.friction = 0.8;
floor.restitution = 0.0;
this.box2d.addChild(floor,true);
this.cardlist2D = new Vector.<Polygon>();
this.cardlist3D = new Vector.<Plane>();
this.offsetlist = new Vector.<Matrix>(this.cardtower.cardlist.length);
this.numCards = 0;
}
private var scene:DistanceSortContainer;
private var world:ConflictContainer;
private var camera:Camera3D;
private var viewport:View;
private var floorTexture:BitmapData;
private var cardTexture:BitmapData;
public function init3D():void{
var stageWidth:Number = this.stage.stageWidth;
var stageHeight:Number = this.stage.stageHeight;
this.viewport = new View(stageWidth,stageHeight);
this.scene = new DistanceSortContainer();
this.scene.sortByZ = true;
this.world = new ConflictContainer();
this.world.threshold = 2;
this.scene.addChild(this.world);
this.camera = new Camera3D();
this.camera.y = -1000;
this.camera.z = -2000;
this.camera.fov = Math.PI * 0.6;
this.camera.view = this.viewport;
this.scene.addChild(this.camera);
var floor:Plane = new Plane(2000,2000,1,1,true);
floor.y = 100;
floor.rotationX = Math.PI * 0.5;
floor.setMaterialToAllFaces(new TextureMaterial(floorTexture.clone()));
this.scene.addChild(floor);
this.content.addChild(this.viewport);
}
private var offsetlist:Vector.<Matrix>;
public function sync():void{
for(var i:int;i < this.numCards;i++){
var card2D:Polygon = this.cardlist2D[i];
var card3D:Plane = this.cardlist3D[i];
var matrix2D:Matrix = card2D.getMatrix();
var matrix3D:Matrix3D = card3D.matrix;
var rawData:Vector.<Number> = matrix3D.rawData;
rawData[0] = matrix2D.a;// cosθ
rawData[1] = matrix2D.b;// sinθ
rawData[4] = matrix2D.c;// -sinθ
rawData[5] = matrix2D.d;// cosθ
rawData[12] = matrix2D.tx * 500;
rawData[13] = matrix2D.ty * 500;// y+ が上方向のため
matrix3D.rawData = rawData;
card3D.matrix = matrix3D;
// 落下に従って適当にバラつかせる
var offset:Matrix = this.offsetlist[i] || new Matrix(1,0,0,1,matrix2D.tx,matrix2D.ty);
var offsetX:Number = matrix2D.tx - offset.tx;
var offsetY:Number = matrix2D.ty - offset.ty;
offsetY = card3D.z < 0 ? -offsetY : offsetY;
this.offsetlist[i] = matrix2D;
card3D.z += offsetY * 50;
card3D.rotationX += offsetX * 0.1;
}
}
public function orbitMove(pitch:Number,yaw:Number):void{
var orbit:Matrix3D = new Matrix3D();
orbit.appendTranslation(0,0,-100 - 100 * this.cardtower.rowLength);
orbit.appendRotation(pitch / Math.PI * 180,new Vector3D(1,0,0));
orbit.appendRotation(yaw / Math.PI * 180,new Vector3D(0,1,0));
this.camera.matrix = orbit;
this.camera.y -= 80 * this.cardtower.rowLength;
}
private var framecount:int;
private function enterframe(event:Event = null):void{
if(!(++framecount % 120)){
this.putCard();
framecount = 0;
}
this.box2d.nextFrame();
this.sync();
var yaw:Number = (this.stage.mouseX / this.stage.stageWidth - 0.5) * -Math.PI * 0.5;
var pitch:Number = Math.sin((this.stage.mouseY / this.stage.stageHeight - 1.0) * Math.PI * 0.5) * Math.PI * 0.5;
this.orbitMove(pitch,yaw);
this.camera.render();
}
}
}
import Box2D.Collision.b2AABB;
import Box2D.Collision.Shapes.b2CircleDef;
import Box2D.Collision.Shapes.b2PolygonDef;
import Box2D.Collision.Shapes.b2ShapeDef;
import Box2D.Common.Math.b2Vec2;
import Box2D.Common.Math.b2XForm;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2World;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.geom.Point;
class Card{
public var x:Number;
public var y:Number;
public var radian:Number;
public var isBridge:Boolean;
public var isOdd:Boolean;
public function Card(x:Number = 0.0,y:Number = 0.0,radian:Number = 0.0,isBridge:Boolean = false,isOdd:Boolean = false){
this.x = x;
this.y = y;
this.radian = radian;
this.isBridge = isBridge;
this.isOdd = isOdd;
}
}
class CardTower{
private var radian:Number = Math.PI * 0.1;
public function CardTower(rowLength:int = 4):void{
this.rowLength = rowLength;
this.cardlist = new Vector.<Card>();
this.radian = Math.abs(this.radian);// ペアカードを正しく配置するため、回転値は必ず正である必要がある
var cardW:Number = Math.sin(this.radian);// 回転角を適用後のカードを囲む矩形の横幅
var cardH:Number = Math.cos(this.radian);// 回転角を適用後のカードを囲む矩形の縦幅
for(var rowIndex:int = 0;rowIndex < rowLength;rowIndex++){
var row:Number = rowLength - rowIndex;
var clmLength:int = rowIndex + 1;
var pairCardList:Vector.<Card> = new Vector.<Card>;
var bridgeCardList:Vector.<Card> = new Vector.<Card>;
for(var clmIndex:int = 0;clmIndex < clmLength;clmIndex++){
var clm:Number = clmIndex - rowIndex * 0.5;
var cardX:Number = clm * cardW * 2.0;// 左右1対で2枚分
var cardY:Number = row * cardH * -1.0;// 下から上に積み上げる
pairCardList.push(
this.addCard(cardX,cardY,-this.radian,true),
this.addCard(cardX,cardY, this.radian,false)
);
if(clmLength < rowLength){
bridgeCardList.push(
this.addBridge(cardX,cardY + cardH,Boolean(clmIndex % 2))
);
}
}
var card:Card;
for each(card in pairCardList) this.cardlist.push(card);
for each(card in bridgeCardList) this.cardlist.push(card);
}
}
public var rowLength:int;
public var cardlist:Vector.<Card>;
public function addCard(x:Number,y:Number,radian:Number,isOdd:Boolean):Card{
var boundX:Number = Math.sin(radian) * 0.5;
var boundY:Number = Math.cos(radian) * 0.5;
return new Card(x + boundX,y + boundY,radian,false,isOdd);
}
public function addBridge(x:Number,y:Number,isOdd:Boolean):Card{
return new Card(x,y,Math.PI * 0.5,true,isOdd);
}
}
class Box2D{
private var world:b2World = null;
public function setDebugDraw(canvas:Sprite,drawScale:Number):void{
var debugDraw:b2DebugDraw = new b2DebugDraw();
debugDraw.m_sprite = canvas;
debugDraw.m_drawScale = drawScale; // 1ポイントをdrawScale(px)として描画する
debugDraw.m_fillAlpha = 0; // 塗りの不透明度
debugDraw.m_lineThickness = 0.1; // 線の太さ
debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit;// | b2DebugDraw.e_coreShapeBit | b2DebugDraw.e_jointBit;
this.world.SetDebugDraw(debugDraw);
}
public function Box2D(left:Number = -1.0,top:Number = -1.0,right:Number = 1.0,bottom:Number = 1.0){
const GRAVITY:Number = 9.80665 * 0.5;
var aabb:b2AABB = new b2AABB();
aabb.lowerBound.Set(left,top);
aabb.upperBound.Set(right,bottom);
var gravity:b2Vec2 = new b2Vec2(0,GRAVITY);
this.world = new b2World(aabb,gravity,true);
}
public function setGravity(x:Number,y:Number):void{
this.world.SetGravity(new b2Vec2(x,y));
}
public function addChild(child:Box2DBody,isStatic:Boolean = false):void{
child.updateProperties();
child.boxBody = this.world.CreateBody(child.boxBodyDef);
child.boxBody.CreateShape(child.boxShapeDef);
child.updatePosition();
if(!isStatic) child.boxBody.SetMassFromShapes();
}
public function removeChild(child:Box2DBody):void{
if(!child.boxBody) return;
this.world.DestroyBody(child.boxBody);
child.boxBody = null;
}
public function nextFrame():void{
this.world.Step(1/60,10);
}
}
class Box2DBody{
internal var boxBody:b2Body;
internal var boxBodyDef:b2BodyDef;
internal var boxShapeDef:b2ShapeDef;
public function Box2DBody(){
this.boxBodyDef = new b2BodyDef();
this.matrix = new Matrix();
}
public var x:Number = 0.0;
public var y:Number = 0.0;
public var rotation:Number = 0.0;
public var density:Number = 0.5;// 密度
public var friction:Number = 0.5;// 摩擦係数
public var restitution:Number = 0.5;// 反発係数
public function updatePosition():void{
if(this.boxBody){
var position:b2Vec2 = this.boxBody.GetPosition();
position.Set(this.x,this.y);
this.boxBody.SetXForm(position,this.rotation);
}
}
public function updateProperties():void{
if(this.boxShapeDef){
this.boxShapeDef.density = this.density;
this.boxShapeDef.friction = this.friction;
this.boxShapeDef.restitution = this.restitution;
}
}
private var matrix:Matrix;
public function getMatrix():Matrix{
if(!this.boxBody) return null;
var xform:b2XForm = this.boxBody.GetXForm();
this.matrix.a = xform.R.col1.x;
this.matrix.b = xform.R.col1.y;
this.matrix.c = xform.R.col2.x;
this.matrix.d = xform.R.col2.y;
this.matrix.tx = xform.position.x;
this.matrix.ty = xform.position.y;
return this.matrix.clone();
}
}
class Polygon extends Box2DBody{
public function Polygon(varticles:Vector.<Vertex>){
super();
var def:b2PolygonDef = new b2PolygonDef();
def.vertexCount = varticles.length;
var vertexIndex:int = 0;
for(var i:int = 0,length:int = varticles.length;i < length;i++){
var v:Vertex = varticles[i];
var vertex:b2Vec2 = def.vertices[i];
vertex.Set(v.x,v.y);
}
this.boxShapeDef = def;
}
}
class Vertex{
public var x:Number;
public var y:Number;
public function Vertex(x:Number,y:Number){
this.x = x;
this.y = y;
}
}
class Circle extends Box2DBody{
public function Circle(radius:Number){
super();
var def:b2CircleDef = new b2CircleDef();
def.radius = radius;
this.boxShapeDef = def;
}
}