Cocos2dx 学习过程
1. 学习 Cocos2dx
2. 学习 JNI 与 NDK 开发
JNI 与 NDK 开发这个必须要掌握的知识点。网上没看到比较全面的介绍。1. 学习JNI 与 NDK 开发推荐大家看Android框架揭秘第4章-JNI与NDK,讲解的简单易懂,也比较全。2. 了解 jni.c 代码
3. 解决技术难点
将图片从 Android 传递到 Cocos
//解决思路:android 端将图片生成 byte[], 通过 jni 接口传到C生成 Texture2D*
signed char* data = (signed char*)info.env->GetByteArrayElements(bitmapinfo_byte, 0);
Texture2D* textureView = new Texture2D();
Image* image = new Image();
bool bRet = image->initWithImageData((unsigned char*)data, bitmapinfo_length);
if (textureView && textureView->initWithImage(image))
{
image->release();
}
info.env->ReleaseByteArrayElements(bitmapinfo_byte, data, 0);
将对象从 Android 传递到 Cocos (由 C 调用 JAVA 返回对象)
JniMethodInfo info;
const char* clazzName = "org.cocos2dx.cpp.AppActivity";
const char* clazzSingle = "getInstance";
const char* clazzSingleSignature = "()Ljava/lang/Object;";
const char* ExxName = "getViewBitmap";
const char* ExxSignature = "()Lcom/cocos2dx/JniClass/BitmapInfo;";
const char* BitmapInfo_getBitByte = "getBitByte";
const char* getBitByteSignature = "()[B";
const char* BitmapInfo_getLength = "getLength";
const char* getLengthSignature = "()I";
if (JniHelper::getStaticMethodInfo(info, clazzName, clazzSingle, clazzSingleSignature))
{
jclass header_class = info.env->FindClass(clazzName);
jmethodID instance_id = info.env->GetStaticMethodID(header_class, clazzSingle, clazzSingleSignature);
jobject instance_object = (jobject)info.env->CallStaticObjectMethod(header_class, instance_id);
jmethodID Exx_id = info.env->GetMethodID(header_class, ExxName, ExxSignature);
jobject bitmapinfo_object = (jobject)info.env->CallObjectMethod(instance_object, Exx_id);
jclass bitmapinfo_class = info.env->GetObjectClass(bitmapinfo_object);
jmethodID bitByte_id = info.env->GetMethodID(bitmapinfo_class, BitmapInfo_getBitByte, getBitByteSignature);
jbyteArray bitmapinfo_byte = (jbyteArray)info.env->CallObjectMethod(bitmapinfo_object, bitByte_id);
jmethodID length_id = info.env->GetMethodID(bitmapinfo_class, BitmapInfo_getLength, getLengthSignature);
jint bitmapinfo_length = (jint)info.env->CallIntMethod(bitmapinfo_object, length_id);
}
JAVA 编写的代码
public static AppActivity appInstance;// 定义单例
public static Object getInstance() {
return appInstance;
}
public BitmapInfo getBitmap() {
BitmapInfo info = new BitmapInfo();
return info;
}
public class BitmapInfo{
private byte[] bitByte;
private int length;
public byte[] getBitByte() {
return bitByte;
}
public void setBitByte(byte[] bitByte) {
this.bitByte = bitByte;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}
将对象从 Android 传递到 Cocos (由 JAVA 将对象传给 C )
JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AppActivity_strokeViewonPenUp(JNIEnv *env, jobject clazz, jobject object)
{
const char* fuctionA = "A";
const char* fuctionASignature = "()Z";
const char* fuctionB = "B";
const char* fuctionBSignature = "()I";
const char* fuctionC = "C";
const char* fuctionCSignature = "()I";
JudgementResult* result = new JudgementResult();
jclass result_class = env->GetObjectClass(object);
jmethodID A_id = env->GetMethodID(result_class, fuctionA, fuctionASignature);
result->setA((bool)env->CallBooleanMethod(object, A_id));
jmethodID B_id = env->GetMethodID(result_class, fuctionB, fuctionBSignature);
result->setB((int)env->CallIntMethod(object, B_id));
jmethodID C_id = env->GetMethodID(result_class, fuctionC, fuctionCSignature);
result->setC((int)env->CallIntMethod(object, C_id));
result->retain();
NotificationCenter::getInstance()->postNotification(msg_HYH_Penup, result);
}
JAVA 编写的代码
public native void strokeViewonPenUp(Object result);
将数组对象从 Cocos 传递到 Android (由 C 将数组对象传给 JAVA )
void CLASS::initView(__Array* mDataArray)
{
if (mDataArray == nullptr)
{
return;
}
int size = mDataArray->count();
JniMethodInfo info;
const char* clazzName = "org.cocos2dx.cpp.AppActivity";
const char* clazzSingle = "getInstance";
const char* clazzSingleSignature = "()Ljava/lang/Object;";
const char* fuctionName = "initView";
const char* fuctionSignature = "([Lcom/cocos2dx/ViewData;)V";
const char* ViewDataClazz = "com/cocos2dx/ViewData";
const char* ViewDataConstructor = "<init>";
const char* ViewDataConstructorSignature = "()V";
const char* SetA = "setA";
const char* setASignature = "(Ljava/lang/String;)V";
const char* setB = "setB";
const char* setBSignature = "(I)V";
if (JniHelper::getStaticMethodInfo(info, clazzName, clazzSingle, clazzSingleSignature))
{
jclass header_class = info.env->FindClass(clazzName);
jmethodID instance_id = info.env->GetStaticMethodID(header_class, clazzSingle, clazzSingleSignature);
jobject instance_object = (jobject)info.env->CallStaticObjectMethod(header_class, instance_id);
jmethodID fuction_id = info.env->GetMethodID(header_class, fuctionName, fuctionSignature);
jobjectArray viewDataArray;
jclass clsViewData = info.env->FindClass(ViewDataClazz);
if (clsViewData == NULL)
{
cocos2d::log("null");
return;
}
viewDataArray = info.env->NewObjectArray(size, clsViewData, NULL);
if (viewDataArray == NULL) {
cocos2d::log("null");
return;
}
for (int i = 0; i < size; ++i) {
ViewData *data = (ViewData *)mDataArray->objectAtIndex(i);
jmethodID viewDataFuction_id = info.env->GetMethodID(clsViewData, ViewDataConstructor, ViewDataConstructorSignature);
jobject viewDataObject = info.env->NewObject(clsViewData, viewDataFuction_id);//对象无参构造函数
viewDataFuction_id = info.env->GetMethodID(clsViewData, SetA, setASignature);
jstring jword = info.env->NewStringUTF(data->getA());
info.env->CallVoidMethod(viewDataObject, viewDataFuction_id, jword);
info.env->DeleteLocalRef(jword);
viewDataFuction_id = info.env->GetMethodID(clsViewData, setB, setBSignature);
info.env->CallVoidMethod(viewDataObject, viewDataFuction_id, data->getB());
info.env->SetObjectArrayElement(viewDataArray, i, viewDataObject);
info.env->DeleteLocalRef(viewDataObject);
}
//
info.env->CallVoidMethod(instance_object, fuction_id, viewDataArray);
}
return;
}
读取SD卡中图片
有时图片资源太大,如果直接放在apk中,会使安装包很大。采用的策略:将资源单独下载,资源缓存到SD卡中,然后读取SD卡中的资源
Texture2D* getTexture(const char* filePath)
{
char * TextureBuffer;
long length;
ifstream in(filePath, ios::in | ios::binary | ios::ate);
length = in.tellg();
log("length.2=%ld", length);
in.seekg(0, ios::beg);
TextureBuffer = new char[length + 1];
in.read(TextureBuffer, length);
in.close();
TextureBuffer[length] = '\0';//防止内存问题
Image *image = new Image();
if (false == image->initWithImageData((unsigned char*)TextureBuffer, length))
{
log("cocos2dx can not load PNG");
}
delete[] TextureBuffer;
Texture2D * texture2D = new Texture2D();
texture2D->initWithImage(image);
image->release();
return texture2D;
}
Cocos2dx 由 JAVA 与 C 层传递数据的几种解决方案
- 在 C 端采用全局变量,将数据存到全局变量中,只能主动读取全局变量(缺点:不知道变量何时更新)
- 不管是 C 到 Java,还是 Java 到 C,就是把数据组装成json然后转成字符串传递(缺点:效率比较低)
- 在 C 端使用 NotificationCenter 消息机制,将数据打包分发(优点:异步回调)
使用 Android View 控件
之前做过一个项目,需要用到 Android 中的自定义的 View 控件,如何解决呢,这就需要了解 Android 框架。参考Android源码设计模式解析与实战第15章的深度拓展。
查看 Cocos2dxActivity.java 中
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CocosPlayClient.init(this, false);
onLoadNativeLibraries();
sContext = this;
this.mHandler = new Cocos2dxHandler(this);
Cocos2dxHelper.init(this);
this.mGLContextAttrs = getGLContextAttrs();
this.init();
if (mVideoHelper == null) {
mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);
}
if(mWebViewHelper == null){
mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
}
}
查看 init();
protected FrameLayout mFrameLayout = null;
// ===========================================================
// Methods
// ===========================================================
public void init() {
// FrameLayout
ViewGroup.LayoutParams framelayout_params =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mFrameLayout = new FrameLayout(this);
mFrameLayout.setLayoutParams(framelayout_params);
// Cocos2dxEditText layout
ViewGroup.LayoutParams edittext_layout_params =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
Cocos2dxEditText edittext = new Cocos2dxEditText(this);
edittext.setLayoutParams(edittext_layout_params);
// ...add to FrameLayout
mFrameLayout.addView(edittext);
// Cocos2dxGLSurfaceView
this.mGLSurfaceView = this.onCreateView();
// ...add to FrameLayout
mFrameLayout.addView(this.mGLSurfaceView);
// Switch to supported OpenGL (ARGB888) mode on emulator
if (isAndroidEmulator())
this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
this.mGLSurfaceView.setCocos2dxEditText(edittext);
// Set framelayout as the content view
setContentView(mFrameLayout);
}
setContentView(mFrameLayout); 这个就是需要找到的。
所以对于 Android 自定义 View 添加到 mFrameLayout 即可。
4. 现在使用比较多的引擎
- Cocos Creator(多平台)
- 白鹭HTML引擎(多平台)
- Unity3d(3D)