如何開發一個蘋果手機App:俄羅斯方塊游戲

俄羅斯方塊游戲,是風靡世界幾十年的經典游戲,相信絕大多數人都玩過這個游戲,那么它是怎樣編寫出來的,我們如何才能自己編寫一個這樣的游戲呢?

游戲開發步驟

要編寫一個游戲,基本按照以下一些步驟進行:

1.游戲規則定義

游戲規則決定了這個游戲怎么玩,好不好玩,也就決定了編寫程序實現的目標。不同的需求導致不同的規則,不同的規則就需要不同的方法去實現。

例如,俄羅斯方塊是由哪些類型方塊組成,棋盤布局是9*18還是10*20,是否允許旋轉,消掉一行獲得的分數和獲得多行獲得的分數如何進行累加,等等。

2.選擇游戲運行平臺和界面設計

在很多種平臺上都可以玩游戲,常見的比如電腦pc或者筆記本上面直接運行游戲exe程序,或者在電腦瀏覽器中運行flash游戲,或者手機上運行游戲App,或者在特殊游戲機比如xbox等上直接運行游戲程序,或者在VR設備上運行游戲程序。

不同的平臺,需要不同的實現方式,也就需要用不同的程序或開發語言開發工具來實現。

例如,在網頁上開發游戲,可以用html+css+js實現,也可以用flash實現;在手機上開發游戲,可以用java語言在android系統中實現,也可以用objective-c在ios系統上實現;在電腦pc機上可以用c語言在windows系統中實現。

界面設計就是根據游戲規則決定人機交互界面,決定了輸入和輸出的方式。

例如,俄羅斯方塊可以設計成左邊是棋盤,右側上面是分數,下一個方塊,還可以放一個重新來一句按鈕,或者暫停按鈕等等。

方塊設計成純一種顏色塊,還是多種顏色塊,或者是方的還是圓的,或者是卡通圖案,這些就是界面設計。

3.考慮編程思路和算法設計

如果決定了在哪種平臺以及用何種開發語言來編程實現游戲,則要考慮編程的思路,數據的存儲,以及具體的算法。

例如,俄羅斯方塊根據棋盤大小是用二維數組來實現,還是用一維數組來實現,還是用字典表來實現

一局游戲是定時計算,還是用一個循環來處理

如何保存下一個類型,如何計算分數,如何判斷一個方塊是否落地不能再移動,如何判斷一行是否已經被填滿,如何判斷游戲已經結束

4.按照前面設計的思路編寫代碼實現

考慮好編程思路和算法設計之后,可以按照設想進行實際的編程開發

不停的驗證思路,通過開發中的實際情況,可能對編程思路進行修改調整,直到完成整個游戲功能

代碼就是一行一行的編寫出來,直到成為一個可以運行的游戲程序

5.游戲測試

測試就是把自己當成實際用戶來運行游戲程序,找到游戲可能存在的問題,避免程序沒有按照預期執行,防止程序崩潰死機等情況,驗證游戲確實按照游戲規則能完整可靠的運行,這個開發人員可以找其他人進行測試,防止自己存在思維盲區。

6.游戲發布

游戲測試完成之后,就可以將游戲發布到相應的渠道或者平臺,讓更多的人可以玩到這個游戲。

例如,蘋果手機App可以發布到蘋果的AppStore,安卓手機App可以發布到各大應用市場。

最簡單的發布就是直接發送給自己的朋友,讓他們大吃一驚。

游戲規則定義

俄羅斯方塊游戲有如下規則:

棋盤由寬度為10格高度為20格的方塊組成

方塊共有7種形態:

長條形1個,正方形1個,T型1個,L型2個,S型2個

方塊出現在最上方正中間,同時知道下一個方塊是什么

方塊往下掉落,每隔1秒下落一行

方塊下落中如果碰到有方塊阻擋不能下落則停止下落

方塊停止下落后,判斷是否有整行都被方塊填滿,如果有,則整行消失,上面的全部行整體下落一行

統計消掉的總行數

方塊在下落過程中,可以按向下方向鍵讓方塊直接掉落到直到停止位置

可以按向左或者向右方向鍵讓方塊進行旋轉,每按鍵一次旋轉90度

如果沒有足夠的空間讓方塊落下則游戲結束

游戲運行平臺和界面設計

如果以最流行的蘋果手機作為游戲運行環境,則需要開發一個蘋果手機App

蘋果手機使用的是iOS操作系統,開發環境需要Mac電腦和開發工具軟件Xcode

開發語言可以使用objective-C,開發游戲可以使開發用工具包cocos2d

如果需要發布到蘋果AppStore,則需要一個開發者賬號,以及一年99美金

界面設計:

程序啟動之后進入主界面,只能豎屏顯示游戲

主界面分為左右部分

左邊為棋盤:顯示整個棋盤,正方形的格子組成,寬10格高20格

按照蘋果手機尺寸320點*480點來設計,20格高度為480點,則寬度10格占用240點

因此左邊棋盤占用240點,右側剩余80點

右邊分為上部和下部

上部為狀態欄靠上顯示:最上方顯示顯示當前消掉的行數,下面顯示方塊類型小圖標

下部為按鈕欄靠下顯示:從上往下顯示3個按鈕,排行榜/關于/重新開始

大概的界面設計如下示意圖:


界面運行邏輯:

點擊排行榜按鈕進入一個新頁面,上面顯示消掉的行數最多的8個行數數字,下面是一個返回按鈕

點擊關于按鈕進入一個新頁面,上面顯示一張說明圖片,下面是一個返回按鈕

點擊重新開始按鈕,將當前棋盤清空并重新開始一局游戲,游戲啟動

游戲啟動之后,第一個方塊從最上面中間開始往下掉落,每秒下落一行

同時,上方的狀態欄,顯示下一個即將出現的方塊類型小圖標

方塊下落過程中,在屏幕上單指左滑可以向左逆時針旋轉當前方塊,或者在屏幕上單指右滑可以向右順時針旋轉當前方塊

還可以在屏幕上單指下滑可以讓當前方塊直接掉落到底部

方塊如果不能再往下移動一行的時候,則方塊停止,同時開始判斷是否能夠消掉某些行

如果能夠消掉某些行,則統計消掉的行數,增加到上方狀態欄顯示消掉的數字上,刷新顯示

消掉的行從屏幕上消除,同時上方的所有行往下整體移動

加入棋盤最上方中間的供方塊出現的地方已經被方塊占住了,則認為這一局游戲結束

游戲結束則彈出一個窗口顯示游戲結束信息,顯示總共消掉了多少行,然后將該記錄保存到數據庫中

彈出窗口上有返回按鈕,點擊返回按鈕,彈出窗口關閉,回到游戲主界面,主界面停止游戲,點擊重新開始按鈕開始新一局

編程思路和算法設計

數據存儲:

考慮用二維列表來存儲棋盤上的每一個格子

[[0,0,0,0,0,0,0,0,0,0],

[0,0,0,0,0,0,0,0,0,0],

......

[0,0,0,0,0,0,0,0,0,0]]

二維列表里面,第一層是放總計20行,第二層里面是放每一行的10個格子

如果格子里面有方塊,則存放數字1,如果沒有方塊則存放數字0

判斷一行如果全部是1,則表示該行填滿了方格,需要消掉

使用下列數字來對7種方塊進行區分,存儲2個變量,當前方塊和下一個方塊

1:長條形 2:正方形 3:T型 4:L型向左 5:L型向右 6:S型左上右下 7:S型右上左下

生成下一個方塊則使用隨機函數生成1到7當中的隨機數

然后不同方塊如果進行了旋轉會出現不同的狀態,定義旋轉狀態如下:

11:豎立長條 12:橫排長條

21:正方形

31:T型尖頭朝下 32:T型尖頭朝左 33:T型尖頭朝上 34:T型尖頭朝右

41:L型短頭向左長頭向上 42:L型短頭向上長頭向右 43:L型短頭向右長頭向下 44:L型短頭向下長頭向左

51:L型短頭向右長頭向上 52:L型短頭向下長頭向右 53:L型短頭向左長頭向下 54:L型短頭向上長頭向左

61:S型左上右下豎立 62:S型左上右下橫排

71:S型右上左下豎立 72:S型右上左下橫排

使用2個變量x,y存儲當前方塊的左上角的方塊的行號和列號

使用變量存儲當前已經消除的行數

算法設計:

點擊重新開始按鈕后,主程序啟動

清空整個棋盤,對二維列表全部數字清零

然后生成第1個隨機數,賦值給當前方塊變量

然后生成第2個隨機數,賦值給下一個方塊變量

刷新顯示頁面

進入定時處理函數,定時間隔時間為1秒:

定時處理函數:

判斷當前方塊如果可以往下移動,則向下移動,修改二維列表變量

如果不可以下移了,則判斷是否可以消除某行,如果消除了某行則上方的所有行的值整體下移一行

如果不可以下移也不可以消除了,則將下一個方塊的數值賦值給當前方塊,隨機數生成下一個方塊

然后判斷當前方塊是否可以放入棋盤最上方的中間位置,如果可以放入則修改二維列表變量,如果不能放入,則游戲結束

判斷當前方塊是否可以下移函數:

根據當前方塊類型變量,以及方塊當前旋轉狀態,結合二維列表變量判斷,舉例:

如果是11豎立長條形,則根據當前方塊左上角的行號和列號,可以知道最下方的坐標位置

比如x=0行,y=5列,則最下方的坐標為(3,5),那么只要看二維列表的(4,5)如果為1則不可下移,否則可以下移

比如如果是31:T型尖頭朝下

x=0,y=4,那么下方可能有3個方塊可能碰到阻礙,分別是(0,4)/(1,5)/(0,6)

那么只要看二維列表的(1,4)/(2,5)/(1,6)只要有一個為1則不可下移,否則可以下移

判斷是否可以消除某行函數:

對二維列表進行循環,如果一行當中的所有值都為1,則可以消除

直接將所有上面的行的值往下復制,最上面一行的值全部賦值為0,同時總的消除行數變量加1

判斷當前方塊是否可以放入棋盤最上方的中間位置函數:

根據當前方塊類型變量,以及方塊當前旋轉狀態,結合二維列表變量判斷,舉例:

如果是1長條形,默認為11樹立長條形

則需要判斷(0,5)/(1,5)/(2,5)/(3,5)這4個坐標在二維列表中是否為1,只要有1個位置為1則不可放入

比如如果是3T型尖頭朝下

則需要判斷(0,4)/(0,5)/(0,6)/(1,5)這4個坐標

旋轉處理:

總共允許有3種滑動手指操作,向下/向左/向右

向下滑動手指,表示將當前方塊直接掉落到最下方直到碰到阻礙停住

向左滑動手指,表示將當前方塊進行逆時針旋轉

向右滑動手指,表示將當前方塊進行順時針旋轉

向下掉落處理:

重復調用前面定義的判斷當前方塊是否可以下移函數

從第一行循環到最后一行坐標,即可得出最多可以掉落到哪一行

逆時針旋轉處理:

根據當前方塊類型變量,以及方塊當前旋轉狀態,結合二維列表變量判斷,舉例:

如果是11豎立長條形,旋轉中心點取從上往下第2個方塊

那么根據旋轉后需要占用的位置,需要判斷(1,4)/(1,6)/(1,7)這3個位置在二維列表中的值必須是0才可以旋轉

旋轉完成后應該左上角坐標應該從(0,5)變成(1,4),方塊當前旋轉狀態從11變成12

請看下面的示意圖:


那么,順時針旋轉和這個類似,只是選擇后的左上角左邊變量變化,以及方塊當前旋轉狀態值變化

具體代碼實現

由于篇幅所限,這里只能大概描述核心的一些代碼實現方式

開發準備:

首先取注冊一個AppleID

然后在Mac電腦上安裝好Xcode開發軟件

引入相關工具開發包:

導入cocos2d開發工具包以及需要的一些系統Frameworks

數據操作類:

AllData.h

#define SIZE 24

#define WIDTH 320

#define HEIGHT 480

@interface AllData : NSObject

@property int next;

@property int current;

@property int currentstatus;

@property int posx;

@property int posy;

@property int alllines;

@property (nonatomic,retain) NSMutableArray *numberdatas;

//取得當前類的實例

+(AllData *) sharedAllData;

//初始化棋盤

-(void)initAllData;

//取得下一個方塊

-(int) getNextValue;

//逆時針旋轉當前方塊

-(void)changeLeft;

//順時針旋轉當前方塊

-(void)changeRight;

//判斷是否可以下移當前方塊

-(boolean)canMoveDown;

//處理消除慢行操作

-(void)removeLines;

@end

存儲控制類:

DBUtil.h

@interface DBUtil : NSObject

+ (NSString *)dataFilePath;

+ (void)initDataBase;

//存儲一局游戲消除行數數字

+(void)insertOneData:(int)topnumber;

//取得最大的8個記錄數:消除行數

+ (NSMutableArray *)getListDataFromDb;

@end

方塊繪畫類:

#import "CommonUtil.h"

#import "AllData.h"

@implementation CommonUtil

//繪畫棋盤當中的一個格子

+ (void) drawOneNumber:(int)number pos:(CGPoint)pos layer:(CCLayer*)layer {

CCSpriteBatchNode *numbatch = [CCSpriteBatchNode batchNodeWithFile:[NSString stringWithFormat:@"num_%d.png",number] capacity:15];

numbatch.anchorPoint = CGPointZero;

[numbatch setPosition:pos];

[layer addChild:numbatch];

CCSprite *sprite1 = [CCSprite spriteWithTexture:numbatch.texture rect:CGRectMake(0, 0, SIZE-2, SIZE-2)];

sprite1.position = ccp(0,0);

sprite1.anchorPoint = CGPointZero;

[numbatch addChild:sprite1];

}

// 繪畫整個棋盤

......

啟動進入主界面:

@implementation AppDelegate

- (void) applicationDidFinishLaunching:(UIApplication*)application

{

......

[[AllData sharedAllData] initAllData];

[DBUtil initDataBase];

[[CCDirector sharedDirector] runWithScene: [MainLayer scene]];

.......

狀態欄和工具欄:

@interface ToolLayer : CCLayer {

}

@implementation ToolLayer

- (void) drawTool {

[self removeAllChildrenWithCleanup:YES];

//畫底部背景

CCSprite *bg = [CCSprite spriteWithFile:@"tool_bootom_back.png"];

bg.anchorPoint = CGPointZero;

[self addChild:bg z:0 tag:0];

//排行按鈕

CCMenuItem *gold = [CCMenuItemImage itemFromNormalImage:@"gold.png" selectedImage:@"gold_pressed.png" target:self selector:@selector(gold:)];

//關于按鈕

CCMenuItem *about = [CCMenuItemImage itemFromNormalImage:@"about.png" selectedImage:@"about_pressed.png" target:self selector:@selector(about:)];

//重新開始按鈕

CCMenuItem *restart = [CCMenuItemImage itemFromNormalImage:@"restart.png" selectedImage:@"restart_pressed.png" target:self selector:@selector(restart:)];

......

// 繪畫消除行數的圖標

// 繪畫消除行數的數字

// 繪畫下一個方塊的圖標

- (void) restart:(id) sender

{

[[AllData sharedAllData] initAllData];

MapLayer *mapLayer = (MapLayer *)[[CCDirector sharedDirector].runningScene getChildByTag:0];

[mapLayer startGame];

[self drawTool];

}

棋盤滑動手勢控制方法:

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

NSSet *allTouches = [event allTouches];

switch ([allTouches count])

{

case 1:

{

UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];

single = [touch1 locationInView:[touch1 view]];

} break;

default:

break;

}

}

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

NSSet *allTouches = [event allTouches];

switch ([allTouches count])

{

case 1:

{

UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];

CGPoint singleend = [touch1 locationInView:[touch1 view]];

float x = single.x - singleend.x;

float y = single.y - singleend.y;

if (x*x > y*y && x*x > 2500 && x > 0) {

//向左橫向滑動超過50

NSLog(@"左");

[self changeLeft];

}else if (x*x > y*y && x*x > 2500 && x < 0) {

//向右橫向滑動超過50

NSLog(@"右");

[self changeRight];

}else if (x*x < y*y && y*y > 2500 && y < 0) {

//向下縱向滑動超過50

NSLog(@"下");

[self moveDown];

}

} break;

default:

break;

}

}

主界面程序:

@interface MapLayer : CCLayer {

}

@implementation MapLayer

-(void) startGame{

[self removeAllChildrenWithCleanup:YES];

int toppos = 0;

for (NSMutableArray *onerow in [AllData sharedAllData].numberdatas) {

int leftpos = 0;

toppos = toppos + SIZE + 1;

for (NSNumber *onepos in onerow) {

leftpos = leftpos + SIZE + 1;

[CommonUtil drawOneNumber:[onepos intValue] pos:CGPointMake(leftpos, toppos) layer:self];

}

}

// 設置定時器處理函數,定時間隔時間1秒

......

}

//定時器處理函數

//判斷當前方塊如果可以往下移動,則向下移動,修改二維列表變量

//如果不可以下移了,則判斷是否可以消除某行,如果消除了某行則上方的所有行的值整體下移一行

//如果不可以下移也不可以消除了,則將下一個方塊的數值賦值給當前方塊,隨機數生成下一個方塊

//然后判斷當前方塊是否可以放入棋盤最上方的中間位置,如果可以放入則修改二維列表變量,如果不能放入,則游戲結束

......

具體代碼這里不貼了,有了詳細的算法程序設計和數據存儲類型,要實現出來并不是太難,只是需要耐心細心而已。

游戲測試

游戲的主體程序完成后,就可以一邊測試一邊修改

測試的時候要注意測試各種邊界情況,例如

將當前方塊移動到最左邊,看看碰到邊界的時候會不會出錯

將當前方塊卡入一個正好插入的空槽形狀中,看程序是不是會出錯

等待當前方塊掉入一個空槽,然后立刻旋轉,理論上應該不能旋轉了,測試看看程序會不會出錯

測試一次性消掉4行,看看程序是不是會出錯

如果自己測試的差不多了,就可以將程序打包發送給朋友測試

有時候,開發的人很難測試自己編寫的程序,但是反而讓別人可以測試出來很多問題

游戲發布

下面介紹一下大概的發布流程,讓大家有個初步印象,詳細的發布流程網上可以搜索到很多

游戲測試到基本沒有錯誤之后,就可以將游戲發布到蘋果的AppStore了

發布App需要購買蘋果的開發者賬號,一年是99美金,目前用信用卡支付也很方便

然后登陸https://developer.apple.com,進行一些證書的設置

生成好證書之后,將證書下載到Mac電腦中導入到Xcode之中

然后使用Xcode進行編譯和打包好一個ipa文件

然后登陸https://itunesconnect.apple.com創建一個App,填寫一些介紹信息,需要一些App的截圖

然后回到Xcode使用Application Loader這個工具軟件將ipa文件上傳到itunesconnect當中

然后回到itunesconnect網站上提交App

然后就是等待蘋果AppStore的審核,一般7-10個工作日可以完成審核

完成審核之后,你的App就會出現在蘋果手機的AppStore里面了,就可以搜索到了

然后就可以告訴你的朋友們,讓他們大吃一驚吧。


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容