天天看點

瘋狂ios講義之實作遊戲邏輯(3)

13.6.9 兩個轉折點的連接配接

兩個轉折點的連接配接是最複雜的一種連接配接情況,因為兩個轉折點又可分為如下幾種情況。

p1、p2位于同一行,但不能直接相連,就必須有兩個轉折點,分向上與向下兩種連接配接情況。

p1、p2位于同一列,但不能直接相連,也必須有兩個轉折點,分向左與向右兩種連接配接情況。

p2在p1的右下角,有6種轉折情況。

p2在p1的右上角,同樣有6種轉折情況。

提示:

對于p2位于p1的左上角、左下角的情況,同樣隻要把p1、p2的位置互換即可。

對于上面4種情況,同樣需要分别進行處理。

p1、p2位于同一行,但它們不能直接相連,是以必須有兩個轉折點,圖13.13顯示了這種相連的示意圖。

從圖13.13可以看到,當p1與p2位于同一行但不能直接相連時,這兩個點既可在上面相連,也可在下面相連,這兩種情況都代表它們可以相連。我們先把這兩種情況都加入結果中,最後計算最近的距離。

實作時可以先建構一個NSDictionary,NSDictionary的key為第一個轉折點,NSDictionary的value為第二個轉折點(每種連接配接情況最多隻有兩個連接配接點),如NSDictionary的count大于1,說明這兩個FKPoint有多種連接配接途徑,那麼程式還需要計算路徑最小的連接配接方式。

p1、p2位于同一列,但它們不能直接相連,是以必須有兩個轉折點,圖13.14顯示了這種相連的示意。

瘋狂ios講義之實作遊戲邏輯(3)

圖13.13同一行不能直接相連

瘋狂ios講義之實作遊戲邏輯(3)

圖13.14同一列不能直接相連

從圖13.14可以看到,當p1與p2位于同一列但不能直接相連時,這兩個點既可在左邊相連,也可在右邊相連,這兩種情況都代表它們可以相連。我們先把這兩種情況都加入結果中,最後計算最近的距離。

實作的方法與同一行不能直接相連的情況相同。

p2位于p1右下角時,一共可能出現6種連接配接情況,圖13.15~圖13.20分别繪制了這6種連接配接情況。

瘋狂ios講義之實作遊戲邏輯(3)

圖13.15p2位于p1右下角有兩個轉折點的情況1

瘋狂ios講義之實作遊戲邏輯(3)

圖13.16p2位于p1右下角有兩個轉折點的情況2

瘋狂ios講義之實作遊戲邏輯(3)

圖13.17p2位于p1右下角有兩個轉折點的情況3

瘋狂ios講義之實作遊戲邏輯(3)

圖13.18p2位于p1右下角有兩個轉折點的情況4

瘋狂ios講義之實作遊戲邏輯(3)

圖13.19p2位于p1右下角有兩個轉折點的情況5

瘋狂ios講義之實作遊戲邏輯(3)

圖13.20p2位于p1右下角有兩個轉折點的情況6

實際上,p2還可能位于p1的右上角,出現的6種連接配接情形與此相似,此處不再詳述。

接下來定義一個getLinkPoints方法對具有兩個連接配接點的情況進行處理。

程式清單:codes/13/Link/Link/sources/board/FKGameService.m

<a href="http://s3.51cto.com/wyfs02/M02/12/6B/wKioL1MGsDzRrbXnAAXBvNKuI-k915.jpg" target="_blank"></a>

<a href="http://s3.51cto.com/wyfs02/M01/12/6A/wKiom1MGsG6z2V24AASJRANw8ik750.jpg" target="_blank"></a>

程式中的粗體字代碼分别調用getYLinkPoints: p2Chanel: pieceHeight:、getXLinkPoints: p2Chanel: pieceWidth:方法來收集各種可能出現的連接配接路徑,兩個方法的代碼如下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

<code>/**</code>

<code> </code><code>* 周遊兩個集合,先判斷第一個集合中元素的x坐标與另一個集合中元素的x坐标是否相同(縱向),</code>

<code> </code><code>* 如果相同,即在同一列,再判斷是否有障礙,沒有則加到NSMutableDictionary中</code>

<code> </code><code>* @return 存放可以縱向直線連接配接的連接配接點的鍵值對</code>

<code> </code><code>*/</code>

<code>- (NSDictionary*) getYLinkPoints:(NSArray*) p1Chanel</code>

<code>    </code><code>p2Chanel:(NSArray*) p2Chanel pieceHeight:(NSInteger) pieceHeight</code>

<code>{</code>

<code>    </code><code>NSMutableDictionary* result = [[NSMutableDictionary alloc]init];</code>

<code>    </code><code>for</code> <code>(</code><code>int</code> <code>i = 0; i &lt; p1Chanel.count; i++)</code>

<code>    </code><code>{</code>

<code>        </code><code>FKPoint* temp1 = [p1Chanel objectAtIndex:i];</code>

<code>        </code><code>for</code> <code>(</code><code>int</code> <code>j = 0; j &lt; p2Chanel.count; j++)</code>

<code>        </code><code>{</code>

<code>            </code><code>FKPoint* temp2 = [p2Chanel objectAtIndex:j];</code>

<code>            </code><code>// 如果x坐标相同(在同一列)</code>

<code>            </code><code>if</code> <code>(temp1.x == temp2.x)</code>

<code>            </code><code>{</code>

<code>                </code><code>// 沒有障礙則加到結果的NSMutableDictionary中</code>

<code>                </code><code>if</code> <code>(![self isYBlockFromP1:temp1 toP2:temp2 pieceHeight:pieceHeight])</code>

<code>                </code><code>{</code>

<code>                    </code><code>[result setObject:temp2 forKey:temp1];</code>

<code>                </code><code>}</code>

<code>            </code><code>}</code>

<code>        </code><code>}</code>

<code>    </code><code>}</code>

<code>    </code><code>return</code> <code>[result copy];</code>

<code>}</code>

<code> </code><code>* 周遊兩個集合,先判斷第一個集合中元素的y坐标與另一個集合中元素的y坐标是否相同(橫向),</code>

<code> </code><code>* 如果相同,即在同一行,再判斷是否有障礙,沒有則加到NSMutableDictionary中</code>

<code> </code><code>* @return 存放可以橫向直線連接配接的連接配接點的鍵值對</code>

<code>- (NSDictionary*) getXLinkPoints:(NSArray*) p1Chanel</code>

<code>    </code><code>p2Chanel:(NSArray*) p2Chanel pieceWidth:(NSInteger) pieceWidth</code>

<code>        </code><code>// 從第一通道中取一個點</code>

<code>        </code><code>// 再周遊第二個通道,看第二通道中是否有點可以與temp1橫向相連</code>

<code>            </code><code>// 如果y坐标相同(在同一行),再判斷它們之間是否有直接障礙</code>

<code>            </code><code>if</code> <code>(temp1.y == temp2.y)</code>

<code>                </code><code>if</code> <code>(![self isXBlockFromP1:temp1 toP2:temp2 pieceWidth:pieceWidth])</code>

<code>                    </code><code>// 沒有障礙則加到結果的NSMutableDictionary中</code>

經過上面的實作之後,getLinkPointsFromPoint: toPoint: width: height:方法可以找出point1、point2兩個點之間所有可能的連接配接情況,該方法傳回一個NSDictionary對象,NSDictionary中每個key-value對代表一種連接配接情況,其中key代表第一個連接配接點,value代表第二個連接配接點。

當point1、point2之間有多種連接配接情況時,程式還需要找出所有連接配接情況中的最短路徑,link(Piece p1, Piece p2)方法中的④号粗體字代碼調用了getShortcutFromPoint: toPoint: turns: distance:方法進行處理,下面進行詳細分析。

13.6.10 找出最短距離

為了找出所有連接配接情況中的最短路徑,程式實作可分為兩步。

①周遊轉折點NSDictionary中的所有key-value對,與原來選擇的兩個點構成一個FKLinkInfo。每個FKLinkInfo代表一條完整的連接配接路徑,并将這些FKLinkInfo收內建一個NSArray集合。

②周遊第1步得到的NSArray集合,計算每個FKLinkInfo中所有連接配接點的總距離,選取與最短距離相差最小的FKLinkInfo傳回即可。

下面的方法實作了上面的思路。

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

<code> </code><code>* 擷取p1和p2之間最短的連接配接資訊</code>

<code> </code><code>* @param p1 第一個點</code>

<code> </code><code>* @param p2 第二個點</code>

<code> </code><code>* @param turns 放轉折點的NSDictionary</code>

<code> </code><code>* @param shortDistance 兩點之間的最短距離</code>

<code> </code><code>* @return p1和p2之間最短的連接配接資訊</code>

<code>- (FKLinkInfo*) getShortcutFromPoint:(FKPoint*) p1 toPoint:(FKPoint*) p2</code>

<code>    </code><code>turns:(NSDictionary*) turns distance:(NSInteger)shortDistance</code>

<code>    </code><code>NSMutableArray* infos = [[NSMutableArray alloc] init];</code>

<code>    </code><code>// 周遊結果NSDictionary</code>

<code>    </code><code>for</code> <code>(FKPoint* point1 in turns)</code>

<code>        </code><code>FKPoint* point2 = turns[point1];</code>

<code>        </code><code>// 将轉折點與選擇點封裝成FKLinkInfo對象,放到NSArray集合中</code>

<code>        </code><code>[infos addObject:[[FKLinkInfo alloc]</code>

<code>            </code><code>initWithP1:p1 p2:point1 p3:point2 p4:p2]];</code>

<code>    </code><code>return</code> <code>[self getShortcut:infos shortDistance:shortDistance];</code>

<code> </code><code>* 從infos中擷取連接配接線最短的那個FKLinkInfo對象</code>

<code> </code><code>* @param infos</code>

<code> </code><code>* @return 連接配接線最短的那個FKLinkInfo對象</code>

<code>- (FKLinkInfo*) getShortcut:(NSArray*) infos shortDistance:(</code><code>int</code><code>) shortDistance</code>

<code>    </code><code>int</code> <code>temp1 = 0;</code>

<code>    </code><code>FKLinkInfo* result = nil;</code>

<code>    </code><code>for</code> <code>(</code><code>int</code> <code>i = 0; i &lt; infos.count; i++)</code>

<code>        </code><code>FKLinkInfo* info = [infos objectAtIndex:i];</code>

<code>        </code><code>// 計算出幾個點的總距離</code>

<code>        </code><code>NSInteger distance = [self countAll:info.points];</code>

<code>        </code><code>// 将循環第一個的差距用temp1儲存</code>

<code>        </code><code>if</code> <code>(i == 0)</code>

<code>            </code><code>temp1 = distance - shortDistance;</code>

<code>            </code><code>result = info;</code>

<code>        </code><code>// 如果下一次循環的值比temp1還小, 則用目前的值作為temp1</code>

<code>        </code><code>if</code> <code>(distance - shortDistance &lt; temp1)</code>

<code>    </code><code>return</code> <code>result;</code>

<code> </code><code>* 計算NSArray中所有點的距離總和</code>

<code> </code><code>* @param points 需要計算的連接配接點</code>

<code> </code><code>* @return 所有點的距離總和</code>

<code>- (NSInteger) countAll:(NSArray*) points</code>

<code>    </code><code>NSInteger result = 0;</code>

<code>    </code><code>for</code> <code>(</code><code>int</code> <code>i = 0; i &lt; points.count - 1; i++)</code>

<code>        </code><code>// 擷取第i個點</code>

<code>        </code><code>FKPoint* point1 = [points objectAtIndex:i];</code>

<code>        </code><code>// 擷取第i + 1個點</code>

<code>        </code><code>FKPoint* point2 = [points objectAtIndex:i + 1];</code>

<code>        </code><code>// 計算第i個點與第i + 1個點的距離,并添加到總距離中</code>

<code>        </code><code>result += [self getDistanceFromPoint:point1 toPoint:point2];</code>

<code> </code><code>* 擷取兩個點之間的最短距離</code>

<code> </code><code>* @return 兩個點的距離距離總和</code>

<code>- (CGFloat) getDistanceFromPoint:(FKPoint*) p1 toPoint:(FKPoint*) p2</code>

<code>    </code><code>int</code> <code>xDistance = </code><code>abs</code><code>(p1.x - p2.x);</code>

<code>    </code><code>int</code> <code>yDistance = </code><code>abs</code><code>(p1.y - p2.y);</code>

<code>    </code><code>return</code> <code>xDistance + yDistance;</code>

至此,《瘋狂連連看》遊戲中兩個方塊可能相連的所有情況都處理完成了,應用程式即可調用FKGameService所提供的(FKLinkInfo*) linkWithBeginPiece:(FKPiece*)p1 endPiece: (FKPiece*) p2方法來判斷兩個方塊是否可以相連,這個過程也是編寫該遊戲最煩瑣的地方。

通過對《瘋狂連連看》遊戲的分析與開發,讀者應該發現編寫一個遊戲并沒有想象的那麼難,開發者需要冷靜、條理化的思維,先分析遊戲中所有可能出現的情況,然後在程式中對所有的情況進行判斷,并進行相應的處理。

本程式中FKGameService元件的實作思路與《瘋狂Android講義》中Android版《瘋狂連連看》遊戲的實作思路基本相同,筆者無法保證這種實作方式為最優算法。這種算法實作起來有些煩瑣,但它的條理十厘清晰,非常适合初、中級程式員學習。

13.7小結

本章介紹了一款常見的單機休閑類遊戲——iOS版的《瘋狂連連看》,這款流行的小遊戲的開發難度适中,而且能充分激發學習熱情,對iOS學習者來說是一個不錯的選擇。學習本章需要重點掌握單機遊戲的界面分析與資料模組化的能力:遊戲玩家眼中看到的是遊戲界面,開發者眼中看到的應該是資料模型。除此之外,單機遊戲通常總會有一個比較美觀的界面,是以,通常都需要通過自定義UIView來實作遊戲主界面。《瘋狂連連看》遊戲中需要判斷兩個方塊(圖檔)是否可以相連,這需要開發者對兩個方塊的位置分别進行處理,并針對不同的情況提供相應的實作,這也是開發單機遊戲需要重點掌握的能力。

——————本文節選自《瘋狂ios講義(上)》

瘋狂ios講義之實作遊戲邏輯(3)

本文轉自

fkJava李剛 51CTO部落格,原文連結:http://blog.51cto.com/javaligang/1361533 ,如需轉載請自行聯系原作者

繼續閱讀