游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

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

发布时间:2013-04-30 08:55:54 Tags:,,,,

作者:Thomas Henshell

本文记述了《Catch the Monkey》从创意到销售的过程。

简介

许多人都想做游戏,特别是手机游戏。好吧,我们也一样!本文是为那些想做手机游戏的人写的。我们的目标主要有两个:

1)证明做手机游戏的梦想是可能实现的

2)分享我们的经验,希望能帮助新手

catch the monkey(fromgamedev.net)

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)。

Marmalade(from gamedev.net)

Marmalade(from gamedev.net)

(由图可知,使用VS2008C++排错和使用iOS模拟器即时追踪)

Marmalade允许用户使用Visual Studio C++编写,然后在其他系统上运行(iOS、Android、Blackberry、Windows Phone、Bada等)。这个模拟器不错,完全达到期望,所以发现这个技术真是赚了。这个价格对独立开发者而言,也是非常划算的。

设计

考虑到这是我们的第一款游戏,我们希望保持设计的简单。最初的概念是:玩家用手指触击来挠农田里猴子的痒痒。随关卡进度,猴子会越来越多,速度会越来越快。直到结束。

在那时,看起来就这么简单,我们只有两个人,所以我们认为不需要正规的说明文件。相反地,我们使用Xmind(免费的!)来模拟我们的脑内想象和设计。这款游戏有意突出美术风格,因为我们的美工能够全天候扑在项目上,但我只能在下班后或周末赶工。

Mind Mapping(from gamedev.net)

Mind Mapping(from gamedev.net)

(思维导图是快速捕捉灵感以及组织参考的好办法。Xmind是一个用于制作思维导图的免费开源工具。)

原型

在开发商业软件时,初步原型是非常重要的。所有来自阅读和解读设计文件所得的猜想和想法都要在这个过程中完成。

我们不是直接编写原型,而是使用了一个相当强大和便宜(注册版仅需40美元!)的游戏制作工具GameMaker 8。 借助这个工具,我们可以把已经绘制好的图像和若干玩法机制组合在一起,看看是否能得到有意思的结果。我们制做第一版原型花了20个小时。因为它是在windows上运行的,没有办法测试真正的触摸/触击机制,所以我们只能用点击模拟触击。所以就出现了一个大问题:这样会有意思吗?

first prototype(from gamedev.net)

first prototype(from gamedev.net)

(使用GameMaker 8制作的第一版《Catch the Monkey》的原型)

答案是:没意思。我们调整了几个变量(猴子的速度、使他们发笑的点击、每次出现的猴子数量),但还是太简单了。内容还不够充实,我估计玩不到2分钟就无聊了。我们可不想做出一款“沉闷的游戏”,所以我们返回绘图面板。

在设计上进行头脑风暴时,我们想到到了使用不同的工具与猴子互动的办法。触击只是第一种办法,之后还可以使用羽毛等其他工具。这似乎有点意思。所以我们又想了若干种工具,然后筛选出容易放到原型中的几种。这样,我们就做出了第二版原型。在这个版本中,农夫有一个工具腰带用于放置各种工具。当一种工具用完,农夫会叫他的妻子再去拿,所以用完的工具要过一下子才会再出现。这样就使玩家不得不考虑什么 时候使用什么工具。玩家还可以控制农夫,让他走到某自个区域或拧起某只猴子。最后,我们添加了摘星星的概念——屏幕上会时常出现星星,玩家要点击才能摘到它。星星可用于之后的升级,尽管我们没有这个设计放到原型中。所以,还是那个问题:这样会有意思吗?

prototype 2(from gamedev.net)

prototype 2(from gamedev.net)

(使用GameMaker 8制作的第二版《Catch the Monkey》的原型,注意屏幕下方的工具带)

答案是:有意思也没意思。似乎有些乐趣卡在这个设定中,呼之欲出,但仍然存在许多障碍。我们知道在不同工具之间做选择(基本上是战略性的)和摘星星是很有趣的(自然、特别又困难)。我们放弃了控制农夫(太麻烦)和补充工具的概念(太复杂和任意)。我们需要一种让玩家可以制定战略和管理资源的游戏机制。

我必须提醒一下,当我们制作原型时,我们并不只是自己玩,还请了其他不参与项目的人做测试并如实地反馈。毕竟,在测试时,做项目的人很难不带偏袒地评判自己的项目。之后你会看到我们如何在这一点上犯错。

结论

在这个阶段,我们又进行了第三也是最后一次头脑风暴。我们想了许多概念,最终想到类似于《魔兽世界》(WOW)中的魔法/冷却机制。在WOW中,玩家不可能随心所欲地施放法术,因为有一个蓝条限制了短时间内的使用次数。但有些法术太强大了,一下子把蓝耗光了,必须经过很长一段时间(几分钟)的冷却才能再次使用,所以一场战斗中不可能多次使用。我们认为,如果所有工具都需要某种通用的能量源,但冷却时间不一样,可能就会产生我们所期望的“战略性平衡”。通过设置这些变量,我们可以保持游戏的新鲜感和乐趣,从而让玩家的沉浸感提升。

另外,我们还制作了星星能量。这次,我们把星星当作一种购买升级的货币,但星星能量也是一种特殊的技能,可以帮助玩家当前的游戏。这样,星星就有了两种功能,从而使玩家面临把星星累积起来用于升级或应付当前游戏的选择。这是一个很不错的机制。玩家面临着一个有趣的挑战,因为设计师把星星能量做得相当实用,但明智的玩家只会节俭地使用星星,以便用作之后的升级。

final toolbelt design(from gamedev.net)

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

这个阶段的设计基本上完成后(设计工作贯穿整个开发周期),我们继续制作游戏的核心。

第二部分:制作核心

简介

在第一分部中,我们简介了《Catch the Monkey》从简单的概念化到技术选择以及原型制作的过程。在原型制作后期,我们的设计充实了很多,但我们并没有把新设定完全地写进文档里。我们知道有12种工具、10种猴子和若干购买升级道具的商店的模糊概念。升级道具的数量以及各种道具的功能还没有确定下来。现在要开始编写代码了!

这一部分要比上一部分长,为了控制文章长度,我着重介绍核心建构阶段中最有趣的方面。

做还是不做?

正如在第一部分中提到的,美工是全职扑在项目上,但我这个程序员却只能兼职,因为我还有其他工作要忙。我们的项目一拖再拖,看不到进展,终于到了几乎不得不叫停的地步。然而,我还是腾出时间做这款游戏。大约安排了6周(50小时*6=300小时)来做它。我做了一个极端的决定:停止商业工作6周,回家100%地投身于这款游戏。虽然我的妻子不太高兴,但支持我把游戏做完。是时间“全力以赴”了。事后证明这是复活项目的正确决定。

我们犯下的最大错误

没有规范地制作设计文案本应该是我们最大的错误,但这个错误在另一个错误的对比下,显得不那么严重了。

如果你研究过《植物大战僵尸》中的僵尸,你会发现,里面的僵尸类型很多,但都是由几个图形部分(头、体、手、臂、足)和向个随意的装饰(头盔、报纸等)。通过重复使用和变化这些部件,你不用费太多脑细胞就做出许多不同类型的僵尸。我们想用类似的办法做出许多类型的猴子,使它们各具能力和弱点。

Plants vs Zombies(from gamedev.net)

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。

所以我们的教训就是,在开始制作游戏以前,在设计阶段,一定要把内存需求确定下来,而不是在制作中期后期才来考虑。如果我们已经知道猴子背向镜头的问题,我们可能会选择另一种画面表现方法,而这款游戏也不会发生显著的变化了。

即时游戏

我认识的许多商业开发者都会尽量避免编写多线程方案。为什么?因为各行其事的两条独立线程之间会发生冲突,这是测试者的噩梦。一旦应用崩溃,就可能是同时发生了多种排列错误,重制错误已经很困难了,更别说永远地修复漏洞。

plant lost(from gamedev.net)

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年使用的最新版本是这样的)。

StarUML(from gamedev.net)

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

我知道这种图表不仅是这款游戏的开发办法,而且可能也是未来所有游戏的开发办法。

paradigm(from gamedev.net)

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了。

GUI Stack(from gamedev.net)

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的四次方的倍数。

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

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

制作各帧都有通用高/宽的子帧,使用Photoshop并不方便。所以我们想到一个妙计,节省了许多时间:

1、在Photoshop中把各帧保存为PNG

2、在Game Maker中创建子帧,将各个PNG从给定动画的文件系统中拖放

3、从Game Maker中导出子帧为动画片,也就是一个长条的PNG(填入各帧,并将帧的名称附到文件名称中)

4、运行这个自定义的子帧程序,这会把它分成最小的2的四次方的矩形PNG图片

the final spritesheet(from gamedev.net)

the final spritesheet(from gamedev.net)

结论

谁才是最苛刻的批评家?听众还是音乐家?音乐家,因为他们有双重压力,一是他们知道漏掉的每一个音符,二是他们知道练习时演奏得有多好。所以虽然创作者对自己的作品极其偏袒和宽容,但他们的设想与真实的结果总是存在残酷的差距。我个人认为,想象中的音乐总是比写在纸上的音乐更动听。

经过6周的努力,我已经完成了游戏的核心部分。我花了大约340个小时。在制作过程中,我玩这款游戏已经不下千次了,带着一点私心,我认为这款游戏确实有趣。同时折腾三五只猴子确实有不可思议的乐趣。因为我总是呆在自己的小屋里,所以美工只能相信我说的话。因为我还没想到怎么配置它,所以他没有办法玩这款游戏,除非用我的笔记本。但是,知道我们的游戏已经有了令我骄傲的核心玩法后,我们面临着最一场最艰难的战斗:改进。(本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

How we Built an iOS game on PC

By Thomas Henshell

This article chronicles Catch the Monkey from ideation to sale worldwide in the App Store.

You can find out more about Mirthwerx and our projects at our website.

Intro

Many people want to get into making games, specifically mobile games. Well, we were one of you! This series is for anyone who wants to jump in and do it. Our goal is twofold:

1) To demonstrate that it is possible

2) To share lessons we learned that will hopefully benefit those starting out

About Us

We at Mirthwerx are a team of two: Thomas the self taught programmer and Alex the artist who studied classical animation at Sheridan. We met 20 years ago in highschool and have tried to make a game ever since.

Before we embarked on this project, I had been writing business web/mobile software with Microsoft technologies for about 15 years. With this background, we knew how to build software properly (OOP, design specs, usability concerns). But you will see later how we failed to apply it.

Part 1: Design and Prototyping

Technology

From day one, we knew we wanted two things:

1) Android is the future, but iPhone is the now. We will build for both
2) We want to build on a windows platform with familiar environment and tools

I started investigating the Mac platform and XCode by buying a mac-mini. After spending a day with ObjectiveC I knew I did not want to work in that language at all, it would drive me batty. Fortunately we could address both goals with one solution: Marmalade (formerly called Airplay before Apple started calling everything Airplay).

Here you can see using VS2008 C++ debugging and tracing in real time with an iOS emulator

Marmalade allows the user to write once in Visual Studio C++ and run anywhere (iOS, Android, Blackberry, Windows Phone, Bada, and more). The simulator is excellent, with all the performance monitoring you’d expect, so it was a total win finding this technology. The pricing for independent developers is also very reasonable.

Design

Given this was our first title, we wanted to keep the design of the app simple. The initial concept was this:
The player swipes their finger to tickle monkeys in a farmer’s field. The monkeys come more and faster in each level. The end.

It seemed so simple at the time, and there were only two of us, that we thought we didn’t need a proper specification document. Instead we used Xmind (free!) for mind mapping all our ideas and kept “the design” in there. The game was intentionally art heavy, as our artist was able to work full time on this project, but I was only able to work after hours/weekends.

Mind Mapping is a powerful way to capture ideas quickly and organize them well for later reference. Xmind is a free open source tool for mind mapping.

Prototype

In business software an initial prototype for the users is critical. It removes all the guess work that comes from reading and interpreting a Word document.

Rather than programming a prototype for real, we used an extremely powerful and inexpensive ($40 for a registered version!) game making tool called GameMaker 8. This allowed us to throw together the graphics that had already been drawn with a few play mechanics and see if we had something fun or not. I think all in the first prototype took 20 hours. Since it was running on a windows screen, there was no way to test the actual touch/swipe mechanic, so we just resorted to clicking, each click simulating a swipe. So the big question: Is it fun?

First prototype of Catch the Monkey made in Game Maker 8

No. It was not fun. We changed around several variables (speed of monkey, clicks to make them laugh, number of monkeys at one time) but it was just too simple. There wasn’t enough to do. We couldn’t see playing it for more than 2 minutes. We had no desire to make a “gag game” so we went back to the drawing board.

In our design brainstorming session we came up with the idea of using different kinds of tools to interact with the monkeys. Tickling was just the initial tool, a feather, but later you could get other tools. This seemed to have some promise. So we thought up several types of tools, narrowed it down to a few that were easy to put into a prototype, and then made prototype 2. In this version the player has an inventory of each tool. When one ran out, the farmer would call his wife for a refill which would appear in a few moments. It made the player have to think about what tool to use when. We also gave the player control of the farmer, they could direct the farmer to walk to certain areas or pickup a certain monkey. Finally we added the concept of catching stars. Every so often a star would pop out and the player would have to click it to catch it. Stars would be used later for upgrades, though we never built it into the prototype. So: Is it fun?

Prototype 2 Made with Game Maker, notice the inventory counts for the differing tools

Yes and no. There was a kernel of fun in there that was trying to get out, but there were still many things blocking it. We knew choosing (essentially strategizing) between tools was fun and catching stars was fun (it was spontaneous, different, and difficult). We dropped controlling the farmer (too cumbersome), dropped the refill concept (too complex and arbitrary). We needed a game mechanic to allow the player to strategize and manage resource(s).

I must note that when we prototyped, we didn’t just do it amongst ourselves, but with others who were not involved in the project to get their honest feedback. Those working on a project are too biased to give a proper perspective to what they are testing. You’ll see later how this also came to bite us in the butt.

Conclusion

At this stage we sat down for our third and final all day brainstorming session. We went through many concepts before considering the mana/cooldown mechanic in WoW. In WoW the player can’t just cast all the spells they want, they have a mana bar limiting the number that can be used in a short period of time. But some spells are so powerful that while they do use up mana, they must cool down for a long time (several minutes) so they cannot be used in a single battle. We felt if every tool required a common energy pool, but had varying cooldowns, we could strike the strategic balance we were looking for. By having enough variables we could keep things fresh and interesting for the player, and therefore they would be engaged and have fun.

One additional thing we decided was to create Star Powers. Stars to this point were only used as a currency to purchase upgrades, but a Star Power is a special ability that can help you now mid level for a certain cost in stars. By making stars dual purpose, and facing the player with a decision for a momentary benefit now rather than a long term pay off later, is a great mechanic we tried to bring to other aspects. It became a fun challenge for us as designers to make star powers that were really really useful, but the smart player only uses sparingly so they can get all the upgrades.

The final toolbelt design.

With the design phase basically finished (design happens all the way through), we proceeded into building the core game.

Part 2: Building the Core

Intro

In the first article, we covered how Catch the Monkey started from initial simple concept, to the technology we chose, through the prototyping phase. At the end of prototyping we had a greatly increased design, but despite knowing better, we didn’t document it thoroughly. We knew we had 12 tools to create, 10 types of monkeys, and some vague concept of a store which would allow the purchase of upgrades. How many upgrades and what they would do was not finalized. It was time to start coding!

This article is longer than the previous, I have attempted to keep it of reasonable length by highlighting only the most interesting aspects from the core construction phase. If you have a specific question, just post a comment and I’ll respond.

We Going to Do this or Not?!

As mentioned in the first article, the artist was working full time but I as the programmer was only able to work part time as I was required by other aspects of the business. The project dragged. It finally reached a point where the project would be cancelled due to lack of progress. Instead, I mapped out the time remaining to build the game. About 6 weeks (50hrs x 6 = 300hrs) should do it. I made an extreme decision: I booked a 6 week hiatus from work to go to my cottage and focus 100% on the game. While my wife was less than thrilled, she was supportive of seeing me get the game done. It was time to go “all in”. Hind sight confirms this was the right way to recover the project.

Our Single Biggest Mistake

Not having a properly defined design document would appear to be our largest mistake, but we made one that completely dwarfed it.

If you study the zombies in Plants vs Zombies, you will see there are many types of zombies, but they are made up of several graphical parts (head, body, arm, arm, legs) and several optional decorators (pylon, helmet, paper). By reusing and varying these components you can have many different types of zombie with minimal memory requirements. We wanted a similar approach with many kinds of monkeys each with varying abilities and weaknesses.

However, and we painfully learned this later, if you want to have this kind of reuse, you have to lay down very specific rules of what the characters can and cannot do. Notice in plants verses zombies that the zombies always face the camera (like the 2D South Park animation). No matter what they do, they never turn away from the camera to a profile view.

Well, early on in our animation and prototyping we decided when the monkey arrives at a plant he will plop down, TURN HIS BACK, and begin digging. Then when he gets a potato, he will TURN BACK and proceed to eat it. We completed all the artwork for the regular monkey before we discovered what a problem this was. When we wanted to have a hat monkey, we thought we would just create a separate hat object, attach it to the monkey, and off we go. Well as we did it we realized the hat (or vest, or sunglasses) has to turn with the monkey as he turns away from the camera. This requires one decorator frame per monkey frame and pixel perfect alignment. This means a whole host of painstakingly researched coordinates per frame to get it all to look right. It was so much work, and we didn’t want to redraw the digging animation, so we made an expedient decision: just duplicate all the frames for the regular monkey to the hat monkey with the hat pasted right into the frame. The artist went ahead and did this for each of the 6 additional types of monkeys.

Here is the math of why this was such a problem later:

1 monkey has a set of interaction sprite sheets (fear, ducky, laughing, walking, climbing, etc.) taking about 20mb of VRAM memory.

7 monkey types x 20mb = 140mb VRAM

The iPhone 3GS (iPod 3+) only has ~55MB of VRAM available (with a 15MB heap) before it starts crashing.
We had initially wanted to target the iPod Touch 2+, but it has only 30MB of VRAM and it became impossible. So we increased the system requirements to iPod 3+ and scrambled to get the VRAM down. We’ll talk more about this in the next article.

So the lesson is always map out memory requirements during the design phase, before you build it, rather than in the middle, or after. Had we of known the ramifications of the monkey turning away from the camera we would have gone a different direction with the art and the game wouldn’t be noticeably different.

Cute Monkeys in a Nasty Real-Time World

Many business developers I know avoid writing multi-threaded solutions when they can avoid it. Why? Because the race conditions that can occur between two separate threads doing their own thing are a nightmare for testing. There are so many permutations of what could be happening simultaneously in the application that if it crashes, it is difficult to reproduce never mind fix permanently.

When it comes to games, they are already real-time in that the Update() loop is executed every so many milliseconds not matter what. There is no concept of “blocking” calls like there is in Windows Forms development. This is just the way games are, and this is not what I’m referring to.

I’m talking about a real-time game verses a turn based game. A turn based game waits for user input, then responds accordingly; while waiting for user interaction there may be things happening on screen, nice effects and such, but the actual state of the game doesn’t change. In a real-time system the game state is constantly changing regardless of player interaction.

For our first time game, we NEVER should have chosen to do a real-time game.

Catch the Monkey was an incredible amount of effort to make everything work in a constantly changing environment. The number of testing scenarios is probably 20 times greater than a turn based system. The ability to replicate scenarios is extremely difficult, even when programming specific unit tests to occur. There was a point late the construction phase I wasn’t sure I could ever get it to stop crashing. Fortunately Marmalade has some amazing memory monitoring tools built into it I was able to find all the issues (I think!).

We learned this lesson so bitterly the next title we are currently working on is turn based.

Object Hierarchy

Obviously the power of OOP is the ability to build small, focused, encapsulated objects and then work with them at a higher level. My goal was to create an object hierarchy that knew how to instantiate, move, and render itself.

There was a time in my career where I didn’t do modelling. Once someone showed me Rational Rose, UML, and modelling I never went back. I always model my code, even personal projects no one will ever see, because I find it the best way to think through the problems before the code gets in the way. Rational Rose (or any proper modelling tool) helps you think through the design as you design. I used Rational Rose for several years, but when I went out on my own I couldn’t afford the $2,000/seat license. Fortunately the Open Source community came to the rescue with StarUML. StarUML is a powerful free object modeling tool. It is virtually identical to Rational Rose (at least to the last version I used in 2003).

Looking at the class design diagram, notice the two fundamental objects: GameObject and UIObject. Both of these inherit from Graphic. Graphic encapsulates all the Marmalade 2D API interaction, and therefore is necessary for rendering whether it is a monkey, a story slide, or a text object.

A GameObject is an object used in a GameScene (which is a level you play). It manages its own state, sprite sheets, depth calculation, scaling (based on depth), click handling, and hit detection. All play objects inherit from GameObject. UIObject is similar to GameObject, but is more lightweight and designed for non-play scenes, such as text, buttons, and images in the store or tool selection screens.

Design Patterns

We used GoF design patterns as necessary. For example:

We used the Factory pattern for our Level class; feed in a week and day, and it spits out a formatted level object, complete with any necessary tutorials.

We used two singletons for caching image files and sound files called GraphicManager and SoundManager, so even though each object is responsible for loading/unloading it’s assets, it does it through these caches to minimize the actual memory used.We used a singleton for player state (number of stars, current progress, which tutorials have fired, upgrades purchased). This made it extremely simple to serialize/deserialize player progress.

We used the Decorator pattern for adding graphical effects to any GameObject, such as fade in, fade out, flashing, etc.
One of the early conceptual struggles I had was how to bring all the different types of screens (a store, a tool selection, story modes, title screens, option/menu screens, game modes) together into a nice organized OOP paradigm. While researching I found two excellent articles by iPhone game maker rivermanmedia:

The Scene System

The GUI Stack

I knew this paradigm was the way forward not just for this game, but probably all future games.

The Scene system breaks down the game into a series of scenes. In Catch the Monkey I ended up with 19, like SceneTitle and SceneDialog. Each of these inherit a common interface from Scene such as: Init(), Update(), Render(), Shutdown(). I created a SceneManager singleton that contains all the logic related to scene creation, shutdown, and transition. Now my code can be blissfully unaware of what else is going on at a higher level. If I want a scene to end and begin a new scene, I call:

SM->ChangeScene(new SceneShop());
If I want the new scene to be focused and on top of the current scene, I call:

SM->AddScene(new SceneOptions());
The SceneManager knows if there currently are other scenes involved, winding them down appropriately, removing their assets from memory, doing a fading transition, then initializing and firing up the new scene. With this in place the real-time game now behaves more like a Windows Form application, with dialogs able to call dialogs and just let the OS worry about sorting it all out.

The second key concept is the GUIStack. The GUI Stack sits inside the SceneManager and replicates “focus” of a scene just like how Windows does for forms and dialogs. By pushing and popping scenes onto the stack, I can control which scene has its Update() and Render() code called. If a scene doesn’t receive the Update() call, it is effectively frozen in time (paused). In pure form, the top scene is the only one to have its Update() called, while all in the stack have their Render() called. Later in testing I removed calling Render() to every scene in the stack for performance improvement. For scenes that require a background scene (such as a dialog window appearing over top of the game screen) I Instead take a screenshot of the current state, then display that as a backdrop to whatever the current scene is.

Using Marmalade in 2D

As previously mentioned, we were targeting both iPhone and Android simultaneously with C++ in Visual Studio 2008. While Marmalade is a 3D framework, we knew we were making a 2D title and therefore focused on the Iw2D APIs. I’ll highlight the fundamentals of 2D animation with Marmalade’s 2D API.

As you you’ll see, Marmalade works at a pretty low level. This isn’t GameSalad here, and that is one of the reasons I chose it. Given the choice, I prefer the flexibility and power of a low level API rather than being limited to what a framework designer decided I should be able to do (or not!).

Marmalade works it magic by using a custom make file called an MKB. This file allows the user to define Marmalade libraries to pull into the project, source code, assets (sounds), fonts, and texture groups.

Marmalade has a resource manager that allows the management of image groups (texture groups) by defining them like this in the MKB file:

# Provide access to resource objects via IDE
["Resources"]
(data)
fonts.group
templates.itx
UI.group
Loading.group
Title.group
You then define all your images in custom group files:

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”
Within the code you can test if resource groups are already loaded into memory, and then load/unload them through two simple function calls:

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

IwGetResManager()->DestroyGroup(“farm”);
Images are loaded (and automatically uploaded to OpenGL VRAM) by asking for the image by name (without the .png extension) by using:

CIw2DImage* img = Iw2DCreateImageResource(name);
Once you have an image in memory, it can be rendered simply by calling the image drawing routine with the image you want, and the 2D vector position.

Iw2DDrawImage(img, CIwSVec2(x,y));
Marmalade automatically queues up all of the drawing calls in the order in which you called them, so this way you can control layering by doing your background draw calls first. So before I would run through my Render() routine I would sort all my objects by depth (lowest to highest) and then draw them in that sequence.

To complete the rendering I call these two routines, which tells Marmalade I’m finished, show it to the world:

Iw2DFinishDrawing();
Iw2DSurfaceShow();

That’s it. Call those drawing routines each frame and you’ve got yourself a game.

Simplifying Sprite Sheets

The game comprises over 4,000 frames of hand drawn animation, most is for the monkeys interacting with their world. To manage all these images, we put them into spritesheets. Two issues needed to be considered:
No dimension of the sprite sheet could be larger than 1024 (iPhone doesn’t like textures bigger than this and Marmalade started fuzzing them)

Sprite sheet dimensions needed to be to the power of 2 (32,64,128,256,512,1024) for the graphics card. If they weren’t, the graphics card would pad them out to make them to the power of 2 anyway.

An example of a GameMaker strip.

Photoshop does not have an easy way to make a sprite sheet where each frame is universal in height/width. So we discovered a trick that saved us dozens of hours:

Save each frame in PNG from photoshop

Create a sprite in Game Maker to drag and drop each PNG frame from the file system for a given animation
Export the sprite from Game Maker as an animation strip, which is each frame appended into one long horizontal PNG with the number of frames appended to the file name.

Run our custom sprite sheet program on the strip, which would break it out into a rectangle of the smallest power of 2 dimension as a PNG.

While it sounds involved, we could go from a collection of png frames to a squared sprite sheet in under 2 minutes. Just for its ability to create sprite strips GameMaker is well worth having!

The final spritesheet, power of 2 sized.You can’t tell, but we also dropped the color depth from 32bit to 16bit for memory.

Conclusion

Who is the harshest critic, the audience or the musician? The musician, for they have the double burden of knowing every note they missed and how much better they played during practice. So while the creator is extremely biased and forgiving of their creation, there is a harsh reality of what they intended and what they ended up making. I would say the music is always much sweeter in the imagination than on the page.

At the end of the 6 weeks I had finished building the core of the game. I came in around 340hrs. Knowing I have a personal biased, having played the game over a thousand times during build cycles, I concluded this game is actually fun. There was something magical about trying to entertain 3-5 monkeys simultaneously. Because I was away at the cottage, the artist had to take my word for it. And since I hadn’t yet figured out how to deploy it, he had no way to play it other than on my laptop. But, knowing we had a good core, something we were proud of, gave us the determination for the toughest fight yet: Polishing.(source:gamedev.net)


上一篇:

下一篇: