JBox2d学习笔记五:android中多边形的创建及canvas重绘

JBox2d学习笔记5:android中多边形的创建及canvas重绘

虽然网上对于Jbox2d的教程非常的少,但是我们可以通过学习JBox2d学习笔记4:Jbox2d-2.2.1.1中下载的官方源码,通过对其中的例子的学习,我们肯定可以熟练掌握Jbox2d物理引擎。它能够对于现实世界的模拟相当到位。废话不多说,下面总结一下,这几天学习到的知识。对于Jbox2d中的一个物体的创建,我有了我自己的理解。对于物理世界的任何一个物体他都有一定的形状,在jbox2d中就有EdgeShape、CircleShpe、PolygonShape、ChainShape。对于物体的定义常用的就是EdgeShape、CircleShpe和PolygonShape,至于ChainShape我还没有了解到这里来。

EdgeShape是定义线性形状的物体,一般用于定义一个封闭的空间。

CircleShape是定义一个原型的形状,常用来定义一个球的形状。PolygonShape是对一个多边形的形状定义,PolygonShape是通过给定的一些点集的凸包来描述的。在jbox2d中的任何一个到单独的PolygonShape都只能定义一个凸多边形,若要定义一个不规则的形状可以用多个PolygonShape来组合构成。定义了多边形的形状以后,对于物体的一些固定的物理属性我们还需要来对其添加定义,FixtureDef就是对起物体的物理性质的定义,FixtureDef可以设置物体的shape(可以设置为前面提到的CircleShpe、PolygonShape和ChainShape),density(物体的密度),friction(摩擦),弹性系数(restitution)。然后接下来就是对物体的定义了,BodyDef定义一个物体,然后设置它的一些属性,主要有position(位置),type(物体的类型,BodyType.DYNAMIC动态的,代表物体时可以东的,BodyType.STATIC代表物体是静止的),angle(物体的旋转角度)。然后根据BodyDef创建物体,最后将物体的固定属性FixtureDef添加进入Body,这样一个物理世界的物体就创建成功了。下面是一个任意多边形物体的创建代码

/**
	 * 创建多边形物体
	 */
	private void createBody() {
		//定义物体的形状是多边形形状
		PolygonShape shape = new PolygonShape();
		shape.set(vecs, edge);//根据点集凸包定义形状,vecs是点集,edge是边数

		//设置多边形物体的一些固定的物理属性
		FixtureDef fd = new FixtureDef();
		fd.shape = shape;
		fd.density = density;
		fd.friction = friction;
		fd.restitution = restitution;

		BodyDef bd = new BodyDef();
		bd.position.set(x, y);
		bd.type = BodyType.DYNAMIC;
		bd.angle = angle;

		//根据bodyDef创建物体到世界中
		Body body = world.createBody(bd);
		//设置好物体的固定属性
		body.createFixture(fd);
	}

虽然上面已经成功添加了多边形物体到模拟的物理世界中, 但是在android的界面上根本就无法显示出相应的形状,所以要想显示出对应的物体形状,就还需要利用android中的canvas将多边形图形绘制到屏幕上。为了能准确的获得物体在物理世界中的位置,我定义了一个类Polygon。

package com.hcd.jbox2d.demo;

import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;

/**
 * 
 * @author jvaeyhcd.com
 *
 */
public class Polygon {

	//定义多边形物体所在的世界
	private World world;
	//多边形物体的坐标
	private float x, y;
	//构成多边形的点
	private Vec2[] vecs;
	//物体的弹性系数
	private float restitution;
	//物体的密度
	private float density;
	//物体的旋转角度和物体的摩擦
	private float angle, friction;
	//自定义多边形物体的边数
	private int edge;
	private Body body;

	public Polygon(World world, float x, float y, Vec2[] vecs, int edge,  float restitution, float density, float friction, float angle) {
		this.world = world;
		this.x = x;
		this.y = y;
		this.vecs = vecs;
		this.restitution = restitution;
		this.density = density;
		this.angle = angle;
		this.edge = edge;
		createBody();
	}
	/**
	 * 创建多边形物体
	 */
	private void createBody() {
		//定义物体的形状是多边形形状
		PolygonShape shape = new PolygonShape();
		shape.set(vecs, edge);

		//设置多边形物体的一些固定的物理属性
		FixtureDef fd = new FixtureDef();
		fd.shape = shape;
		fd.density = density;
		fd.friction = friction;
		fd.restitution = restitution;

		BodyDef bd = new BodyDef();
		bd.position.set(x, y);
		bd.type = BodyType.DYNAMIC;
		bd.angle = angle;

		//根据bodyDef创建物体到世界中
		body = world.createBody(bd);
		//设置好物体的固定属性
		body.createFixture(fd);
	}

	public World getWorld() {
		return world;
	}

	public void setWorld(World world) {
		this.world = world;
	}

	public float getX() {
		return body.getPosition().x;
	}

	public void setX(float x) {
		this.x = x;
	}

	public float getY() {
		return body.getPosition().y;
	}

	public void setY(float y) {
		this.y = y;
	}

	public Vec2[] getVecs() {
		return vecs;
	}

	public void setVecs(Vec2[] vecs) {
		this.vecs = vecs;
	}

	public float getRestitution() {
		return restitution;
	}

	public void setRestitution(float restitution) {
		this.restitution = restitution;
	}

	public float getDensity() {
		return density;
	}

	public void setDensity(float density) {
		this.density = density;
	}

	public float getAngle() {
		return body.getAngle();
	}

	public void setAngle(float angle) {
		this.angle = angle;
	}

	public float getFriction() {
		return friction;
	}

	public void setFriction(float friction) {
		this.friction = friction;
	}

	public int getEdge() {
		return edge;
	}

	public void setEdge(int edge) {
		this.edge = edge;
	}

	public Body getBody() {
		return body;
	}

	public void setBody(Body body) {
		this.body = body;
	}
}

然后根据调用Polygon中的数据来在屏幕上画出多边形的形状。

private void drawPolygon(Polygon polygon) {
			paint.setAntiAlias(true);
			paint.setColor(Color.rgb(111, 100, 10));
			Path path = new Path();
			Vec2[] vecs = polygon.getVecs();
			Vec2[] tmp = new Vec2[polygon.getEdge()];

			//根据旋转的角度获得各个点的坐标
			for (int i = 0; i < polygon.getEdge(); i++) {
				float vecX = (float) (vecs[i].x * Math.cos(polygon.getAngle()) - vecs[i].y
						* Math.sin(polygon.getAngle()));
				float vecY = (float) (vecs[i].x * Math.sin(polygon.getAngle()) + vecs[i].y
						* Math.cos(polygon.getAngle()));
				tmp[i] = new Vec2(vecX, vecY);
			}

			path.moveTo((polygon.getX() + tmp[0].x) * RATE,
					(polygon.getY() + tmp[0].y) * RATE);// 此点为多边形的起点
			for (int i = 1; i < polygon.getEdge(); i++) {
				path.lineTo((polygon.getX() + tmp[i].x) * RATE,
						(polygon.getY() + tmp[i].y) * RATE);
			}
			path.close(); // 使这些点构成封闭的多边形
			canvas.drawPath(path, paint);
		}

由于上面画多边形的过程是用path画的,所以一定要现根据跟定的点击求出他的凸包的集合并且按照一致的顺序排列,不然画出来的形状很奇诡,所以在activity中创建多边形点集的时候要先求出他的点集的凸包。

private void createPolygon2() {
		Vec2[] vecs = new Vec2[8];
		vecs[0] = new Vec2(40 / RATE, 0);
		vecs[1] = new Vec2(40 / RATE, 40 / RATE);
		vecs[2] = new Vec2(0, 40 / RATE);
		vecs[3] = new Vec2(0, 0);
		vecs[4] = new Vec2(10 / RATE, 0);
		vecs[5] = new Vec2(50 / RATE, 0);
		vecs[6] = new Vec2(30 / RATE, 70 / RATE);
		vecs[7] = new Vec2(30 / RATE, 30 / RATE);
		Vec2[] vecst = GrahamScanUtils.getGrahamScan(vecs);//求出点集的凸包
		polygon2 = new Polygon(world, 100 / RATE, 100 / RATE, vecst, vecst.length, 0.5f,
				0.5f, 0.5f, 0.0f);
	}

求点集的凸包可以根据grmham‘s Scan算法来封装一个工具类GrahamScanUtils

package com.hcd.jbox2d.utils;

import org.jbox2d.common.Vec2;

/**
 * 求解凸包集合的工具类
 * 给定的一个点的集合,所构成的凸包的集合
 * 我所理解的凸包就是给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它能包含点集中所有点的
 * @author jvaeyhcd.com
 *
 */
public class GrahamScanUtils {

	private static Vec2[] vecs, vecst;
	private static int top;

	/**
	 * 计算两点之间的距离
	 * @param a
	 * @param b
	 * @return 两点的距离
	 */
	private static double distance(Vec2 a, Vec2 b) {
		return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
	}

	/**
	 * 求线段v0v1与线段v0v2的叉积
	 * @param v1
	 * @param v2
	 * @param v0
	 * @return 叉积,如果大于0,则说明直线v0v1在v0v2的顺时针方向,即v0v1在v0v2下面
	 */
	private static double cross(Vec2 v1, Vec2 v2, Vec2 v0) {
		return (v1.x - v0.x) * (v2.y - v0.y) - (v2.x - v0.x) * (v1.y - v0.y);
	}
	private static void grahamScan() {
		Vec2 tmp;
		int k = 0;
		//找出最左下角的点作为起点
		for (int i = 1; i < vecs.length; ++i) {
			if ((vecs[i].y < vecs[k].y) || ((vecs[i].y == vecs[k].y) && (vecs[i].x < vecs[k].x))) {
				k = i;
			}
		}
		tmp = vecs[0];
		vecs[0] = vecs[k];
		vecs[k] = tmp;

		//按照级角大小逆时针排序
		for (int i = 1; i < vecs.length; ++i) {
			k = i;
			for (int j = i + 1; j < vecs.length; ++j) {
				if ((cross(vecs[j], vecs[k], vecs[0]) > 0) || ((cross(vecs[j], vecs[k], vecs[0]) == 0) && (distance(vecs[0], vecs[j]) < distance(vecs[0], vecs[k])))) {
					k = j;
				}
			}
			tmp = vecs[i];
			vecs[i] = vecs[k];
			vecs[k] = tmp;
		}

		//先把极交最小的3个存入栈中
		top = -1;
		vecst[++top] = vecs[0];
		vecst[++top] = vecs[1];
		vecst[++top] = vecs[2];

		for (int i = 3; i < vecs.length; ++i) {
			//当叉积为0时说明三点共线,共线的点也保留在凸包中
			while(cross(vecs[i], vecst[top], vecst[top - 1]) > 0){
				top--;
			}
			vecst[++top] = vecs[i];
		}
		top++;
	}

	public static Vec2[] getGrahamScan(Vec2[] vec) {
		vecs = vec;
		vecst = new Vec2[vecs.length];
		grahamScan();
		Vec2[] vecsres = new Vec2[top];
		for (int i = 0; i < top; i++) {
			vecsres[i] = vecst[i];
		}
		return vecsres;
	}
}

原创文章,转载请注明: 转载自Jvaeyhcd's Blog

本文链接地址: http://jvaeyhcd.com/blog/116