用户交互这里指的就是用户在手机上的点击,滑动以及晃动手机等行为,从而得到相应的反馈。今天学习Cocos2dx,遇到交互问题,所以就写出来和大家分享一下。我这里是以Android连接为例的,因为目前我只会Android相关的开发。好了,不多说,看下面步骤:
第一步:在Android中,交互操作的入口在SurfaceView或是GLSurfaceView中的onTouchEvent时间中。本例代码所在位置org.cocos2dx.lib---->Cocos2dxGLSurfaceView.java
- public boolean onTouchEvent(final MotionEvent pMotionEvent) {
-
- final int pointerNumber = pMotionEvent.getPointerCount();
- final int[] ids = new int[pointerNumber];
- final float[] xs = new float[pointerNumber];
- final float[] ys = new float[pointerNumber];
-
- for (int i = 0; i < pointerNumber; i++) {
- ids[i] = pMotionEvent.getPointerId(i);
- xs[i] = pMotionEvent.getX(i);
- ys[i] = pMotionEvent.getY(i);
- }
-
- switch (pMotionEvent.getAction() & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_POINTER_DOWN:
- final int indexPointerDown = pMotionEvent.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- final int idPointerDown = pMotionEvent.getPointerId(indexPointerDown);
- final float xPointerDown = pMotionEvent.getX(indexPointerDown);
- final float yPointerDown = pMotionEvent.getY(indexPointerDown);
-
- this.queueEvent(new Runnable() {
- @Override
- public void run() {
- Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown(idPointerDown, xPointerDown, yPointerDown);
- }
- });
- break;
-
- case MotionEvent.ACTION_DOWN:
-
- final int idDown = pMotionEvent.getPointerId(0);
- final float xDown = xs[0];
- final float yDown = ys[0];
-
- this.queueEvent(new Runnable() {
- @Override
- public void run() {
- Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown(idDown, xDown, yDown);
- }
- });
- break;
-
- case MotionEvent.ACTION_MOVE:
- this.queueEvent(new Runnable() {
- @Override
- public void run() {
- Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionMove(ids, xs, ys);
- }
- });
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- final int indexPointUp = pMotionEvent.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- final int idPointerUp = pMotionEvent.getPointerId(indexPointUp);
- final float xPointerUp = pMotionEvent.getX(indexPointUp);
- final float yPointerUp = pMotionEvent.getY(indexPointUp);
-
- this.queueEvent(new Runnable() {
- @Override
- public void run() {
- Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionUp(idPointerUp, xPointerUp, yPointerUp);
- }
- });
- break;
-
- case MotionEvent.ACTION_UP:
-
- final int idUp = pMotionEvent.getPointerId(0);
- final float xUp = xs[0];
- final float yUp = ys[0];
-
- this.queueEvent(new Runnable() {
- @Override
- public void run() {
- Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionUp(idUp, xUp, yUp);
- }
- });
- break;
-
- case MotionEvent.ACTION_CANCEL:
- this.queueEvent(new Runnable() {
- @Override
- public void run() {
- Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionCancel(ids, xs, ys);
- }
- });
- break;
- }
-
- return true;
- }
第二步: Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown(idDown, xDown, yDown)等相关语句的方法在org.cocos2dx.lib------>Cocos2dxRender.java,代码如下:
- private static native void nativeTouchesBegin(final int id, final float x, final float y);
- private static native void nativeTouchesEnd(final int id, final float x, final float y);
- private static native void nativeTouchesMove(final int[] ids, final float[] xs, final float[] ys);
- private static native void nativeTouchesCancel(final int[] ids, final float[] xs, final float[] ys);
-
- public void handleActionDown(final int id, final float x, final float y) {
- Cocos2dxRenderer.nativeTouchesBegin(id, x, y);
- }
-
- public void handleActionUp(final int id, final float x, final float y) {
- Cocos2dxRenderer.nativeTouchesEnd(id, x, y);
- }
-
- public void handleActionCancel(final int[] ids, final float[] xs, final float[] ys) {
- Cocos2dxRenderer.nativeTouchesCancel(ids, xs, ys);
- }
-
- public void handleActionMove(final int[] ids, final float[] xs, final float[] ys) {
- Cocos2dxRenderer.nativeTouchesMove(ids, xs, ys);
- }
第三步: private static native void nativeTouchesBegin(final int id, final float x, final float y)这是本地方法,调用c++内容的语句。而这些内容被打包在Android工程中的libs/armeabi/cocos2dcpp.so文件中。所以我们在使用这些方法时一定要加载这个.so文件。代码位置在org.cocos2dx.lib--->Cocos2dxActivity.java,内容如下:
- protected void onLoadNativeLibraries() {
- try {
- ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
- Bundle bundle = ai.metaData;
- String libName = bundle.getString("android.app.lib_name");
- System.loadLibrary(libName);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
bundle.getString("android.app.lib_name")文件是找出.so文件。和它相关内容在Android工程中AndroidManifest.xml里,内容如下:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.cocos.CocosProject4"
- android:versionCode="1"
- android:versionName="1.0"
- android:installLocation="auto">
-
- <uses-sdk android:minSdkVersion="9"/>
- <uses-feature android:glEsVersion="0x00020000" />
-
- <application android:label="@string/app_name"
- android:icon="@drawable/icon">
-
-
- <meta-data android:name="android.app.lib_name"
- android:value="cocos2dcpp" />
-
- <activity android:name="org.cocos2dx.cpp.AppActivity"
- android:label="@string/app_name"
- android:screenOrientation="landscape"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:configChanges="orientation">
-
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- <supports-screens android:anyDensity="true"
- android:smallScreens="true"
- android:normalScreens="true"
- android:largeScreens="true"
- android:xlargeScreens="true"/>
-
- <uses-permission android:name="android.permission.INTERNET"/>
- </manifest>
第四步:cocos2dcpp.so文件的生成,因为native 方法都在这里面。它的生成在Android工程中的jni文件里么的Android.mk文件(相关内容参考链接)。native方法所在文件路径cocos2d-x-3.6/cocos/platform/android/jni/TouchesJni.cpp,内容如下:
- #include "base/CCDirector.h"
- #include "base/CCEventKeyboard.h"
- #include "base/CCEventDispatcher.h"
- #include "platform/android/CCGLViewImpl-android.h"
-
- #include <android/log.h>
- #include <jni.h>
-
- using namespace cocos2d;
-
- extern "C" {
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeTouchesBegin(JNIEnv * env, jobject thiz, jint id, jfloat x, jfloat y) {
- intptr_t idlong = id;
- cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesBegin(1, &idlong, &x, &y);
- }
-
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeTouchesEnd(JNIEnv * env, jobject thiz, jint id, jfloat x, jfloat y) {
- intptr_t idlong = id;
- cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesEnd(1, &idlong, &x, &y);
- }
-
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeTouchesMove(JNIEnv * env, jobject thiz, jintArray ids, jfloatArray xs, jfloatArray ys) {
- int size = env->GetArrayLength(ids);
- jint id[size];
- jfloat x[size];
- jfloat y[size];
-
- env->GetIntArrayRegion(ids, 0, size, id);
- env->GetFloatArrayRegion(xs, 0, size, x);
- env->GetFloatArrayRegion(ys, 0, size, y);
-
- intptr_t idlong[size];
- for(int i = 0; i < size; i++)
- idlong[i] = id[i];
-
- cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesMove(size, idlong, x, y);
- }
-
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeTouchesCancel(JNIEnv * env, jobject thiz, jintArray ids, jfloatArray xs, jfloatArray ys) {
- int size = env->GetArrayLength(ids);
- jint id[size];
- jfloat x[size];
- jfloat y[size];
-
- env->GetIntArrayRegion(ids, 0, size, id);
- env->GetFloatArrayRegion(xs, 0, size, x);
- env->GetFloatArrayRegion(ys, 0, size, y);
-
- intptr_t idlong[size];
- for(int i = 0; i < size; i++)
- idlong[i] = id[i];
-
- cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesCancel(size, idlong, x, y);
- }
-
- }
第五步:cocos2d::Director::getInstance()->getOpenGLView()->handleTouchesBegin(1, &idlong, &x, &y)这个方法是如何起作用的呢,文件位置在
cocos2d-x-3.6/cocos/platform/CCGLView.cpp,内如如下:
- void GLView::handleTouchesBegin(int num, intptr_t ids[], float xs[], float ys[])
- {
- intptr_t id = 0;
- float x = 0.0f;
- float y = 0.0f;
- int unusedIndex = 0;
- EventTouch touchEvent;
-
- for (int i = 0; i < num; ++i)
- {
- id = ids[i];
- x = xs[i];
- y = ys[i];
-
- auto iter = g_touchIdReorderMap.find(id);
-
-
- if (iter == g_touchIdReorderMap.end())
- {
- unusedIndex = getUnUsedIndex();
-
-
- if (unusedIndex == -1) {
- CCLOG("The touches is more than MAX_TOUCHES, unusedIndex = %d", unusedIndex);
- continue;
- }
-
- Touch* touch = g_touches[unusedIndex] = new (std::nothrow) Touch();
- touch->setTouchInfo(unusedIndex, (x - _viewPortRect.origin.x) / _scaleX,
- (y - _viewPortRect.origin.y) / _scaleY);
-
- CCLOGINFO("x = %f y = %f", touch->getLocationInView().x, touch->getLocationInView().y);
-
- g_touchIdReorderMap.insert(std::make_pair(id, unusedIndex));
- touchEvent._touches.push_back(touch);
- }
- }
-
- if (touchEvent._touches.size() == 0)
- {
- CCLOG("touchesBegan: size = 0");
- return;
- }
-
- touchEvent._eventCode = EventTouch::EventCode::BEGAN;
- auto dispatcher = Director::getInstance()->getEventDispatcher();
- dispatcher->dispatchEvent(&touchEvent);
- }
第六步:使用。首先要给_eventDispathcer(在CCNote.cpp文件中)添加listener,示例如下:
- auto listener = EventListenerTouchAllAtOnce::create();
- listener->onTouchesBegan = CC_CALLBACK_2(ParticleDemo::onTouchesBegan, this);
- listener->onTouchesMoved = CC_CALLBACK_2(ParticleDemo::onTouchesMoved, this);
- listener->onTouchesEnded = CC_CALLBACK_2(ParticleDemo::onTouchesEnded, this);
- _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
其次就是我们实际操作,使交互产生什么样的变化,代码如下:
- void ParticleDemo::onTouchesBegan(const std::vector<Touch*>& touches, Event *event)
- {
- onTouchesEnded(touches, event);
- }
-
- void ParticleDemo::onTouchesMoved(const std::vector<Touch*>& touches, Event *event)
- {
- return onTouchesEnded(touches, event);
- }
-
- void ParticleDemo::onTouchesEnded(const std::vector<Touch*>& touches, Event *event)
- {
- auto touch = touches[0];
-
- auto location = touch->getLocation();
-
- auto pos = Vec2::ZERO;
- if (_background)
- {
- pos = _background->convertToWorldSpace(Vec2::ZERO);
- }
-
- if (_emitter != nullptr)
- {
- _emitter->setPosition(location -pos);
- }
- }
上面代码是ParticleTest中的内容,通过dispathcer将参数最终传递给要变化的_mitter,就是粒子发射器的移动。