学哥量化交易学习
持续学习,提高认知,磨炼技艺,从零基础学编程并跨过量化程序交易的大门。
 返回首页
 
 
z004 如何开发一个苹果手机App:俄罗斯方块游戏
作者:学哥
发布时间:2017-1-23


俄罗斯方块游戏,是风靡世界几十年的经典游戏,相信绝大多数人都玩过这个游戏,那么它是怎样编写出来的,我们如何才能自己编写一个这样的游戏呢?

游戏开发步骤
要编写一个游戏,基本按照以下一些步骤进行: 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里面了,就可以搜索到了 然后就可以告诉你的朋友们,让他们大吃一惊吧。
学哥量化交易学习团队
   致力于:持续学习,提高认知,磨炼技艺,持续输出高质量的教程和课程,帮助更多的人从零基础学编程并跨过量化程序交易的大门。
零基础学编程Python入门课程
课程包含以下内容:
1.图文教程《零基础学编程入门-从Python到Web网站.pdf》,学哥原创版权,共计1024页高清图文教程。
2.视频课程《零基础学编程Python入门》,学哥原创版权,共计24节课45个高清视频。
3.视频教程《玩转魔方》,学哥原创版权,可以送给孩子,共计16个高清视频。
4.图文教程《如何寻找编程相关的电子书下载资源.pdf》。
5.加入学哥编程学习辅导QQ群在线交流。

客服 10:00-17:00
QQ: 44556287
微信公众号
学哥量化交易学习
 
  微信公众号:学哥量化交易学习     QQ群1:603559164    客服QQ号:44556287
工信部备案/许可证编号:沪ICP备15050545号-4