来北京工作两年了,终于从一个3D游戏编程菜鸟,过渡到了一个有经验的初级开发者。这两年工作甚忙,自己也没有意识到要写篇日志来总结下自己这两年的工作和生活,以及对未来职业发展做个清晰的规划。最近,由于工作的不顺心,想跳槽,去参加了一次笔试和面试,结果以失败告终。我觉得,其实不是我知识水平不够,而是学到的东西太混乱,自己没有一个清晰的思路把问题和解决方案说明白,给面试官的感觉是能力不行。回来后,我也没有急着去投下家,而是仔细反思了一段时间,总结了下这些年,自己在编程道路上的得与失,找准自己的定位,做充分的准备。
归纳起来主要有一下几点失误:
1.急于求成造成事倍功半
我是一个动手能力很强的人,做事情的时候很迫切想看到结果,这就导致节奏过快,忽视了学习过程中应有的步骤,遗漏掉了许多基础知识点。我一直都有一个错误的观念,就是先看当下能用到的知识,其他的东西到后面用到了再学。所以在外行看来,我会相当多的东西,其实,我的基础一点都不扎实。由于有太多的东西放在了后期学习,所以现在我每天都在还债,学习那些曾经遗漏的知识。
大学的时候,因为c语言学的还不错,花了一周时间就看完一本c++语言大学教程,自认为已经掌握c++了。后来花了一两个月的时间学习MFC,会写计算器之类的图形界面程序了,自认为自己会MFC了。结果呢?距离学习开始到现在,4年过去了,每年都会再学到很多c++的知识,我现在还在看《STL源码剖析》,《C++ Templates》,如果不学好这些东西,根本没法看懂大牛写的代码。那些大型游戏引擎源码,从语法层面来说,你确保百分百能读懂吗?语法都弄不明白,还怎么去读懂他背后神秘的算法呢?
假设一下,如果你会c++基本语法,但是你不懂STL,不懂模板,不懂动态多态,你来做一款射击类小游戏,如何下手呢?我想,你肯定会为子弹集合设计一个链表,为飞机集合设计一个链表,如果飞机类型有多种,那每种类型的飞机都会设计一个链表,那么游戏循环应该是这样写的:
while(true)
{
selfBulletList.update(elapse);
enemyBulletList.update(elapse);
enemyList.update(elapse);
player.update(elapse);
}
如果你会上面所提到的知识,这里只用stl::list就可以搞定了,你什么链表实现代码也不用写。而且,游戏里所有类型物体,都可以使用动态多态原理放到同一个list里。游戏循环就会变成这样:
std::list<GameObject *> objectList;
while(true)
{
for(GameObject * p : objectList) //c++11语法
p->update(elapse);
}
是不是很简洁,是不是节剩了很多时间,是不是少调了很多bug?重点不是这一个地方简洁了,如果你是一个多产的程序员,你一个月写了5款小游戏,上面那些冗余的代码你要写5遍?
再举个例子,驾驭500行以内的代码量,对菜鸟来说基本上都是小意思。但代码量超过5k行的时候,有些人就抗不住了,程序开始失控了,bug层出不穷,各种指针崩溃问题难以查明原因。开始网上到处求助,病急乱投医,结果连问题出在哪自己都描述不清楚,别人又如何帮助你解决呢?
所以,一味的追求结果,而忽视了程序员应具备的基础知识以及完善的知识体系,在求职、工作以及后续的学习当中,是要付出沉重代价的。急于求成的心态,不但会浪费你大量时间跟精力,还会打消你的自信心,让你在面试官 面前,把技术缺陷暴露的一览无余,成为一个会说话的哑巴。
我不会告诉你,我就是这样一路走过来的。
2.学习速度过快,超过吸收领会速度
这两年,我大概看过10本左右的编程技术书籍。你要问我对那本映像最深刻,我的回答是没有。因为看的太快了,也没有做什么读书笔记,事后也没有复习过,所以看完一段时间后,就忘却了书里的内容——这不就相当于没看。
而且,经常会碰到一些很难理解的知识,就跳过了,事后也没有回过头来再看——这不就是,会的还是会的,不会还是不会的。
3.没有做到举一反三,缺乏发现问题的能力
没有养成一个思考的好习惯,学完一门知识就学完了,也没有多思考他的用处,也没有思考知识本身的缺陷。解决了一个问题,当面对另一个变体的问题后,就想不到一个合适的解决方案。
当策划的一个设计方案存在问题的时候,自己总是不能在动手实现之前发现设计缺陷,等到写了部分代码的时候,才恍然发现,这个设计按照现有的接口,无法实现!或者,实现了这个设计方案,却无意间破坏了其他功能。
4.闭门造车,缺乏交流,把别人的建议不当回事
一直以来,我自己写代码,做demo,从来没有跟别人交流过,也没采纳过别人的意见。感觉就是一个人在盲目的摸索,把自己禁锢在自己的思想里,自认为自己水平不错,其实,自己只是一个码农。
5.盲目读/写代码浪费时间
很多时候,在学习一项技术的时候,看到一部分后,实在难以继续看懂,我就会根据自己的理解,用自己的方式来实现。虽然这是好事,但是,毕竟自己只是这样猜测,做出的东西功能很简单,最终要实现一个完整的功能,还得需要理解哪些技术。花大量时间瞎猜、写代码、调bug,把这些时间拿来仔细学习这些技术相关的知识,已经绰绰有余了。
举个例子,我之前学习碰撞检测的时候,我先参考过BigWorld1.8源码,它是用bsp实现的,勉强看懂了部分代码,然后凭自己的理解,实现了一个简单的bsp碰撞检测,结果射线拾取效率还过得去,体碰撞处理的效率实在很低。准备继续看Bigworld如何优化体碰撞时,怎么也看不懂,因为我是抛开其他功能只看碰撞部分的,而碰撞检测又必须依赖一些底层模块,比如地形、物体组织结构、数学库等。我只想学习碰撞,然后给我的demo加入碰撞检测,暂时不想花时间学习其他的东西,所以就放弃了研究BigWorld。
于是,我在网上搜索一些轻量级的碰撞检测库,找到了一个叫Opcode的开源库。这个库大致是使用AABB树来解决问题的。看到一部后源码后,实在难以继续下去了,然后我又就根据自己对Opcode的理解,对先前的bsp代码进行了改版,改成了我所谓的“八叉树”(其实就是AABB树的一种),利用八分空间的思想,将三角网格分成八部分,每次碰撞检测的时候,都从整体到局部去检测,这次效率确实是提高了,我的目的终于达成了。
功能是实现了,但是仔细想想,觉得很不值。研究BigWorld、Opcode、自己实现bsp和AABB树,中间还有在网上搜集资料的时间,学习一些零散的计算几何知识,这些时间加起来,大概有3个多月。我觉得静下心来,我看完《3D数学基础》+《实时碰撞检测算法技术》+Opcode源码的时间也不会超过3个月。而且,如果我安照后者方案进行,我还会形成一套完整的知识体系,而且还能将Opcode以最恰当的方式集成到我的demo中来。
结果,我现在所了解的碰撞检测知识,也只是我写过代码的那部分,其余的知识一无所知,这不就是一种悲哀吗。
7.小结
当学习一项技术的时候,切忌不要从源码开始,上来就读“核心”源码,而是从相关的书籍、文档开始。如果一段代码看不懂,大致有两个原因,一是不了解代码背后所使用到的算法;二是不熟悉此代码所依赖的其他模块。找准问题的本质,切莫硬着皮头看下去,否则只能身心俱疲。
不要为了看书而看书,看完一部分要做个总结,用自己的话把核心技术描述出来,也就是做个读书笔记。隔一段时间,再复习一下自己的笔记,避免遗忘。好书应该坚持看完,看到一半,那不叫看过,那叫翻过。
基础知识一定要扎实,热门技术也一定要熟知。以免在笔试、面试的时候吃哑巴亏。
对于代码狂来说,少写代码多读书,读别人代码。团队合作,大部分时间都是理解别人代码,理解别人提供的接口。
总觉得每次方案小修改,都有大量代码要改改,那是因为你的程序架构不好。
多练习跟别人交流,锻炼表达能力。能把问题描述清楚的程序员没有几个,能用问题的严重性说服设计者改变方案的程序员更少。
任何编程工作都是技术活,没有所谓的简单。写脚本跟写引擎,同样都是技术活,能把脚本写好的人也没有几个,你不信的话,可以去应聘主程职位,我不信所有写脚本的都可以胜任主程。