溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

發(fā)布時間:2020-07-13 14:45:15 來源:網(wǎng)絡(luò) 閱讀:315 作者:fkJava李剛 欄目:移動開發(fā)

13.6.9 兩個轉(zhuǎn)折點的連接

兩個轉(zhuǎn)折點的連接是最復雜的一種連接情況,因為兩個轉(zhuǎn)折點又可分為如下幾種情況。

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

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

p2p1的右下角,有6種轉(zhuǎn)折情況。

p2p1的右上角,同樣有6種轉(zhuǎn)折情況。

提示:

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

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


1.同一行不能直接相連

p1p2位于同一行,但它們不能直接相連,因此必須有兩個轉(zhuǎn)折點,圖13.13顯示了這種相連的示意圖。

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

實現(xiàn)時可以先構(gòu)建一個NSDictionary,NSDictionarykey為第一個轉(zhuǎn)折點,NSDictionaryvalue為第二個轉(zhuǎn)折點(每種連接情況最多只有兩個連接點),如NSDictionarycount大于1,說明這兩個FKPoint有多種連接途徑,那么程序還需要計算路徑最小的連接方式。

2.同一列不能直接相連

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

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

13.13同一行不能直接相連

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

13.14同一列不能直接相連

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

實現(xiàn)的方法與同一行不能直接相連的情況相同。

3p2位于p1右下角的6種轉(zhuǎn)折情況

p2位于p1右下角時,一共可能出現(xiàn)6種連接情況,圖13.15~圖13.20分別繪制了這6種連接情況。

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

13.15p2位于p1右下角有兩個轉(zhuǎn)折點的情況1


瘋狂ios講義之實現(xiàn)游戲邏輯(3)

13.16p2位于p1右下角有兩個轉(zhuǎn)折點的情況2

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

13.17p2位于p1右下角有兩個轉(zhuǎn)折點的情況3


瘋狂ios講義之實現(xiàn)游戲邏輯(3)

13.18p2位于p1右下角有兩個轉(zhuǎn)折點的情況4

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

13.19p2位于p1右下角有兩個轉(zhuǎn)折點的情況5

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

13.20p2位于p1右下角有兩個轉(zhuǎn)折點的情況6


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

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

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

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

瘋狂ios講義之實現(xiàn)游戲邏輯(3)

瘋狂ios講義之實現(xiàn)游戲邏輯(3)瘋狂ios講義之實現(xiàn)游戲邏輯(3)瘋狂ios講義之實現(xiàn)游戲邏輯(3)瘋狂ios講義之實現(xiàn)游戲邏輯(3)瘋狂ios講義之實現(xiàn)游戲邏輯(3)

程序中的粗體字代碼分別調(diào)用getYLinkPoints: p2Chanel: pieceHeight:、getXLinkPoints: p2Chanel: pieceWidth:方法來收集各種可能出現(xiàn)的連接路徑,兩個方法的代碼如下。

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

/**
 * 遍歷兩個集合,先判斷第一個集合中元素的x坐標與另一個集合中元素的x坐標是否相同(縱向),
 * 如果相同,即在同一列,再判斷是否有障礙,沒有則加到NSMutableDictionary中
 * @return 存放可以縱向直線連接的連接點的鍵值對
 */
- (NSDictionary*) getYLinkPoints:(NSArray*) p1Chanel
    p2Chanel:(NSArray*) p2Chanel pieceHeight:(NSInteger) pieceHeight
{
    NSMutableDictionary* result = [[NSMutableDictionary alloc]init];
    for (int i = 0; i < p1Chanel.count; i++)
    {
        FKPoint* temp1 = [p1Chanel objectAtIndex:i];
        for (int j = 0; j < p2Chanel.count; j++)
        {
            FKPoint* temp2 = [p2Chanel objectAtIndex:j];
            // 如果x坐標相同(在同一列)
            if (temp1.x == temp2.x)
            {
                // 沒有障礙則加到結(jié)果的NSMutableDictionary中
                if (![self isYBlockFromP1:temp1 toP2:temp2 pieceHeight:pieceHeight])
                {
                    [result setObject:temp2 forKey:temp1];
                }
            }
        }
    }
    return [result copy];
}
/**
 * 遍歷兩個集合,先判斷第一個集合中元素的y坐標與另一個集合中元素的y坐標是否相同(橫向),
 * 如果相同,即在同一行,再判斷是否有障礙,沒有則加到NSMutableDictionary中
 * @return 存放可以橫向直線連接的連接點的鍵值對
 */
- (NSDictionary*) getXLinkPoints:(NSArray*) p1Chanel
    p2Chanel:(NSArray*) p2Chanel pieceWidth:(NSInteger) pieceWidth
{
    NSMutableDictionary* result = [[NSMutableDictionary alloc]init];
    for (int i = 0; i < p1Chanel.count; i++)
    {
        // 從第一通道中取一個點
        FKPoint* temp1 = [p1Chanel objectAtIndex:i];
        // 再遍歷第二個通道,看第二通道中是否有點可以與temp1橫向相連
        for (int j = 0; j < p2Chanel.count; j++)
        {
            FKPoint* temp2 = [p2Chanel objectAtIndex:j];
            // 如果y坐標相同(在同一行),再判斷它們之間是否有直接障礙
            if (temp1.y == temp2.y)
            {
                if (![self isXBlockFromP1:temp1 toP2:temp2 pieceWidth:pieceWidth])
                {
                    // 沒有障礙則加到結(jié)果的NSMutableDictionary中
                    [result setObject:temp2 forKey:temp1];
                }
            }
        }
    }
    return [result copy];
}


經(jīng)過上面的實現(xiàn)之后,getLinkPointsFromPoint: toPoint: width: height:方法可以找出point1point2兩個點之間所有可能的連接情況,該方法返回一個NSDictionary對象,NSDictionary中每個key-value對代表一種連接情況,其中key代表第一個連接點,value代表第二個連接點。

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


13.6.10 找出最短距離

為了找出所有連接情況中的最短路徑,程序?qū)崿F(xiàn)可分為兩步。

遍歷轉(zhuǎn)折點NSDictionary中的所有key-value對,與原來選擇的兩個點構(gòu)成一個FKLinkInfo。每個FKLinkInfo代表一條完整的連接路徑,并將這些FKLinkInfo收集成一個NSArray集合。

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

下面的方法實現(xiàn)了上面的思路。

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

 
/**
 * 獲取p1和p2之間最短的連接信息
 * @param p1 第一個點
 * @param p2 第二個點
 * @param turns 放轉(zhuǎn)折點的NSDictionary
 * @param shortDistance 兩點之間的最短距離
 * @return p1和p2之間最短的連接信息
 */
- (FKLinkInfo*) getShortcutFromPoint:(FKPoint*) p1 toPoint:(FKPoint*) p2
    turns:(NSDictionary*) turns distance:(NSInteger)shortDistance
{
    NSMutableArray* infos = [[NSMutableArray alloc] init];
    // 遍歷結(jié)果NSDictionary
    for (FKPoint* point1 in turns)
    {
        FKPoint* point2 = turns[point1];
        // 將轉(zhuǎn)折點與選擇點封裝成FKLinkInfo對象,放到NSArray集合中
        [infos addObject:[[FKLinkInfo alloc]
            initWithP1:p1 p2:point1 p3:point2 p4:p2]];
    }
    return [self getShortcut:infos shortDistance:shortDistance];
}
/**
 * 從infos中獲取連接線最短的那個FKLinkInfo對象
 * @param infos
 * @return 連接線最短的那個FKLinkInfo對象
 */
- (FKLinkInfo*) getShortcut:(NSArray*) infos shortDistance:(int) shortDistance
{
    int temp1 = 0;
    FKLinkInfo* result = nil;
    for (int i = 0; i < infos.count; i++)
    {
        FKLinkInfo* info = [infos objectAtIndex:i];
        // 計算出幾個點的總距離
        NSInteger distance = [self countAll:info.points];
        // 將循環(huán)第一個的差距用temp1保存
        if (i == 0)
        {
            temp1 = distance - shortDistance;
            result = info;
        }
        // 如果下一次循環(huán)的值比temp1還小, 則用當前的值作為temp1
        if (distance - shortDistance < temp1)
        {
            temp1 = distance - shortDistance;
            result = info;
        }
    }
    return result;
}
/**
 * 計算NSArray中所有點的距離總和
 * @param points 需要計算的連接點
 * @return 所有點的距離總和
 */
- (NSInteger) countAll:(NSArray*) points
{
    NSInteger result = 0;
    for (int i = 0; i < points.count - 1; i++)
    {
        // 獲取第i個點
        FKPoint* point1 = [points objectAtIndex:i];
        // 獲取第i + 1個點
        FKPoint* point2 = [points objectAtIndex:i + 1];
        // 計算第i個點與第i + 1個點的距離,并添加到總距離中
        result += [self getDistanceFromPoint:point1 toPoint:point2];
    }
    return result;
}
/**
 * 獲取兩個點之間的最短距離
 * @param p1 第一個點
 * @param p2 第二個點
 * @return 兩個點的距離距離總和
 */
- (CGFloat) getDistanceFromPoint:(FKPoint*) p1 toPoint:(FKPoint*) p2
{
    int xDistance = abs(p1.x - p2.x);
    int yDistance = abs(p1.y - p2.y);
    return xDistance + yDistance;
}


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

通過對《瘋狂連連看》游戲的分析與開發(fā),讀者應(yīng)該發(fā)現(xiàn)編寫一個游戲并沒有想象的那么難,開發(fā)者需要冷靜、條理化的思維,先分析游戲中所有可能出現(xiàn)的情況,然后在程序中對所有的情況進行判斷,并進行相應(yīng)的處理。

提示:

本程序中FKGameService組件的實現(xiàn)思路與《瘋狂Android講義》中Android版《瘋狂連連看》游戲的實現(xiàn)思路基本相同,筆者無法保證這種實現(xiàn)方式為最優(yōu)算法。這種算法實現(xiàn)起來有些煩瑣,但它的條理十分清晰,非常適合初、中級程序員學習。

13.7小結(jié)

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



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

瘋狂ios講義之實現(xiàn)游戲邏輯(3)


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI