遊戲地圖的繪製--卡馬克演算法(轉)
地圖是遊戲中必不可少的一種預算元素,尤其是在RPG、ACT等型別的遊戲中作用更為重要,一個漂亮的地圖效果和一個流暢的捲動速度會大大增加玩家的遊戲體驗。而遊戲中地圖滾動的重繪有多種演算法,由於手機效能的限制和開發週期等其他非技術條件,需要根據情況靈活選擇所需的技術。本文將主要介紹如何使用OPhone API來繪製2D遊戲中的場景,也即地圖的繪製方法。
地圖繪製及滾動的常用演算法
無縫圖片滾動畫法
最簡單的一種畫地圖方法,無需使用陣列,只需要使用一張無縫的背景圖片,在螢幕上繪製兩次,以此來實現最簡單的地圖滾動效果和圖片的重複使用以節約資源。
如下圖,紅色虛線部分為螢幕,使用一個偏移量在螢幕中錯開位置貼上兩次圖片,通過不斷改變偏移量的大小來實現動畫效果。
程式碼舉例:
view plain
copy to clipboard
?
//imgBack圖片物件
//posX圖片在X軸方向上的偏移量
canvas.drawBitmap(imgBack, -posX, 0, paint);
canvas.drawBitmap(imgBack, imgBack.getHeight()+posX, 0, paint);
if(posX==-imgBack.getHeight())
posX=0; //imgBack圖片物件 //posX圖片在X軸方向上的偏移量 canvas.drawBitmap(imgBack, -posX, 0, paint); canvas.drawBitmap(imgBack, imgBack.getHeight()+posX, 0, paint); if(posX==-imgBack.getHeight()) posX=0;
優點與侷限:此演算法非常簡單,由於是單張圖片反覆滾動生成的背景圖片,所以對於美術人員的限制較少,利於發揮,而且外觀效果好。但因為不是地圖Tile組成的,資源複用率不高,只能用於生成不太複雜的地圖。而且由於沒有Tile的存在,無法針對不同的Tile計算碰撞。最終使得這種畫法只能用於繪製簡單螢幕背景圖片,而無法用在有複雜物理碰撞的地圖層。
裁剪區畫法
我們平時所玩的遊戲一般場景都是大於螢幕的尺寸的,也就是說在遊戲中的主角移動的時候,後面的地圖將會隨著主角的位置變化而發生移動,我們稱之為地圖的卷軸效果。而對諸如RPG,ACT這類地圖場景比較大的型別的遊戲來說,地圖都不是一整張的背景圖直接使用,而是採用一種“拼接”的方式,這樣做既能節省記憶體的佔用,同時也能使圖片資源的利用率達到最大化。下圖就是2D遊戲常用的圖片樣式:
從圖中我們能夠看出,我們可以把整張圖片進行分割,並將分割後的圖片進行編號,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
為每塊圖素編號之後,就可以設計自己的地圖了。這裡需要使用一種叫做“地圖編輯器”的工具軟體。我們這裡使用“mapwin”進行地圖的設計,使用步驟如下圖所示:
上面的四個輸入框分別代表地圖小塊的寬度和高度,以及我們要建立的整個場景的水平和垂直的地圖塊數,輸入後點擊“OK”如下圖所示:
下面需要引入一張圖片,引入方法為“File——Import”,選取一張圖片並點選確定,隨後就能看到如下的圖片:
剩下的工作想必你就可以想到了,用滑鼠在右邊區域選取一個圖塊,然後將其放到左邊黑色區域中即可,拼接完的效果如下圖:
接下來要把地圖資料匯出,匯出放下如下圖:
最後我們需要的資料是這樣的:
const short ss_map0[10][10] = {
{ 1, 1, 1, 1, 1, 1, 1, 5, 1, 1 },
{ 10, 10, 10, 1, 1, 1, 1, 1, 1, 1 },
{ 8, 8, 8, 1, 1, 1, 1, 1, 1, 1 },
{ 9, 9, 9, 1, 1, 1, 1, 14, 15, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 16, 17, 1 },
{ 1, 1, 1, 6, 11, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 11, 1, 1, 1, 21, 1 },
{ 1, 4, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
實際上就是一個二維陣列,陣列中的數字即為地圖塊的索引號。
使用二維陣列儲存地圖資訊,另外有一張圖片素材,根據地圖陣列的不同下標,配合public boolean clipRect(float left, float top, float right, float bottom,Region.Op op) 裁剪區方法,將對應的Tile顯示在正確的位置上。
如下圖所示,紅色虛線部分為螢幕,紅色實線為裁剪區,通過讀取地圖陣列,將相應的位置設定為裁剪區,並用將圖片素材相對於裁剪區偏移一定x,y位置的方法,使得要繪製的Tile正好對應出現在裁剪區中。
程式碼舉例:
view plain
copy to clipboard
?
// 繪製切割圖片
public void drawClipImg(int XDest, int YDest, int Width, int Height,
int XSrc, int YSrc, Bitmap img, Paint g,Canvas canvas)
{
canvas.clipRect(XDest, YDest, XDest + Width, YDest + Height,
Region.Op.REPLACE);
canvas.drawBitmap(img, XDest - XSrc, YDest - YSrc, g);
canvas.clipRect(0, 0, Const.SCREEN_WIDTH, Const.SCREEN_HEIGHT,
Region.Op.REPLACE);
} // 繪製切割圖片 public void drawClipImg(int XDest, int YDest, int Width, int Height, int XSrc, int YSrc, Bitmap img, Paint g,Canvas canvas) { canvas.clipRect(XDest, YDest, XDest + Width, YDest + Height, Region.Op.REPLACE); canvas.drawBitmap(img, XDest - XSrc, YDest - YSrc, g); canvas.clipRect(0, 0, Const.SCREEN_WIDTH, Const.SCREEN_HEIGHT, Region.Op.REPLACE); }
相對於前一種畫法,圖片資源的利用率提高了很多,可以繪製很複雜的地圖。由於Tile的存在,可以針對不同的Tile計算碰撞,可以用於地圖物理層的繪製。
最常見的地圖繪製優化——只繪製當前螢幕
上面的繪製方法都是將整個地圖的資料全部畫出來的,這樣做實際上也存在很大的浪費,因為玩家實際上只能看見螢幕中的一塊區域,其他大部分的地圖即使被繪製也不能反映到螢幕上,反而因為這個不必要的步驟大大增加了CPU的負擔,從而影響了遊戲的流暢程度。因此,在實際開發中,常用的優化方法就是隻繪製當前螢幕的地圖塊。程式碼如下:
view plain
copy to clipboard
?
//計算單元格起始位置下標
int startIndexX =leftTopY/ MAP_TILE_SIZE;
int startIndexY =leftTopX/ MAP_TILE_SIZE;
//再使用上面得到的資料修改雙迴圈繪製的條件即可,
for (int i = startIndexX; i 1; i++)
for (int j = startIndexY; j 1; j++) //計算單元格起始位置下標 int startIndexX =leftTopY/ MAP_TILE_SIZE; int startIndexY =leftTopX/ MAP_TILE_SIZE; //再使用上面得到的資料修改雙迴圈繪製的條件即可, for (int i = startIndexX; i
卡馬克卷軸演算法的引入
上面的演算法雖然在一定程度上解決了地圖繪製的效率問題,但對於某些資源嚴重不足的手機,或者由於地圖塊比較小、迴圈次數過多的情況,仍然會造成畫圖時螢幕閃爍。因此,在這種情況下,仍然需要對上述演算法做進一步的優化。
不論採用哪種優化演算法,一個基本的思路就是儘量減少繪製的次數,從而減少對系統資源的消耗。卡馬克卷軸演算法就是這樣演算法的一個經典例子。
單方向卷軸
對於橫版遊戲來說,如果角色向右側移動,則地圖向左側滾動。由於角色每次移動若干個步長,因此地圖中新畫出的區域寬度也為若干個畫素,那麼如果讓系統重繪所有螢幕區域,很明顯,大部分割槽域都是和上一螢幕區域相同的,如此造成成了資源的浪費。而卡馬克演算法的思路就是——如果上一次繪製過的地圖也能夠部分重用到本次地圖繪製上來就好了。那麼很容易想到在記憶體中建立一個和螢幕一樣大或略大的緩衝區即可很好的完成這個設想。
由上圖可以看到,區域B為相同的地圖區域,這個區域在下一次螢幕重繪時,可以被重新利用。區域A是在下一次螢幕重繪中不被採用的區域,這區域應當被捨棄,但是如果稍微留意一下的話,不難發現區域A和區域C的面積大小其實居然是一樣的。
那麼如果建立一個和螢幕大小相同的緩衝,在其被捨棄掉的繪製區域A中畫上新的區域C,再把區域B和區域C拼合到螢幕上,是不是就能達到減少系統資源消耗的目的了呢?卡馬克卷軸的基本原理正是如此。
圖顯示了卡馬克卷軸的最基本原理,首先在記憶體中建立一塊和螢幕一樣大小(或略大)的緩衝區。然後在本應由於地圖移動而被捨棄掉的區域1上面繪製,由於地圖滾動而出現的新地圖區域。最後把兩個區域按照地圖的實際位置拼合到螢幕上。
雙軸滾動的卡馬克卷軸
對於俯視遊戲,或者有Y軸捲動的遊戲來說,單單一個方向的地圖捲動並不夠用。那麼如果是出現兩個方向的捲動會如何呢。不必擔心,上面的思路演算法一樣能適應這種情況。
由上圖可以看到,區域D為相同的地圖區域,這個區域在下一次螢幕重繪時,可以被重新利用。區域ABC是在下一次螢幕重繪中不被採用的區域,可以在這個3個區域上繪製上下一次需要重繪的區域A’B’C’。再將繪製好的四個區域拼合到螢幕的對應位置。
上圖顯示了雙軸滾動的卡馬克卷軸的基本繪製原理,需要特別注意的是:在緩衝區的繪製順序和在螢幕上拼合的順序是完全相反的。
卡馬克演算法的實現
卡馬克卷軸緩衝畫法的一般步驟如下:
1. 初始化所有地圖資料,並且全屏繪製初始的地圖
2. 若人物移動,則呼叫攝像機演算法,修正地圖偏移量
3. 地圖偏移量不滿足地圖的邊界條件,就重繪緩衝區
4. 重繪緩衝區
5. 後臺緩衝區的四個子區按照順序畫到螢幕上
地圖類——Map的設計
欄位定義
view plain
copy to clipboard
?
//地圖資料
public byte mapData[][];
//移動緩衝區的當前座標視窗
public int sx,sy;
//地圖圖片
private Bitmap imgMap;
public GameView m_View;
//常量
public final static int MAP_TILE_SIZE = 24;
/** 緩衝區寬高,命名方式為:Carmack width or height */
private int bufWidth, bufHeight;
/** 緩衝區寬的圖塊數,與高的圖塊數*/
private int carTileWidth, carTileHeight;
/** 螢幕寬高命名方式為:screen width or height */
private int scrWidth, scrHeight;
/** 緩衝切割線,命名方式為:Carmack x or y */
private int carx, cary;
/** 地圖在緩衝區的X 、Y偏移量,命名方式為:map offset x or y */
private int mapOffx, mapOffy;
/** 緩衝區,命名方式為:Carmack buffer */
public Bitmap carBuffer;
/** 緩衝區畫筆,命名方式為:Carmack Graphics */
private Canvas carGp;
/** 緩衝區增大的大小(上下大小是一樣的) */
private int buffSize;
/** 圖片寬度的所切割的圖塊數量。 */
private int imageTileWidth;
/** 地圖圖片 */
private Bitmap mapImage;
Paint paint=new Paint();
/** 地圖陣列 */
private byte mapArray[][];
/** 圖塊大小,寬高一致 */
private int tileSize;
/** 圖塊的寬度數量,與高度數量 */
private int tileW, tileH;
/** 地圖的寬高 */
private int mapLastx, mapLasty; //地圖資料 public byte mapData[][]; //移動緩衝區的當前座標視窗 public int sx,sy; //地圖圖片 private Bitmap imgMap; public GameView m_View; //常量 public final static int MAP_TILE_SIZE = 24; /** 緩衝區寬高,命名方式為:Carmack width or height */ private int bufWidth, bufHeight; /** 緩衝區寬的圖塊數,與高的圖塊數*/ private int carTileWidth, carTileHeight; /** 螢幕寬高命名方式為:screen width or height */ private int scrWidth, scrHeight; /** 緩衝切割線,命名方式為:Carmack x or y */ private int carx, cary; /** 地圖在緩衝區的X 、Y偏移量,命名方式為:map offset x or y */ private int mapOffx, mapOffy; /** 緩衝區,命名方式為:Carmack buffer */ public Bitmap carBuffer; /** 緩衝區畫筆,命名方式為:Carmack Graphics */ private Canvas carGp; /** 緩衝區增大的大小(上下大小是一樣的) */ private int buffSize; /** 圖片寬度的所切割的圖塊數量。 */ private int imageTileWidth; /** 地圖圖片 */ private Bitmap mapImage; Paint paint=new Paint(); /** 地圖陣列 */ private byte mapArray[][]; /** 圖塊大小,寬高一致 */ private int tileSize; /** 圖塊的寬度數量,與高度數量 */ private int tileW, tileH; /** 地圖的寬高 */ private int mapLastx, mapLasty;
方法定義
CarMapBuffer(int, int, int, int)構造器
CarMapBuffer(int, int, int)構造器的代理
setMap(Image, byte[][])設定地圖引數
initBuffer()初始化繪製地圖
scroll(int, int)捲動地圖演算法
updateBuffer(int, int)繪製緩衝區
getIndexCarX()獲得切割線所在的圖塊索引X
getIndexCarY()獲得切割線所在的圖塊索引Y
getBufferCarX()獲得切割線在Buffer中的X位置
getBufferCarY()獲得切割線在Buffer中的Y位置
相關推薦
遊戲地圖的繪製--卡馬克演算法(轉)
地圖是遊戲中必不可少的一種預算元素,尤其是在RPG、ACT等型別的遊戲中作用更為重要,一個漂亮的地圖效果和一個流暢的捲動速度會大大增加玩家的遊戲體驗。而遊戲中地圖滾動的重繪有多種演算法,由於手機效能的限制和開發週期等其他非技術條件,需要根據情況靈活選擇所需的技術。本文
使用高德地圖繪制多邊形區域(轉)
ctype color 點擊 create user ins .com stroke 簡單 轉自:http://www.cnblogs.com/mengxingxinqing/p/6087201.html 最近遇到一個需求,在網頁上面編輯多邊形的區域,並且需要能夠判斷一個
網口掃盲一:網絡卡初步認識(轉) 網口掃盲一:網絡卡初步認識
網口掃盲一:網絡卡初步認識 網路介面卡又稱網絡卡或網路介面卡(NIC),英文名Network Interface Card.它是使計算機聯網的裝置.平常所說的網絡卡就是將PC機和LAN連線的網路介面卡.網絡卡(NIC) 插在計算機主機板插槽中,負責將使用者要傳遞的資料轉換為網路上其它裝置能夠識別
堪稱最好的A*演算法(轉)
如此好貼,不能不轉!原文地址:http://dev.gameres.com/Program/Abstract/Arithmetic/AmitAStar.mht 本文版權歸原作者、譯者所有,我只是轉貼;如果侵害到您的權益,請聯絡我,我將刪除本文。 基本上,這文章可以說是最佳A*演算法文件。極力推薦! Am
高德地圖API呼叫和標準(轉)
看過高德地圖API的同學都知道,高德地圖不同端呼叫是不一樣的,作為一個前端菜鳥,前一陣分別在pc端和移動端分別呼叫了高德地圖。情況是這個樣子的,PC端呢我們可以用高德API的web端的javascript程式碼。呼叫沒有問題,具體是這樣的: <!DOCTYPE htm
資料分析06sklearn資料集及K近鄰演算法(轉)
機器學習應用程式的步驟 (1)收集資料 我們可以使用很多方法收集樣本護具,如: 公司自有資料 製作網路爬蟲從網站上抽取資料、 第三方購買的資料 合作機構提供的資料 從RSS反饋或者API中得到資訊、裝置傳送過來的實測資料。 (2)準備輸入資料 得到資料之後
Dijkstra演算法(轉)
基本思想 通過Dijkstra計算圖G中的最短路徑時,需要指定起點s(即從頂點s開始計算)。 此外,引進兩個集合S和U。S的作用是記錄已求出最短路徑的頂點(以及相應的最短路徑長度),而U則是記錄還未求出最短
遞迴演算法(轉)
轉自:https://blog.csdn.net/feizaosyuacm/article/details/54919389目錄: 1.簡單遞迴定義 2.遞迴與迴圈的區別與聯絡 3.遞迴的經典應用1.簡單遞迴定義什麼叫遞迴?(先定義一個比較簡單的說法,為了理解,不一定對)遞迴
SM系列國密演算法(轉)
原文地址:科普一下SM系列國密演算法(從零開始學區塊鏈 189) 眾所周知,為了保障商用密碼的安全性,國家商用密碼管理辦公室制定了一系列密碼標準,包括SM1(SCB2)、SM2、SM3、SM4、SM7、SM9、祖沖之密碼演算法(ZUC)那等等。其中SM1、SM4、SM7、祖沖之密碼(ZUC)是對稱演算法;SM
Web開發來一發(九)Nginx負載均衡演算法(轉)
參考:http://www.cnblogs.com/lvgg/p/6140584.html Nginx負載均衡演算法主要以下幾種方式: 1、輪詢(預設方式) 每個請求按時間順序逐一分配到不同的後端服務,如果後端某臺伺服器宕機,自動剔除故障系統,使使用者訪問不受影響。 2、weight
五大常用演算法之一:分治演算法(轉)
分治演算法 一、基本概念 在電腦科學中,分治法是一種很重要的演算法。字面上的解釋是“分而治之”,就是把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合併。這個技巧是很多高效演算法的基礎,如排序演算法(快速排序,
傻子也能看懂的弗洛伊德演算法(轉)
我們來想一想,根據我們以往的經驗,如果要讓任意兩點(例如從頂點a點到頂點b)之間的路程變短,只能引入第三個點(頂點k),並通過這個頂點k中轉即a->k->b,才可能縮短原來從頂點a點到頂點b的路程。那麼這個中轉的頂點k是1~n中的哪個點呢?甚至有時候不只通過一個點,而是經過兩個點或
幾種常見的濾波演算法(轉)
1、限幅濾波法(又稱程式判斷濾波法) A、方法: 根據經驗判斷,確定兩次取樣允許的最大偏差值(設為A) 每次檢測到新值時判斷: 如果本次值與上次值之差<=A,則本次值有效 如
HMM學習2之前向-後向演算法(轉)
void BaumWelch(HMM *phmm, int T, int *O, double **alpha, double **beta, double **gamma, int *pniter, double *plogprobinit, double *plogprobfinal) { int
利用VS建立一個遊戲(3)遊戲地圖繪製(程式碼+註釋)
首先注意的是我們要將地圖素材放置專案資料夾中。#include "stdafx.h" #include "MyGameFrame.h" //源cpp檔案引申出的標頭檔案 也可以用resource.h代替 #include <stdio.h>
(轉)PostGIS+QGIS+GeoServer+OpenLayers實現數據的存儲、服務的發布以及地圖的顯示
切換 以及 data- about 100% tail error -s image http://blog.csdn.net/gisshixisheng/article/details/41575833 標題比較長,主要呢是實現以下幾點: 1、將shp數據導入到Pos
CentOS 6.9配置網卡IP/網關/DNS命令詳細介紹及一些常用網絡配置命令(轉)
linux. centos fly fig details oba routing href 修改dns 一、IP 即時生效(重啟後失效): ifconfig eth0 192.168.1.102 netmask 255.255.255.0 //添加IP地址 r
【總結整理】地圖投影(轉)
相對 十分 body 角度 投影 解析 經緯度坐標 福特 曲面 1.地球橢球體 地球是一個表面很復雜的球體,人們以假想的平均靜止的海水面形成的“大地體”為參照,推求出近似的橢球體,理論和實踐證明,該橢球體近似一個以地球短軸為軸的橢園而旋轉
(轉)CocosCreator零基礎制作遊戲《極限跳躍》二、制作遊戲開始場景
AI 技術 文件夾 color 關系 新建 cos 默認 會同 CocosCreator零基礎制作遊戲《極限跳躍》二、制作遊戲開始場景 我們剛剛分析了《極限跳躍》這款遊戲,下面我們開始制作第一個遊戲場景,也就是遊戲的開始場景。 首先,打開CocosCreator,新建H
(轉)CocosCreator零基礎制作遊戲《極限跳躍》一、遊戲分析
www. HA 官方文檔 準備 發布 cdr nbsp 之前 很快 CocosCreator零基礎制作遊戲《極限跳躍》一、遊戲分析 無意中發現了小遊戲《極限跳躍》感覺挺有意思的,正好這段時間和朋友討論準備用js還是unity做小遊戲,初定用js,使用CocosCreat