前言
众所周知,Region是android graphics一族中比较低调的工具类,主要原因还是在碰撞检测方面存在一些不足,甚至可以说成事不足败事有余,以至于难以用于2D游戏开发。
用法
既然这么失败的工具为什么要介绍呢,主要我们介绍一下它能成事的部份,以及正确的用法。
- Region类能成事的部份主要还是Op布尔操作和矩阵操作,但是这个似乎又和Path的作用重合,另外一部分contain包含关系判断,contain能准确的判断点和矩形是不是被包含了,但是其他形状那就没办法了。
- 成事不足败事有余的部份,是quickXXX 快速检测方法,返回值true-能确保物体没有碰撞,但false无法确保是不是已经碰撞了,换句话说true是100%没碰撞,但是false还需要你自己进一步确认,不过这点可以作为减少判断的优化方法,但不是判定方法。
学习的意义 对于一些粒子,我们不太关注大小,这个时候是可以利用中心点去检测的,另外quickXXX其实用处不大,不过可以减少一部分代码,但是我们仍然还需要了解它的用法。
非Path用法
对于非Path用法,Region还是相当简单的,直接使用set方法即可
java
mainRegion.set((int) -radius, (int) -radius, (int) radius, (int) radius);
Path方法
这个用法比较奇怪,需要2个参数,最后一个是Region类,弄不好就是鸡生蛋蛋生鸡一样令人迷惑,第二个可以看作被裁剪的区域,如下操作,求并集区域。不过话说回来,这个意义在哪里?
java
circlePath.reset();
circlePath.addCircle(x- width/2f,y - height/2f,10, Path.Direction.CCW);
circleRegion.setPath(circlePath,mainRegion);
小试一下
实现开头的图片效果
定义一些变量
java
private float x; //x事件坐标
private float y; //y事件坐标
//所以形状
Path[] objectPaths = new Path[5];
//形状区域检测
Region objectRegion = new Region();
//小圆球区域
Region circleRegion = new Region();
//小圆
Path circlePath = new Path();
//绘制区域
Region mainRegion = new Region();
构建物体
三角形、圆等物体
java
for (int i = 0; i < objectPaths.length; i++) {
Path path = objectPaths[i];
if (path == null) {
path = new Path();
objectPaths[i] = path;
} else {
path.reset();
}
}
Path path = objectPaths[0];
path.moveTo(radius / 2, -radius / 2);
path.lineTo(0, -radius);
path.lineTo(radius / 2, -radius);
path.close();
path = objectPaths[1];
path.moveTo(-radius / 2, radius / 2);
path.lineTo(-radius / 2 - 100, radius / 2);
path.arcTo(-radius / 2 - 100, radius / 2, -radius / 2, radius / 2 + 100, 0, 180, false);
path.lineTo(-radius / 2, radius / 2);
path.close();
path = objectPaths[2];
path.addCircle(-radius + 200f, -radius + 100f, 50f, Path.Direction.CCW);
path = objectPaths[3];
path.addRoundRect(-radius / 2, -radius / 2, -radius / 2 + 20, 0, 10, 10, Path.Direction.CCW);
path = objectPaths[4];
path.addRect(120, 120, 200, 200, Path.Direction.CCW);
区域检测
检测是否发生了碰撞,准确度不高,但还能凑合
ini
circlePath.reset();
circlePath.addCircle(x- width/2f,y - height/2f,10, Path.Direction.CCW);
circleRegion.setPath(circlePath,mainRegion);
mCommonPaint.setColor(Color.CYAN);
for (int i = 0; i < objectPaths.length; i++) {
objectRegion.setPath(objectPaths[i],mainRegion);
if(!objectRegion.quickReject(circleRegion)){
Log.d("RegionView"," 可能发生了碰撞");
mCommonPaint.setColor(Color.YELLOW);
}else{
mCommonPaint.setColor(Color.CYAN);
}
canvas.drawPath(objectPaths[i], mCommonPaint);
}
总结
到这里结束了,对于Region类,我们其实最有用的还是contain类方法,contain(x,y)准确度很高,便于我们检测粒子是不是在几何内部,本篇没有涉及到,但是有些绘制地图类的应用会使用到这个,主要原因是地图纵横交错,不是矩形也不是圆形,甚至还有飞地,因此使用containXXX是最好的方案之一。
全部代码
ini
public class RegionView extends View {
private final DisplayMetrics mDM;
private TextPaint mCommonPaint;
public RegionView(Context context) {
this(context, null);
}
public RegionView(Context context, AttributeSet attrs) {
super(context, attrs);
mDM = getResources().getDisplayMetrics();
initPaint();
setClickable(true); //触发hotspot
}
private void initPaint() {
//否则提供给外部纹理绘制
mCommonPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mCommonPaint.setAntiAlias(true);
mCommonPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mCommonPaint.setStrokeCap(Paint.Cap.ROUND);
mCommonPaint.setFilterBitmap(true);
mCommonPaint.setDither(true);
mCommonPaint.setStrokeWidth(dp2px(20));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
widthSize = mDM.widthPixels / 2;
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
heightSize = widthSize / 2;
}
setMeasuredDimension(widthSize, heightSize);
}
private float x;
private float y;
//所以形状
Path[] objectPaths = new Path[5];
//形状区域检测
Region objectRegion = new Region();
//小圆球区域
Region circleRegion = new Region();
//小圆
Path circlePath = new Path();
//绘制区域
Region mainRegion = new Region();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
if (width < 1 || height < 1) {
return;
}
int save = canvas.save();
canvas.translate(width / 2f, height / 2f);
float radius = Math.min(width / 2f, height / 2f);
mainRegion.set((int) -radius, (int) -radius, (int) radius, (int) radius);
for (int i = 0; i < objectPaths.length; i++) {
Path path = objectPaths[i];
if (path == null) {
path = new Path();
objectPaths[i] = path;
} else {
path.reset();
}
}
Path path = objectPaths[0];
path.moveTo(radius / 2, -radius / 2);
path.lineTo(0, -radius);
path.lineTo(radius / 2, -radius);
path.close();
path = objectPaths[1];
path.moveTo(-radius / 2, radius / 2);
path.lineTo(-radius / 2 - 100, radius / 2);
path.arcTo(-radius / 2 - 100, radius / 2, -radius / 2, radius / 2 + 100, 0, 180, false);
path.lineTo(-radius / 2, radius / 2);
path.close();
path = objectPaths[2];
path.addCircle(-radius + 200f, -radius + 100f, 50f, Path.Direction.CCW);
path = objectPaths[3];
path.addRoundRect(-radius / 2, -radius / 2, -radius / 2 + 20, 0, 10, 10, Path.Direction.CCW);
path = objectPaths[4];
path.addRect(120, 120, 200, 200, Path.Direction.CCW);
circlePath.reset();
circlePath.addCircle(x- width/2f,y - height/2f,10, Path.Direction.CCW);
circleRegion.setPath(circlePath,mainRegion);
mCommonPaint.setColor(Color.CYAN);
for (int i = 0; i < objectPaths.length; i++) {
objectRegion.setPath(objectPaths[i],mainRegion);
if(!objectRegion.quickReject(circleRegion)){
Log.d("RegionView"," 可能发生了碰撞");
mCommonPaint.setColor(Color.YELLOW);
}else{
mCommonPaint.setColor(Color.CYAN);
}
canvas.drawPath(objectPaths[i], mCommonPaint);
}
mCommonPaint.setColor(Color.WHITE);
canvas.drawPath(circlePath,mCommonPaint);
canvas.restoreToCount(save);
}
@Override
public void dispatchDrawableHotspotChanged(float x, float y) {
super.dispatchDrawableHotspotChanged(x, y);
this.x = x;
this.y = y;
postInvalidate();
}
@Override
protected void dispatchSetPressed(boolean pressed) {
super.dispatchSetPressed(pressed);
postInvalidate();
}
public float dp2px(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDM);
}
public static int argb(float red, float green, float blue) {
return ((int) (1 * 255.0f + 0.5f) << 24) |
((int) (red * 255.0f + 0.5f) << 16) |
((int) (green * 255.0f + 0.5f) << 8) |
(int) (blue * 255.0f + 0.5f);
}
}