相关资料
雨松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执行耗时有明显减少。
实现代码: