《Cocos2dx面试题》 |
版本 |
作者 |
参与者 |
完成日期 |
备注 |
Cocos2dx_JobView_V01_1.0 |
严立钻 |
|
2018.08.22 |
|
|
|
|
|
|
|
|
|
|
|
##《Cocos2dx面试题》发布说明:
++++“Cocos2dx”是2014年比较流行的2D游戏引擎,当然也有3D版本,最近一直在做Unity3d相关的工作,所以对于Cocos2dx没有特别关注,但是作为一款优秀的游戏引擎,还是值得我们参考学习的;
++++“Cocos2dx面试题”:不仅仅包含Cocos2dx内容,还包括相关C++、Lua相关知识点;通过这篇博文,让大家熟悉一下Cocos2dx面试相关内容;
##《Unity面试题》目录:
#第一篇:一场模拟面试
#第二篇:Cocos2dx和C++知识点
#第一篇:一场模拟面试 |
#第一篇:一场模拟面试
++++立钻哥哥:由于整理Cocos2dx的时间比较仓促,也没有特别在意章节排版,直接利用一场模拟面试来揭开Cocos2dx剖析之路;
++++A.1、请列举标准C/C++的基本变量类型(例如:int),以及它们的字节长度(2分)
++++A.2、请用#define语句完成以下需求(4分)
++++A.3、请计算sizeof的值(5分)
++++A.4、在VC6下定义如下结构(4分)
++++A.5、头文件中的ifndef/define/endif 干什么用?(2分)
++++A.6、简述进程和线程的区别?(2分)
++++A.7、多线程编程时,线程间同步的方法有哪些?(2分)
++++A.8、#include
++++A.9、有关内存的思考题?(8分)
++++A.10、请写出下面代码的输出结果?(8分)
++++A.11、请写出下面代码的输出结果?(8分)
++++A.12、下面一段代码会产生内存泄露,请问如何修改能避免内存泄露? 并简述理由?(8分)
++++A.13、编写strcpy()函数?(10分)
++++A.14、如下两种写法有什么区别?(5分)
++++A.15、关键字static在C语言中的作用?(5分)
++++A.16、C++是安全类型的语言吗?(2分)
++++A.17、一个空类的大小,并解释?(2分)
++++A.18、面向对象与面向过程?(2分)
++++A.19、引用与指针?(2分)
++++A.20、多态?(2分)
++++A.21、深度优先遍历与广度优先遍历?(2分)
++++A.22、内存对齐问题?(2分)
++++A.23、写一个链表逆序?(2分)
++++A.24、写一个根据概率随机掉装备的函数?(2分)
++++A.25、四人过桥问题?(2分)
++++A.26、输入任意四个0~9中的数,通过+-*/()运算得到24,写算法或伪代码?
##A.1、请列举标准C/C++的基本变量类型(例如:int),以及它们的字节长度(2分) |
++A.1、请列举标准C/C++的基本变量类型(例如:int),以及它们的字节长度(2分)
++++立钻哥哥:
bool |
1个字节[*] |
||
char |
1个字节[*] |
||
short |
2个字节[**] |
||
int |
4个字节[****] |
||
float |
4个字节[****] |
||
long |
4个字节[****] |
||
double |
8个字节[********] |
||
#include
int main(void ){ printf(“int = %d\n”, sizeof(short int)); printf(“int = %d\n”, sizeof(int)); printf(“long int = %d\n”, sizeof(long int)); printf(“char=%d\n”, sizeof(char)); printf(“float=%d\n”, sizeof(float)); printf(“double=%d\n”, sizeof(double)); } //立钻哥哥:int main(void){} |
|||
16位系统 |
long是4字节 |
int是2字节 |
|
32位系统 |
long是4字节 |
int是4字节 |
|
64位系统 |
long是8字节 |
int是4字节 |
##A.2、请用#define语句完成以下需求(4分) |
++A.2、请用#define语句完成以下需求(4分)
--A、定义一个常量MSOFDAY,表示1天多少毫秒?
--B、定义一个宏x(a),计算数组元素的个数,a为数组变量名称,x(a)返回数组元素个数?
++++立钻哥哥:
#define MSOFDAY 1*24*60*60*1000 #define x(a) sizeof(a)/sizeof(*a) |
##A.3、请计算sizeof的值(5分) |
++A.3、请计算sizeof的值(5分)
char str[] = “Hello”;
char *p = str;
int n = 10;
请计算:
===>sizeof(str) =
===>sizeof(p) =
===>sizeof(n) =
++++立钻哥哥:
char str[] = “Hello”; char *p = str; int n = 10; ===>sizeof(str) = 6; ===>sizeof(p) = 4; ===>sizeof(n) = 4; |
++A.3.1、
void Func(char str[100]){
请计算:
sizeof(str) =
}
++++立钻哥哥:
void Func(char str[100]){ sizeof(str) = 4; } |
++A.3.2、
void *p = malloc(100);
请计算:
sizeof(p) =
void *p = malloc(100); sizeof(p) = 4; |
##A.4、在VC6下定义如下结构(4分) |
++A.4、在VC6下定义如下结构(4分)
typeof struct{
char c1;
char c2;
long n;
}stru;
请问sizeof(stru)等于多少?
并说明理由?
++++立钻哥哥:8字节;
++++结构体采取的内存分配原则是:四字节对齐;
++++CPU在读取内存数据的时候:4字节对齐会取得更快的速度;
++++1字节8位,4字节正好32位;
++++而32位机器的寄存器,地址什么的都是32位,正好一次处理就完成;
++++补充:如果double n ===>立钻哥哥:12字节;
##A.5、头文件中的ifndef/define/endif 干什么用?(2分) |
++A.5、头文件中的ifndef/define/endif 干什么用?(2分)
++++立钻哥哥:定义并防止重复定义;
++++可以在其中声明函数;
##A.6、简述进程和线程的区别?(2分) |
++A.6、简述进程和线程的区别?(2分)
++++立钻哥哥:进程是系统分配内存的最小开销,一个进程可以有多个线程,线程依附于进程而存在;线程可以共享进程中的局部变量;
++++一个进程内的所有线程共享同一全局内存空间;(这使得线程间很容易共享信息,但是这种容易性也会带来了同步问题);
++++进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单元; ++++线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位; ++++线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源; ++++一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行; |
++++进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性; ++++进程和线程的区别在于:简而言之,一个程序至少有一个进程,一个进程至少有一个线程; ++++线程的划分尺度小于进程,使得多线程程序的并发性高; ++++进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率; ++++线程在执行过程中与进程还是有区别的:每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口;但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制; |
++++进程和线程的区别: --A、地址空间和其他资源:进程间相互独立,同一进程的各线程间共享;某进程内的线程在其它进程不可见; --B、通信:进程间通信IPC:线程间可以直接读写进程数据段(如全局变量)来进行通信; --C、调度和切换:线程上下文切换比进程上下文切换要快得多; |
##A.7、多线程编程时,线程间同步的方法有哪些?(2分) |
++A.7、多线程编程时,线程间同步的方法有哪些?(2分)
++++立钻哥哥:互斥量、信号量、事件、临界区;
++++Windows中的4种线程同步技术:
--A、Events(事件):CEvent:作为标志在线程之间的传递信号;简单地说:类似一个布尔型变量的开关作用;
--B、Critical Sections(临界段):CCriticalSection:在进程中作为关键字以获得对“共享资源”的访问;
--C、Mutexes(互斥量):CMutex:与临界段的工作方式相似,只是该对象可以用于多进程中的线程同步,而不是用于单进程中;
--D、Semaphores(信号量):CSemaphore:在给定的限制条件下,允许多个进程同时访问共享资源;
++Linux进程通讯方式: ++++立钻哥哥:管道、共享内存、消息队列、信号、socket等; ++++IPC(Interprocess Communication,IPC):进程间通信: --A、消息传递:(管道、FIFO(有名管道)、消息队列); --B、同步:(互斥量、条件变量、读写锁、信号量); --C、共享内存区:(匿名共享内存区、有名共享内存区); --D、进程调用:(Solaris门、SunRPC); |
++Linux下如何创建线程: ==>pthread_create(&id, NULL, move, stack); ==>pthread_join(pthread_t tid, void** status); |
##A.8、#include |
++A.8、#include
++++立钻哥哥:
++++
++++“filename.h”:编译器从用户的工作路径开始搜索filename.h;
##A.9、有关内存的思考题?(8分) |
++A.9、有关内存的思考题?(8分)
++++立钻哥哥:
++请问运行Test函数会有什么样的结果? void GetMemory(char* p){ p = (char*)malloc(100); }
void Test(void){ char* str = NULL; GetMemory(str); strcpy(str, “hello world”); printf(str); } ++++立钻哥哥:会中断,因为str并没有提前分配内存; ++++程序崩溃:因为GetMemory并不能传递动态内存,Test函数中str一直都是NULL,strcpy(str, “helloworld”);,将使程序崩溃;
|
++请问运行Test函数会有什么样的结果? char* GetMemory(void){ char p[] = “helloword”; return p; }
void Test(void){ char* str = NULL; str = GetMemory(); printf(str); } ++++立钻哥哥:程序报错,因为栈区分配空间已经被释放; ++++可能是乱码:因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是NULL,但其原有的内容已经被清除,新内容不可知;
|
++请问运行Test函数会有什么样的结果? void GetMemory(char** p, int num){ *p = (char*)malloc(num); }
void Test(void){ char* str = NULL; GetMemory(&str, 100); strcpy(str, “hello”); printf(str); } ++++立钻哥哥:hello; ++++能够输出“hello”,内存泄露;
|
++请问运行Test函数会有什么样的结果? void Test(void){ char* str = (char*)malloc(100); strcpy(str, “hello”); free(str);
if(str != NULL){ strcpy(str, “world”); print(str); } } ++++立钻哥哥:world; ++++篡改动态内存区的内容,后果难以预料,非常危险,因为free(str)之后,str成为野指针,if(str != NULL)语句不起作用;
|
##A.10、请写出下面代码的输出结果?(8分) |
++A.10、请写出下面代码的输出结果?(8分)
class MyClass{ public: MyClass(){ printf(“Construct MyClass\n”); };
void SayHello(void){ printf(“Hello\n”); } };
void TestMyClass(void){ static MyClass MyClassForTesting; MyClassForTesting.SayHello(); }
int main(void){ TestMyClass(); TestMyClass();
return 0; } |
++++立钻哥哥:construct MyClass Hello Hello
++++补充拓展:总结static的应用和作用:
--A、函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用的时候仍然维持上次的值;
--B、在模块内的static全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问;
--C、在模块内的static函数只可以被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的类模块中;
--D、在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
--E、在类中的static成员函数属于整个类所拥有,这个函数不接受this指针,因而只能访问类的static成员变量;
##A.11、请写出下面代码的输出结果?(8分) |
++A.11、请写出下面代码的输出结果?(8分)
class Base{ public: Base(){}; ~Base(){};
virtual void WhoAmI(void){ printf(“Base\n”); };
void OutPutMessage(void){ WhoAmI(); }; };
class Derived : public Base{ public: Derived(){}; ~Derived(){};
virtual void WhoAmI(void){ printf(“Derived\n”); }; };
int main(void){ Derived D; Base *pB = &D; D.OutputMessage(); pB->OutputMessage();
return 0; } |
++++立钻哥哥:Derived Derived
##A.12、下面一段代码会产生内存泄露,请问如何修改能避免内存泄露? 并简述理由?(8分) |
++A.12、下面一段代码会产生内存泄露,请问如何修改能避免内存泄露? 并简述理由?(8分)
class Base{ public: Base(){ m_piPtr = new int; } ~Base(){ delete m_piPtr; }
private: int *m_piPtr; };
class Derived : public Base{ public: Derived(){ m_plDerived = new long; } ~Derived(){ delete m_plDerived; }
private: long* m_plDerived; };
int main(void){ Base* p = new Derived; delete p;
return 0; } |
++++立钻哥哥: virtual ~Base(){ delete m_piPtr; }
++++拓展说明1:C++中为什么基类析构函数一般要声明为虚函数:
--1.A、类析构函数要声明为虚函数:这样派生类调用析构函数才能层层回调,释放资源;(这也是虚函数的作用:提供回调的指针)
++++拓展说明2:基类的析构函数为什么是虚函数:
--2.A、一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄露;
--2.B、当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用;
++++拓展说明3:并不是要把所有类的析构函数都写成虚函数:
--3.A、因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间;所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数;
++补充:《EffectiveC++》条款07:
++++立钻哥哥:条款07:为多态基类声明virtual析构函数;
++++析构函数的运作方式是:最深沉派生的那个class其析构函数最先被调用,然后是其每一个base class的析构函数被调用;
++++请记住:polymorphic(带多态性质的)base classes应该声明一个virtual析构函数;
--如果class带有任何virtual函数,它就应该拥有一个virtual析构函数;
++++Classes的设计目的:如果不是作为base classes使用,或不是为了具备多态性(polymorphically),就不该声明virtual析构函数;
##A.13、编写strcpy()函数?(10分) |
++A.13、编写strcpy()函数?(10分)
已知strcpy函数原型是:
char *strcpy(char *strDest, const char *strSrc); |
其中:strDest是目的字符串,strSrc是源字符串;
不调用C++/C的字符串函数,请编写函数strcpy。
++++立钻哥哥:
char *myStrcpy(char *strDest, const char* strSrc){ if(strDest == NULL || strSrc == NULL){ return NULL; }
char *strDestCopy = strDest; while((*strDest++ = *strSrc++) != ‘\0’);
return strDestCopy; }
|
char *myStrcpy(char *strDest, const char* strSrc){ Assert((strDest != NULL) && (strSrc != NULL));
char *tmp = strDest; while((*strDest++ = *strSrc++) != ‘\0’);
return tmp; }
|
char *myStrcpy(char *dst, const char* src){ char *dcp; for(dcp = dst; *dcp = *src; dcp++, src++);
return dst; }
|
##A.14、如下两种写法有什么区别?(5分) |
++A.14、如下两种写法有什么区别?(5分)
1、 for(int i = 0; i < NUM; i++){ if(a < b){ DoSomething1(); }else{ DoSomething2(); } }
2、 if( a < b){ for(int i = 0; i < NUM; i++){ DoSomething1(); }
}else{ for(int i = 0; i < NUM; i++){ DoSomething2(); } }
|
++++立钻哥哥:
++++第一种写法:
--优点:程序简洁;
--缺点:多执行了N-1次逻辑判断,并且打断了循环“流水线”作用,使得编译器不能对循环进行优化处理,降低了效率;
++++第二种写法:
--优点:循环的效率高;
--缺点:程序不简洁;
##A.15、关键字static在C语言中的作用?(5分) |
++A.15、关键字static在C语言中的作用?(5分)
++++立钻哥哥:
++++1、在函数体内,一个被声明为静态的变量在这一函数调用过程中维持不变;
++++2、在模块内(一个.c文件内),被声明的静态变量可以被模块内所有函数访问,但不能被模块外的其他函数访问;
++++3、在模块内,一个被声明为静态的函数只能被同一模块内的其他函数调用;
##A.16、C++是安全类型的语言吗?(2分) |
#第二篇:Cocos2dx和C++知识点 |
#第二篇:Cocos2dx和C++知识点
++++B.1、对Cocos2dx的整体描述,整体认识?
++++B.2、如何阅读分析Cocos2dx源码及整体框架?
++++B.3、介绍一下你的项目?
++++B.4、使用Cocos2dx的心得?
++++B.5、是否了解CocoStudio?
++++B.6、用过哪些UI控件?
++++B.7、Cocos2dx的消息传递机制?
++++B.8、Cocos2dx内存自动回收机制?
++++B.9、Cocos2dx触摸机制?
++++B.10、在CCLayer处理用户触摸?
++++B.11、Cocos2dx触摸分发器?
++++B.12、Cocos2dx中图片的缓存和加载方式?
++++B.13、常见的内存管理的方式?
++++B.14、C++显式堆内存管理?
++++B.15、C++11中的智能指针?
##B.1、对Cocos2dx的整体描述,整体认识? |
++B.1、对Cocos2dx的整体描述,整体认识?
++++立钻哥哥:
++++Cocos2dx的游戏元素是:大总管CCDirector,场景CCScene,层CCLayer,精灵CCSprite,再加上动作CCAction,再配合一些动画CCAnimation和粒子特效CCParticleSystem等就构成了Cocos2dx游戏;
++++游戏领域作为GUI的先驱,其计算机技术应用也引领着软件开发前沿,比如Cocos2d-3.0系列大量采用了C++11特性:override和final、lambda表达式等;
++++Cocos2dx游戏框架:内存管理、图层渲染;
++Cocos2dx是一个简单而强大的二维游戏引擎
++++立钻哥哥:Cocos2dx的原型是Cocos2d,目的是封装底层绘图代码,简化2D游戏的开发过程,避免每次都“重新发明轮子”;
++++从本质上说:Cocos2d是一个图形引擎,封装了复杂的图形接口,通过抽象出精灵、动作等概念,降低了游戏开发难度,简化了开发过程;
++++Cocos2dx:基于Cocos2d-iPhone的多平台二维游戏引擎,为开发者封装了功能强大的绘图代码,使开发者专注于游戏开发而不是绘图操作;
++++AppDelegate:Cocos2dx项目中的程序入口文件,提供对程序生命周期的控制事件;
++++CCNode::addChild方法:用于将一个游戏元素添加到另一个元素中;
++简单介绍一下Cocos2d的特性:
++++流程控制(flow control):非常容易管理不同场景(scene)之间的流程控制;
++++精灵(sprite):快速而方便的精灵用于显示一切可见的元素;
++++节点(node):基于树结构的分层组织方式,方便管理不同层次的游戏元素,同时提供了统一管理的计时器(scheduler);
++++动作(action):应用于精灵或其他游戏元素的动画效果,可以组合成复杂的动作,如移动(move)、旋转(rotate)和缩放(scale)等;
++++特效(effect):包括波浪(wave)、旋转(twirl)和透镜(lens)等视觉特效;
++++平面地图(tiled map):支持矩形和六边形的平面地图;
++++菜单(menu):创建游戏中常见的菜单;
++++用户输入:提供接受用户触摸事件、传感器(如加速度计)等输入的简单解决方案;
++++文档(document):编程指南,API参考、视频教学以及很多简单可靠的测试样例;
++++MIT许可:免费开放的协议,但是请谨记尊重版权;
++++基于OpenGL:深度优化的绘制方式,支持硬件加速;
++导演(CCDirector)
++++立钻哥哥:在Cocos2dx引擎中,CCDirector类是整个游戏的组织和控制核心,游戏的运行规则,游戏内的CCScene(场景)、布景(CCLayer)、角色(CCSprite)等的运动,均由CCDirector管理,其在游戏中起着指定游戏规则让游戏内的场景、布景和任务有序的运行;
+++++在整个游戏里面,一般只有一个导演,游戏开始和结束时,都需要调用CCDirector的方法完成游戏初始化或销毁工作,其提供了一些场景管理的方法,如runWithScene,drawScene,pushScene和replaceScene等;
++++CCDirector控制FPS的显示隐藏,窗口大小,游戏的进入,退出,关卡的切换,取得正交模式,取得正在运行的场景,取得GL视图,游戏主循环入口等等;
##B.2、如何阅读分析Cocos2dx源码及整体框架? |
++B.2、如何阅读分析Cocos2dx源码及整体框架?
++++立钻哥哥:阅读源代码的最好方式是top-down的方式:先弄懂整个框架,再重点突破重要和感兴趣的模块;
++1、先看看测试用例TestCpp中的主函数,也是整个Win32程序的入口
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int mCmdShow){ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine);
//create the application instance AppDelegate app; CCEGLView* eglView = CCEGLView::sharedOpenGLView(); eglView->SetViewName(“TestCpp”); eglView->setFrameSize(1028, 720);
return CCApplication::sharedApplicaiton()->run(); } //立钻哥哥:int APIENTRY _tWinMain(){} |
++++这个入口有2个非常重要的类CCEGLView和CCApplication,在整个程序中都是单例;
+++++[CCEGLView]:是用来管理窗口和绘制;在CCEGLView::Create()中做了一下工作:
--A、registerClass注册窗口:其中非常重要的消息处理函数CCEGLView::_WindowProc()就是在这个地方定义的;整个游戏中的窗口的键盘、鼠标消息响应就可以在这个函数中进行处理;
--B、initGL初始化OpenGL引擎;(CCEGLView::initGL()中设置了像素的格式,创建了OpenGL的RenderContext,OpenGL最终渲染的结果会显示到该窗口的DC上)
++2、再看看CCApplication
++++立钻哥哥:CCApplication是用来管理程序的逻辑,最后一句CCApplication::sharedApplication()->run(); :整个程序就开始告诉运转起来了;
CCApplication* CCApplication::sharedApplication(){ CC_ASSERT(sm_pSharedApplication); return sm_pSharedApplicaition; } |
++AppDelegate是Application的子类,在这个子类的构造函数中声明了这个全局唯一的静态变量:sm_pSharedApplication;
CCApplication:CCApplication() : m_hInstance(NULL), m_hAccelTable(NULL){ m_hInstance = GetModuleHandle(NULL); m_nAnimationInterval.QuadPart = 0; CC_ASSERT(!sm_pSharedApplication); sm_pSharedApplication = this; } //立钻哥哥:CCApplication::CCApplication(){} |
+++Application::Run()做了什么,让整个程序运行起来了:
int CCApplication::run(){ PVRFrameEnableControlWindow(false);
//Main message Loop: MSG msg; LARGE_INTEGER nFreq; LARGE_INTEGER nLast; LARGE_INTEGER nNow;
QueryPerformanceFrequency(&nFreq); QueryPerformanceCounter(&nLast);
//Initialize instance and cocos2d if(!applicationDidFinishLaunching()){ return 0; }
CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView(); pMainWnd->centerWindow(); ShowWindow(pMainWnd->getHWnd(), SW_SHOW);
while(1){ if(!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){ //Get current time tick QueryPerformanceCounter(&nNow);
//If it’s the time to draw next frame, draw it, else sleep a while. if(nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart){ nLast.QuadPart = nNow.QuadPart; CCDirector::sharedDirector()->mainLoop(); }else{ Sleep(0); } continue; } //立钻哥哥:if(!PeekMessage()){}
if(WM_QUIT == msg.message){ //Quit message loop break; }
//Deal with windows message
if(!m_hAcceelTable || !TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg)){ TranslateMessage(&msg); DispatchMessage(&msg); }
return (int)msg.wParam;
} //立钻哥哥:while(1){}
} //立钻哥哥:int CCApplication::run(){} |
++++[applicationDidFinishLaunching()]:这部分和测试用例有关系;
++++在while(1){}主循环中做了两件事:
--1A、转发窗口消息,交给之前定义的CCEGLView::_WindowProc()进行处理;
--2B、每隔m_nAnimationInterval.QuadPart时间,也就是游戏的一帧,进行一次处理;
++3、最重要的一个类CCDirector导演类
++++立钻哥哥:CCDirector导演类是一个单例,负责整个游戏场景管理,逻辑更新以及绘制;
++++CCDirector::sharedDirector()->mainLoop()的每帧的mainLoop()都做了啥事情:
void CCDisplayLinkDirector::mainLoop(void){ if(m_bPurgeDirectorInNextLoop){ m_bPurgeDirectorInNextLoop = false; purgeDirector(); }else if(!m_bInvalid){ drawScene();
//release the objects CCPoolManager::sharedPoolManager()->pop(); }
} //立钻哥哥:void CCDisplayLinkDirector::mainLoop(){} |
++++CCDirector导演类的每帧做的最重要的事情就是drawScene():
void CCDirector::drawScene(void){ ... ...
if(!m_bPaused){ m_pScheduler->update(m_fDeltaTime); } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//draw the scene if(m_pRunningScene){ m_pRunningScene->visit(); }
//swap buffers if(m_pobOpenGLView){ m_pobOpenGLView->swapBuffers(); }
} //立钻哥哥:void CCDirector::drawScene(){} |
++++CCDirector::drawScene()做了两方面的工作:
--1A、更新调度器m_pScheduler:比如场景中的动作等;
--2B、绘制场景中的对象结点,树形的方式;
##B.3、介绍一下你的项目? |
++B.3、介绍一下你的项目?
++++立钻哥哥:我一共准备了N个项目,其中2个项目已发布在360平台上:
++++《ylzServPlane》:飞机大战,是基于Cocos2dx-2.2.3框架,利用这个小游戏,主要是熟悉Cocos2dx-2.2.3开发环境,掌握利用Cocos2dx框架开发游戏的基本技能;
++++《ylzServFind》:找宝石,是基于Cocos2dx-3.2框架,利用这个小游戏,主要是熟悉Cocos2dx-3.2开发环境,掌握3.0系列与2.0系列的差异和相关开发技能;
++++项目练习是我以后一个重要的学习途径:通过一个项目熟悉一个开发框架;
++++现在正在做的还有:《ylzServGuess》猜猜看,这个游戏很重要,肩负着三大任务:http、sqlite、UI等任务,这个项目基于Cocos2dx-2.2.2框架;
++++以后会陆续改写更多项目:《ylzServ塔防》、《ylzServ找你妹》、《ylzServ太空大战》、《ylzServ****》;
++++以后重点突破领域有:TileMap瓦片地图项目、CocosStudioUI场景动画项目等;
##B.4、使用Cocos2dx的心得? |
++B.4、使用Cocos2dx的心得?
++++立钻哥哥:Cocos2dx本身是C++的,尽管开发团队为了降低学习难度和入门门框做了很多工作,但是如果C++功底不够,我觉得也学不到什么东西,充其量学会了使用一个工具而已;
++++作为一个优秀的程序员,数学是非常重要的;
++++C/C++还是值得学习的,虽然这个比较难,但是自有其价值所在;
##B.5、是否了解CocoStudio? |
++B.5、是否了解CocoStudio?
++++立钻哥哥:CocoStudio作为触控推出的一款工具,肯定是有其战略意义的;
++++CocoStudio作为专业的UI、场景、动画、数据等开发工具,其功能相当强大,是以后必须认真学习掌握的必备工具;
++++CocosStudio是一套专业的永久免费的游戏开发工具集,帮助开发快速创建游戏资源,将大部分繁琐的游戏开发工作使用编辑器来快速制作;
++++CocoStudio包含了游戏开发中核心的几个游戏编辑器:UI编辑器、动画编辑器、场景编辑器、数据编辑器,用于处理游戏中的动画资源、UI界面、游戏场景、游戏数据,针对于开发团队中不同的职业进行深度设计,规范了整个开发流程,让开发团队中每个人各执其职,发挥自己最大的作用;
++++[动画编辑器]:用于编辑游戏中使用的角色动画、特效动画、场景动画等动态的游戏资源;(主要使用人员是:美术设计师)
++++[UI编辑器]:用于编辑游戏中的所有的图形界面;(主要使用人员是美术设计师)
++++[数据编辑器]:用于将数值策划编辑的数值表导入数据编辑器中,将复杂表进行分解,导出成属性表,数值表等几种常用的数据文件;
++++[场景编辑器]:用于编辑游戏中的场景元素、游戏关卡;(主要使用人员是策划或者关卡设计师)
##B.6、用过哪些UI控件? |
++B.6、用过哪些UI控件?
++++立钻哥哥:UI控件其实很多的,留意了,用过一次,基本就会了,如果没有注意到,就不会;
++++常见UI控件:CCSprite、CCNode、CCLayer、CCScene;
++++[CCScale9Sprite]:九宫格Sprite;
++++[CCControlButton]:事件按钮;
++++[CCControlSlider]:拖动条;
++++[CCControlColorPicker]:颜色选择器;
++++[CCControlSwitch]:开关控件;
++++[CCControlPotentionmeter]:压力计;
++++[CCControlStepper]:分段控件;
++++[CCScrollView]:滚动视图;
++++[CCTableView]:列表视图;
##B.7、Cocos2dx的消息传递机制? |
++B.7、Cocos2dx的消息传递机制?
++++立钻哥哥:
++++[1、采用函数回调]:主要是用于MenuItem:
++++[2、TouchEvent响应]:为更多的响应处理提供可能;
++++[3、触摸监听绑定]:触摸函数可以用lambda来写;
##B.8、Cocos2dx内存自动回收机制? |
++B.8、Cocos2dx内存自动回收机制?
++++立钻哥哥:Cocos2dx提供了一种类似Java的内存回收机制;(在C++中,不再使用的变量,需要手动释放(delete),不然内存就会溢出)
++++在使用Cocos2dx时,在新创建实例时,加入autorelease(),这样就可以自动释放不再需要的内存了:
{ CCScene* pScene = CCScene::node(); CCLayer* pLayer = new MyTestController(); pLayer->autorelease(); } |
++++为了实现对象的引用计数记录,Cocos2dx实现了自己的根类CCObject,引擎中的所有派生类都派生自CCOject,CCObject的定义:
class CC_DLL CCObject : public CCCopying{ public: unsigned int m_uID; //对象id,在脚本引擎中使用 int m_nLuaID; //Lua中的引用ID,同样被脚本引擎使用
protected: unsigned int m_uReference; //引用数量 bool m_bManaged; //标识此对象是否已设置为autorelease
public: CCObject(void); virtual ~CCObject(void); void release(void); void retain(void); CCObject* autorelease(void); CCObject* copy(void); bool isSingleRefrence(void); unsigned int retainCount(void); virtual bool isEqual(const CCObject* pObject); virtual bool update(ccTime dt){ CC_UNUSED_PARAM(dt); }; friend class CCAutoreleasePool; }; //立钻哥哥:class CC_DLL CCObject:public CCCopying{} ++++每个对象包含一个用来控制生命周期的引用计数器,它就是CCObject的成员变量m_uReference; ++++我们可以通过retainCount()方法获得对象当前的引用计数值; ++++autorelease()的作用是将对象放入自动回收池(CCAutoreleasePool):当回收池自身被释放的时候,就会对池中的所有对象执行一次release()方法,实现灵活的垃圾回收; ++++通过回收池管理器CCPoolManager的push()或pop()方法来创建或释放回收池,其中CCPoolManager也是一个单例对象; ++++引擎维护着一个回收池,所有的autorelease对象都添加到了这个池中; |
++++自动回收池是可嵌套的:多个自动回收池排列成栈结构,当我们手动创建了回收池后,回收池会压入栈的顶端,autorelease对象仅添加到顶端的池中;当顶层的回收池被弹出释放时,它内存所有的对象都会被释放一次,此后出现的autorelease对象则会添加到下一个池中:
//步骤a obj1->autorelease(); obj2->autorelease();
//步骤b CCPoolManager::sharedPoolManager()->push();
//步骤c for(int i = 0; i < n; i++){ obj_array[i]->autorelease(); }
//步骤d CCPoolManager::sharedPoolManager()->pop();
//步骤e obj3->autorelease();
++++当执行步骤a时,obj1和obj2被加入到回收池1中; ++++步骤b创建了一个新的回收池,此时回收池2接管所有autorelease操作; ++++步骤c是一个循环,其中把n个对象加入回收池2中; ++++步骤d释放了回收池2,因此回收池2中的n个对象都被释放了一次,同时回收池1接管autorelease操作; ++++步骤e调用obj3的autorelease()方法,把obj3加入回收池中; |
++Cocos2dx内存管理原则
++++1A、程序段必须成对执行retain()和release()或者执行autorelease()来开始和结束对象的引用;
++++2B、工厂方法返回前,应通过autorelease()结束该对象的引用;
++++3C、对象传值前,应考虑到新旧对象相同的特殊情况;
++++4D、尽量使用release()而不是autorelease()来释放对象引用,以确保性能最优;
++++5E、保存CCObject的子类对象时,应严格使用Cocos2dx提供的容器,避免使用STL容器,对象必须以指针形式存入;
++++6F、如果希望自定义的类也拥有Cocos2dx的内存管理功能,可以把CCObject作为自定义的基类,并在实现类时严格遵守Cocos2dx的内存管理原则;
++复杂的内存管理
++++内存管理一直是一个不易处理的问题,开发者必须考虑分配回收的方式和时机,针对堆和栈做不同的优化处理等等;
++++内存管理的核心是动态分配的对象必须保证在使用完毕后有效地释放内存,即管理对象的生命周期;
++++过于零碎的对象分配回收可能导致堆中的内存碎片化,降低内存的使用效率;
++++Boost库引入的智能指针(smart pointer)从对象所有权传递的角度来解决内存管理问题;(各种基于C++的第三方工具库和引擎往往都会实现自己的智能内存管理机制来解决内存管理的难题,试图将开发者从烦琐而晦涩的内存管理中解放出来)
++现有的智能内存管理技术
++++立钻哥哥:实现智能管理内存的技术主要有两种:一是引用计数;二是垃圾回收;
++++[引用计数]:它是一种很有效的机制,通过给每个对象维护一个引用计数器,记录该对象当前被引用的次数;引用计数的重要规则是每一个程序片段必须负责任地维护引用计数,在需要维持对象生存的程序段的开始和结束分别增加和减少一次引用计数,这样我们就可以实现十分灵活的智能内存管理了;(引用计数解决了对象的生命周期管理问题,但堆碎片化和管理烦琐的问题仍然存在)
++++[垃圾回收]:它通过引入一种自动的内存回收器,试图将程序员从复杂的内存管理任务中完全解放出来;垃圾回收器还可以压缩使用中的内存,以缩小堆所需要的工作空间;
++工厂方法
++++立钻哥哥:工厂方法是程序设计中一个典型的设计模式,指的是基类中只定义创建对象的接口,将实际的实现推迟到子类中;
++++工厂方法返回前,通过autorelease()结束该对象的引用
CCObject* factoryMethod(){ CCObject* ret = new CCObject();
... ... //这里对ret对象进行必要的初始化操作
ret->autorelease(); return ret; } |
++释放:release()还是autorelease()
++++立钻哥哥:autorelease()并不是毫无代价的,其背后的垃圾池机制同样需要占用内存和CPU资源,每次执行autorelease()的过程,实际上对应的是执行成对的retain()和release(),以及一次成对的容器存取,还包括其他的逻辑判断;
++++过多不必要的autorelease()将导致垃圾池臃肿膨胀,在存在大量内存操作的过程中会尤为严重地挤占本来就紧张的系统资源;
++++建议在开发过程中应该避免滥用autorelease(),只在工厂方法等不得不用的情况下使用,尽量以release()来释放对象引用;
++Cocos2dx中与内存管理有关的宏
++++[CC_SAFE_DELETE(p)]:使用delete操作符删除一个C++对象p,如果p为NULL,则不进行操作;
++++[CC_SAFE_DELETE_ARRAY(p)]:使用delete[]操作符删除一个C++数组p,如果p为NULL,则不进行操作;
++++[CC_SAFE_FREE(p)]:使用free()函数删除p,如果p为NULL,则不进行操作;
++++[CC_SAFE_RELEASE(p)]:使用release()方法释放Cocos2dx对象p的一次引用,如果p为NULL,则不进行操作;
++++[CC_SAFE_RELEASE_NULL(p)]:使用release()方法释放Cocos2dx对象p的一次引用,再把p赋值为NULL,如果p已经为NULL,则不进行操作;
++++[CC_SAFE_RETAIN(p)]:使用retain()方法增加Cocos2dx对象p的一次引用;如果p为NULL,则不进行操作;
##B.9、Cocos2dx触摸机制? |
++B.9、Cocos2dx触摸机制?
++++立钻哥哥:返回true,则本层接受;如果是false,就不接受,向下传递;
//2.0版本 virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent); virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent); virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent); virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent); ++++[ccTouchesBegan]:触摸事件开始,也就是手指按下时; ++++[ccTouchesMoved]:触摸移动事件,也就是手指在屏幕滑动的过程; ++++[ccTouchesEnded]:触摸事件结束,也就是手指松开时; ++++[ccTouchesCancelled]:打断触摸事件,一般是系统层次的消息,如手机来电话,触摸事件就会被中断;
|
//3.0版本 //单点触摸 virtual bool onTouchBegan(Touch *touch, Event *unused_event); virtual void onTouchMoved(Touch *touch, Event *unused_event); Virtual void onTouchEnded(Touch *touch, Event *unused_event); virtual void onTouchCancelled(Touch *touch, Event *unused_event);
//多点触摸 virtual void onTouchesBegan(const std::vector<Touch*>& touches, Event *unused_event); virtual void onTouchesMoved(const std::vector<Touch*>& touches, Event *unused_event); virtual void onTouchesEnded(const std::vector<Touch*>& touches, Event *unused_event); virtual void onTouchesCancelled(const std::vector<Touch *>& touches, Event* unused_event); |
++++【单点触摸】:只有注册的Layer才能接收触摸事件:
--A、[onTouchBegan()]:
----A.1、如果返回true:本层的后续Touch事件可以被触发,并阻挡向后层传递;
----A.2、如果返回false:本层的后续Touch事件不能被触发,并向后传递,也就是不会调用;
--B、[onTouchMoved()]:
virtual bool onTouchBegan(CCTouch *pTouch, CCEvent *pEvent); |
----B.1、返回false,则ccTouchMoved(), ccTouchEnded()不会再接收到消息;
----B.2、返回true,则ccTouchMoved(), ccTouchEnded()可以接受到消息;
++小结
++++[CCLayer触摸支持]:CCLayer提供了一套现成的触摸实现,只需启用TouchEnable属性即可接收Cocos2dx的标准触摸事件;(开发者需要重载以ccTouches为前缀的响应函数)
++++[标准触摸事件]:Cocos2dx提供的一种触摸事件,注册了这种事件的所有对象都会平等地接收到触摸事件,并且对于多点触摸,标准触摸事件会同时提供所有的触摸点;
++++[带目标的触摸事件]:这是另一种触摸事件,注册了这种事件的对象会按照优先级投递,先接收到事件的对象有权吞噬此事件,以阻止把事件分发给其他对象;(利用吞噬功能屏蔽某个事件)(例如:一个提示框可以屏蔽掉提示框以外的所有对象接收到触摸事件)
++++[触摸分发器(CCTouchDispatcher)]:由引擎调度、负责接收来自系统的触摸事件;(这允许任何对象在分发器中注册触摸事件,并根据Cocos2dx的机制把触摸事件分发给注册者)(开发者可以利用addStandardDelegate与addTargetedDelegate方法把自己注册为事件接收者,利用removeDelegate方法取消曾经注册过的触摸事件)
##B.10、在CCLayer处理用户触摸? |
++B.10、在CCLayer处理用户触摸?
++++立钻哥哥:CCLayer继承自CCNode,在CCLayer中可以实现单点触摸、多点触摸和重力感应回调等3种不同形式的交互;
++++这里我们来重点讨论一下:在CCLayer处理用户触摸;
++在用户自定义的图层处理用户触摸事件步骤
++++A、步骤1:编写图层类继承CCLayer;
++++B、步骤2:在初始化阶段(init方法),将此层的属性设置为接收触摸消息:
setTouchEnabled(true); //开启屏幕触摸 |
++++C、步骤3:重载CCLayer函数:virtual void registerWithTouchDispatcher(void);,因为默认的方式为:[Standard Touch Delegate],需要在函数中添加以下语句:
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true); |
--第二个参数是代理的优先级,如果数值越小优先级越高;
--第三个参数是true:表示当前图层处理消息后不再进行消息的后续传递,如果需要继续传递消息需要设置为false;
++++D、步骤4:重载触摸响应函数,接收触摸消息需要重载以下函数:
ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent); //按下 ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent); //滑动 ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent); //抬起 |
++++E、步骤5:在触摸响应函数中通过pTouch得到用户的触摸点:
CCPoint point = pTouch->getLocation(); |
##B.11、Cocos2dx触摸分发器? |
++B.11、Cocos2dx触摸分发器?
++++立钻哥哥:为了实现触摸事件,CCLayer已经封装好了简单的接口(继承了CCTouchDelegate类)来实现触摸事件的响应;
++++触摸事件有两种:标准触摸代理和目标触摸代理;
++++[标准触摸]:在层初始化时调用setTouchEnable(true); 方法,即可实现标准触摸,实现处理事件回调函数,处理触摸事件即可;
//optional virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent); virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent); virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent); virtual void ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent);
|
void TouchLayer::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent){ if(pTouches->Count() == 1){ CCTouch *touch = dynamic_cast(pTouches->anyObject());
//由于在不同平台下触摸点的坐标系与OpenGL呈现区域的参数可能不尽相同 CCPoint position = touch->locationInView(); //获取游戏画面中的点位置 positon = CCDirector::sharedDirector()->convertToGL(position);
//此处处理触摸事件
}else{ //如果不止一个触摸点,则在此处理多点触摸事件 }
} //立钻哥哥:void TouchLayer::ccTouchesMoved(){} |
void CCLayer::setTouchEnabled(bool enabled){ if(m_bTouchEnabled != enabled){ m_bTouchEnabled = enabled; if(m_bRunning){ if(enabled){ this->registerWithTouchDispatcher(); }else{ CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this); } } } } //立钻哥哥:void CCLayer::setTouchEnabled(){} |
++++进入[registerWithTouchDispatcher()]方法中看到调用了:pDispatcher->addStandardDelegate(this, 0); 方法,也就是将该对象注册为标准代理; ++++对于标准代理,只要事件分发器接收到用户的触摸事件,就会分发给所有的订阅者; ++++当系统存在多个触摸点时,所有的触摸点都会传递给回调函数,然后许多情况下每个触摸点之间是独立的,屏幕上是否存在其他触摸点我们并不关心; |
++++[目标代理]:要我该层使用目标代理,首先调用addTargetedDelegate()开启目标代理;
++1、首先调用addTargetedDelegate()开启目标代理: CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true); ++++第二个参数为优先级;第三个参数为是否吞噬触摸;
|
++2、然后重载事件回调函数: virtual bool ccTouchesBegan(CCTouch *pTouches, CCEvent *pEvent); virtual void ccTouchesMoved(CCTouch *pTouches, CCEvent *pEvent); virtual void ccTouchesEnded(CCTouch *pTouches, CCEvent *pEvent); virtual void ccTouchesCancelled(CCTouch *pTouches, CCEvent *pEvent); ++++第一个参数为CCTouch*,已不再是CCSet*集合,而是一个触摸点; ++++ [ccTouchBegan()]方法返回的是一个布尔值,表示声明是否要捕捉传入的这个触摸点,返回true表示要捕捉,那么在后面Moved和Ended中处理触摸事件即可; ++++如果注册的时候选择了要吞噬触摸,触摸事件不会再分发下去,则优先级低的对象无法接收到触摸;
|
++3、实现一个对象的触摸代理分以下几步: ++++A、第1步:此对象继承CCStandardTouchDelegate/CCTargetedTouchDelegate接口; ++++B、第2步:使用addStandardDelegate/addTargetedDelegate方法把自己注册给触摸事件分发器CCTouchDispatcher; ++++C、第3步:重载事件处理回调函数;(注意目标代理触摸开始事件Began中必须针对需要接受的事件返回true) ++++D、第4步:当不再需要接收触摸事件,使用removeDelegate方法来注销触摸事件的接收;
|
++触摸分发器原理
//来看看CCDispatcher的主要成员
//注册标准触摸事件 void addStandardDelegete(CCTouchDelegate *pDelegate, int nPriority);
//注册目标触摸事件 void addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches);
//注销触摸派发 void removeDelegate(CCTouchDelegate *pDelegate); void removeAllDelegate(void);
//重新设定指定对象的事件优先级 void setPriority(int nPriority, CCTouchDelegate *pDelegate);
//分发事件 void touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex);
protected: CCArray* m_pTargetedHandlers; //记录注册了带目标的触摸事件对象 CCArray* m_pStandardHandlers; //记录注册了标准触摸事件的对象 |
##B.12、Cocos2dx中图片的缓存和加载方式? |
++B.12、Cocos2dx中图片的缓存和加载方式?
++++立钻哥哥:在Cocos2dx中进行图片加载时,如果是第一次加载图片就要把图片加载到缓存,然后从缓存中加载图片,如果缓存中已经有了,就直接从缓存中加载;
++++图片的缓存有两种类型:一种是CCTextureCache,另一种是CCSpriteFrameCache;
++++[CCTextureCache]:是普通的图片加载缓存,一般直接加载的图片都会放到这个缓存里面,这是一个会经常用到的缓存,这个缓存如果不手动释放,它是会一直占用内存的,它有很多函数接口,都是为了方便进行内存管理等的;
--[removeAllTextures()]:清空所有的缓存;
--[removeUnusedTextures()]:清除没用的缓存;
--[dumpCachedTextureInfo()]:输出缓存的信息;
##B.13、常见的内存管理的方式? |
++B.13、常见的内存管理的方式?
++++立钻哥哥:
++++1A、在适当的时候释放内存;
++++2B、使用纹理贴图集的方法,尽量拼接图片,使得图片的边长保持在2的N次方,同时最好将有一点逻辑关系的图片打包在一张大图里,从而能够有效地节约内存;
++++3C、使用CCSpriteBatchNode来减少相同图片的渲染操作;
##B.14、C++显式堆内存管理? |
++B.14、C++显式堆内存管理?
++++立钻哥哥:C++使用new关键字在运行时给一个对象动态分配内存,并返回堆上内存的地址供应用程序访问,通过动态分配的内存需要在对象不再被使用时通过delete运算符将其内存归还给内存池;
++++显式的内存管理在性能上有一定优势,但是极其容易出错,事实上,我们总是不能通过人的思维去保证一个逻辑的正确;
++++不能正确处理堆内存的分配与释放通常会导致以下问题:
--1A、野指针:指针指向的内存单元已经被释放,但是其他一些指针可能还指向它,这些内存可能已经被重新分配给其他对象,从而导致不可预测的结果;
--2B、重复释放:重复释放一个已经被释放的内存单元,或者释放一个野指针(也是重复释放)都会导致C++运行时错误;
--3C、内存泄露:不再被使用的内存单元如果不被释放就会一直占用内存单元,如果这些操作不断重复就会导致内存占用不断增加,在游戏中内存泄露尤其严重,因为可能每一帧都在创建一个永远不会被回收的游戏对象;
##B.15、C++11中的智能指针? |
++立钻哥哥推荐的拓展学习链接(Link_Url):
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
++++Unity5.x用户手册:https://blog.csdn.net/VRunSoftYanlz/article/details/81712741
++++Unity面试题ABC:https://blog.csdn.net/vrunsoftyanlz/article/details/78630687
++++Unity面试题D:https://blog.csdn.net/VRunSoftYanlz/article/details/78630838
++++Unity面试题E:https://blog.csdn.net/vrunsoftyanlz/article/details/78630913
++++Unity面试题F:https://blog.csdn.net/VRunSoftYanlz/article/details/78630945
++++Cocos2dx面试题:https://blog.csdn.net/VRunSoftYanlz/article/details/78630967
++++Unity知识点0001:https://blog.csdn.net/vrunsoftyanlz/article/details/80302012
++++Unity知识点0008:https://blog.csdn.net/VRunSoftYanlz/article/details/81153606
++++Unity引擎基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78881685
++++Unity面向组件开发:https://blog.csdn.net/vrunsoftyanlz/article/details/78881752
++++Unity物理系统:https://blog.csdn.net/vrunsoftyanlz/article/details/78881879
++++Unity2D平台开发:https://blog.csdn.net/vrunsoftyanlz/article/details/78882034
++++UGUI基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78884693
++++UGUI进阶:https://blog.csdn.net/vrunsoftyanlz/article/details/78884882
++++UGUI综合:https://blog.csdn.net/vrunsoftyanlz/article/details/78885013
++++Unity动画系统基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78886068
++++Unity动画系统进阶:https://blog.csdn.net/vrunsoftyanlz/article/details/78886198
++++Navigation导航系统:https://blog.csdn.net/vrunsoftyanlz/article/details/78886281
++++Unity特效渲染:https://blog.csdn.net/vrunsoftyanlz/article/details/78886403
++++Unity数据存储:https://blog.csdn.net/vrunsoftyanlz/article/details/79251273
++++Unity中Sqlite数据库:https://blog.csdn.net/vrunsoftyanlz/article/details/79254162
++++WWW类和协程:https://blog.csdn.net/vrunsoftyanlz/article/details/79254559
++++Unity网络:https://blog.csdn.net/vrunsoftyanlz/article/details/79254902
++++C#事件:https://blog.csdn.net/vrunsoftyanlz/article/details/78631267
++++C#委托:https://blog.csdn.net/vrunsoftyanlz/article/details/78631183
++++C#集合:https://blog.csdn.net/vrunsoftyanlz/article/details/78631175
++++C#泛型:https://blog.csdn.net/vrunsoftyanlz/article/details/78631141
++++C#接口:https://blog.csdn.net/vrunsoftyanlz/article/details/78631122
++++C#静态类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630979
++++C#中System.String类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630945
++++C#数据类型:https://blog.csdn.net/vrunsoftyanlz/article/details/78630913
++++Unity3D默认的快捷键:https://blog.csdn.net/vrunsoftyanlz/article/details/78630838
++++游戏相关缩写:https://blog.csdn.net/vrunsoftyanlz/article/details/78630687
++++设计模式简单整理:https://blog.csdn.net/vrunsoftyanlz/article/details/79839641
++++专题:设计模式(精华篇):https://blog.csdn.net/VRunSoftYanlz/article/details/81322678
++++U3D小项目参考:https://blog.csdn.net/vrunsoftyanlz/article/details/80141811
++++UML类图:https://blog.csdn.net/vrunsoftyanlz/article/details/80289461
++++U3D_Shader编程(第一篇:快速入门篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80372071
++++U3D_Shader编程(第二篇:基础夯实篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80372628
++++框架知识点:https://blog.csdn.net/VRunSoftYanlz/article/details/80862879
++++游戏框架(UI框架夯实篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80781140
++++游戏框架(初探篇):https://blog.csdn.net/VRunSoftYanlz/article/details/80630325
++++Lua快速入门篇(基础概述):https://blog.csdn.net/VRunSoftYanlz/article/details/81041359
++++Lua快速入门篇(XLua教程):https://blog.csdn.net/VRunSoftYanlz/article/details/81141502
++++Lua快速入门篇(Xlua拓展):https://blog.csdn.net/VRunSoftYanlz/article/details/81173818
++++UnityAPI.Rigidbody刚体:https://blog.csdn.net/VRunSoftYanlz/article/details/81784053
++++UnityAPI.Material材质:https://blog.csdn.net/VRunSoftYanlz/article/details/81814303
++++UnityAPI.Android安卓:https://blog.csdn.net/VRunSoftYanlz/article/details/81843193
++++UnityAPI.AndroidJNI安卓JNI:https://blog.csdn.net/VRunSoftYanlz/article/details/81879345
++++UnityAPI.Transform变换:https://blog.csdn.net/VRunSoftYanlz/article/details/81916293
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
--_--VRunSoft:lovezuanzuan--_--