`

Cocos2d-x 3.x游戏开发之旅

 
阅读更多

Cocos2d-x 3.x游戏开发之旅

钟迪龙 著  

ISBN 978-7-121-24276-2

201410月出版

定价:79.00

516

16

内容提要

Cocos2d-x 3.x游戏开发之旅》是《Cocos2d-x游戏开发之旅》的升级版,修改了Cocos2d-x 2.0版进阶到3.0版后的一些内容,新增了对CocoStudioUI编辑器、Cocos2d-x 3.x新特性以及网络方面的知识点。主要介绍常用的API使用方式;如何通过官方Demo获取更多关于Cocos2d-xAPI使用方法;CocoStudioCocos Code IDE基础使用方法;通过23个游戏实例介绍Cocos2d-x在实际开发中的应用;手机网络游戏开发入门;在实际的手游开发过程中遇到的问题及解决方法。

Cocos2d-x 3.x游戏开发之旅》适合Cocos2d-x初学者或者已经学习了Cocos2d-x 2.0版本想继续使用Cocos2d-x 3.x版本开发游戏的读者,同时也适合刚进入游戏开发行业,希望了解手游开发者在实际开发中遇到的问题及解决方案的读者。

目录

1  开学典礼 / 1

1.1  出场人物介绍 / 1

1.2  入学要求——本书适合人群 / 2

1.3  这内容有点糟糕——本书风格 / 2

1.4  你需要这些技能——学Cocos2d-x需要什么知识 / 3

1.5  我们为什么要学Cocos2d-x 3.0 / 4

1.6  先给我们的代码找个温馨的家——环境搭建 / 4

1.6.1  黑色风暴之VS2013 / 4

1.6.2  我的Cocos2d-x版本 / 5

1.6.3  我们还需要做一些准备工作 / 6

1.6.4  半自动配置环境变量 / 6

1.6.5  看看前辈们的毕业设计——运行官方Demo / 8

1.7  广告时间——提供一些学习资源 / 10

2  不离不弃的HelloWorld / 12

2.1  简单到无法想象——直接运行HelloWorld项目 / 12

2.2  新建Cocos2d-x项目遇到的麻烦——成功新建 一个HelloWorld / 14

2.2.1  创建Cocos2d-x 3.0项目 / 14

2.3  HelloWorld,没有想象中的简单——简单解析HelloWorld / 16

2.3.1  HelloWorld是如何显示到窗口的 / 16

2.3.2  场景其实不是真正的场景 / 17

2.4  实例来了 / 20

2.4.1  题外话——一个工程多个项目,避免重复编译 / 20

2.4.2  创建属于自己的HelloWorld / 23

3  我们来学习怎么爬行 / 28

3.1  环游世界——场景 / 28

3.2  一切的主宰——导演 / 29

3.2.1  加载第一个场景 / 29

3.2.2  简单的切换场景 / 29

3.2.3  包含特效的切换场景 / 31

3.2.4  赠送的——推进和弹出场景 / 33

3.3  你走你的,我走我的——层 / 35

3.4  比对象还要可爱的对象——精灵来了 / 36

3.5  我得下命令——菜单 / 37

3.6  当个背包客——节点 / 39

3.7  Cocos2d-x 3.0新成员——ValueVectorMap / 40

3.7.1  有容乃大——Value / 41

3.7.2  给我排好队——Vector / 42

3.7.3  对号入座——Map / 44

3.7.4  Value的超能力——整数、浮点数、字符串之间的转换 / 46

4  我们来学习怎么走路 / 47

4.1  吉他独奏《天空之城》——播放声音 / 47

4.2  控件篇1——我最爱的九妹和按钮事件 / 48

4.2.1  你好,九妹——可拉伸图片 / 48

4.2.2  解决使用extensions库时编译报错的问题 / 49

4.2.3  九妹和按钮搭配 / 51

4.3  可视化的世界——CocoStudio UI编辑器 / 53

本节实例源代码为:Chapter4_2_Scale9Sprite / 53

4.3.1  还在用代码写UI?你傻了吧 / 53

4.3.2  只需要拖动——UI编辑器初探 / 54

4.3.3  更丰富的UI——导入图片资源 / 56

4.3.4  更丰富的UI——导出最终文件 / 58

4.3.5  展示成果——在游戏中加载UI / 59

4.3.6  我要互动——按钮监听事件 / 62

4.4  番外篇——CocoStudio UI编辑器进阶功能 / 65

4.4.1  要死、要死、要死了之血量条 / 65

4.4.2  我要藏起来之滑动层 / 68

5  我们来学习怎么打架 / 72

5.1  动作讲解1——简简单单的运动 / 72

5.1.1  你想去哪,你想走多远——MoveToMoveBy / 72

5.1.2  我要减肥——ScaleToScaleBy / 74

5.1.3  卡牌翻转特效——用ScaleTo就可以实现 / 77

5.1.4  闪亮登场——Blink / 77

5.2  动作讲解2——更复杂的运动 / 79

5.2.1  我要你按照我的路线走——BezierToBezierBy / 79

5.2.2  我没让你停,重复动作——RepeatForever / 81

5.2.3  动作一起做,一边走一边转一边跳——SequenceSpawn / 82

5.3  动作讲解3——动作监听 / 84

5.3.1  小若,到家了告诉我一声——动作结束监听 / 84

5.3.2  更方便的函数回调——lambda / 85

5.3.3  动作家族族谱 / 88

5.4  屏幕触摸事件 / 89

5.4.1  屏幕事件初探 / 89

5.4.2  单点触摸——截断事件,一个对象的垄断 / 91

5.4.3  多点触摸——太弱了,一起上吧 / 94

6  是时候跑起来了——《LittleRunner/ 99

6.1  这是一个什么游戏 / 99

6.2  找个主角回来——创建主角精灵 / 100

6.2.1  创建筛选器 / 100

6.2.2  创建实体基类 / 101

6.2.3  创建主角类 / 102

6.2.4  创建游戏场景 / 103

6.2.5  修改游戏窗口大小 / 106

6.3  其实她不想跑——创建无限滚动地图 / 107

6.3.1  每一帧都可以操作——scheduleUpdate初探 / 107

6.3.2  翻滚吧,地图——地图无限滚动 / 109

6.4  开心的时候——让主角跳起来 / 111

6.4.1  Button的应用——创建跳跃按钮 / 111

6.4.2  JumpBy的应用——给主角添加跳跃动作 / 112

6.5  金钱诱惑——加入怪物 / 114

6.5.1  怪物诞生 / 114

6.5.2  怪物巢穴——创建怪物管理器 / 116

6.5.3  贪婪的主角——怪物碰撞检测 / 119

6.5.4  怪物碰不到主角——使用组合的小麻烦 / 122

6.5.5  疼了就退后——增加主角受伤时的动作 / 123

6.6  监控一切——创建分数标签、血量条等属性对象 / 125

7  精灵来了第二季 / 129

7.1  一大波精灵来袭——大量精灵单独绘制的缺陷 / 129

7.1.1  2.0的回忆——大量绘制精灵的尴尬 / 129

7.1.2  3.0新功能——Auto-batching / 130

7.1.3  没有完美的事物——Auto-batching“驾驭手册” / 131

7.1.4  谁先上的问题——setGlobalZOrdersetLocalZOrdersetOrderOfArrival / 134

7.1.5  附赠甜品——Auto-batching源码级深入分析 / 138

7.2  把相同的精灵装上车——使用SpriteBatchNode优化绘制 / 144

7.2.1  一次渲染——SpriteBatchNode的特别之处 / 144

7.2.2  什么是纹理——Texture简单解说 / 146

7.3  遇到不同的精灵怎么办——TexturePacker工具使用 / 147

7.3.1  同一张图片,同一个纹理 / 148

7.3.2  将多张图片打包到一起 / 149

7.3.3  加载打包后的图片 / 151

7.4  是动画,不是动作——Animation / 152

7.4.1  用打包前的图片创建动画 / 152

7.4.2  用打包后的图片创建动画 / 154

7.4.3  额外赠送——动画创建辅助类 / 156

8  游戏实例之《跑跑跑》 / 160

8.1  更丰富的世界——使用Tiled地图 / 160

8.1.1  世界诞生——创建跑步场景 / 160

8.1.2  Tiled地图来了——使用Tiled Map Editor创建地图文件 / 162

8.1.3  你报错了吗——Tiled Editor常见路径错误 / 163

8.2  嘿,培养新人——创建新的主角 / 164

8.2.1  主角诞生——创建实体类和主角类 / 164

8.2.2  固定主角出生点——对象层的使用 / 167

8.2.3  让主角跑——添加主角动画 / 169

8.3  更智能的主角——添加角色控制器 / 170

8.3.1  创建控制器基类 / 170

8.3.2  带着它就能跑——主角移动控制器 / 172

8.3.3  焦点是主角——让地图随着主角滚动 / 175

8.4  更多的内容 / 178

8.4.1  让主角上下移动——三方移动控制器 / 178

8.4.2  添加障碍物——Tiled障碍层的使用 / 181

8.4.3  添加能吃的物品以及胜利条件 / 187

9  用多线程做定时器?你疯了——schedule很强大 / 191

9.1  我爱单线程——schedule介绍 / 191

9.2  每一帧都有我——scheduleUpdateupdate / 191

9.3  定时器可以这么做——schedule和回调函数 / 193

9.3.1  不调用update函数,调用自己的函数 / 193

9.3.2  真正的定时器 / 194

9.4  让一切都停下来——unSchedule / 195

9.5  触发器可以这么做——scheduleOnce和回调函数 / 197

9.6  准确的计时功能——制作定时器 / 198

9.6.1  schedule并没有那么准时——越走越慢的时间 / 198

9.6.2  更准确地计时——不会变慢的时间 / 200

9.6.3  制作定时——方便的定时回调功能封装 / 203

9.7  额外赠送,我总是很关注我暗恋的女孩——观察者模式 / 205

9.7.1  你喜欢谁?我帮你留意——观察者模式扫盲 / 205

9.7.2  Cocos2d-x提供的观察者模式工具类——NotificationCenter / 206

9.7.3  addObserverpostNotification函数传递数据的区别 / 209

9.7.4  额外赠送——自己实现简单的观察者 / 211

10  野外生存训练 / 216

10.1  师父走了,你怎么办——学会查看官方Demo / 216

10.2  迟早都要学会——学习查看Demo源代码 / 217

10.3  别忘了开源——学习查看Cocos2d-x源代码 / 218

10.3.1  为什么addObserverpostNotification不能同时传递数据 / 218

10.4  物竞天择,没用的就要被淘汰——retainrelease / 221

10.4.1  为什么会有retain / 221

10.4.2  真正的凶手autoRelease / 222

10.4.3  看代码实际点 / 222

10.4.4  原理来了 / 224

10.4.5  实际情况 / 225

10.4.6  到底什么时候要retain / 225

10.5  什么?你忘了保存记忆?我们一起十年了啊——保存数据 / 226

10.6  我能摆脱编译的噩梦——读取Csv配置文件 / 227

10.6.1  什么是Csv文件——Csv格式文件扫盲 / 228

10.6.2  如何创建一个Csv文件 / 228

10.6.3  预热——编写字符串工具类 / 229

10.6.4  一切都是对象——Csv文件对象类 / 231

10.6.5  进入主题——编写Csv文件读取工具类 / 233

10.7  再给编译噩梦粉碎一击——更方便扩展的JSON / 238

10.7.1  JSON又是什么——基础扫盲 / 238

10.7.2  拿好你的武器——导入JsonCpp解析库 / 239

10.7.3  就是这么简单——读取JSON文件 / 241

10.7.4  进阶挑战——读取嵌套结构的JSON文件 / 242

10.7.5  进阶挑战——读取数组结构的JSON文件 / 243

10.7.6  进阶挑战——输出JSON文件 / 244

11  睡觉的时候不要吃东西——有限状态机 / 247

11.1  5分钟告诉你什么是有限状态机 / 247

11.1.1  简单认识 / 247

11.1.2  一个很“好玩”的游戏 / 247

11.1.3  实现简单有限状态机的类 / 248

11.2  状态模式之我很胖但我很强 / 251

11.2.1  什么是状态模式 / 251

11.2.2  用状态模式实现有限状态机 / 252

11.3  真正的状态机来了 / 258

11.3.1  创建状态机类 / 258

11.3.2  被释放的木头类 / 260

11.4  事件驱动,你想象不到的强大 / 263

11.4.1  彻底抛弃update函数——新的状态机类 / 263

11.4.2  更智能的状态类 / 266

11.4.3  见证新的运作情况 / 267

12  我比Csv更厉害——强大的Lua / 269

12.1  LuaC++牵手 / 269

12.1.1  Lua的堆栈和全局表 / 269

12.1.2  LuaC++的第一次通信 / 269

12.2  Demo讲解之LuaC++牵手 / 270

12.2.1  引入Lua / 271

12.2.2  开始使用 / 272

12.2.3  执行Lua脚本 / 273

12.2.4  重置栈顶索引,将全局变量放到堆栈中 / 274

12.2.5  最后一步,C++取得字符串 / 274

12.3  简单解析Lua的堆栈 / 276

12.4  来点高难度的,获取Lua表结构的数据 / 276

12.4.1  什么是table / 277

12.4.2  获取table变量 / 277

12.4.3  lua_gettable函数 / 277

12.5  C++调用Lua函数 / 280

12.6  Lua调用C++的函数 / 281

13  自力更生——自己写一个UI模块 / 285

13.1  刚开始必须要轻松一下——UI模块思路简介 / 285

13.1.1  它能为我们做些什么——UI模块包含的功能 / 285

13.1.2  一览众山小——UI模块设计思路图 / 286

13.2  XML文件和属性模块 / 287

13.2.1  一切的源头——UIXML配置文件 / 287

13.2.2  知识补充1——TinyXML介绍 / 289

13.2.3  知识补充2——TinyXMLHelloWorld / 289

13.2.4  知识补充3——用TinyXML读取UI模块的XML配置文件 / 292

13.2.5  封装,我们要记住封装——XML标签节点对象 / 296

13.2.6  我负责保存,你负责修饰——XML标签节点属性设置器 / 302

13.3  尝点甜头——创建控件 / 305

13.4  主角来了——组件 / 308

13.4.1  控件基类——MMBase / 308

13.4.2  普通窗口控件——MMNormalWin / 309

13.4.3  标签控件——MMLabel / 311

13.4.4  甜点来了——测试新的控件类 / 312

13.5  控件管理模块 / 314

13.5.1  我是老大——MMWinManager控件管理器 / 314

13.5.2  我是先锋——MMWinSystem控件系统 / 318

13.5.3  我是前台——MMWinDesktop控件顶层桌面 / 321

13.6  控件工厂 / 322

13.6.1  抽象工厂之MMWinBaseFactory / 322

13.6.2  控件工厂之MMWinFactory / 323

13.6.3  装饰工厂之MMWinProperityFactory / 324

13.6.4  最后的事情——运行项目 / 326

14  倾情奉献——因为偷懒而诞生的《卡牌塔防》游戏(上篇)/ 329

14.1  开篇——最后的游戏实例 / 329

14.1.1  为什么是塔防游戏 / 329

14.1.2 《卡牌塔防》功能简介——我们能学到哪些知识 / 329

14.2  启程——关卡编辑器 / 330

14.2.1  一个英雄一个坑——炮台坐标编辑器 / 331

14.2.2  按照我给的路径走——怪物坐标编辑器 / 348

14.2.3  添加更多方便的操作 / 356

14.3  磨刀不误砍柴工——预备知识 / 361

14.3.1  导演的助手——场景管理器 / 361

14.3.2  数据读取模块 / 364

14.3.3  全局参数 / 365

14.3.4  请说英文好吗?不,说中文——I18N工具类 / 366

15  倾情奉献——因为偷懒而诞生的《卡牌塔防》游戏(下篇)/ 372

15.1  英雄诞生 / 372

15.1.1  创建关卡场景 / 372

15.1.2  地图层 / 373

15.1.3  实体基类 / 375

15.1.4  英雄管理器1——炮台对象 / 378

15.1.5  英雄管理器2——英雄对象 / 382

15.1.6  英雄管理器3——炮台和英雄的关系 / 386

15.1.7  英雄管理器4——加入创建英雄的功能 / 387

15.2  怪物来袭 / 389

15.2.1  怪物管理器 / 389

15.2.2  移动控制器1——控制器基类 / 394

15.2.3  移动控制器2——移动控制器基类 / 394

15.2.4  移动控制器3——按指定坐标列表移动 / 396

15.2.5  怪物来了 / 400

15.3  英雄进攻 / 404

15.3.1  英雄的一次攻击流程 / 404

15.3.2  Hero里获取怪物列表 / 405

15.3.3  查找并锁定攻击目标 / 406

15.3.4  英雄的攻击 / 407

15.3.5  子弹管理器1——子弹类 / 409

15.3.6  子弹管理器2——子弹管理器 / 413

15.3.7  子弹管理器3——英雄开始发射子弹 / 416

15.4  华丽的装饰 / 417

15.4.1  怪物血量条 / 417

15.4.2  炮台操作按钮——英雄华丽升级 / 419

15.4.3  怪物起点和终点魔法台 / 425

16  倾情奉献——因为偷懒而诞生的《卡牌塔防》游戏(终结篇)/ 428

16.1  关卡信息UI / 428

16.1.1  利用UI编辑器制作关卡信息面板 / 428

16.1.2  关卡信息数据刷新——NotificationCenter的应用 / 431

16.1.3  怪物数量刷新 / 435

16.1.4  怪物安息——怪物死亡后塔魂数量刷新 / 435

16.1.5  堡垒安息——怪物到达堡垒后扣除魔力值 / 436

16.1.6  打怪升级——英雄升级扣除塔魂 / 438

16.2  谢幕 / 439

16.2.1  关卡选择——根据关卡数加载游戏 / 439

16.2.2  胜利条件判断 / 443

16.2.3  失败条件判断 / 446

16.3  移植游戏到Android平台 / 447

16.3.1  傻瓜式的Android环境搭建1——准备工作 / 447

16.3.2  傻瓜式的Android环境搭建2——解压和安装 / 448

16.3.3  傻瓜式的Android环境搭建3——测试一下吧 / 451

16.3.4  准备工作——导入Android项目 / 453

16.3.5  一劳永逸——修改Android.mk文件 / 455

16.3.6  开始编译——build_native.py文件 / 458

17  我不要一个人玩游戏——网络游戏入门 / 460

17.1  只能给你一块敲门砖——本章带给大家的内容 / 460

17.2  左手画方右手画圆——多线程 / 461

17.3  从最简单的开始——从建立连接到接收数据 / 464

17.3.1  偷偷和别人连接——使用ODSocket与服务器建立连接 / 465

17.3.2  不务正业——编写一个测试服务端 / 467

17.3.3  言归正传——编写我们的Cocos2d-x客户端 / 469

17.4  你得真的像个网游——服务端与客户端交互逻辑 / 471

17.4.1  逃避多线程——更安全的数据接收处理 / 471

17.4.2  进入游戏——服务端通知客户端进入游戏场景 / 476

17.4.3  更多的人——请求在线人数,添加所有玩家到场景中 / 480

17.4.4  新人要报到——有新玩家加入,通知已在线玩家 / 484

18  额外的一些东西 / 486

18.1  常见错误及解决方案 / 486

18.1.1  调用可变参数函数时,总是莫名报错 / 486

18.1.2  无法解析的外部符号(或命令) / 486

18.1.3  Android移植编译报错——undefined reference to / 487

18.1.4  Label变成了一个黑块——多线程引起的Bug / 487

18.1.5  中断窗口 / 487

18.1.6  重复切换场景后无端报错 / 488

18.2  游戏发布流程(概述) / 489

18.2.1  给游戏加入广告SDK / 489

18.2.2  加入统计SDK / 490

18.2.3  上传游戏到各个市场 / 491

18.3  未来的趋势 / 492

18.3.1  云后端服务 / 492

18.3.2  社交SDK / 492

18.3.3  Cocos Code IDE / 493

18.3.4  CocoStudio / 493

18.4  安全意识 / 493

18.4.1  加密的数据类型,拒绝游戏修改器 / 494

18.4.2  防止打包党的一些小技巧 / 495

18.5  游戏开发思维 / 497

18.5.1  不管游戏多快,一帧就是一帧,我们能做很多事情 / 497

18.5.2  随机应变,利用不经意的Bug创造更多玩法 / 497

18.6  最后的最后 / 498

18.6.1  最快速找到木头的方式 / 498

18.6.2  给读者的惊喜——丁小若寄语 / 499

18.6.3  请提供你的意见和你发现的Bug / 499

前言

2.03.0时代

我第一次接触Cocos2d-x的时候,它刚好发布了2.0版本,开始从1.x时代跨越到2.x时代。当时我还很庆幸,因为2.x相比1.x做出了很大的改动,而我,无须体验这种改动所带来的“怨念”。

一年前,我费尽心思所写的《Cocos2d-x游戏开发之旅》出版了,当时还是Cocos2d-x 2.0时代。有读者喜欢它的风格,也有读者讨厌它的唠叨,不管如何,它似乎帮助了不少读者入了Cocos2d-x的门。

然而,一年以来,Cocos2d-x 3.0版本渐渐浮出水面,我们要迎来3.0时代了,我有幸再一次见证了这种跨越时代的时刻。3.0终将替代2.0,为此,才有了这本《Cocos2d-x 3.x游戏开发之旅》。

这不是全新的内容

本书主要目的是把2.0的内容升级为3.0版本,并不是重新写一本书,大部分内容结构是一样的,从目录中就可以看出来。如果说,这是一本全新的、内容完全不一样的书,那我可能就变成了一个专门写书的骗子了。在编写2.0版本的书时,我已经把我几年所积累的知识都花光了。现在仅仅是过了一年时间,我这一年时间的积累,还不足以写成一本全新的书。

所以,我特意在前言部分把这个情况说清楚,避免和读者之间造成不必要的误会。

当然,即使仅仅是内容的升级,我也花费了很多心血,一句句代码的检查、修改、优化,一行行文字的调整、修改、新增,并且加入了一些3.0特有的新内容,新增了网络游戏、广告接入、游戏发布等方面的内容。

我不想再“生”了

一年前,编写完2.0版本的书时,我累趴了,写书比想象中累多了。那时我就告诉自己,“我这辈子再也不写书了!”,就像一位妈妈刚生完孩子一样,完全没有继续生下一个的勇气了。

然而,和所有的妈妈一样,时间长了,就忘记了痛苦,只记住了孩子带来的欢乐。我也不知道是什么原因让我决定写这本书,也许是太多读者和我抱怨说之前的书版本太旧了。于是,这本Cocos2d-x 3.x的书就这样诞生了。而我现在,依旧很想说一句,我这辈子再也不写书了!呵呵,也许这是倾尽全力完成一件事情之后,一种兴奋和满意的咆哮。

那么,希望本书能继续让各位初学者快乐地开启游戏开发之旅。

本书主要内容

本书共18章,如果读者没有Cocos2d-x的使用经验,建议从第1章开始阅读。对于有Cocos2d-x基础的读者,我为大家列出了各个章节的主要内容,供读者参考。

1章:简单介绍本书写作理念、Cocos2d-x 3.0环境搭建和推荐的一些学习资源。

23章:介绍Cocos2d-x 3.0的最基本知识、基本结构。

46章:介绍CocoStudio UI编辑器的使用、动作以及屏幕触摸事件的使用,通过一个简单的游戏实例将前面章节的内容串联起来。

710章:对Cocos2d-x 3.0更高阶的内容进行介绍,包括渲染效率的提高、动画、TexturePacker图片打包、Tiled地图游戏实例、定时器、消息派发、函数回调、内存管理、数据保存、Csv文件读取、JSON文件读/写。

1112章:介绍LuaC++通信的知识、有限状态机的基础知识和应用。

13章:分享我自己开发的基于Cocos2d-x 3.0的自定义UI模块,详细介绍自定义UI模块的设计思路和实现过程。

1416章:详细讲解本书最后一个游戏实例《卡牌塔防》,该游戏使用了本书介绍的大部分知识。作为本书的最后一个实例,用了三章的篇幅详细地介绍游戏的思路、框架搭建、功能实现的过程。

17章:介绍网游游戏开发的基本流程,编写能和服务端进行命令交互的客户端。

18章:分享我曾经遇到的错误和解决方案,介绍游戏发布、广告接入、安全等方面的知识。

2.0版本一书的不同之处

也许有些读者已经买过2.0版本的《Cocos2d-x游戏开发之旅》,这里列出本书与2.0版本一书的不同之处,方便读者参考:

1.所有代码均升级为3.0版本。

2.删除部分已过时的内容,对原有内容进行升级优化。

3.增加ValueVectorMap3.0触摸机制、lambda函数、Auto-Batching3.0新特性的内容讲解。

4.增加JSONCocoStudio UI编辑器、网络游戏等内容讲解。

5.增加游戏发布流程、未来趋势、安全处理的内容。

致谢

我想,这次我最应该感谢的,是那些认可我的读者,那些能看懂书中幽默风格的读者。是你们让我知道,我不是一个“精神病人”。

最后,我想感谢我的家人,家人给我的不仅仅是支持,而是一个完整的开心的家庭。并且,我不得不再一次感谢的,就是本书的旁白丁小若,感谢她愿意授予我使用她名字的权限,感谢她给予我幽默的灵感。

谨以此书献给我可爱的家人和朋友们。

由于水平有限,书中难免有错误之处,希望读者朋友多指正。也欢迎大家到我的博客就书中的问题与我互动。

 

钟迪龙

2014.08.05

 

 

 

 

  • 大小: 244.4 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics