Box2D試作:光源(視界)処理
Box2Dと視界(光源)アルゴリズムの組み合わせ方のテスト。
/**
* Copyright o_healer ( http://wonderfl.net/user/o_healer )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/7dgb
*/
package {
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
//Box2D
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
[SWF(width="465", height="465", frameRate="30", backgroundColor="0xFFFFFF")]
public class GameMain extends Sprite {
//==Const==
//画面サイズ
static public const VIEW_W:int = 465;
static public const VIEW_H:int = 465;
//重力
static public const GRAVITY:Number = 0.0;
//Box2Dと実際の表示の比率(Box2Dに大きすぎる値を入れると上手く動かなかったりする)
static public const PHYS_SCALE:Number = 10;
static public function PHYS_to_IMAGE(in_Val:Number):Number{return in_Val * PHYS_SCALE;}
static public function IMAGE_to_PHYS(in_Val:Number):Number{return in_Val / PHYS_SCALE;}
//Box2Dの画面外の余白
static public const BOX2D_RANGE_OFFSET:int = 100;
//==Var==
//Layer
public var m_Layer_ShadowCast:Sprite = new Sprite();
//Box2D
public var m_Box2D_World:b2World;
//光源の球
public var m_LightBody:b2Body;
//マウスで動かせる球
public var m_MouseBallBody:b2Body;
public var m_MouseJoint:b2MouseJoint;
//境界線を描くためのGraphics
public var m_LightShape:Shape = new Shape();
public var m_LightGraphics:Graphics = m_LightShape.graphics;
//光源の表示用Bitmap
public var m_BitmapData_Light:BitmapData = new BitmapData(VIEW_W, VIEW_H, true, 0x00000000);
//==Function==
//Init
public function GameMain(){
//Layer
{
addChild(m_Layer_ShadowCast);
}
//Box2D
{
//考慮する領域
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set(IMAGE_to_PHYS(-BOX2D_RANGE_OFFSET), IMAGE_to_PHYS(-BOX2D_RANGE_OFFSET));
worldAABB.upperBound.Set(IMAGE_to_PHYS(VIEW_W+BOX2D_RANGE_OFFSET), IMAGE_to_PHYS(VIEW_H+BOX2D_RANGE_OFFSET));
//重力ベクトル
var gravity:b2Vec2 = new b2Vec2(0.0, GRAVITY);
//Sleep
var useSleep:Boolean = true;
//物理world
m_Box2D_World = new b2World(worldAABB, gravity, useSleep);
}
//周囲
{
//D
CreateCollision_Box(
VIEW_W/2,//X
VIEW_H - 5,//Y
VIEW_W,//W
10,//H
0,//Rot
true//IsFix
);
//U
CreateCollision_Box(
VIEW_W/2,//X
5,//Y
VIEW_W,//W
10,//H
0,//Rot
true//IsFix
);
//L
CreateCollision_Box(
5,//X
VIEW_H/2,//Y
10,//W
VIEW_H,//H
0,//Rot
true//IsFix
);
//R
CreateCollision_Box(
VIEW_W - 5,//X
VIEW_H/2,//Y
10,//W
VIEW_H,//H
0,//Rot
true//IsFix
);
}
//段差
{
CreateCollision_Box(
VIEW_W*1/4,//X
VIEW_H*3/4,//Y
VIEW_W/4,//W
10//H
);
CreateCollision_Box(
VIEW_W*3/4,//X
VIEW_H*3/4,//Y
VIEW_W/4,//W
10//H
);
CreateCollision_Box(
VIEW_W*3/4,//X
VIEW_H*1/4,//Y
VIEW_W/4,//W
10//H
);
}
//Light
{
CreateCollision_LightSphere(
VIEW_W/2, VIEW_H/2, 6
);
}
//Mouse
{
CreateCollision_MouseSphere(
10
);
}
//m_BitmapData_Light
{
addChild(new Bitmap(m_BitmapData_Light));
//addChild(m_LightShape);
}
//Update
{
addEventListener(Event.ENTER_FRAME, Update);
}
}
//Create : Collision : Box
public function CreateCollision_Box(in_X:int, in_Y:int, in_W:int, in_H:int, in_Rot:Number = 0, in_FixFlag:Boolean = false):void{
//Image
var sprite:Sprite;
{
sprite = new Sprite();
sprite.graphics.beginFill(0xDDDDDD);
sprite.graphics.drawRect(-in_W/2, -in_H/2, in_W, in_H);
sprite.graphics.endFill();
m_Layer_ShadowCast.addChild(sprite);
}
//Shape Def
var shapeDef:b2PolygonDef;
{
shapeDef = new b2PolygonDef();
shapeDef.SetAsBox(IMAGE_to_PHYS(in_W/2), IMAGE_to_PHYS(in_H/2));
if(in_FixFlag){
shapeDef.density = 0;//Fix
}else{
shapeDef.density = 1;//tekitou
}
}
//Body Def
var bodyDef:b2BodyDef;
{
bodyDef = new b2BodyDef();
bodyDef.position.Set(IMAGE_to_PHYS(in_X), IMAGE_to_PHYS(in_Y));
bodyDef.angle = in_Rot;
}
//Body
var body:b2Body;
{
body = m_Box2D_World.CreateBody(bodyDef);
body.CreateShape(shapeDef);
body.SetMassFromShapes();
body.m_userData = sprite;
}
}
//Create : Collision : Sphere for Light
public function CreateCollision_LightSphere(in_X:int, in_Y:int, in_R:int, in_FixFlag:Boolean = false):void{
//Image
var sprite:Sprite;
{
sprite = new Sprite();
sprite.graphics.beginFill(0xFFFF88);
sprite.graphics.drawCircle(0,0, in_R);
sprite.graphics.endFill();
sprite.filters = [new GlowFilter(0xBBBB00)];
addChild(sprite);
}
//Shape Def
var shapeDef:b2CircleDef;
{
shapeDef = new b2CircleDef();
shapeDef.radius = IMAGE_to_PHYS(in_R);
if(in_FixFlag){
shapeDef.density = 0;//Fix
}else{
shapeDef.density = 1;//tekitou
}
}
//Body Def
var bodyDef:b2BodyDef;
{
bodyDef = new b2BodyDef();
bodyDef.position.Set(IMAGE_to_PHYS(in_X), IMAGE_to_PHYS(in_Y));
}
//Body
var body:b2Body;
{
body = m_Box2D_World.CreateBody(bodyDef);
body.CreateShape(shapeDef);
body.SetMassFromShapes();
body.m_userData = sprite;
}
m_LightBody = body;
}
//Create : Collision : Sphere for Mouse
public function CreateCollision_MouseSphere(in_R:int):void{
//Image
var sprite:Sprite;
{
sprite = new Sprite();
sprite.graphics.beginFill(0xDDDDDD);
sprite.graphics.drawCircle(0,0, in_R);
sprite.graphics.endFill();
m_Layer_ShadowCast.addChild(sprite);
}
//Shape Def
var shapeDef:b2CircleDef;
{
shapeDef = new b2CircleDef();
shapeDef.radius = IMAGE_to_PHYS(in_R);
shapeDef.density = 1;//tekitou
}
//Body Def
var bodyDef:b2BodyDef;
{
bodyDef = new b2BodyDef();
bodyDef.position.Set(IMAGE_to_PHYS(30), IMAGE_to_PHYS(30));
}
//Body
var body:b2Body;
{
body = m_Box2D_World.CreateBody(bodyDef);
body.CreateShape(shapeDef);
body.SetMassFromShapes();
body.m_userData = sprite;
}
//Joint
{
var mjd:b2MouseJointDef = new b2MouseJointDef();
mjd.body1 = m_Box2D_World.GetGroundBody();
mjd.body2 = body;
//mjd.target.Set(IMAGE_to_PHYS(mouseX), IMAGE_to_PHYS(mouseY));
mjd.target.Set(bodyDef.position.x, bodyDef.position.y);
mjd.maxForce = 300.0 * body.GetMass();
mjd.timeStep = 1.0/30.0;
m_MouseJoint = m_Box2D_World.CreateJoint(mjd) as b2MouseJoint;
}
}
//Update
public function Update(e:Event=null):void{
//今回進める時間
var DeltaTime:Number = 1.0/30.0;
//物理まわりの更新
Update_Phys(DeltaTime);
//光源(視界)処理
Update_Light();
}
//物理まわりの更新
public function Update_Phys(in_DeltaTime:Number):void{
//マウスのところに移動
m_MouseJoint.SetTarget(new b2Vec2(IMAGE_to_PHYS(mouseX), IMAGE_to_PHYS(mouseY)));
//物理エンジンをDeltaTimeだけ進める
m_Box2D_World.Step(in_DeltaTime, 10);
//
for (var b:b2Body = m_Box2D_World.m_bodyList; b; b = b.m_next) {
//画像との同期
if(b.m_userData != null){
var sprite:Sprite = b.m_userData as Sprite;
sprite.x = PHYS_to_IMAGE(b.GetPosition().x);
sprite.y = PHYS_to_IMAGE(b.GetPosition().y);
//b.m_userData.m_VX = PHYS_to_IMAGE(b.m_linearVelocity.x);
//b.m_userData.m_VY = PHYS_to_IMAGE(b.m_linearVelocity.y);
sprite.rotation = b.GetAngle() * 180/Math.PI;
}
//擬似摩擦
{
const Ratio:Number = 0.9;//本当はDeltaTime依存の値にした方が良い
b.GetLinearVelocity().x *= Ratio;
b.GetLinearVelocity().y *= Ratio;
b.SetAngularVelocity(b.GetAngularVelocity() * Ratio);
}
}
}
//光源(視界)処理
public function Update_Light():void{
//初期化
m_LightGraphics.clear();
m_LightGraphics.lineStyle(1,0x00FFFF,1.0);
var SrcX:Number = PHYS_to_IMAGE(m_LightBody.GetPosition().x);
var SrcY:Number = PHYS_to_IMAGE(m_LightBody.GetPosition().y);
var DstX:Number;
var DstY:Number;
var Distance:Number;
//境界線を描く
for (var b:b2Body = m_Box2D_World.m_bodyList; b; b = b.m_next) {
//Check
{
if(b.m_userData == null){continue;}
if(m_LightBody == b){continue;}
}
var xf:b2XForm = b.m_xf;
for (var s:b2Shape = b.GetShapeList(); s; s = s.m_next){
switch (s.m_type){
case b2Shape.e_circleShape:
{
var circle:b2CircleShape = (s as b2CircleShape);
var Center:b2Vec2 = b2Math.b2MulX(xf, circle.m_localPosition);
var CenterX:Number = PHYS_to_IMAGE(Center.x);
var CenterY:Number = PHYS_to_IMAGE(Center.y);
var Rad:Number = PHYS_to_IMAGE(circle.m_radius);
var GapX:Number = SrcX - CenterX;
var GapY:Number = SrcY - CenterY;
Distance = Math.sqrt(GapX*GapX + GapY*GapY);
if(Rad < Distance){
//擬似計算。厳密にはDistanceとRadから角度なりを求めて厳密な接点を求めるべき。
var SideX:Number = GapY*Rad/Distance;
var SideY:Number = -GapX*Rad/Distance;
DstX = CenterX + SideX;
DstY = CenterY + SideY;
// m_LightGraphics.moveTo(SrcX, SrcY);
// m_LightGraphics.lineTo(DstX, DstY);
m_LightGraphics.moveTo(DstX, DstY);
m_LightGraphics.lineTo(DstX + VIEW_W/Distance * (DstX-SrcX), DstY + VIEW_H/Distance * (DstY-SrcY));
SideX = -SideX;
SideY = -SideY;
DstX = CenterX + SideX;
DstY = CenterY + SideY;
// m_LightGraphics.moveTo(SrcX, SrcY);
// m_LightGraphics.lineTo(DstX, DstY);
m_LightGraphics.moveTo(DstX, DstY);
m_LightGraphics.lineTo(DstX + VIEW_W/Distance * (DstX-SrcX), DstY + VIEW_H/Distance * (DstY-SrcY));
}
}
break;
case b2Shape.e_polygonShape:
{
var i:int;
var poly:b2PolygonShape = (s as b2PolygonShape);
var vertexCount:int = poly.GetVertexCount();
var localVertices:Array = poly.GetVertices();
//b2Assert(vertexCount <= b2_maxPolygonVertices);
var vertices:Array = new Array(b2Settings.b2_maxPolygonVertices);
for (i = 0; i < vertexCount; ++i)
{
vertices[i] = b2Math.b2MulX(xf, localVertices[i]);
DstX = PHYS_to_IMAGE(vertices[i].x);
DstY = PHYS_to_IMAGE(vertices[i].y);
//不要かもしれないが、一応画面からギリギリはみ出るくらいの線にしておく
Distance = Math.min(Math.abs(DstX-SrcX), Math.abs(DstY-SrcY));
if(Distance < 1){Distance = 1;}
// m_LightGraphics.moveTo(SrcX, SrcY);
// m_LightGraphics.lineTo(DstX, DstY);
m_LightGraphics.moveTo(DstX, DstY);
m_LightGraphics.lineTo(DstX + VIEW_W/Distance * (DstX-SrcX), DstY + VIEW_H/Distance * (DstY-SrcY));
}
}
break;
}
}
}
//=実際の光の範囲の描画=
const CT_FORCE_DARK:ColorTransform = new ColorTransform(0,0,0,1, 0,0,1,-0);
//まずはリセット
{
m_BitmapData_Light.fillRect(m_BitmapData_Light.rect, 0x80000000);
}
//境界線をDraw
{
m_BitmapData_Light.draw(m_LightShape, null, CT_FORCE_DARK);
}
//物体もDraw
{
m_BitmapData_Light.draw(m_Layer_ShadowCast, null, CT_FORCE_DARK);
}
//光源の位置からFloodFill
{
m_BitmapData_Light.floodFill(SrcX, SrcY, 0x80FFFFFF);
}
}
}
}