Adobe页游峰会小记

(我去面壁吧,时隔7个月我终于又更新了篇。。)
和老大和老板到北京参加了这个Adobe页游峰会。持续了整整一天,收获不少。
我简单说下。

早上主要是开幕词还有Adobe官方的介绍,大家也听过很多了。

接下来是国内flash3D开发商的演示,我是第一个,有个插曲是当时上报名字时我老大和我一起上交的,但可能没具体说是谁讲,Adobe用的老大名字,实际是我讲,我上台后澄清了下。我是偏技术性的介绍,没做产品演示,因为已经有展台展示了。主要介绍项目用到的2个创新点:基于Stage3D的GUI系统和高级Shader语法包装器,另外谈了一点游戏优化的心得,并再次反映了Flash11驱动兼容性的问题。这是ppt下载链接:

封神无双Flash3D项目开发心得

其他三家厂商的演讲偏演示和非技术性,我挺担心我是不是让气氛变凝重了(笑)。不过他们的演示都很棒,成都敬天的MMO完成度很高,松果的格斗高手也是按次时代标准做的,画面非常绚丽,用到很多高级效果,这是我们公司稳妥起见不敢在flash上实现的。 最让我佩服的是上海端点,他们CEO史珉是大牛,公司10个人,仅凭3个前端和3个美术,竟能在半年多点完成这么棒的客户端效果,而且他自己还是从零开始学的3D,佩服的五体投地,和他交流中感受到了那种技术狂人独有的气质,以做游戏为乐趣。不仅他们游戏风格像火炬之光,连开发方式也很像(火炬之光就是17人团队6个月开发完成的)!

中午和Adobe还有演讲的几个厂商一起吃饭。饭桌上讨论不少技术问题,但最核心的还是我们开发商关心的显卡兼容性问题。虽然Adobe11.2已经将驱动限制提前到2008年,11.3计划推到2006年,但即使这样部分集成显卡还是不能通过此名单,强制为软件渲染,所以我们希望能同时够开放这类显卡的限制,这样进入用户才能更多。另外获悉ie6虽然能够升级到flash11,但Adobe不提供ie6此版本的技术支持,这也是一个风险。最后
关于从目前10版本完全过渡到11这个过程的长短也做了讨论,可能不是一个太短的时间 ╮(╯▽╰)╭。总之我们只要时时保持领先,就不会临危不乱。

下午Adobe海外的传教士Tom演示了很多海外优秀Stage3D的作品。但可以看出,海外主要还是以休闲和单机游戏为主,几乎没有MMO,画面上说实话国内的厂商更炫,Tom自己也希望国内的优秀产品能推向国外(笑)。

其他的亮点有对新收费版Alchemy技术的介绍,能直接将C/C++转换成可读性不错的AS代码,然后再编译成ABC字节码;基于Alchemy2的虚幻引擎导出Flash工具的一个Demo也是赚足眼球,虚幻引擎一直都超级给力!另外一些引擎工具如Minko的ShaderLab、Starling Stage3D 2D渲染框架的介绍也让人眼前一亮,超级酷。

最后很实用的是人人游戏技术总监介绍的通过AIR移植webgame的一些心得和注意事项,以后我们或许也会考虑一直到移动平台,非常有参考价值,不过也对游戏内存和界面设计都有更高的要求。

遗憾的是,会议延迟了大概一个小时,最后的抽奖环节没等到我们就要去机场赶飞机了。。奖品是一个iTouch╮(╯▽╰)╭不好不坏。下次广州站一定不能错过抽奖:)

资源下载优化小结

有3个月没有写blog,一般都会说”好快,一眨眼就三个月了,不敢相信“等等是吧,不过我却觉得很漫长,每天都很长,因为做了太多事情。有很多都是很值得记录的东西,但一直偷懒,或者说是一直保持着一个高度紧凑的生活步调,突然就没有让周末静下来写blog的时间插入的余地(借口)。但最近看了《软件随想录》后觉得还是要坚持一周写一篇,里面说到写作能力是区分优秀程序员和普通程序员的重要能力,或许真的如此。

好了,以前的记录后面慢慢补上,现在先记录下最近对资源下载量优化的心得。

内部开发时,都是局域网测试,没有过多考虑下载大小问题,没有把这项工作重视起来,一直拖到现在,放到外网才发现下载的确太慢了(进入第一个场景的局部区域一共下载了13M!),不得不做优化。其实优化空间是很大的,我自己心里一直都清楚,只是没安排到计划里。丑媳妇总要见公婆,既然见了不行,那就动手术整容吧。

主要从以下几点入手
1. 首先资源都没有打包,这是余地之一
2. 图片格式为了方便统一用的png,这是大大的余地,也是最关键的
3. 不必要的资源都可以延后加载,余地之三
4. 另外由于打包规则没有分的很细,不相关的资源打成一个包会增加下载大小,这点也是要细化到让每个目录每个文件才能做到极致,目前还没有。

以上4点分别如下处理
1. 策划配置由于总体大小不都,全部打包,当然是若干个文件一个包,不是全部一个包。这样做之后最开始的loading画面的下载量由1M左右降到了300k

2. 最开始是打算图片打成很多小包,swf压缩png就是用的jpg压缩颜色和通道,所以大小可以变小,但问题就是上面的第4点,目前的打包策略只是单纯的遍历文件,逻辑上没有分组,但贴图在很多场景都能够共用,不相关性很大,如果要完全按相关性分组,工作量实在太大,所以打算不打包图片,直接从减少文件大小入手。最开始想到的就是jpg,压缩率最高的格式非他莫属,那么带alpha通道的图片呢?恩,也单独存成jpg,既然jpg压缩比最高,压alpha的8位位图也会很小吧,开始还天真的去搜有没有已经实现的带alpha的jpeg格式,还搜到这篇文章(讲把带alpha数据的png嵌入到jpg的标签,不过要用js辅助png还不能超过64k,pass)。就这么做了一个版本,的确下载量下降的非常明显,降了一半。jpg的转换和之前png的转换都是用开源库实现的(诶,你问为什么不用第三方工具?因为转换前的贴图格式是自定义的,肯定要自己写导jpg和png啦),采用的都是最高压缩比方式,不过jpg质量默认用的50%,不敢太低,理论上20以上都是可以接受的,但美术肯定会杀了我,质量下降还是很明显的,只能统一转换后再微调。 一张png变成了2张jpg,下载和写代码都很麻烦,rgb贴图要等alpha下载完才能合成最终图片,所以干脆把两张图合成一个文件,就叫ajpg吧(alpha jpg),文件开头存一个alpha数据的偏移,这样读写文件都很方便了。你说不能用普通图片查看软件看啦?其实是可以查看RGB颜色的那张图,因为大多数软件都是要遍历到第一个标签字节0xff才认为是正式的图像数据开始,所以自动跳过了开头的偏移字节。
本来以为图片格式就告一段落的,后来发现一些图片转换jpg后反而比png大,虽然数量不多,但还是引起了我的注意,比如原png是103k,一张rgb的jpg是80k,但alpha的jpg却有60k!只是8位图哦,虽然和图内容也有关,但也太坑了。突然想到前面那篇嵌入png的文章,为什么是用png存alpha?只因为无损吗,还是说会更小?jpg说不定真的对8位灰度存储没那么强力呢,是对RGB压缩才更在行是吗?于是我又试了下把alpha用png保存再合成到ajpg里,果然几乎全部ajpg都变的更小了!给个直观的数据就吧,比如原来一个文件夹png一共2M,转为第一版的ajpg减到了1M,然后第二版的ajpg减到了900k!而且对比单个文件,alpha的数据普遍变得更小,png在这场战斗胜出,恭喜恭喜(你说可以把jpg的质量再调低?alpha通道很影响图片效果的,失真太大导致边缘锯齿和黑边严重,得不偿失)。

3. 不必要的资源比如图标、表情,甚至还有场景其他区域的地表贴图(这是bug,之前全部一次下载了)都可以需要时才加载。地表贴图的延迟加载又省了客观的数量。这是纯逻辑问题
4. 是不是所有资源都要打包?当然不是,之前虽然已经排除了如mp3这种流式资源,但打包后还是会比不打包多下载,可能这是必然的,因为打包的一个优点就是减少后面需求包内其他文件的http请求,但会导致初次下载量大,毕竟你需要文件小A时就把他一家都给拖下来了。所以为了“更小,更小”这个理想的目标,我最后只把地图文件打包了,其他那些非相关性太强的资源就算压缩率高我也排除了。下载量再次下降。

折腾了半天,现在进入相同的场景,总下载量由最初的13M变为了3.2M!300k进入角色选择。这种规格已经和目前比较小的2D页游相当,可以让用户很流畅的进入,300k进入选择角色就是瞬间的事情吧?。当然以后资源还会增加,配置也会,但最后不会增加太多下载量。如果还不能接受,只有请美术吃雪糕,告诉他们降贴图质量,减物件数量,减贴图数量。。。这也是我最不愿意做的,好的程序不该处处为难美术。

后面还有要做的就是细化打包的规则,把资源更紧凑的组织起来,充分利用打包的优点,这样才能达到极致。另外目前只处理了初次进入,进入后在后台还需要静悄悄的加载附近的场景区域,让用户体验更好,不过要先整小再整流畅,整小已经基本达成,流畅度又是一项长期任务。(心底之声:尼玛,全都用光纤玩游戏不就好了,中国网速就不能再快点?啊?玩笑)

AS如何接收64位整形

as里的整形都是32位,如果要从网络消息里读取64位整形变量(比如我们的角色id),该用什么类型接收存储呢?

最先想到的是用自定义类,比如叫BigInt,包含两个uint分别接收低32位和高32位,恩,C++思维这样没问题。但首先每次接收我都要new一个BigInt然后接收,分配对象数量过多;而且,我要用这个64位整形的值作为dictionary的key怎么办呢,每个对象就算其数值相同,但对象本身不同,根本不能作为key来查询;目前我还不知道如何定义对象的比较操作符(没有运算符重载的无奈),所以先放弃此方案。

只能另辟蹊径了,一般很自然想到的是用字符串接收,如果是8个字符的字符串会比较占用内存,因为一个字符其实是16位整形,虽然这样比较容易查看变量的值,那么用4个字符的呢,一个字符分别存储64位整形的16位,听起来不错,但这样你在解析ByteArray时就要花点功夫了。而且就算4个字符串的String对象他实际内存也不是想当然的8个字节,因为一个”"字符串就占用了24个字节。此方案pass。

最后想来想去,8个字节的对象,8个字节的对象…等等,Number不就是8个字节吗(准确说根据数值范围是4-8个字节),用byteArray.readDouble()接收固定8个字节即可,而且是原始类型,内存占用最小,而且可以直接用作dictionary的key,唯一的问题就是没有可读性,要把一个double的内存转成等价64位整形显示需要周折一下,大多数情况下,需要查看64位值只有在用户界面显示和调试的时候需要,因此专门为Number转成64位写一个转换函数即可。

package deltax
{
  import flash.utils.ByteArray;
  import flash.utils.Endian;  

  /**
   * 用Number来接收64位整形 
   * @author xophiix
   * 
   */  
  public final class NumberTo64bit
  {
    private static var m_convertBuffer:ByteArray = new ByteArray;

    public static function from64bit( _64bitBuffer:ByteArray ):Number
    {
      return _64bitBuffer.readDouble();
    }
    /**
     * 转换接收64位整形的number变量为整形字符串,16进制或10进制 
     * @param number 储存64位整形的Number类型变量
     * @param hex 是否转为16进制
     * @param prefix 如果16进制是否要0x前缀
     */    
    public static function toString( number:Number, hex:Boolean = true, prefix:Boolean = true ):String
    {
      m_convertBuffer.endian = Endian.LITTLE_ENDIAN;
      m_convertBuffer.position = 0;
      m_convertBuffer.writeDouble( number );
      m_convertBuffer.position = 0;

      var lowerPart:uint = m_convertBuffer.readUnsignedInt();
      var highPart:uint = m_convertBuffer.readUnsignedInt();

      if ( hex )
      {
        var combined:String = highPart.toString(16) + lowerPart.toString(16);      
        return prefix ? "0x" + combined : combined;
      }
      else
      {
        //highPart = uint.MAX_VALUE;
        //lowerPart = uint.MAX_VALUE;
        var combinedNumber:Number = highPart * ( Number(uint.MAX_VALUE) + 1 ) + lowerPart;            
        return combinedNumber.toPrecision(19);
      }
    }
  }
}

 

虽然很多人说脚本语言本身效率就比C/C++慢很多倍,不必太计较,要奔放点。我不敢苟同,多少人抱怨他们的程序性能和内存过大,然后向flash抱怨,但实际是自身对资源管理、对象分配太过不在乎导致,完全可以优化到能接受的程度。性能问题我觉得对任何语言都是要认真对待的。

 

 


读《高效程序员的45个习惯》

最近读完了这本敏捷开发经典,感慨良多,可以说很感动,内心思绪在翻滚。

过去的项目经验教训历历在目,如果早点运用一些习惯的话结果肯定会更好。

 

如果能一开始进行代码复查,bug会如此难以控制?

如果能加入单元测试和一台持续构建服务器?

如果遇到问题首先想怎么解决而不是找是谁的责任?

如果作为指导者授人以渔而不是灌输挖苦和命令?

如果不要老是没完成代码甚至没编译就提交?

如果团队成员都能共享代码,有良好气氛的分享知识?

如果每个人能够互相了解别人系统 做到别人生病离职也不会对其代码束手无策?

如果出的bug能好好记录解决方法便于查找?

如果团队能积极学习和接受新的好的事物?

如果能更好的对团队进行知识投资?

如果做大的设计前能找人商量?

如果经常报告进度经常审查代码而不是埋头开发?

如果能用清晰简洁的代码沟通而不是对晦涩的代码做无谓的注释?

如果面对警告都当做错误严肃对待?

如果我早点看到这本书?

如果我不是接受现实闭门造车 而是努力先从自身做起让团队接受新的好的习惯?

如果….

…….

虽然没几个团队能做到所有的如果,但在团队成员平均水平差不多时,能做到80%的团队项目质量一定不会比没做到的差。

过去的虽不可再挽回,但必须要进行反思和总结,团队必须有进步。

虽然我们也是所谓scrum的开发,有站立会议,有周会,也有短迭代增量开发,但还不够,很多东西没有记录,没有坚持。

 

如果是下一个项目,我会引入些新的习惯:

单元测试

持续集成:专门测试和编译工程的机器,及时发现代码问题

代码复查:捡拾游戏式,平日就轮流互相复查其他人代码,必要时才做要花大量时间的大范围复查。

建立一个内部wiki(或blog聚合),每个人都可以发些技术心得、术语表、项目bug记录、解决问题方法清单。只言片语也行,类似微博。

 

软件开发是演化的,开发团队也是进化的,没有进化的团队一无所成。

 

Visual Studio 2005 reproducible linker error (LKN1257 caused by C1083)

上周项目工程编译发布Dll版本时出现了一个从未遇见的连接错误

fatal error C1083: Cannot open compiler intermediate file: ‘../../bin/releasedll/*****.lib’: Not enough space
LINK : fatal error LNK1257: code generation failed

后来发现是因为那个依赖的静态库工程生成的lib文件过大导致,有260多M。虽然最终链接完成后的exe只有6M。这一度困扰了我们。

找了很久才发现这是VS2005的一个bug,并在VS2010修复(看这里),但怎么可能现在马上迁移到VS2010.

最终解决办法很无奈,只有把那个静态库工程的所有文件添加到依赖工程里,由于依赖工程里本身有部分和库文件重合,所以实际转移文件不算太多,但始终觉得这是没办法的办法。

在此记录一下,下个项目都移到VS2010吧,SP1也出了,新语言特性也那么诱人,据说STL性能提升很明显?