在前面的刚体的上衣教程中,我们学会了简单的刚体贴图,确实非常简单,只需要根据b2Body刚体的坐标和角度实时更新userData的坐标和角度就可以了。那么多边形刚体贴图呢?
放心啦,没你想象的那么复杂,和简单刚体的贴图方法是一样一样的,重点是如何绘制和多边形刚体一样的图形,很幸运,我们在用drawPath自由绘制图形中已经学会了,结合运行时创建多边形刚体,我们可以轻松给多边形刚体啦(如果你还不熟练,强烈建议你好好看看这两篇教程)。效果如下:
完整的代码和注释如下:
package
{
import Box2D.Collision.b2AABB;
import Box2D.Collision.Shapes.b2PolygonShape;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2FixtureDef;
import Box2D.Dynamics.b2World;
import Box2DSeparator.b2Separator;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.geom.Matrix;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.display.GraphicsPathCommand;
/**
* http://www.ladeng6666.com
* @author ladeng6666
*/
public class MainWithSeparator extends Sprite
{
private const segmentLength:Number = 20;
private var world:b2World;
//绘制图形的画布
private var spriteCanvas:Sprite;
private var spriteCommand:Vector.<int> = new Vector.<int>();
private var spriteData:Vector.<Number> = new Vector.<Number>();
//绘制图像的纹理
private var woodBmd:BitmapData = new Wood();
private var prePoint:Point = new Point();
private var curPoint:Point = new Point();
private var verticesList:Vector.<b2Vec2> = new Vector.<b2Vec2>();
private var isDrawing:Boolean = false;
public function MainWithSeparator()
{
//创建box2D世界
world = LDEasyBox2D.createWorld();
//创建box2D调试图
addChild(LDEasyBox2D.createDebug(world));
//创建地面
LDEasyBox2D.createWrapWall(world,stage);
spriteCanvas = new Sprite();
addChild(spriteCanvas);
//侦听事件
addEventListener(Event.ENTER_FRAME, loop);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMove);
}
private function onStageMouseMove(e:MouseEvent):void
{
//如果鼠标没有按下,isDrawing为false,跳出
if(!isDrawing) return;
spriteCanvas.graphics.lineTo(mouseX, mouseY);
//记录鼠标坐标为当前curPoint,并计算curPoint与上一个点prePoint的距离
curPoint = new Point(mouseX, mouseY);
var distance:Number = Point.distance(prePoint, curPoint);
//当前后两个点的距离大于线段距离时,添加顶点
if (distance >= segmentLength) {
//记录顶点到verticesList数组中
verticesList.push(new b2Vec2(mouseX / 30, mouseY / 30));
prePoint = curPoint.clone();
//存储绘制命令和绘制顶点
spriteCommand.push(GraphicsPathCommand.LINE_TO);
spriteData.push(mouseX);
spriteData.push(mouseY);
}
}
private function onStageMouseUp(e:MouseEvent):void
{
spriteCommand.push(GraphicsPathCommand.LINE_TO);
spriteData.push(spriteData[0]);
spriteData.push(spriteData[1]);
//鼠标弹起后,停止绘制
isDrawing = false;
//画布里的内容
spriteCanvas.graphics.clear();
//在鼠标位置随机创建一个多边形刚体
var polygonBody:b2Body=createPolygon();
if (polygonBody != null) {
createUseData(polygonBody, spriteCommand, spriteData);
}
//清空存储命令和顶点的数组
spriteCommand = new Vector.<int>();
spriteData = new Vector.<Number>();
//清空存储顶点的Vector数组
verticesList = new Vector.<b2Vec2>();
}
/**
* 鼠标按下事件侦听
* @param e
*/
private function onStageMouseDown(e:MouseEvent):void
{
//鼠标按下后,开始绘制
isDrawing = true;
//设置线条样式
spriteCanvas.graphics.lineStyle(2);
spriteCanvas.graphics.moveTo(mouseX, mouseY);
//存储绘制命令和绘制顶点
spriteCommand.push(GraphicsPathCommand.MOVE_TO);
spriteData.push(mouseX);
spriteData.push(mouseY);
//定义鼠标点为起点
curPoint = new Point(mouseX, mouseY);
prePoint = curPoint.clone();
verticesList.push(new b2Vec2(mouseX / 30, mouseY / 30));
}
/**
* 刷新屏幕
* @param e
*/
private function loop(e:Event):void
{
world.Step(1 / 30, 10, 10);
world.ClearForces();
world.DrawDebugData();
for (var body:b2Body = world.GetBodyList(); body; body=body.GetNext()) {
if (body.GetUserData() != null) {
//根据刚体的坐标个角度,更新绑定的userData
body.GetUserData().x = body.GetPosition().x * 30;
body.GetUserData().y = body.GetPosition().y * 30;
body.GetUserData().rotation = body.GetAngle() * 180 / Math.PI;
}
}
}
/**
* 创建多边形刚体
* @return
*/
private function createPolygon():b2Body
{
//1.创建刚体需求b2BodyDef
var bodyRequest:b2BodyDef = new b2BodyDef();
bodyRequest.type = b2Body.b2_dynamicBody;
bodyRequest.position.Set(0 , 0);//记得米和像素的转换关系
//2.Box2D世界工厂更具需求创建createBody()生产刚体
var body:b2Body=world.CreateBody(bodyRequest);
//3.创建敢提形状需求b2ShapeDef的子类
//创建矩形刚体形状需求
var fixtureRequest:b2FixtureDef = new b2FixtureDef();
fixtureRequest.density = 3;
fixtureRequest.friction = 0.3;
fixtureRequest.restitution = 0.2;
//创建一个Separator对象
var separator:b2Separator = new b2Separator();
//验证顶点是否符合创建多边形的标准
var validate:int = separator.Validate(verticesList);
//如果是顶点因非顺时针不符标准,则反转数组中的顶点顺序
if (validate == 2) {
verticesList.reverse();
}else if (validate != 0) {
//如果不符合多边形标准,跳出
return null;
}
//将顶点分解成多个凸多边形,组合成复杂的多边形
separator.Separate(body, fixtureRequest, verticesList);
return body;
}
/**
* 根据存储的路径、顶点绘制多边形图形,并绑定到刚体中
* @param body 要添加贴图的刚体
* @param commandVector 存储绘图命令的刚体
* @param posVector 存储绘图顶点的刚体
*/
private function createUseData(body:b2Body, commandVector:Vector.<int>, posVector:Vector.<Number>):void {
//创建一个空白的画布
var canvas:Sprite = new Sprite();
canvas.graphics.clear();
canvas.graphics.lineStyle(3, 0x4D370B);
//利用存储的命令和顶点绘制并填充图形
canvas.graphics.beginBitmapFill(woodBmd);
canvas.graphics.drawPath(commandVector, posVector);
canvas.graphics.endFill();
addChild(canvas);
//把画布绑定到刚体中
body.SetUserData(canvas);
}
}
}
如上面的代码所示,多边形刚体贴图同样是用body.GetPosition()和body.GetAngle()方法实时更新userData的坐标和角度。
不用的是,刚体和userData的注册点都在舞台的左上角,为什么会这样呢?仔细看一下,你会发现:
第152行: bodyRequest.position.Set(0 , 0);//刚体的坐标是(0,0)
第192行:addChild(canvas);//canvas添加到舞台后,默认的坐标也是(0,0)
所以,未来在项目中给多边形贴图时,要注意这个问题哦。