相关资料
雨松MOMO带你走进游戏开发的世界之主角的移动与地图的平滑滚动
http://blog.csdn.net/xys289187120/article/details/6649274
卡马克卷轴算法研究_地图双缓冲
http://wenku.baidu.com/view/a51f0b8ca0116c175f0e48c3.html
矩形相交判断
http://hi.baidu.com/jiyeqian/blog/item/c14e52c24794b4170ff47715.html
实现原理
将地图每次移动时的相交区域作为重复利用资源(缓冲区),如图a1b1c1d1(移动前)和a2b2c2d2(移动后)相交得到的矩形为aabbccdd。
根据判断矩形是否相交公式为:
通过中心点距离判断(中心点距离 X<= 矩形1宽度/2 + 矩形2宽度/2)且(中心点距离 Y<= 矩形1高度/2 + 矩形2高度/2)
获取相交矩形公式为:
如果相交,则相交矩形的左上角坐标为(max(a1.x,a2.x),max(a1.y,a2.y))和右下角坐标(min(d1.x,d2.x),min(d1.y,d2.y))
当每次屏幕移动时,实际是将地图移动在相反的方向移动,移动后根据当前屏幕所在地图中的相对位置,将屏幕中的区域贴砖。在每次移动中实际不同的区域只是非AaBbCcDd相交区域,所以可以在贴砖时增加判断当前贴砖区域是否是在相交区域,如果是则不用贴砖,只把相交区域外的区域进行贴砖即可,实际就是少贴砖,减少CPU的运算量。
注意采用该算法后,占用内寸不会减少,由于卡马克卷轴涉及到很多坐标运算,所以在当前内存消耗中,有时候会多占用一些内存资源。
具体实现代码如下:
/**
* 滚动屏幕(移动距离可以利用onTouchEvent获得)
* @param distanceX X方向移动距离
* @param distanceY Y方向移动距离
*/
public void scrollMap(float distanceX, float distanceY) {
// TODO Auto-generated method stub
//触摸位移灵敏度
int newScrLeft = screenInMapLoc.x + (int)distanceX/5;
int newScrTop = screenInMapLoc.y + (int)distanceY/5;
int mapWidth = mMapView[0].length * TILE_WIDTH;
int mapHeight = mMapView.length * TILE_HEIGHT;
//设置后则是考虑相交区域
lastScrInMapLoc = new Point(screenInMapLoc.x,screenInMapLoc.y);
if(newScrLeft>0 && newScrLeft + SCREEN_WIDTH<mapWidth){
screenInMapLoc.x = newScrLeft;
}
else if(newScrLeft<=0){
screenInMapLoc.x = 0;
}
else{
screenInMapLoc.x = mapWidth - SCREEN_WIDTH;
}
if(newScrTop>0 && newScrTop + SCREEN_HEIGHT<mapHeight){
screenInMapLoc.y = newScrTop;
}
else if(newScrTop<=0){
screenInMapLoc.y = 0;
}
else{
screenInMapLoc.y = mapHeight - SCREEN_HEIGHT;
}
Long begin = System.currentTimeMillis();
bufferImage = drawBufferImage();
Long end = System.currentTimeMillis();
Log.d("cramack","耗时:" + (end - begin) + "ms");
}
private Bitmap drawBufferImage(){
//与屏幕大小一致的缓冲图
Bitmap _bufferImage = Bitmap.createBitmap(TILE_X_COUNT*TILE_WIDTH,TILE_Y_COUNT*TILE_HEIGHT,Config.RGB_565);
Rect itscRect = null;
Bitmap itscImage = null;
//当前屏幕左上角所处地图中的网格坐标
int startX = screenInMapLoc.x / TILE_WIDTH;
int startY = screenInMapLoc.y / TILE_HEIGHT;
offsetPt.x = -screenInMapLoc.x % TILE_WIDTH;
offsetPt.y = -screenInMapLoc.y % TILE_HEIGHT;
Rect newScrRect = new Rect(screenInMapLoc.x,
screenInMapLoc.y,
screenInMapLoc.x + SCREEN_WIDTH,
screenInMapLoc.y + SCREEN_HEIGHT);
if(lastScrInMapLoc!=null){
Rect lastScrRect = new Rect(lastScrInMapLoc.x,lastScrInMapLoc.y,
lastScrInMapLoc.x + SCREEN_WIDTH,
lastScrInMapLoc.y + SCREEN_HEIGHT);
//两矩形交集
itscRect = intersectRect(lastScrRect,newScrRect);
itscImage = Bitmap.createBitmap(itscRect.width(),
itscRect.height(),Config.RGB_565);
//获取重复区域中上一次缓冲区中的图像
Canvas itscCanvas = new Canvas(itscImage);
itscCanvas.save();
itscCanvas.clipRect(new Rect(0,0,itscRect.width(),itscRect.height()));
itscCanvas.drawBitmap(bufferImage,
lastScrRect.left - itscRect.left,lastScrRect.top-itscRect.top,defaultPaint);
itscCanvas.restore();
}
Canvas canvas = new Canvas(_bufferImage);
defaultPaint.setColor(Color.BLACK);
canvas.drawRect(0, 0,
_bufferImage.getWidth(),_bufferImage.getHeight(),defaultPaint);
int rowCount = mMapView.length;
int colCount = mMapView[0].length;
//在缓冲图中贴砖
for (int j = 0; j < TILE_X_COUNT; j++) {
for (int i = 0; i < TILE_Y_COUNT; i++) {
int left = j * TILE_WIDTH + offsetPt.x;
int top = i * TILE_HEIGHT + offsetPt.y;
//剪切显示要贴砖的区域
Rect clipRect = new Rect(left,top,left+TILE_WIDTH,top+TILE_HEIGHT);
//贴砖区域在地图中的区域
Rect absClipRect = new Rect(screenInMapLoc.x + left,
screenInMapLoc.y + top,
screenInMapLoc.x + left+TILE_WIDTH,
screenInMapLoc.y + top+TILE_HEIGHT);
//如果当前要贴砖区域不属于移动的重复区域则贴砖
if (!includeRect(itscRect,absClipRect)) {
canvas.save();
canvas.clipRect(clipRect);
//将素材图片要贴砖部分左顶点对齐后偏移到缓冲图中的贴钻区域
if(startY+i<rowCount && startX+j<colCount){
Point lay1TilePt = getTilePt(bufferMap,mMapView[startY+i][startX+j]);
canvas.drawBitmap(bufferMap,
-lay1TilePt.x*TILE_WIDTH + left,
-lay1TilePt.y*TILE_HEIGHT + top,
defaultPaint);
Point lay2TilePt = getTilePt(bufferMap,mMapActor[startY+i][startX+j]);
canvas.drawBitmap(bufferMap,
-lay2TilePt.x*TILE_WIDTH + left,
-lay2TilePt.y*TILE_HEIGHT + top,
defaultPaint);
}
canvas.restore();
}
}
}
if (lastScrInMapLoc!=null && itscRect!=null) {
//在新坐标下画重复(相交)区域
canvas.save();
int mLeft = itscRect.left - newScrRect.left;
int mTop = itscRect.top - newScrRect.top;
canvas.clipRect(new Rect(mLeft,mTop,mLeft+itscRect.width(),
mTop + itscRect.height()));
canvas.drawBitmap(itscImage,mLeft,mTop,defaultPaint);
canvas.restore();
lastScrInMapLoc = new Point(screenInMapLoc.x,screenInMapLoc.y);
}
return _bufferImage;
}
/**
* 对屏幕进行刷新
*/
public void run() {
// TODO Auto-generated method stub
Paint tipPaint = new Paint();
tipPaint.setColor(Color.RED);
tipPaint.setTextSize(32);
while(mIsRunning){
synchronized (mSurfaceHolder) {
Canvas canvas = mSurfaceHolder.lockCanvas();
Long begin = System.currentTimeMillis();
drawBackground(canvas);
Long end = System.currentTimeMillis();
String tip = "interval:" + (end-begin) + "ms";
canvas.drawText(tip , SCREEN_WIDTH - tipPaint.measureText(tip) , SCREEN_HEIGHT - 40 , tipPaint);
mSurfaceHolder.unlockCanvasAndPost(canvas);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
结论
在模拟器中未使用卡马克卷轴算法:
在模拟器中使用卡马克卷轴算法:
采用卡马克卷轴算法后,CPU执行耗时有明显减少。
实现代码: