卡马克卷轴算法实现android版

相关资料

雨松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执行耗时有明显减少。


实现代码:

点击下载

发布了38 篇原创文章 · 获赞 4 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/tomatozq/article/details/7194265