Cocos2d-x 程序移植到Windows Phone 8小记

将一个在iOS和Android上运行良好的Cocos2d-x 程序移植到Windows Phone 8的设备。

网路上面有一些文章,大体可以按照这些文章来进行,下面说几点遇到的坑:

1.E_OUTOFMEMORY

程序移植过去后,在Lumia 920上运行的好好的,但是在更高配的Lumia 1520却报

D3D11 ERROR: ID3D11Device::CreateTexture2D: Returning E_OUTOFMEMORY, meaning memory was exhausted

这样的错误,实在匪夷所思。毕竟Lumia 1520的配置远比Lumia 920要好。

不管怎么样,参照:http://msdn.microsoft.com/zh-cn/library/windowsphone/develop/jj681682%28v=vs.105%29.aspx  这篇文章,添加了ID_FUNCCAP_EXTEND_MEM标记后运行OK了。

2.使用自己的shader

这实在耗费了我一些时间,另外开文在此:http://www.ispinel.com/2014/07/03/12393/

3.libcurl

这个工程在cocos2dx\platform\third_party\wp8\libcurl,添加进解决方案,然后把项目引用其就行。

4.多线程

由于之前的代码广泛使用到pthread,遇到这块的时候,我一方面参考了CCPThreadWinRT.h这个文件设定(后来也被废除),另外一方面使用了C++ 11的std::thread以及Parallel Patterns Library进行取代.

5.中文输入法

这个问题解决的我实在想说一句WTF。如果在UI库上,激活输入法是通过CCEGLView::setIMEKeyboardState方式,那么会遇到问题。在Windows Phone 8下面会出现在中文输入法时,会同时接收到用户输入的英文和中文,而在Windows Phone 8.1下面,则完全不能接受到中文。即便Cocos2d-x 的官方sample也是如此。解决方式:项目改成XAML模式,当要激活键盘输入时,通过CCEGLView::OpenXamlEditBox来打开一个单独的专门输入页面。

Read more

Cocos2d-x:在Windows Phone/Windows RT上使用自定义Shader

Cocos2d-x 是一个新兴的并且非常流行的2D开源游戏引擎,现在主要面向移动平台,其在2.2版本开始终于支持Windows Phone 8 和 Windows RT平台.

通常来说,在成品游戏上,都需要首先将shader预编译,然后再给游戏所用,因为这能减少游戏中的性能消耗。

但是估计Cocos2d-x的游戏对shader要求不高,预设shader也比较简单和少,导致其并没有这样一套shader预编译的机制,一直都是游戏直接在运行时编译shader.

于是到了Windows Phone/Windows RT这样的平台,其强制要求使用预编译shader,而不能在游戏运行时编译shader,因此Cocos2d-x在解决这个问题的时候,就犯难了.

另外Windows Phone/Windows RT仅仅支持DirectX(feature level 是9.3),而Cocos2d-x从设计之初就从来未想过要对渲染器进行包装(似乎假设了所有硬件都能运行OpenGL一样),导致OpenGL代码在整个引擎中满天飞,这是我严重不喜欢Cocos2d-x的一个地方.

于是遇到Windows Phone/Windows RT这样的仅仅支持DirectX的平台,Cocos2d-x想要支持,便变得非常的困难,需要重新设计整个渲染器才行。

幸亏,有了Google.

AngelProject是一个来自Google的开源项目,可以将OpenGL ES的接口转换成DirectX 9/11。于是Cocos2d-x使用了AngelProject,终于能支持Windows Phone和Windows RT.

在这里使用的版本是Cocos2d-x 2.2.3.

在加载shader的时候,通过代码查看可知,在CCGLProgram::initWithVertexShaderByteArray函数,如果是Windows Phone/Windows RT平台,会跳转到initWithPrecompiledProgramByteArray函数,进一步到CCPrecompiledShaders::loadProgram加载预编译shader二进制代码.

CCPrecompiledShaders类有一个computeHash函数,用来计算你输入shader代码对应的SHA1码,以此为Key,再到precompiledshaders.h中查找对应的shaer预编译二进制码.

换句话说,如果你不小心修改了ccShaders.cpp中引用的那些shader文本文件,即使你仅仅只是添加了一个空格,或者添加了一个空行等等看起来毫无影响的事情,但是实际上其生产的SHA1码已经改变,于是导致无法在预编译shader中找到对应的二进制代码.

非常愚蠢的设定.

于是接下来是个问题:我有一个shader,是自己写的,需要给Cocos2d-x使用,该如何做呢?

以下步骤:

Read more

C++11:右值引用-移动语义

先来看这样一个例子:

class TestClass
{
public:
	TestClass()
	{
		d = new int[10];
	}
	TestClass(const TestClass &t)
	{
		d = new int[10];
	}
	~TestClass()
	{
		delete d;
		d = nullptr;
	}

private:
	int *d;
};

TestClass get_test_class()
{
	TestClass t;
	return t;
}

void run_test()
{
	TestClass t2 = get_test_class();
}

在这个例子中,首先get_test_class函数中的TestClass t语句进行了一次构造,并且开辟了heap内存,接着在值传回来时,其拷贝构造函数为了实现深拷贝,又开辟了一次heap内存。也就是说,这样为了拿到一个对象,总共有两次heap内存的开辟。并且其中一次是临时变量,其生命期结束后就会被销毁,看起来似乎造成了性能的浪费。

Read more

C++11:POD类型

POD概念在C++中是一个非常重要的概念,是Plain Old Data的缩写.

POD类型是指该类型是一个平凡而普通的类型,由以下两部分概念构成:

平凡的(trivial)

1.拥有平凡的默认构造函数(trivial constructor)和析构函数(trivial destructor).

平凡的意思是什么都不干。通常来说,不定义类的构造函数,编译器会生成一个默认构造函数。而一旦我们定义了构造函数,即使这个构造函数无参数,没任何代码,那么也不再是平凡的(也可以通过C++11的新特性=default让其恢复平凡).

2.拥有平凡的拷贝构造函数(trivial copy constructor)和移动构造函数(trivial move constructor).

3.拥有平凡的拷贝赋值运算符(trivial assignment operator)和移动赋值运算符(trivial move operator).

4.不能包含虚函数以及虚基类.

标准布局

1.所有非静态成员有相同的访问权限.

Read more

C++11:变长模板

C++11中添加了变长模板的支持,模板参数已经可以像printf函数那样接受不受限个参数个数,我们先来看个简单的样例:

void func()
{

}

template<class A,class...B>
void func(A argHead, B...argTail)
{
	func(argTail...);
}

int test_func()
{
	func(1, 2, 3, 4, 6);
	return 0;
}

在func有两个版本,第一个为无参数传递的特化版本,另外一个就是主体变长模板参数版本.
在func的class…B表示B这个参数是可变长的,称之为:模板参数包(template parameter pack)。

Read more

C++:一些约束

利用模板可以制作一些在编译期就能侦察到的违反你理想中的规定的情况,所谓约束.

第一个约束用来要求class D一定继承与B:

template<typename D,typename B>
struct must_have_base
{
	~mush_have_base()
	{
		void(*p)(D*, B*) = constraint;
	}

private:
	static void constraint(D* d, B* b)
	{
		b = d;
	}
};

constraint是一个静态函数,并且在must_have_base中声明了一个其函数指针,于是迫使编译器在编译期就要做出评估操作.

另外一个约束要求一个类型可以按照下标形式访问:

template <typename T>
struct must_be_subscriptable
{
	~must_be_subscriptable()
	{
		void(*p)(T) = constraint;
	}

private:
	static void constraint(T const &T_is_not_subscriptable)
	{
		sizeof(T_is_not_subscriptable[0]);
	}
};

还有一个约束可看这里:must_be_subscriptable_as_decayable_pointer

Read more

return top