android开发学习之路——连连看之游戏逻辑(五)
? ? GameService組件則是整個(gè)游戲邏輯實(shí)現(xiàn)的核心,而且GameService是一個(gè)可以復(fù)用的業(yè)務(wù)邏輯類。
?
(一)定義GameService組件接口
? ? 根據(jù)前面程序?qū)ameService組件的依賴,程序需要GameService組件包含如下方法。
? ? ·start():初始化游戲狀態(tài),開(kāi)始游戲的方法。
? ? ·Piece[][] getPieces():返回表示游戲狀態(tài)的Piece[][]數(shù)組。
? ? ·boolean hasPieces():判斷Pieces[][]數(shù)組中是否還剩Piece對(duì)象;如果所有Piece都被消除了,游戲也就勝利了。
? ? ·Piece findPiece(float touchX,float touchY):根據(jù)觸碰點(diǎn)的X、Y坐標(biāo)來(lái)獲取。
? ? ·LinkInfo link(Piece p1,Piece p2):判斷p1、p2兩個(gè)方塊是否可以相連。
? ? 為了考慮以后的可拓展性,需先為GameService組件定義如下接口。
? ? 接口代碼如下:src\org\crazyit\link\board\GameService
1 public interface GameService 2 { 3 /** 4 * 控制游戲開(kāi)始的方法 5 */ 6 void start(); 7 8 /** 9 * 定義一個(gè)接口方法, 用于返回一個(gè)二維數(shù)組 10 * 11 * @return 存放方塊對(duì)象的二維數(shù)組 12 */ 13 Piece[][] getPieces(); 14 15 /** 16 * 判斷參數(shù)Piece[][]數(shù)組中是否還存在非空的Piece對(duì)象 17 * 18 * @return 如果還剩Piece對(duì)象返回true, 沒(méi)有返回false 19 */ 20 boolean hasPieces(); 21 22 /** 23 * 根據(jù)鼠標(biāo)的x座標(biāo)和y座標(biāo), 查找出一個(gè)Piece對(duì)象 24 * 25 * @param touchX 鼠標(biāo)點(diǎn)擊的x座標(biāo) 26 * @param touchY 鼠標(biāo)點(diǎn)擊的y座標(biāo) 27 * @return 返回對(duì)應(yīng)的Piece對(duì)象, 沒(méi)有返回null 28 */ 29 Piece findPiece(float touchX, float touchY); 30 31 /** 32 * 判斷兩個(gè)Piece是否可以相連, 可以連接, 返回LinkInfo對(duì)象 33 * 34 * @param p1 第一個(gè)Piece對(duì)象 35 * @param p2 第二個(gè)Piece對(duì)象 36 * @return 如果可以相連,返回LinkInfo對(duì)象, 如果兩個(gè)Piece不可以連接, 返回null 37 */ 38 LinkInfo link(Piece p1, Piece p2); 39 }?
(二)實(shí)現(xiàn)GameService組件
? ? GameService組件的前面三個(gè)方法實(shí)現(xiàn)起來(lái)都比較簡(jiǎn)單。
? ? 前3個(gè)方法的代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl
1 public class GameServiceImpl implements GameService 2 { 3 // 定義一個(gè)Piece[][]數(shù)組,只提供getter方法 4 private Piece[][] pieces; 5 // 游戲配置對(duì)象 6 private GameConf config; 7 8 public GameServiceImpl(GameConf config) 9 { 10 // 將游戲的配置對(duì)象設(shè)置本類中 11 this.config = config; 12 } 13 14 @Override 15 public void start() 16 { 17 // 定義一個(gè)AbstractBoard對(duì)象 18 AbstractBoard board = null; 19 Random random = new Random(); 20 // 獲取一個(gè)隨機(jī)數(shù), 可取值0、1、2、3四值。 21 int index = random.nextInt(4); 22 // 隨機(jī)生成AbstractBoard的子類實(shí)例 23 switch (index) 24 { 25 case 0: 26 // 0返回VerticalBoard(豎向) 27 board = new VerticalBoard(); 28 break; 29 case 1: 30 // 1返回HorizontalBoard(橫向) 31 board = new HorizontalBoard(); 32 break; 33 default: 34 // 默認(rèn)返回FullBoard 35 board = new FullBoard(); 36 break; 37 } 38 // 初始化Piece[][]數(shù)組 39 this.pieces = board.create(config); 40 } 41 42 // 直接返回本對(duì)象的Piece[][]數(shù)組 43 @Override 44 public Piece[][] getPieces() 45 { 46 return this.pieces; 47 } 48 49 // 實(shí)現(xiàn)接口的hasPieces方法 50 @Override 51 public boolean hasPieces() 52 { 53 // 遍歷Piece[][]數(shù)組的每個(gè)元素 54 for (int i = 0; i < pieces.length; i++) 55 { 56 for (int j = 0; j < pieces[i].length; j++) 57 { 58 // 只要任意一個(gè)數(shù)組元素不為null,也就是還剩有非空的Piece對(duì)象 59 if (pieces[i][j] != null) 60 { 61 return true; 62 } 63 } 64 } 65 return false; 66 } 67 ..... 68 }? ? 前面3個(gè)方法實(shí)現(xiàn)得很簡(jiǎn)單。下面會(huì)詳細(xì)介紹后面的兩個(gè)方法findPiece(float touchX,float touchY)和link(Piece p1,Piece p2)。
?
(三)獲取觸碰點(diǎn)的方塊
? ? 當(dāng)用戶觸碰游戲界面時(shí),事件監(jiān)聽(tīng)器獲取的時(shí)該觸碰點(diǎn)在游戲界面上的X、Y坐標(biāo),但程序需要獲取用戶觸碰的是哪塊方塊,就要把獲取的X、Y坐標(biāo)換算成Piece[][]二維數(shù)組中的兩個(gè)索引值。
? ? 考慮到游戲界面上每個(gè)方塊的寬度、高度都是相同的,因此將獲取得X、Y坐標(biāo)除以圖片得寬、高即可換算成Piece[][]二維數(shù)組中的索引。
? ? 根據(jù)觸碰點(diǎn)X、Y坐標(biāo)獲取對(duì)應(yīng)方塊得代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
1 // 根據(jù)觸碰點(diǎn)的位置查找相應(yīng)的方塊 2 @Override 3 public Piece findPiece(float touchX, float touchY) 4 { 5 // 由于在創(chuàng)建Piece對(duì)象的時(shí)候, 將每個(gè)Piece的開(kāi)始座標(biāo)加了 6 // GameConf中設(shè)置的beginImageX/beginImageY值, 因此這里要減去這個(gè)值 7 int relativeX = (int) touchX - this.config.getBeginImageX(); 8 int relativeY = (int) touchY - this.config.getBeginImageY(); 9 // 如果鼠標(biāo)點(diǎn)擊的地方比board中第一張圖片的開(kāi)始x座標(biāo)和開(kāi)始y座標(biāo)要小, 即沒(méi)有找到相應(yīng)的方塊 10 if (relativeX < 0 || relativeY < 0) 11 { 12 return null; 13 } 14 // 獲取relativeX座標(biāo)在Piece[][]數(shù)組中的第一維的索引值 15 // 第二個(gè)參數(shù)為每張圖片的寬 16 int indexX = getIndex(relativeX, GameConf.PIECE_WIDTH); 17 // 獲取relativeY座標(biāo)在Piece[][]數(shù)組中的第二維的索引值 18 // 第二個(gè)參數(shù)為每張圖片的高 19 int indexY = getIndex(relativeY, GameConf.PIECE_HEIGHT); 20 // 這兩個(gè)索引比數(shù)組的最小索引還小, 返回null 21 if (indexX < 0 || indexY < 0) 22 { 23 return null; 24 } 25 // 這兩個(gè)索引比數(shù)組的最大索引還大(或者等于), 返回null 26 if (indexX >= this.config.getXSize() 27 || indexY >= this.config.getYSize()) 28 { 29 return null; 30 } 31 // 返回Piece[][]數(shù)組的指定元素 32 return this.pieces[indexX][indexY]; 33 }? ? 上面得代碼根據(jù)觸碰點(diǎn)X、Y坐標(biāo)來(lái)計(jì)算它在Piece[][]數(shù)組中得索引值。調(diào)用了getIndex(int relative,int size)進(jìn)行計(jì)算。
? ??getIndex(int relative,int size)方法的實(shí)現(xiàn)就是拿relative除以size,只是程序需要判斷可以整除和不能整除兩種情況:如果可以整除,說(shuō)明還在前一塊方塊內(nèi);如果不能整除,則對(duì)應(yīng)于下一塊方塊。
? ???getIndex(int relative,int size)方法的代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
1 // 工具方法, 根據(jù)relative座標(biāo)計(jì)算相對(duì)于Piece[][]數(shù)組的第一維 2 // 或第二維的索引值 ,size為每張圖片邊的長(zhǎng)或者寬 3 private int getIndex(int relative, int size) 4 { 5 // 表示座標(biāo)relative不在該數(shù)組中 6 int index = -1; 7 // 讓座標(biāo)除以邊長(zhǎng), 沒(méi)有余數(shù), 索引減1 8 // 例如點(diǎn)了x座標(biāo)為20, 邊寬為10, 20 % 10 沒(méi)有余數(shù), 9 // index為1, 即在數(shù)組中的索引為1(第二個(gè)元素) 10 if (relative % size == 0) 11 { 12 index = relative / size - 1; 13 } 14 else 15 { 16 // 有余數(shù), 例如點(diǎn)了x座標(biāo)為21, 邊寬為10, 21 % 10有余數(shù), index為2 17 // 即在數(shù)組中的索引為2(第三個(gè)元素) 18 index = relative / size; 19 } 20 return index; 21 }?
(四)判斷兩個(gè)方塊是否可以相連
? ? ?判斷兩個(gè)方塊是否可以相連是本程序需要處理的最繁瑣的地方:兩個(gè)方塊可以相連的情形比較多,大致可分為:
? ? ·兩個(gè)方塊位于同一條水平線,可以直接相連。
? ? ·兩個(gè)方塊位于同一條豎直線,可以直接相連。
? ? ·兩個(gè)方塊以兩條線段相連,有1個(gè)拐點(diǎn)。
? ? ·兩個(gè)方塊以三條線段相連,有2個(gè)拐點(diǎn)。
? ? 下面link(Piece p1,Piece p2)方法把這四種情況分開(kāi)進(jìn)行處理。
? ? ?代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
? ? 上面的代碼就前面提到的4種情況,對(duì)應(yīng)了4個(gè)不同的方法。我們需要為這4個(gè)方法提供實(shí)現(xiàn)。
? ? 為了實(shí)現(xiàn)上面4個(gè)方法,可以對(duì)兩個(gè)Piece的位置關(guān)系進(jìn)行歸納。
? ? ·p1于p2在同一行(indexY值相同)。
? ? ·p1與p2在同一列(indexX值相同)。
? ? ·p2在p1的右上角(p2的indexX>p1的indexX,p2的indexY<p1的indexY)。
? ? ·p2的p1的右下角(p2的indexX>p1的indexX,p2的indexY>p1的indexY)。
? ? 至于p2在p1的左上角,或者p2在p1的左下角這兩種情況,程序可以重新執(zhí)行l(wèi)ink方法,將p1和p2兩個(gè)參數(shù)的位置互換即可。
?
(五)定義獲取通道的工具方法
? ? 這里所謂的通到,指的是一個(gè)方塊上、下、左、右四個(gè)方向的空白方塊。
? ? 下面是獲取某個(gè)坐標(biāo)點(diǎn)四周通道的4個(gè)方法的代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
1 /** 2 * 給一個(gè)Point對(duì)象,返回它的左邊通道 3 * 4 * @param p 5 * @param pieceWidth piece圖片的寬 6 * @param min 向左遍歷時(shí)最小的界限 7 * @return 給定Point左邊的通道 8 */ 9 private List<Point> getLeftChanel(Point p, int min, int pieceWidth) 10 { 11 List<Point> result = new ArrayList<Point>(); 12 // 獲取向左通道, 由一個(gè)點(diǎn)向左遍歷, 步長(zhǎng)為Piece圖片的寬 13 for (int i = p.x - pieceWidth; i >= min 14 ; i = i - pieceWidth) 15 { 16 // 遇到障礙, 表示通道已經(jīng)到盡頭, 直接返回 17 if (hasPiece(i, p.y)) 18 { 19 return result; 20 } 21 result.add(new Point(i, p.y)); 22 } 23 return result; 24 } 25 26 /** 27 * 給一個(gè)Point對(duì)象, 返回它的右邊通道 28 * 29 * @param p 30 * @param pieceWidth 31 * @param max 向右時(shí)的最右界限 32 * @return 給定Point右邊的通道 33 */ 34 private List<Point> getRightChanel(Point p, int max, int pieceWidth) 35 { 36 List<Point> result = new ArrayList<Point>(); 37 // 獲取向右通道, 由一個(gè)點(diǎn)向右遍歷, 步長(zhǎng)為Piece圖片的寬 38 for (int i = p.x + pieceWidth; i <= max 39 ; i = i + pieceWidth) 40 { 41 // 遇到障礙, 表示通道已經(jīng)到盡頭, 直接返回 42 if (hasPiece(i, p.y)) 43 { 44 return result; 45 } 46 result.add(new Point(i, p.y)); 47 } 48 return result; 49 } 50 51 /** 52 * 給一個(gè)Point對(duì)象, 返回它的上面通道 53 * 54 * @param p 55 * @param min 向上遍歷時(shí)最小的界限 56 * @param pieceHeight 57 * @return 給定Point上面的通道 58 */ 59 private List<Point> getUpChanel(Point p, int min, int pieceHeight) 60 { 61 List<Point> result = new ArrayList<Point>(); 62 // 獲取向上通道, 由一個(gè)點(diǎn)向右遍歷, 步長(zhǎng)為Piece圖片的高 63 for (int i = p.y - pieceHeight; i >= min 64 ; i = i - pieceHeight) 65 { 66 // 遇到障礙, 表示通道已經(jīng)到盡頭, 直接返回 67 if (hasPiece(p.x, i)) 68 { 69 // 如果遇到障礙, 直接返回 70 return result; 71 } 72 result.add(new Point(p.x, i)); 73 } 74 return result; 75 } 76 77 /** 78 * 給一個(gè)Point對(duì)象, 返回它的下面通道 79 * 80 * @param p 81 * @param max 向上遍歷時(shí)的最大界限 82 * @return 給定Point下面的通道 83 */ 84 private List<Point> getDownChanel(Point p, int max, int pieceHeight) 85 { 86 List<Point> result = new ArrayList<Point>(); 87 // 獲取向下通道, 由一個(gè)點(diǎn)向右遍歷, 步長(zhǎng)為Piece圖片的高 88 for (int i = p.y + pieceHeight; i <= max 89 ; i = i + pieceHeight) 90 { 91 // 遇到障礙, 表示通道已經(jīng)到盡頭, 直接返回 92 if (hasPiece(p.x, i)) 93 { 94 // 如果遇到障礙, 直接返回 95 return result; 96 } 97 result.add(new Point(p.x, i)); 98 } 99 return result; 100 }?
(六)沒(méi)有轉(zhuǎn)折點(diǎn)的橫向連接
? ? 如果兩個(gè)Piece在Piece[][]數(shù)組中的第二維索引值相等,那么這兩個(gè)Piece就位于同一行,如前面的link(Piece p1,Piece p2)方法中,調(diào)用isXBlock(Point p1,Point p2,int pieceWidth)判斷p1、p2之間是否有障礙。
? ? 下面是isXBlock方法的代碼:src\org\crazyit\link\board\impl\GameServiceImpl.java
1 /** 2 * 判斷兩個(gè)y座標(biāo)相同的點(diǎn)對(duì)象之間是否有障礙, 以p1為中心向右遍歷 3 * 4 * @param p1 5 * @param p2 6 * @param pieceWidth 7 * @return 兩個(gè)Piece之間有障礙返回true,否則返回false 8 */ 9 private boolean isXBlock(Point p1, Point p2, int pieceWidth) 10 { 11 if (p2.x < p1.x) 12 { 13 // 如果p2在p1左邊, 調(diào)換參數(shù)位置調(diào)用本方法 14 return isXBlock(p2, p1, pieceWidth); 15 } 16 for (int i = p1.x + pieceWidth; i < p2.x; i = i + pieceWidth) 17 { 18 if (hasPiece(i, p1.y)) 19 {// 有障礙 20 return true; 21 } 22 } 23 return false; 24 }? ? 如果兩個(gè)方塊位于同一行,且它們之間沒(méi)有障礙,那么這兩個(gè)方塊就可以消除,兩個(gè)方塊的連接信息就是它們的中心。
?
(七)沒(méi)有轉(zhuǎn)折點(diǎn)的縱向連接
? ???如果兩個(gè)Piece在Piece[][]數(shù)組中的第一維索引值相等,那么這兩個(gè)Piece就位于同一列,如前面的link(Piece p1,Piece p2)方法中,調(diào)用isYBlock(Point p1,Point p2,int pieceWidth)判斷p1、p2之間是否有障礙。
? ? ?下面是isYBlock方法的代碼:src\org\crazyit\link\board\impl\GameServiceImpl.java
/*** 判斷兩個(gè)x座標(biāo)相同的點(diǎn)對(duì)象之間是否有障礙, 以p1為中心向下遍歷* * @param p1* @param p2* @param pieceHeight* @return 兩個(gè)Piece之間有障礙返回true,否則返回false*/private boolean isYBlock(Point p1, Point p2, int pieceHeight){if (p2.y < p1.y){// 如果p2在p1的上面, 調(diào)換參數(shù)位置重新調(diào)用本方法return isYBlock(p2, p1, pieceHeight);}for (int i = p1.y + pieceHeight; i < p2.y; i = i + pieceHeight){if (hasPiece(p1.x, i)){// 有障礙return true;}}return false;}?
(八)一個(gè)轉(zhuǎn)折點(diǎn)的連接
? ? 對(duì)于兩個(gè)方塊連接線上只有一個(gè)轉(zhuǎn)折點(diǎn)的情況,程序需要先找到這個(gè)轉(zhuǎn)折點(diǎn)。為了找到這個(gè)轉(zhuǎn)折點(diǎn),程序定義一個(gè)遍歷兩個(gè)通道并獲取它們交點(diǎn)的方法。
? ? 代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
/*** 遍歷兩個(gè)通道, 獲取它們的交點(diǎn)* * @param p1Chanel 第一個(gè)點(diǎn)的通道* @param p2Chanel 第二個(gè)點(diǎn)的通道* @return 兩個(gè)通道有交點(diǎn),返回交點(diǎn),否則返回null*/private Point getWrapPoint(List<Point> p1Chanel, List<Point> p2Chanel){for (int i = 0; i < p1Chanel.size(); i++){Point temp1 = p1Chanel.get(i);for (int j = 0; j < p2Chanel.size(); j++){Point temp2 = p2Chanel.get(j);if (temp1.equals(temp2)){// 如果兩個(gè)List中有元素有同一個(gè), 表明這兩個(gè)通道有交點(diǎn)return temp1;}}}return null;}? ? 為了找出兩個(gè)方塊連接線上的連接點(diǎn),程序同樣需要分析p1、p2兩個(gè)點(diǎn)的位置分布。根據(jù)前面的分析,我們知道p2要么位于p1的右上角,要么位于p1的右下角。
? ? 當(dāng)p2位于p1的右上角時(shí),應(yīng)該計(jì)算p1的向左通道與p2的向下通道是否有交點(diǎn),p1的向上通道與p2的向左通道是否有交點(diǎn)。
? ? 當(dāng)p2位于p1的右上角時(shí),應(yīng)該計(jì)算p1的向右通道與p2的向上通道是否有交點(diǎn),p1的向下通道與p2的向左通道是否有交點(diǎn)。
? ? 根據(jù)p1與p2具有上面兩種分布情形,程序提供如下方法進(jìn)行處理。
? ? 代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
1 /** 2 * 獲取兩個(gè)不在同一行或者同一列的座標(biāo)點(diǎn)的直角連接點(diǎn), 即只有一個(gè)轉(zhuǎn)折點(diǎn) 3 * 4 * @param point1 第一個(gè)點(diǎn) 5 * @param point2 第二個(gè)點(diǎn) 6 * @return 兩個(gè)不在同一行或者同一列的座標(biāo)點(diǎn)的直角連接點(diǎn) 7 */ 8 private Point getCornerPoint(Point point1, Point point2, int pieceWidth, 9 int pieceHeight) 10 { 11 // 先判斷這兩個(gè)點(diǎn)的位置關(guān)系 12 // point2在point1的左上角, point2在point1的左下角 13 if (isLeftUp(point1, point2) || isLeftDown(point1, point2)) 14 { 15 // 參數(shù)換位, 重新調(diào)用本方法 16 return getCornerPoint(point2, point1, pieceWidth, pieceHeight); 17 } 18 // 獲取p1向右, 向上, 向下的三個(gè)通道 19 List<Point> point1RightChanel = getRightChanel(point1, point2.x, 20 pieceWidth); 21 List<Point> point1UpChanel = getUpChanel(point1, point2.y, pieceHeight); 22 List<Point> point1DownChanel = getDownChanel(point1, point2.y, 23 pieceHeight); 24 // 獲取p2向下, 向左, 向下的三個(gè)通道 25 List<Point> point2DownChanel = getDownChanel(point2, point1.y, 26 pieceHeight); 27 List<Point> point2LeftChanel = getLeftChanel(point2, point1.x, 28 pieceWidth); 29 List<Point> point2UpChanel = getUpChanel(point2, point1.y, pieceHeight); 30 if (isRightUp(point1, point2)) 31 { 32 // point2在point1的右上角 33 // 獲取p1向右和p2向下的交點(diǎn) 34 Point linkPoint1 = getWrapPoint(point1RightChanel, point2DownChanel); 35 // 獲取p1向上和p2向左的交點(diǎn) 36 Point linkPoint2 = getWrapPoint(point1UpChanel, point2LeftChanel); 37 // 返回其中一個(gè)交點(diǎn), 如果沒(méi)有交點(diǎn), 則返回null 38 return (linkPoint1 == null) ? linkPoint2 : linkPoint1; 39 } 40 if (isRightDown(point1, point2)) 41 { 42 // point2在point1的右下角 43 // 獲取p1向下和p2向左的交點(diǎn) 44 Point linkPoint1 = getWrapPoint(point1DownChanel, point2LeftChanel); 45 // 獲取p1向右和p2向下的交點(diǎn) 46 Point linkPoint2 = getWrapPoint(point1RightChanel, point2UpChanel); 47 return (linkPoint1 == null) ? linkPoint2 : linkPoint1; 48 } 49 return null; 50 }? ? 上面代碼分別處理了p2位于p1的右上、右下的兩種情形。
? ? 在上面程序中用到isLeftUp、isLeftDown、isRightUp、isRightDown四個(gè)方法來(lái)判斷p2位于p1的左上、左下、右上、右下4種情形。這4個(gè)方法的實(shí)現(xiàn),只要對(duì)它們的X、Y坐標(biāo)進(jìn)行簡(jiǎn)單判斷即可。
? ? 4個(gè)方法的代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
1 /** 2 * 判斷point2是否在point1的左上角 3 * 4 * @param point1 5 * @param point2 6 * @return p2位于p1的左上角時(shí)返回true,否則返回false 7 */ 8 private boolean isLeftUp(Point point1, Point point2) 9 { 10 return (point2.x < point1.x && point2.y < point1.y); 11 } 12 13 /** 14 * 判斷point2是否在point1的左下角 15 * 16 * @param point1 17 * @param point2 18 * @return p2位于p1的左下角時(shí)返回true,否則返回false 19 */ 20 private boolean isLeftDown(Point point1, Point point2) 21 { 22 return (point2.x < point1.x && point2.y > point1.y); 23 } 24 25 /** 26 * 判斷point2是否在point1的右上角 27 * 28 * @param point1 29 * @param point2 30 * @return p2位于p1的右上角時(shí)返回true,否則返回false 31 */ 32 private boolean isRightUp(Point point1, Point point2) 33 { 34 return (point2.x > point1.x && point2.y < point1.y); 35 } 36 37 /** 38 * 判斷point2是否在point1的右下角 39 * 40 * @param point1 41 * @param point2 42 * @return p2位于p1的右下角時(shí)返回true,否則返回false 43 */ 44 private boolean isRightDown(Point point1, Point point2) 45 { 46 return (point2.x > point1.x && point2.y > point1.y); 47 }?
(九)兩個(gè)轉(zhuǎn)折點(diǎn)的連接
? ? 兩個(gè)轉(zhuǎn)折點(diǎn)的1連接又是最復(fù)雜的一種連接情況,因?yàn)閮蓚€(gè)轉(zhuǎn)折點(diǎn)又可分為如下幾種情況。
? ??·p1、p2位于同一行,不能直接相連,就必須有兩個(gè)轉(zhuǎn)折點(diǎn),分向上與向下兩種連接情況。
? ??·p1、p2位于同一列,不能直接相連,也必須有兩個(gè)轉(zhuǎn)折點(diǎn),分向左與向右兩種連接情況。
? ??·p2在p1的右下角,有6種轉(zhuǎn)折情況。
? ??·p2在p1的右上角,有6種轉(zhuǎn)折情況。
? ? 對(duì)于上面4種情況,同樣需要分別進(jìn)行處理。
? ? 1.同一行不能直接相連
? ? p1、p2位于同一行,但它們不能直接相連,因此必須有兩個(gè)轉(zhuǎn)折點(diǎn)。當(dāng)p1與p2位于同一行不能直接相連時(shí),這兩個(gè)點(diǎn)既可在上面相連,也可在下面相連。這兩種情況都代表它們可以相連,我們先把這兩種情況都加入結(jié)果中,最后再去計(jì)算最近的距離。
? ? 實(shí)現(xiàn)時(shí)可以先構(gòu)建一個(gè)Map,Map的key為第一個(gè)轉(zhuǎn)折點(diǎn),Map的value為第二轉(zhuǎn)折點(diǎn),如果Map的size()大于1,說(shuō)明這兩個(gè)Point有多種連接途徑,那么程序還需要計(jì)算路徑最小的連接方式。
? ? 2.同一列不能直接相連
? ??p1、p2位于同一列,但它們不能直接相連,因此必須有兩個(gè)轉(zhuǎn)折點(diǎn)。當(dāng)p1與p2位于同一列不能直接相連時(shí),這兩個(gè)點(diǎn)既可在左邊相連,也可在右邊相連。這兩種情況都代表它們可以相連,我們先把這兩種情況都加入結(jié)果中,最后再去計(jì)算最近的距離。
? ? 同樣的,我們實(shí)現(xiàn)時(shí)也是構(gòu)建一個(gè)Map。當(dāng)size()大于1,還要計(jì)算最小的連接方式。
? ? 3.p2位于p1右下角的六種轉(zhuǎn)折情況
? ? 有一條垂直通道與p1的向右通道和p2的向左通道相交的方式。有一條水平通道與p1的向下通道和p2的向上通道相交的方式。即可在上面相連,也可在下面相連。即可在左邊相連,也可在右邊相連。共6種相連情況。
? ? 4.p2位于p1右上角的六種轉(zhuǎn)折情況
? ? 與3類似,不再敘述。
? ? 對(duì)具有兩個(gè)連接點(diǎn)的情況進(jìn)行處理的代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
1 /** 2 * 獲取兩個(gè)轉(zhuǎn)折點(diǎn)的情況 3 * 4 * @param point1 5 * @param point2 6 * @return Map對(duì)象的每個(gè)key-value對(duì)代表一種連接方式, 7 * 其中key、value分別代表第1個(gè)、第2個(gè)連接點(diǎn) 8 */ 9 private Map<Point, Point> getLinkPoints(Point point1, Point point2, 10 int pieceWidth, int pieceHeight) 11 { 12 Map<Point, Point> result = new HashMap<Point, Point>(); 13 // 獲取以point1為中心的向上, 向右, 向下的通道 14 List<Point> p1UpChanel = getUpChanel(point1, point2.y, pieceHeight); 15 List<Point> p1RightChanel = getRightChanel(point1, point2.x, pieceWidth); 16 List<Point> p1DownChanel = getDownChanel(point1, point2.y, pieceHeight); 17 // 獲取以point2為中心的向下, 向左, 向上的通道 18 List<Point> p2DownChanel = getDownChanel(point2, point1.y, pieceHeight); 19 List<Point> p2LeftChanel = getLeftChanel(point2, point1.x, pieceWidth); 20 List<Point> p2UpChanel = getUpChanel(point2, point1.y, pieceHeight); 21 // 獲取Board的最大高度 22 int heightMax = (this.config.getYSize() + 1) * pieceHeight 23 + this.config.getBeginImageY(); 24 // 獲取Board的最大寬度 25 int widthMax = (this.config.getXSize() + 1) * pieceWidth 26 + this.config.getBeginImageX(); 27 // 先確定兩個(gè)點(diǎn)的關(guān)系 28 // point2在point1的左上角或者左下角 29 if (isLeftUp(point1, point2) || isLeftDown(point1, point2)) 30 { 31 // 參數(shù)換位, 調(diào)用本方法 32 return getLinkPoints(point2, point1, pieceWidth, pieceHeight); 33 } 34 // p1、p2位于同一行不能直接相連 35 if (point1.y == point2.y) 36 { 37 // 在同一行 38 // 向上遍歷 39 // 以p1的中心點(diǎn)向上遍歷獲取點(diǎn)集合 40 p1UpChanel = getUpChanel(point1, 0, pieceHeight); 41 // 以p2的中心點(diǎn)向上遍歷獲取點(diǎn)集合 42 p2UpChanel = getUpChanel(point2, 0, pieceHeight); 43 Map<Point, Point> upLinkPoints = getXLinkPoints(p1UpChanel, 44 p2UpChanel, pieceHeight); 45 // 向下遍歷, 不超過(guò)Board(有方塊的地方)的邊框 46 // 以p1中心點(diǎn)向下遍歷獲取點(diǎn)集合 47 p1DownChanel = getDownChanel(point1, heightMax, pieceHeight); 48 // 以p2中心點(diǎn)向下遍歷獲取點(diǎn)集合 49 p2DownChanel = getDownChanel(point2, heightMax, pieceHeight); 50 Map<Point, Point> downLinkPoints = getXLinkPoints(p1DownChanel, 51 p2DownChanel, pieceHeight); 52 result.putAll(upLinkPoints); 53 result.putAll(downLinkPoints); 54 } 55 // p1、p2位于同一列不能直接相連 56 if (point1.x == point2.x) 57 { 58 // 在同一列 59 // 向左遍歷 60 // 以p1的中心點(diǎn)向左遍歷獲取點(diǎn)集合 61 List<Point> p1LeftChanel = getLeftChanel(point1, 0, pieceWidth); 62 // 以p2的中心點(diǎn)向左遍歷獲取點(diǎn)集合 63 p2LeftChanel = getLeftChanel(point2, 0, pieceWidth); 64 Map<Point, Point> leftLinkPoints = getYLinkPoints(p1LeftChanel, 65 p2LeftChanel, pieceWidth); 66 // 向右遍歷, 不得超過(guò)Board的邊框(有方塊的地方) 67 // 以p1的中心點(diǎn)向右遍歷獲取點(diǎn)集合 68 p1RightChanel = getRightChanel(point1, widthMax, pieceWidth); 69 // 以p2的中心點(diǎn)向右遍歷獲取點(diǎn)集合 70 List<Point> p2RightChanel = getRightChanel(point2, widthMax, 71 pieceWidth); 72 Map<Point, Point> rightLinkPoints = getYLinkPoints(p1RightChanel, 73 p2RightChanel, pieceWidth); 74 result.putAll(leftLinkPoints); 75 result.putAll(rightLinkPoints); 76 } 77 // point2位于point1的右上角 78 if (isRightUp(point1, point2)) 79 { 80 // 獲取point1向上遍歷, point2向下遍歷時(shí)橫向可以連接的點(diǎn) 81 Map<Point, Point> upDownLinkPoints = getXLinkPoints(p1UpChanel, 82 p2DownChanel, pieceWidth); 83 // 獲取point1向右遍歷, point2向左遍歷時(shí)縱向可以連接的點(diǎn) 84 Map<Point, Point> rightLeftLinkPoints = getYLinkPoints( 85 p1RightChanel, p2LeftChanel, pieceHeight); 86 // 獲取以p1為中心的向上通道 87 p1UpChanel = getUpChanel(point1, 0, pieceHeight); 88 // 獲取以p2為中心的向上通道 89 p2UpChanel = getUpChanel(point2, 0, pieceHeight); 90 // 獲取point1向上遍歷, point2向上遍歷時(shí)橫向可以連接的點(diǎn) 91 Map<Point, Point> upUpLinkPoints = getXLinkPoints(p1UpChanel, 92 p2UpChanel, pieceWidth); 93 // 獲取以p1為中心的向下通道 94 p1DownChanel = getDownChanel(point1, heightMax, pieceHeight); 95 // 獲取以p2為中心的向下通道 96 p2DownChanel = getDownChanel(point2, heightMax, pieceHeight); 97 // 獲取point1向下遍歷, point2向下遍歷時(shí)橫向可以連接的點(diǎn) 98 Map<Point, Point> downDownLinkPoints = getXLinkPoints(p1DownChanel, 99 p2DownChanel, pieceWidth); 100 // 獲取以p1為中心的向右通道 101 p1RightChanel = getRightChanel(point1, widthMax, pieceWidth); 102 // 獲取以p2為中心的向右通道 103 List<Point> p2RightChanel = getRightChanel(point2, widthMax, 104 pieceWidth); 105 // 獲取point1向右遍歷, point2向右遍歷時(shí)縱向可以連接的點(diǎn) 106 Map<Point, Point> rightRightLinkPoints = getYLinkPoints( 107 p1RightChanel, p2RightChanel, pieceHeight); 108 // 獲取以p1為中心的向左通道 109 List<Point> p1LeftChanel = getLeftChanel(point1, 0, pieceWidth); 110 // 獲取以p2為中心的向左通道 111 p2LeftChanel = getLeftChanel(point2, 0, pieceWidth); 112 // 獲取point1向左遍歷, point2向右遍歷時(shí)縱向可以連接的點(diǎn) 113 Map<Point, Point> leftLeftLinkPoints = getYLinkPoints(p1LeftChanel, 114 p2LeftChanel, pieceHeight); 115 result.putAll(upDownLinkPoints); 116 result.putAll(rightLeftLinkPoints); 117 result.putAll(upUpLinkPoints); 118 result.putAll(downDownLinkPoints); 119 result.putAll(rightRightLinkPoints); 120 result.putAll(leftLeftLinkPoints); 121 } 122 // point2位于point1的右下角 123 if (isRightDown(point1, point2)) 124 { 125 // 獲取point1向下遍歷, point2向上遍歷時(shí)橫向可連接的點(diǎn) 126 Map<Point, Point> downUpLinkPoints = getXLinkPoints(p1DownChanel, 127 p2UpChanel, pieceWidth); 128 // 獲取point1向右遍歷, point2向左遍歷時(shí)縱向可連接的點(diǎn) 129 Map<Point, Point> rightLeftLinkPoints = getYLinkPoints( 130 p1RightChanel, p2LeftChanel, pieceHeight); 131 // 獲取以p1為中心的向上通道 132 p1UpChanel = getUpChanel(point1, 0, pieceHeight); 133 // 獲取以p2為中心的向上通道 134 p2UpChanel = getUpChanel(point2, 0, pieceHeight); 135 // 獲取point1向上遍歷, point2向上遍歷時(shí)橫向可連接的點(diǎn) 136 Map<Point, Point> upUpLinkPoints = getXLinkPoints(p1UpChanel, 137 p2UpChanel, pieceWidth); 138 // 獲取以p1為中心的向下通道 139 p1DownChanel = getDownChanel(point1, heightMax, pieceHeight); 140 // 獲取以p2為中心的向下通道 141 p2DownChanel = getDownChanel(point2, heightMax, pieceHeight); 142 // 獲取point1向下遍歷, point2向下遍歷時(shí)橫向可連接的點(diǎn) 143 Map<Point, Point> downDownLinkPoints = getXLinkPoints(p1DownChanel, 144 p2DownChanel, pieceWidth); 145 // 獲取以p1為中心的向左通道 146 List<Point> p1LeftChanel = getLeftChanel(point1, 0, pieceWidth); 147 // 獲取以p2為中心的向左通道 148 p2LeftChanel = getLeftChanel(point2, 0, pieceWidth); 149 // 獲取point1向左遍歷, point2向左遍歷時(shí)縱向可連接的點(diǎn) 150 Map<Point, Point> leftLeftLinkPoints = getYLinkPoints(p1LeftChanel, 151 p2LeftChanel, pieceHeight); 152 // 獲取以p1為中心的向右通道 153 p1RightChanel = getRightChanel(point1, widthMax, pieceWidth); 154 // 獲取以p2為中心的向右通道 155 List<Point> p2RightChanel = getRightChanel(point2, widthMax, 156 pieceWidth); 157 // 獲取point1向右遍歷, point2向右遍歷時(shí)縱向可以連接的點(diǎn) 158 Map<Point, Point> rightRightLinkPoints = getYLinkPoints( 159 p1RightChanel, p2RightChanel, pieceHeight); 160 result.putAll(downUpLinkPoints); 161 result.putAll(rightLeftLinkPoints); 162 result.putAll(upUpLinkPoints); 163 result.putAll(downDownLinkPoints); 164 result.putAll(leftLeftLinkPoints); 165 result.putAll(rightRightLinkPoints); 166 } 167 return result; 168 }? ? 上面代碼調(diào)用了getYLinkPoints、getXLinkPoints方法來(lái)收集各種可能出現(xiàn)的連接路徑。
? ??getYLinkPoints、getXLinkPoints兩種方法的代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
/*** 遍歷兩個(gè)集合, 先判斷第一個(gè)集合的元素的y座標(biāo)與另一個(gè)集合中的元素y座標(biāo)相同(橫向),* 如果相同, 即在同一行, 再判斷是否有障礙, 沒(méi)有 則加到結(jié)果的map中去* * @param p1Chanel* @param p2Chanel* @param pieceWidth* @return 存放可以橫向直線連接的連接點(diǎn)的鍵值對(duì)*/private Map<Point, Point> getXLinkPoints(List<Point> p1Chanel,List<Point> p2Chanel, int pieceWidth){Map<Point, Point> result = new HashMap<Point, Point>();for (int i = 0; i < p1Chanel.size(); i++){// 從第一通道中取一個(gè)點(diǎn)Point temp1 = p1Chanel.get(i);// 再遍歷第二個(gè)通道, 看下第二通道中是否有點(diǎn)可以與temp1橫向相連for (int j = 0; j < p2Chanel.size(); j++){Point temp2 = p2Chanel.get(j);// 如果y座標(biāo)相同(在同一行), 再判斷它們之間是否有直接障礙if (temp1.y == temp2.y){if (!isXBlock(temp1, temp2, pieceWidth)){// 沒(méi)有障礙則直接加到結(jié)果的map中 result.put(temp1, temp2);}}}}return result;}? ? 經(jīng)過(guò)上面的處理,getLinkPoints(Point point1,Point point2,int pieceWidth,int pieceHeight)方法可以找出point1、point2兩個(gè)點(diǎn)之間的所有可能的連接情況,該方法返回一個(gè)Map對(duì)象,每個(gè)key-value對(duì)代表一種連接情況,其中key代表第一個(gè)連接點(diǎn),value代表第二個(gè)連接點(diǎn)。
? ? 但point1、point2之間有多種連接情況時(shí),程序還需要找出所有連接情況中的最短路徑,,上面代碼中調(diào)用了getShortcut(Point p1,Point p2,turns,getDistance(Point p1,Point p2))方法進(jìn)行處理。
?
(十)找出最短距離
? ? 為了找出最短路徑,程序可分為兩步。
? ? 1.遍歷轉(zhuǎn)折點(diǎn)Map中所有key-value對(duì),與原來(lái)選擇的兩個(gè)點(diǎn)構(gòu)成了一個(gè)LinkInfo。每個(gè)LinkInfo代表一條完整的連接路徑,并將這些LinkInfo收集成一個(gè)List集合。
? ? 2.遍歷第一步得到的List<LinkInfo>集合,計(jì)算每個(gè)LinkInfo中連接全部連接點(diǎn)的總距離,選與最短距離相差最小的LinkInfo返回即可。
? ? 代碼如下:src\org\crazyit\link\board\impl\GameServiceImpl.java
1 /** 2 * 獲取p1和p2之間最短的連接信息 3 * 4 * @param p1 5 * @param p2 6 * @param turns 放轉(zhuǎn)折點(diǎn)的map 7 * @param shortDistance 兩點(diǎn)之間的最短距離 8 * @return p1和p2之間最短的連接信息 9 */ 10 private LinkInfo getShortcut(Point p1, Point p2, Map<Point, Point> turns, 11 int shortDistance) 12 { 13 List<LinkInfo> infos = new ArrayList<LinkInfo>(); 14 // 遍歷結(jié)果Map, 15 for (Point point1 : turns.keySet()) 16 { 17 Point point2 = turns.get(point1); 18 // 將轉(zhuǎn)折點(diǎn)與選擇點(diǎn)封裝成LinkInfo對(duì)象, 放到List集合中 19 infos.add(new LinkInfo(p1, point1, point2, p2)); 20 } 21 return getShortcut(infos, shortDistance); 22 } 23 24 /** 25 * 從infos中獲取連接線最短的那個(gè)LinkInfo對(duì)象 26 * 27 * @param infos 28 * @return 連接線最短的那個(gè)LinkInfo對(duì)象 29 */ 30 private LinkInfo getShortcut(List<LinkInfo> infos, int shortDistance) 31 { 32 int temp1 = 0; 33 LinkInfo result = null; 34 for (int i = 0; i < infos.size(); i++) 35 { 36 LinkInfo info = infos.get(i); 37 // 計(jì)算出幾個(gè)點(diǎn)的總距離 38 int distance = countAll(info.getLinkPoints()); 39 // 將循環(huán)第一個(gè)的差距用temp1保存 40 if (i == 0) 41 { 42 temp1 = distance - shortDistance; 43 result = info; 44 } 45 // 如果下一次循環(huán)的值比temp1的還小, 則用當(dāng)前的值作為temp1 46 if (distance - shortDistance < temp1) 47 { 48 temp1 = distance - shortDistance; 49 result = info; 50 } 51 } 52 return result; 53 } 54 55 /** 56 * 計(jì)算List<Point>中所有點(diǎn)的距離總和 57 * 58 * @param points 需要計(jì)算的連接點(diǎn) 59 * @return 所有點(diǎn)的距離的總和 60 */ 61 private int countAll(List<Point> points) 62 { 63 int result = 0; 64 for (int i = 0; i < points.size() - 1; i++) 65 { 66 // 獲取第i個(gè)點(diǎn) 67 Point point1 = points.get(i); 68 // 獲取第i + 1個(gè)點(diǎn) 69 Point point2 = points.get(i + 1); 70 // 計(jì)算第i個(gè)點(diǎn)與第i + 1個(gè)點(diǎn)的距離,并添加到總距離中 71 result += getDistance(point1, point2); 72 } 73 return result; 74 } 75 76 /** 77 * 獲取兩個(gè)LinkPoint之間的最短距離 78 * 79 * @param p1 第一個(gè)點(diǎn) 80 * @param p2 第二個(gè)點(diǎn) 81 * @return 兩個(gè)點(diǎn)的距離距離總和 82 */ 83 private int getDistance(Point p1, Point p2) 84 { 85 int xDistance = Math.abs(p1.x - p2.x); 86 int yDistance = Math.abs(p1.y - p2.y); 87 return xDistance + yDistance; 88 }? ? 到這,連連看游戲中兩個(gè)方塊可能相連的所有情況都處理完成了,應(yīng)用程序即可調(diào)用GameServiceImpl所提供的Link(Piece p1,Piece p2)方法來(lái)判斷兩個(gè)方塊是否可以相連了,這個(gè)過(guò)程也是最繁瑣的地方。
? ? 通過(guò)連連看游戲的分析與學(xué)習(xí),加強(qiáng)了開(kāi)發(fā)者界面分析與數(shù)據(jù)建模的能力。通過(guò)自定義View來(lái)實(shí)現(xiàn)游戲的主界面。連連看中需要判斷兩個(gè)方塊是否可以相連,需要開(kāi)發(fā)這對(duì)兩個(gè)方塊的位置分門(mén)別類地進(jìn)行處理,也加強(qiáng)開(kāi)發(fā)者冷靜、條理化的思維。
?
具體實(shí)現(xiàn)步驟連接:
android開(kāi)發(fā)學(xué)習(xí)之路——連連看之游戲界面(一)
android開(kāi)發(fā)學(xué)習(xí)之路——連連看之?dāng)?shù)據(jù)模型(二)
android開(kāi)發(fā)學(xué)習(xí)之路——連連看之加載圖片(三)
android開(kāi)發(fā)學(xué)習(xí)之路——連連看之游戲Activity(四)
android開(kāi)發(fā)學(xué)習(xí)之路——連連看之游戲邏輯(五)
轉(zhuǎn)載于:https://www.cnblogs.com/weilongfu/p/7388081.html
總結(jié)
以上是生活随笔為你收集整理的android开发学习之路——连连看之游戏逻辑(五)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: PHP错误的分类
- 下一篇: python sys模块