JigLibFlashでTetris3D
これの解説編です。
WCAN mini AS vol.11でお話しした点も含めてダラダラと記述。
Papervision3D rev910とJigLibFlash。
JigLibFlash v0.32時点でのお話し。
Box2DASとPapervision3Dの連携と同じようなことをやっていて、jiglibflashも演算用オブジェクト(RigidBodyクラス)で計算させてその結果をPV3Dのオブジェクトのtransform使って適用しています。
Papervision3DのPrimitiveオブジェクトで対応してるのは
Cube・Sphere・Plane・Cylinderで、jiglibのほうで対応する演算用オブジェクトは、JBox・JSphere・JPlane・JCapsuleです。
JCarというのも用意されてます。jiglibflashのサンプル見てください。
JBox・JSphere・JPlane・JCapsuleの各クラスはRigidBodyクラスを継承している。
こいつがそれぞれで物理演算してます。
(もっと細かく記述するとJNumber3DとかJMatrix3DとかいうPV3Dそのままでクラス名だけ変えたっぽいもののメソッドをいっぱいたたいてる)
Papervision3DPhysicsクラスにcreateSphere・createCube・createGroundというメソッドがあり、それぞれ、Sphere・Cube・Plane(地面用なのでrotationX入ります)を用意してくれるメソッド。
PV3DのPrimitiveオブジェクトとjiglibflashのそれぞれに対応した演算用オブジェクトを用意してくれます。
PhysicsSystemクラスがjiglibflashのコアまたはそれに近いクラスっぽいよとお話ししましたが、
jiglibflashの演算オブジェクトを物理演算の世界に加えるPapervision3DPhysics.addBodyメソッドや逆に削除するPapervision3DPhysics.removeBodyメソッドはどちらもPhysicsSystemクラスの同名メソッドたたいてるだけです。
(addBody・removeBodyともにPapervision3DPhysicsが継承してるAbstractPhysicsクラスのメソッド)
RigidBodyオブジェクトを配列で管理してるのはPhysicsSystemクラス。
生成されたRigidBodyオブジェクトの演算メソッドをたたいてるのもPhysicsSystemクラス。
createCubeやら用意されてるけど、削除する方は用意されていないので、削除は自分でやらないとだめです。
RigidBodyオブジェクトと紐付きされてるPV3DのPrimitiveオブジェクトを取得する方法はPapervision3DPhysics.getMesh(RigidBody)メソッド。
removeBodyだけだと、PV3DのPrimitiveオブジェクトがSceneに残ってしまう。
もちろんそいつは演算対象ではないのですり抜けたりとおかしなことになります。
RigidBody.movableプロパティをfalseにするとそのオブジェクトは固定されます。(なので、地面に使う。)
この状態だと力が’加えられても演算処理がスキップされるようになっているので、動かすときは
x/y/zの各プロパティを変えちゃうとかかな。
RigidBody.isActive()メソッドでRigidBody内部の変数_activityの値が取得できる。
こいつはBooleanで動いてるときはtrueだけど、演算が終わって完全に停止するとfalseになります。
falseで演算処理がスキップされるので処理を軽くするという目的もあると思う。
もちろん、falseだと動きませんがRigidBody.movableプロパティと違うのは他のオブジェクトの影響などで力が加えられるとtrueになって動き出します。
または、RigidBody.setActive()メソッドでもtrueにできます。
RigidBody.frictionプロパティは摩擦の指定。たぶん、0〜1じゃないかな。
0でつるつる。1できゅっきゅ。
WCANで10にしてCube消えて驚いてたのはこのプロパティ。
RigidBody.restitutionプロパティは反発力。
値を大きくするとぴょんぴょんはねる。
RigidBody.collisionsプロパティに衝突したオブジェクトの情報がCollisionInfoオブジェクトという形で衝突したオブジェクト数ぶんの配列として取得できる。
RigidBody.currentState.linVelocityプロパティは取得した時の移動量。
JNumber3Dオブジェクトが取得できる。x/y/zのすべてが0だったらその時は停止してる。
取得した時の状態にもよりますが、衝突した瞬間はx/y/zすべて0になると思うので、すべて0=完全停止とはならないよ。
RigidBody.currentStateてのは現在の状態。PhysicsStateオブジェクトが入ってる。一つ前の状態のoldStateってのもあります。
RigidBody.addWorldForce(JNumber3D(Number, Number, Number), RigidBody.currentState.position)メソッドでオブジェクト動かせます。たぶん、現在のポジションからX・Y・Z軸方向にどれだけ力を加えるみたいな感じ?Numberで指定した値=指定したピクセル分動くというわけではない。
これのrotation版がaddWorldTorque(JNumber3D(Number, Number, Number))メソッド。
Papervision3DPhysics.step()メソッドのこととか、以下のコードにもコメントで解説あります。
(Papervision3DPhysics.step()メソッドは継承してるAbstractPhysicsのメソッド)
//AS3/////////////////////////////////////////////////////////////////////////// // // Tetris3D // Papervision3D rev.910 & jiglibflash // //////////////////////////////////////////////////////////////////////////////// package { import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.text.TextField; import flash.text.TextFormat; import flash.text.TextFieldAutoSize; import flash.events.Event; import flash.events.KeyboardEvent; import org.papervision3d.view.BasicView; import org.papervision3d.view.Viewport3D; import org.papervision3d.cameras.Camera3D; import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.materials.shadematerials.*; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.render.QuadrantRenderEngine; import net.eternitydesign.util.DO3DController; import TetrisBlock; import jiglib.geometry.*; import jiglib.math.*; import jiglib.cof.JConfig; import jiglib.physics.*; import jiglib.physics.constraint.*; import jiglib.plugin.papervision3d.*; import com.flashdynamix.utils.SWFProfiler; public class Main extends BasicView { private var birdView:Viewport3D; private var birdViewCamera private var tetrisStage:DisplayObject3D; private var tetrisCtrl:DO3DController; private var physics:Papervision3DPhysics; private var ground:RigidBody; private var light:PointLight3D; private var tetrisBlock:TetrisBlock; private var dCubeNoText:TextField; private var textFormat:TextFormat; public function Main() { super(stage.stageWidth, stage.stageHeight, true, false); SWFProfiler.init(stage, this); stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; // 俯瞰のため birdView = new Viewport3D(200, 200); birdViewCamera = new Camera3D(); birdViewCamera.rotationX = 90; birdViewCamera.y = 800; birdViewCamera.z = 0; addChild(birdView); tetrisStage = new DisplayObject3D(); scene.addChild(tetrisStage); tetrisCtrl = new DO3DController(stage); tetrisCtrl.set(tetrisStage, 1, 20); // Papervision3DPhysics(DisplayObjectContainer3D, Number) // 2番目のパラメータのNumberは physics.step() を利用する時でないと意味がないよ(他にもあるかもだけど) physics = new Papervision3DPhysics(tetrisStage, 5); //trace("physics.gravity : " + physics.engine.gravity); // 物理演算世界の重力設定。デフォルトはJNumber3D(0, -10, 0)です。 // physics.engineプロパティで取得できるのはPhysicsSystemオブジェクト physics.engine.setGravity(new JNumber3D(0, -2, 0)); light = new PointLight3D(true, true); light.x = 0; light.y = 400; light.z = -300; camera.y = 450; camera.z = -500; startRendering(); var materiaList:MaterialsList = new MaterialsList(); materiaList.addMaterial(new FlatShadeMaterial(light, 0xCCCCCC), "all"); // 床生成 ground = physics.createCube(materiaList, 200, 200, 5); ground.movable = false; ground.friction = 0.2; ground.restitution = 0.8; //viewport.getChildLayer(physics.getMesh(ground)).layerIndex = 1; //physics.createGround(new ColorMaterial(0xCCCCCC), 500, 0); tetrisBlock = new TetrisBlock(physics, tetrisStage); tetrisBlock.addEventListener("FALL_COMPLETE", checkFall) tetrisBlock.createBlock(); stage.addEventListener(KeyboardEvent.KEY_DOWN, tetrisBlock.blockKeyEvent); textFormat = new TextFormat("_sans", 10, 0xAAAAAA); textFormat.letterSpacing = 1; dCubeNoText = new TextField(); dCubeNoText.autoSize = TextFieldAutoSize.LEFT; dCubeNoText.defaultTextFormat = textFormat; dCubeNoText.x = 400; dCubeNoText.y = 530; addChild(dCubeNoText); // PV3DのZ-sort関連で描画おかしくなるのをなんとかしてくれるレンダーなんですが、 // 今回、大きさの異なるviewportを2つ利用しており、このレンダー使うと双方が同じ大きさに勝手になってしまうので利用しないことにした。 //renderer = new QuadrantRenderEngine(QuadrantRenderEngine.ALL_FILTERS); } private function checkFall(e:Event) { trace("checkFall"); tetrisBlock.createBlock(); dCubeNoText.text = "消したCubeの数 : " + tetrisBlock.totalDestroyCube; } protected override function onRenderTick(e:Event = null):void { // 物理演算をしろ!っていうメソッドです。 // すぐ下のphysics.step()は、PhysicsSystem.getInstance().integrate(Number) // をやってるんですが、Number部分が固定値ではなく、Papervision3DPhysicsの第2パラメータのNumberを利用して毎フレーム計算しています。 // WCANでもお話ししましたが、フレームレートの低下をごまかしてくれるようになっています。 // 一応、traceでフレームレートの変化にともなってNumberの値が増減するのを確認しています。 // ただ、ちゃんと調べたわけではないですが、加える力(?)を大きくさせて移動量などを増やしているので、ブロック移動のための力まで増幅されているのか操作が難しくなるので今回はstep()使っていません。 PhysicsSystem.getInstance().integrate(0.2); //physics.step(); renderer.renderScene(scene, camera, viewport); renderer.renderScene(scene, birdViewCamera, birdView); } } }
ファイル一式です。
flaはCS4ですが、Mainクラスを指定してるだけなのでCS4以外の方は、メインクラス?(ステージクラスだっけ名称忘れた)にMainを指定するだけでOKです。
Tetris3Dのダウンロード