Categories

iRobot 之梦

irobotiRobot 其实只是一开始胡乱找来的一个 code name ,不过现在已经成了一个小小的梦想的代名词。这个梦似乎是从 2007 年 12 月开始的。那天是去考六级,按照我以往的习惯去得有点早,教室还不让进,于是我便在东区长廊平台那里逛,想想俱乐部实践项目的事情。实践项目就是微软那边每年都会有的发放到各个俱乐部去的项目,具体做什么由自己定,当然他们那边是希望尽量和微软技术沾边的,虽然不多,但是微软也会给项目拨一些钱,不管怎么说,一些人一起做一个东西就是一件快乐的事情,这也正是俱乐部一直都在思考的一个问题啊:大家人是聚到一起了,可是要做点什么呢?虽然大家也并不是闲着,但也总想多一些点子吧! 🙂

实际上微软那边并没有给微软技术俱乐部一些特别的限制,所以各个学校的模式也有不少区别,有的学校组织相当庞大,而有的学校则更像一个项目团队。而我们学校的俱乐部则一直都是人数比较少,除了内部的活动之外,对外的技术活动基本上主要就是讲授式的“MSTC 小课堂”、讨论式的“技术沙龙”和承办式的“PBM (Powered by MSTC)”三种形式。虽然俱乐部的技术人员都是相当厉害的,但是却很少有一起做一个什么项目。我想大概大家个人能力太强本身也是一个原因吧,大家都很厉害,都有自己的想法,反而不太容易一起做事了。所以对于微软的实践项目,我们俱乐部似乎只参加过一次。就是之前 Rhythm 他们这一个时代的人一起做的 MCP (MSTC Challenge Platform) ,好像还是拿的一等奖。其实在夏令营的时候看过一些实践项目的展示,虽然也有做得相当不错的,但是另一些则似乎不怎么样,大概算是 C 语言课程 project 的水平,但是人家有项目拿来展示,我们却什么都没有,这是不争的事实,所以还是非常不甘心的。

顺便提一下 MCP ,其实是一个简单的平台:参赛选手为老鼠写 AI ,让老鼠在地图上走,吃到尽量多的米。俱乐部还用这个办过一次比赛,效果相当不错。只是后来没有再继续办下去。而现在计算机学院在办的那个 Java Challenge 比赛其实和当时我们的那个 MCP 差不多──或者说,除了一个是 Java 一个是 C# 之外,就没有区别了,好像连素材都是一样的。似乎倒不是谁抄袭谁这么不雅的问题,因为据说两个平台的开发人员是有严重重叠的。 :p

但我始终还是觉得老是老鼠吃大米太不好玩了。所以站在东区三楼那宽阔的长廊上的时候我突然有了一个野心勃勃的想法,那就是做一个更复杂更有趣的 AI 平台出来,姑且叫做 MCP II 吧,如果大家一起做的话,实践项目也有了,岂不是一举两得?怎么个更有趣法呢?要有角色、种族,不能老是一些一模一样的老鼠,要有攻击、防御、魔法以及各种属性和道具,而且要是团队合作,还要有建筑、商店、基地,以及故事情节、事件等等。想来想去,觉得要写出能处理这么复杂情况的 AI 似乎是太难了,可以允许有一个由人控制的角色出现在其中,但是要进行限制,不能完全抢了 AI 的风头。这就是我最初的想法了,一个“超级好玩”的半 AI 游戏平台──如同小孩子的梦一样的超级幼稚的想法,因为连我自己都不知道“超级好玩”到底应该是什么样子,而且后来和大家讨论的时候,总有人说你这个听起来好像像某某游戏,结果我总是没有玩过,就好像是连蛋糕都没有吃过就开始 yy 如何烤出最美味的蛋糕来了。

不过这确实是一个非常诱人的梦,虽然也觉得很困难,但是也有不少人有兴趣。趁 POM 、元宵 Party 等机会,经过长时间的讨论,大家商定了游戏的一个大致构想。首先否定了半 AI 的想法,因为要拿来做比赛的话,如果用半 AI 还不如直接让大家去打魔兽或星际比赛。而任务的形式大家则倾向于可以做得更灵活一些,而不只是“杀死对方”。在使用的技术方面,一开始我想用 PyGame ,但是并不知道效率如何,后来经过一些简单的调研,决定使用 C++ 的一个引擎 HGE 来做,脚本使用 Lua ——一个非常小巧且很容易嵌入的脚本语言。

后来我们组建了团队,虽然不能算是梦幻团队,但是也都是非常有实力的人,沉着稳重的 houshui ,精于美工的 Roxxane ,项目经验丰富的 cerror 还有个人能力强悍的 eshock 和 hzqtc 。按照我的想法,我先要做一个 prototype 出来。

我希望为 AI 编写人员提供一个尽量简洁的接口,以一种 State Machine 的形式来编写 AI ,在一个 State 中有 main loop ,是要做的事,而每个 State 也有相应的事件处理函数,在遇到事件时可以切换状态等。虽然不能保证完全平等,但是我还是决定把每个 AI 放在单独的线程中,让它们“并行”地执行,尽量地平分 CPU 资源而不相互影响。不过,实际上如果一个 State 的代码在执行完之前就经由某一个 event handler 切换 State 了,那么这个 State 是要提前中断的,我当时对此颇有些混乱,最后用 Lua 的 coroutine 来处理,在 C++ 和 Lua 之间切换状态就已经比较麻烦了,再弄一个 coroutine 出来就更加 tricky 了,不过至少是能解决当时的问题了,虽然我现在再来处理这个问题的时候发现自己当时确实是明显把问题想得太复杂了,而且每一步的思考都严重受到“是否效率会不行”的制约,导致了许多错误的决定,这也就是 premature optimization 的害处吧。以下是我当时画的一个图:

Create Player
  |
Event Loop                                             State Loop
===============                                        ================
+-> process event                                +-----> call loop() of current state <-----+
|     call event handler                         |         some action is called in loop(): |
|     return from event handler                  | +-------< yield to event loop <-+        |
|   if state loop coroutine == current state     | |                               |        |
|     resume state loop coroutine >--------------+-+-------------------------------+        |
|   else                                         | |     yield to event loop <-----+        |
|     create a new coroutine for current state   | |     loop >-----------------------------+
|     call exit() function of last state if any  | |
|     call init() function of current state      | |
|     resume state loop coroutine >--------------+ |
|   if some action needed to be executed <---------+
|     perform action (e.g. walk):
|       while (!stop && destination not reached)
|         move(speed)
|         process event
|           call event handler
|           return from event handler
|         sleep
+-< loop

大概最难的地方还是在于设计 AI 接口,又要让游戏不太 naive ,又要让 AI 编程变得容易,真是非常矛盾,后来发现就算 AI 编程接口变得“只要想得到就都能方便地写出来”这么简单,要“想”一个好的 AI 也是相当不容易的事。当然,另一方面,图形编程也挺麻烦的,初次接触游戏编程的许多概念,而且素材也都是从网上找来的,残缺不全。倒是 Roxxane 空余时间自己也做了一些素材:

fighter

梳理好 Lua 和 C++ 的嫁接以及图形的渲染,我就做了一个 prototype 出来,有简单的地图,人物可以在上面走来走去,避开障碍物,AI 由 Lua 脚本写成,是一个有限状态机的形式,这是当时的一个示例 AI ,就是按照之字形走来走去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
InitialState = {
    loop = function()
        irobot.turn(0.3)
        irobot.goto(ZigZagState)
    end
}
 
ZigZagState = {
    hitObstacle = function()
        irobot.log("hitObstacle")
        irobot.goto(TurnBackState)
    end,
 
    loop = function()
        irobot.turn(-0.6)
        irobot.walk(50)
        irobot.turn(0.6)
        irobot.walk(50)
    end
}
 
TurnBackState = {
    loop = function()
        irobot.turn(1.57)
        irobot.goto(ZigZagState)
    end
}

做出一个可运行的 prototype 之后,也算基本框架弄好了,于是我向大家展示了一下,然后不时聚在一起讨论,并商榷接下去要怎么弄。不过之后其实进展不是很大,似乎并不是因为积极性不够,也许是不知道从何下手?或者是这个事情的优先级一直不是很高的缘故?后来 eshock 动手加了不少东西,包括资源管理以及魔法攻击等的实现。不过整个 iRobot 也几乎就到这个程度便永久搁置了。 🙁

其实这件事情对我来说还是相当受挫的,因为也许我还有一些自己独立写的程序可以用来炫耀,但是却似乎从来没有处理好过超过两个人的团队的开发任务。另一方面,iRobot 也就变成一个未完成的梦而搁置了。

直到今年开学之后,由于又是一堆事情凑到一起让我觉得像一个无头苍蝇一样混乱,想在空余时间找点其他休闲一点的事情来做,才突然又想起了 iRobot 。实际上现在的空余时间比当时要少了许多,而且经过上次失败之后我也不敢再多提什么计划、进度之类的,反正也是娱乐吧,于是就自己悄悄写了。而且由于没有一定要做一个什么东西出来的压力,诸如性能啊之类的各方面问题也变得不那么重要了,粗略调研了一下,最后就用 PyGame 来做了。我把它叫做 iRobot2 。

实际上,除了名字还叫做 irobot 以及还是由 AI 来控制之外,原来的众多设定都已经没有了,我找到的素材有限也算是一个原因吧,从一个叫做 robokill 的游戏中提取了一些机器人的素材,于是整个游戏就变成了类似于坦克大战的东西了。实际上选择用 Python 来做让许多工作都变得简单了不少。主程序和脚本都是用 Python ,不用像原来那样在 C++ 和 Lua 之间不停地来回切换了,虽然 Python 无法很好地运行在一个沙盒内,让我不能很好地避免一些不合法的代码(如 sys.exit()),但是我想这样的代码大概对用户并没有太大的好处吧,这样的事情可以以后再考虑;另外,Python 没有访问权限的控制(其实像 Java 之类的访问权限也可以通过反射来绕过),所以在将数据传递给用户 AI 的时候需要非常小心,不要包含任何到内部数据结构的引用,否则诸如 robot._internal_structure.hp = max_hp 这样的代码出现就很不好了,虽然会带来一些性能的下降,但是这一点还是比较好处理的。还有诸如状态,我现在可以很方便地用异常来处理状态的中途退出,并且 Python 提供了许多很高层的线程同步支持,用一个带优先级的线程消息队列来为 AI 提供事件的处理是再方便不过的了。

所以后来游戏能跑起来之后我就将一些片断做成 flash 放到 cc98 的 MSTC 版上,当然我仍然没有想过这个东西会在什么时候做完或者根本是否能做完。到现在为止我还有两个主要的问题没法解决:

  1. 素材问题。就算是我不考虑版权之类的问题,最大限度地利用能从 Internet 上找到的资源,也还是相当残缺不全的。包括角色、地图的元素以及爆炸等,有相当多的东西,不是专业的人还是没法弄啊。
  2. AI 编程。虽然 AI 编程实际上是用户的事,但是我想至少我要做一个 non-trivial 的 example 出来。我原来以为抽象为状态机之后 AI 编程就会变得简单,然而现在我发现远远不是我想像中的那么简单。就最简单的情况,我在行进中碰到了队友,如何绕过去?这样一个问题似乎也变得颇为麻烦。

而且虽然同样是空闲时间在做,实际上现在的空闲时间是越来越少的。所以再也不敢保证什么时候能做出来甚至是否能做出来的问题了。只是最近微软有一个什么什么杯的比赛,说是为了鼓励更多的人参与,只要有一个 demo 就可以先进行参赛,所以我就趁机场以及飞机上的一点空闲,再后面加了加班,用傻瓜式的工具做了一个 demo 出来,既然有 demo 了,那么想再写一篇 blog 好了,所以才一不小心又写了这么多字。

不过,在做出 demo 之后我似乎突然有点明白 demo 的重要性了,不知道是不是因为听着背景音乐让我想起“空之轨迹”的场景了,反正看着 demo 就突然有一种热血沸腾很有干劲想要把这个东西做完的感觉。 ^_^

顺便把 demo 放在这里,youtube 被盾掉了,vimeo 又总是穿上去之后突然说失败,自己又懒的折腾在 WordPress 里面怎么嵌入视频,于是直接上传上来给出链接好了,代码则都是在 github 上的。

最后再闲话一下吧。其实这个东西,对我来说,无非也就是和很多其他的小项目一样,都是有趣玩玩吧,而且都是有一个共同的特点就是规模比较小,通常至少都可以在热情消退之前做出一些模样来。但其实今天被提醒看了微软实习生招聘宣传海报上的一句话:

当你长大成人的时候,是否会有那么一刹那的顿悟,让你知道你究竟想成为怎样的一个人?

遗憾的是,我似乎从来就没有顿悟过。从来都是漫无目的,或者说是循规蹈矩、得过且过。有人跟我谈及梦想及野心,诸如要“干一番事业之类的”,好像我从来就没有想过这样的问题,亦或者其实想过但是却没有得出什么结论吧,亦或者只是舒舒服服地活着,能做自己喜欢的事也是梦想的话。可是到最后我却连什么是自己喜欢的事也讲不清楚。似乎喜欢很多东西,然而总是零零碎碎。如导师所说,我研究生有两年半的时间,需要好好想想,这么一段时间如果花心思去做一件事情,应该是可以做得很好的。从来就懒而且也不喜欢对没有谱的事情做太多规划,而且要花这么长时间去解决的一个问题大概也不是随便就能定下来的吧。但是有一点必须得明了了:是得好好想一想了。其实我也不想就这样浑浑噩噩一直到死都还没有领会到“长大成人的顿悟”吧。不知不觉又是这么晚了。

15 comments to iRobot 之梦

  • xiaojay

    video 链结有问题 404

  • 原来有这么多人在做iRobot,相当期待 :p
    新模版也很不错~~

  • 我很期待地去点链接,但两个视频都是404……

  • 试试跟数媒、工设之类的专业交流看看?

    我只会一点 python 和一点素描,估计帮不上忙 囧

  • boisde

    恩。我也点不进链接。。

  • 各位,不好意思,手写链接写错了,现在应该 OK 了。

    @quark
    恩,以前是有一个不小的队伍的,但是现在只能算一个人在做着玩玩。

    @dotnil
    合作确实不错,但是其实现在这个东西的性质有些模糊。就我自己来说,也并不能每天都有稳定的时间来做这个东西,只是偶尔有空突然又加一些,这样进度不一致,合作起来大家都会很累。 🙁

  • 司马睿风

    怎么像是RM XP的素材?残念的RM

  • maxint

    原来你也是cad实验室的啊,ym
    看上面的人物素材,在rpg maker素材库中容易得到的

    感觉你所说的团队合作和人生规则的困惑,应该是我们这个阶段的人都会有的,深有同感。

  • 你这视频用什么工具做的?

  • @Moligaloo
    Ulead Video Studio (绘声绘影)。 🙂

  • xia0pang

    感觉把服务器(对战平台)和客户端(AI代码)分开也许会比较安全~完全通过socket来发数据,这样服务器就可以验证数据的合法性了
    哈哈,等什么时候有空再把这东西做做吧~

  • @xia0pang
    一开始倒是这么想的,但是我想到服务器和客户端要同时维护一个几乎相同的 world ,要考虑代码的重用,许多麻烦就来了,而且两个 world 如何高效地同步呢?服务端一个 object 移动了、爆炸了等等,要增量地反映到客户端,如果这种同步做好了应该回放也就 OK 了,但是也比较难,所以后来就想干脆先做成单机单进程的方式了,大概也是我一开始就陷入太多的细节考虑得太多了吧,这也是我的一个大毛病。

  • alcarx

    有host的地址么,是放在google code上了?还是github?

  • @alcarx
    在 github 上,页面里有链接的。

  • Chrysalis

    唉..好久没有见你的blog上有新文章出现
    我很期待新文章里的插图啊 一定很好看