• 开发者分享在PC上制作iOS游戏的经验(上)


    作者:Thomas Henshell
    本文记述了《Catch the Monkey》从创意到销售的过程。
    简介
    许多人都想做游戏,特别是手机游戏。好吧,我们也一样!本文是为那些想做手机游戏的人写的。我们的目标主要有两个:
    1)证明做手机游戏的梦想是可能实现的 2)分享我们的经验,希望能帮助新手

    <ignore_js_op>catch-the-monkeyfromgamedev.net_1.png

    catch the monkey(fromgamedev.net)

    关于我们
    我们的团队只有两个人:Thomas(自学成才的程序员)和Alex(在Sheridan大学学习动画的美工)。20年前,我们在中学时认识了,从那时起就开始尝试做游戏。
    在我们开始这个项目以前,我已经使用Microsoft的技术编写商业网页/手机软件15年了。因为这些经验,我们知道如何制作一款好软件(面向对像编程、设计规格、易用性等)。但你之后会看到我们在这方面是如何失败的。
    第一部分:设计和原型制作 技术
    从第一天起,我们就知道我们的目标有两个:
    1)Android是未来,但iPhone是现在。我们要现在也要未来。 2)我们想用熟悉的环境和工具制作一个windows平台。 为了研究Mac平台和XCode,我买了一部mac-mini。花了一天研究ObjectiveC后,我发现我完全不想使用那种语言,它会让我疯掉的。幸运的是,我们想到了一石二鸟的方案:Marmalade(在Apple开始把所有东西都叫作Airplay以前,它叫作Airplay)。

    <ignore_js_op>Marmaladefrom-gamedev.net_.jpg

    Marmalade(from gamedev.net)

    (由图可知,使用VS2008C++排错和使用iOS模拟器即时追踪)
    Marmalade允许用户使用Visual Studio C++编写,然后在其他系统上运行(iOS、Android、Blackberry、Windows Phone、Bada等)。这个模拟器不错,完全达到期望,所以发现这个技术真是赚了。这个价格对独立开发者而言,也是非常划算的。
    设计
    考虑到这是我们的第一款游戏,我们希望保持设计的简单。最初的概念是:玩家用手指触击来挠农田里猴子的痒痒。随关卡进度,猴子会越来越多,速度会越来越快。直到结束。
    在那时,看起来就这么简单,我们只有两个人,所以我们认为不需要正规的说明文件。相反地,我们使用Xmind(免费的!)来模拟我们的脑内想象和设计。这款游戏有意突出美术风格,因为我们的美工能够全天候扑在项目上,但我只能在下班后或周末赶工。

    <ignore_js_op>Mind-Mappingfrom-gamedev.net_.png

    Mind Mapping(from gamedev.net)

    (思维导图是快速捕捉灵感以及组织参考的好办法。Xmind是一个用于制作思维导图的免费开源工具。)
    原型
    在开发商业软件时,初步原型是非常重要的。所有来自阅读和解读设计文件所得的猜想和想法都要在这个过程中完成。
    我们不是直接编写原型,而是使用了一个相当强大和便宜(注册版仅需40美元!)的游戏制作工具GameMaker 8。 借助这个工具,我们可以把已经绘制好的图像和若干玩法机制组合在一起,看看是否能得到有意思的结果。我们制做第一版原型花了20个小时。因为它是在windows上运行的,没有办法测试真正的触摸/触击机制,所以我们只能用点击模拟触击。所以就出现了一个大问题:这样会有意思吗?

    <ignore_js_op>first-prototypefrom-gamedev.net_.png

    first prototype(from gamedev.net)

    (使用GameMaker 8制作的第一版《Catch the Monkey》的原型)
    答案是:没意思。我们调整了几个变量(猴子的速度、使他们发笑的点击、每次出现的猴子数量),但还是太简单了。内容还不够充实,我估计玩不到2分钟就无聊了。我们可不想做出一款“沉闷的游戏”,所以我们返回绘图面板。
    在设计上进行头脑风暴时,我们想到到了使用不同的工具与猴子互动的办法。触击只是第一种办法,之后还可以使用羽毛等其他工具。这似乎有点意思。所以我们又想了若干种工具,然后筛选出容易放到原型中的几种。这样,我们就做出了第二版原型。在这个版本中,农夫有一个工具腰带用于放置各种工具。当一种工具用完,农夫会叫他的妻子再去拿,所以用完的工具要过一下子才会再出现。这样就使玩家不得不考虑什么 时候使用什么工具。玩家还可以控制农夫,让他走到某自个区域或拧起某只猴子。最后,我们添加了摘星星的概念——屏幕上会时常出现星星,玩家要点击才能摘到它。星星可用于之后的升级,尽管我们没有这个设计放到原型中。所以,还是那个问题:这样会有意思吗?

    <ignore_js_op>prototype-2from-gamedev.net_.png

    prototype 2(from gamedev.net)

    (使用GameMaker 8制作的第二版《Catch the Monkey》的原型,注意屏幕下方的工具带)
    答案是:有意思也没意思。似乎有些乐趣卡在这个设定中,呼之欲出,但仍然存在许多障碍。我们知道在不同工具之间做选择(基本上是战略性的)和摘星星是很有趣的(自然、特别又困难)。我们放弃了控制农夫(太麻烦)和补充工具的概念(太复杂和任意)。我们需要一种让玩家可以制定战略和管理资源的游戏机制。
    我必须提醒一下,当我们制作原型时,我们并不只是自己玩,还请了其他不参与项目的人做测试并如实地反馈。毕竟,在测试时,做项目的人很难不带偏袒地评判自己的项目。之后你会看到我们如何在这一点上犯错。
    结论
    在这个阶段,我们又进行了第三也是最后一次头脑风暴。我们想了许多概念,最终想到类似于《魔兽世界》(WOW)中的魔法/冷却机制。在WOW中,玩家不可能随心所欲地施放法术,因为有一个蓝条限制了短时间内的使用次数。但有些法术太强大了,一下子把蓝耗光了,必须经过很长一段时间(几分钟)的冷却才能再次使用,所以一场战斗中不可能多次使用。我们认为,如果所有工具都需要某种通用的能量源,但冷却时间不一样,可能就会产生我们所期望的“战略性平衡”。通过设置这些变量,我们可以保持游戏的新鲜感和乐趣,从而让玩家的沉浸感提升。
    另外,我们还制作了星星能量。这次,我们把星星当作一种购买升级的货币,但星星能量也是一种特殊的技能,可以帮助玩家当前的游戏。这样,星星就有了两种功能,从而使玩家面临把星星累积起来用于升级或应付当前游戏的选择。这是一个很不错的机制。玩家面临着一个有趣的挑战,因为设计师把星星能量做得相当实用,但明智的玩家只会节俭地使用星星,以便用作之后的升级。

    <ignore_js_op>final-toolbelt-designfrom-gamedev.net_.png

    工具带的最终设计(from gamedev.net)

    这个阶段的设计基本上完成后(设计工作贯穿整个开发周期),我们继续制作游戏的核心。
    第二部分:制作核心 简介
    在第一分部中,我们简介了《Catch the Monkey》从简单的概念化到技术选择以及原型制作的过程。在原型制作后期,我们的设计充实了很多,但我们并没有把新设定完全地写进文档里。我们知道有12种工具、10种猴子和若干购买升级道具的商店的模糊概念。升级道具的数量以及各种道具的功能还没有确定下来。现在要开始编写代码了!
    这一部分要比上一部分长,为了控制文章长度,我着重介绍核心建构阶段中最有趣的方面。
    做还是不做?
    正如在第一部分中提到的,美工是全职扑在项目上,但我这个程序员却只能兼职,因为我还有其他工作要忙。我们的项目一拖再拖,看不到进展,终于到了几乎不得不叫停的地步。然而,我还是腾出时间做这款游戏。大约安排了6周(50小时*6=300小时)来做它。我做了一个极端的决定:停止商业工作6周,回家100%地投身于这款游戏。虽然我的妻子不太高兴,但支持我把游戏做完。是时间“全力以赴”了。事后证明这是复活项目的正确决定。
    我们犯下的最大错误
    没有规范地制作设计文案本应该是我们最大的错误,但这个错误在另一个错误的对比下,显得不那么严重了。 如果你研究过《植物大战僵尸》中的僵尸,你会发现,里面的僵尸类型很多,但都是由几个图形部分(头、体、手、臂、足)和向个随意的装饰(头盔、报纸等)。通过重复使用和变化这些部件,你不用费太多脑细胞就做出许多不同类型的僵尸。我们想用类似的办法做出许多类型的猴子,使它们各具能力和弱点。

    <ignore_js_op>Plants-vs-Zombiesfrom-gamedev.net_.jpg

    Plants vs Zombies(from gamedev.net)

    然而,之后我们痛苦地发现,如果你想重复利用,你必须把各个角色能做什么不能做什么写成非常具体的规定。注意,《植物大战僵尸》版的僵尸总是面向镜头的(就像2D动画《南方公园》)。无论它们做什么,它们永远不会侧身对着镜头。
    在我们的动画和原型阶段初期,我们的设计是,当猴子到达某一棵植物时,它会扑通地坐下来,转过身开始挖地。然后当它挖到土豆,它会再转过身去吃掉土豆。我们完成了标准猴子的所有美术工作之后才发现问题。当我们制作戴帽子的猴子时,我们以为只需要做一个独立的帽子对象,再把它跟猴子组合起来。在我们这么做的时候,才意识到当猴子转身时,帽子(或马甲、墨镜)必须与猴子一起转动。这要求每一帧装饰品和每一帧猴子都要完美地对齐。工作量大得惊人,但我们不想重制挖地的动画,所以我们做了一个权宜之计:只复制所有常规猴子的帧给戴帽猴,也就是说,帽子是贴在帧上的。美工提前开工,对其他6种猴子都这么处理了。
    以下计算解释了另一个问题是怎么产生的:
    1只猴子有一套动画子帧(恐惧、愉快、嬉笑、行走、爬等),大约需要20MB的VRAM(视频存储器)内存。 7种猴子类型*20MB=140MB VRAM 在崩溃以前,iPhone 3GS (iPod 3+)只有大约55MB的VRAM可用(15MB的堆) 我们最初希望以iPod Touch 2+为目标设备,但它只有30MB的VRAM,所以不能选它了。我们把系统要求提高到iPod 3+,同时争取减少VRAM。
    所以我们的教训就是,在开始制作游戏以前,在设计阶段,一定要把内存需求确定下来,而不是在制作中期后期才来考虑。如果我们已经知道猴子背向镜头的问题,我们可能会选择另一种画面表现方法,而这款游戏也不会发生显著的变化了。
    即时游戏
    我认识的许多商业开发者都会尽量避免编写多线程方案。为什么?因为各行其事的两条独立线程之间会发生冲突,这是测试者的噩梦。一旦应用崩溃,就可能是同时发生了多种排列错误,重制错误已经很困难了,更别说永远地修复漏洞。

    <ignore_js_op>plant-lostfrom-gamedev.net_.png

    plant lost(from gamedev.net)

    说到游戏,它们已经是即时的了,因为Update()循环每隔毫秒就会执行一次。在Windows Forms开发中,没有所谓的“阻塞”调用的概念。这就是游戏,不是我现在要说的。
    我说的是即时游戏和回合制游戏。回合制游戏要等待玩家输入,然后做出相应的回应;而等待用户互动时,屏幕上可能会显示一些东西,如洪亮的特效等,但游戏的实际状态并未改变。在即时系统中,游戏状态一直在变化,无视玩家的交互作用。 对于我们的游戏处女作,我们肯定不会选择即时游戏。
    在《Catch the Monkey》中,玩家要费很多功夫让所有东西在不断变化的环境下发挥作用。测试场景的数量可能20倍于回合制游戏。复制场景是极其困难的,甚至当程序性特殊单位的测试发生时。在构建阶段后期的某一时刻,我不知道怎么让它停止崩溃。幸运的是,Marmalade内置了一些非常了不起的内存监视工具,可以找到所有问题(个人认为)。
    这个教训太深刻了。现在我们正在制作另一款回合制游戏。
    对象层级结构
    显然,面向对象编程(OOP)能够构建小的、聚焦的、封闭的对象,然后在此基础上达到更高层次。我的目标是制作一个知道如何实体化、移动和渲染自身的对象层级。
    在我的职业生涯中,有一段时间我是不做模型的。当我知道Rational Rose、UML和建模时,我就一头扎进建模中去了。我总是自己模拟代码,甚至不对外公开的个人项目也是如果,因为我发现这是在代码建模以前思考问题的最好办法。Rational Rose(或任何合适的建模工具)可以帮助你一边思考一边设计。我使用Rational Rose已经好几年了,但当我准备做自己的项目时,我发现自己承担不起2000美元的版权费。幸好有开源社区提供的StarUML。StarUML是一个强大的、免费的对象建模工具。其实它与Rational Rose差不多(至少我在2003年使用的最新版本是这样的)。

    <ignore_js_op>StarUMLfrom-gamedev.net_.jpg

    StarUML(from gamedev.net)

    看上图类设计图,注意两个基本对象:GameObject和UIObject。二者都继承Graphic的属性。Graphic包括了所有Marmalade 2D API交互作用,所以无论是猴子、故事板还是文本对象都必须渲染。
    GameObject就是在GameScene(你玩游戏的关卡)中使用的对象。它管理自己的状态、子帧、深度计算、缩放(根据深度)、点击操作和碰撞侦测。所有游戏对象都继承GameObject。UIObject与GameObject类似,但更简单,而且是用于游戏场景的,如文本、按钮和商店或工具选择面板上的图像。
    设计模式
    必要时,我们使用GoF设计模式。例如:
    1、我们在Level类中使用Factory模式;输入周和日,它产生格式化的关卡对象,包括所有必要的指南。
    2、我们使用GraphicManager和SoundManager这两个单例模式来缓存图像文件和声音文件,所以即使各个对象都要负责载入/卸载它的资料,也是通过缓存来最小化实际使用的内在。我们使用单例模式来实现玩家状态(星星数量、当前进度、已完成的指南、购买的升级道具)。这使得序列化/并行化玩家进度变得极其简单。
    3、我们使用Decorator模式来给所有GameObject添加图形效果,例如淡入、淡出、闪烁等。
    在早期概念上,我有过挣扎,我不知道如何将所有不同类型的场景(游戏邦注:商店、工具选项、剧情模式、标题页面、选项/菜单页面、游戏模式)组织成一个整齐又系统的OOP图表。虽然通过搜索,我找到了两篇关于iPhone游戏制作工作室rivermanmedia的文章:
    The Scene System The GUI Stack
    我知道这种图表不仅是这款游戏的开发办法,而且可能也是未来所有游戏的开发办法。

    <ignore_js_op>paradigmfrom-gamedev.net_.jpg

    paradigm(from gamedev.net)

    Scene系列将游戏分成一系列场景。在《Catch the Monkey》中有19个,如SceneTitle和SceneDialog。这些都从Scene继承通用界面,如Init()、Update()、Render()、Shutdown()。我创建了包含所与场景生成、关闭和转换有关的SceneManager单例。现在,我的代码可以更准确地感知游戏的状态变化。如果我想结束和开始新场景,我可以调用:

    SM->ChangeScene(new SceneShop());

    如果我想让新场景聚焦和位于当前场景的顶部,我可以调用:

    SM->AddScene(new SceneOptions());

    SceneManager知道当前是否有其他场景加入,正确地处理这些场景,从内存中移除它们的材料,做一个淡出过渡,然后初始化和产生新场景。现在,即时游戏可以像Windows Form那样运行了,对话框可以调用对话,至于分类就看OS了。

    <ignore_js_op>GUI-Stackfrom-gamedev.net_1.png

    GUI Stack(from gamedev.net)

    第二个重要概念是GUIStack。这个GUIStack就在SceneManager内,复制屏幕的“聚焦”就像Windows对窗体和对话框的处理方式。通过推送和弹出场景到stack,我可以控制哪个场景调用Update()和Render()代码。如果场景没有接收Update()调用,这就会立即冻结(暂停)。在纯窗体中,顶部场景是唯一一个有自己的Update()调用的,而stack的所有场景都有它们自己的Render()调用。在后来的测试中,我移除了调用Render()到各个stack中的场景,以便提高性能。对于需要背景的场景(游戏邦注:如出现在游戏屏幕顶部的对话窗口),我则对当前状态截图,然后作为背景来表现,无论这个当前场景是什么。
    使用Marmalade
    正如我前面提到的,我们的目标设备是iPhone和Android。虽然Marmalade是一个3D工具,但我们知道也能用它制作2D游戏,因此我们只要专注于Iw2D API。我会重点介绍使用Marmalade的2D API制作2D动画的基本原理。
    正如所见,Marmalade的运作程度低。这不是GameSalad,那是我选择它的理由之一。考虑到这个选择,我倾向于低级API的灵活度和性能,而不是被设计师决定的框架约束。
    Marmalade的神奇之处在于使用了一个自定义制作文件MKB。这个文件允许用户定义Marmalade的类库,以便在项目、源代码、素材(声音)、字体和文本类中使用。
    Marmalade有一个资源管理器,与MKB文件类似,允许用户通过定义管理图像类(文本类):

    # Provide access to resource objects via IDE ["Resources"] (data) fonts.group templates.itx UI.group Loading.group Title.group

    你可以在自定义类文件中定义你的图像:

    UI.GROUP CIwResGroup { name “UI” shared true useTemplate “image”    ”image_template”

    “./accountbuttons.png” “./account1.png” “./account2.png” “./account3.png” “./black.png” “./bluestarbg.png” “./pause.png”

    在代码中,你可以测试资源类已经载入内存中,并且通过两个简单的函数调用载入/卸载:

    if (IwGetResManager()->GetGroupNamed(“farm”, IW_RES_PERMIT_NULL_F) != NULL) IwGetResManager()->LoadGroup(“farm.group”);

    或者:

    IwGetResManager()->DestroyGroup(“farm”);

    通过名称(不需要.png拓展名)寻找图像,从而把图像载入(和自动卸载到OpenGL VRAM):

    CIw2DImage* img = Iw2DCreateImageResource(name);

    一旦内存中有图像,就可以通过调用带有目标图像和2D矢量位置的图像绘制程序简单地渲染:

    Iw2DDrawImage(img, CIwSVec2(x,y));

    Marmalade自动地将所有绘制调按你调用的顺序排列起来,所以你通过首先调用背景绘制来控制图层。所以在我运行Render()程序以前,我会按深度(最低到最高)把所有对象分类,然后按那个顺序绘制。
    为了完成渲染,我调用这两个程序,告诉Marmalade我完成了,可以显示了:

    Iw2DFinishDrawing(); Iw2DSurfaceShow();

    就是这样。每一帧都调用这些绘制程序,你就得到你的游戏了。
    简化子帧
    游戏是由4000帧手绘动画组成的,大多是猴子与游戏世界的交互活动。为了管理这些图像,我们将它们放进子帧中。这需要考虑到两个问题:
    1、这些子帧的尺寸不能超过1024(iPhone不支持大过这个值的材质,Marmalade无法完好显示)
    2、因为显示卡,子帧的尺寸必须是2的四次方的倍数(32,64、128、256、512、1024)。否则,显示卡会将它们补充到2的四次方的倍数。

    <ignore_js_op>an-example-of-a-gamemaker-stripfrom-gamedev.net%E5%89%AF%E6%9C%AC.jpg

    an example of a gamemaker strip(from gamedev.net)

    制作各帧都有通用高/宽的子帧,使用Photoshop并不方便。所以我们想到一个妙计,节省了许多时间:
    1、在Photoshop中把各帧保存为PNG 2、在Game Maker中创建子帧,将各个PNG从给定动画的文件系统中拖放 3、从Game Maker中导出子帧为动画片,也就是一个长条的PNG(填入各帧,并将帧的名称附到文件名称中) 4、运行这个自定义的子帧程序,这会把它分成最小的2的四次方的矩形PNG图片

    <ignore_js_op>the-final-spritesheetfrom-gamedev.net_.jpg

    the final spritesheet(from gamedev.net)

    结论
    谁才是最苛刻的批评家?听众还是音乐家?音乐家,因为他们有双重压力,一是他们知道漏掉的每一个音符,二是他们知道练习时演奏得有多好。所以虽然创作者对自己的作品极其偏袒和宽容,但他们的设想与真实的结果总是存在残酷的差距。我个人认为,想象中的音乐总是比写在纸上的音乐更动听。
    经过6周的努力,我已经完成了游戏的核心部分。我花了大约340个小时。在制作过程中,我玩这款游戏已经不下千次了,带着一点私心,我认为这款游戏确实有趣。同时折腾三五只猴子确实有不可思议的乐趣。因为我总是呆在自己的小屋里,所以美工只能相信我说的话。因为我还没想到怎么配置它,所以他没有办法玩这款游戏,除非用我的笔记本。但是,知道我们的游戏已经有了令我骄傲的核心玩法后,我们面临着最一场最艰难的战斗:改进。

  • 相关阅读:
    BZOJ 3251 树上三角形:LCA【构成三角形的结论】
    BZOJ 2442 [Usaco2011 Open]修剪草坪:单调队列优化dp
    2018湖南省赛选拔
    扩展BSGS-传送门
    倒数第N个字符串
    HDU-6070 Dirt Ratio(二分+线段树+分数规划)
    第一场多校
    HDU5923-Prediction-有继承味道的并查集
    POJ2516费用流
    POJ3436:ACM Computer Factory-最大流
  • 原文地址:https://www.cnblogs.com/lancidie/p/3099545.html
Copyright © 2020-2023  润新知