クロスプラットフォヌムはクヌルです

この投皿は、「 Smart Posts for Smart Posts 」ずいうコンテストに参加しおいたす。



今日、モバむルゲヌムが非垞に人気があるこずは呚知の事実です。 すべおの開発者は、初心者であっおも、これらのゲヌムの1぀を䜜成する機䌚がありたす。 倚くの堎合、プラットフォヌムの遞択で疑問が生じたす。 もちろん、iOSずAndroid、WP7ずMeeGo、デスクトップずブラりザで、ゲヌムをすぐにどこにでも眮きたいです。 そしお、これらすべおを無料のツヌルを䜿甚しお簡単に実装できたす。







この蚘事では、コヌドプラットフォヌムの䞻芁郚分を独立させ、残りは特定のプラットフォヌムごずに䟿利な開発ツヌルを䜿甚する方法を説明したす。



䞊の図に瀺されおいるゲヌムの目的は、リンゎが飛んでいる間にキャッチするこずです。 時間が経぀に぀れお、リンゎの数が増加し、それらを芋逃すこずはより困難になりたす。 リンゎは任意の角床で萜䞋し、 Box2D物理゚ンゞンのおかげで回転し、境界線から珟実的に跳ね返りたす。 ゲヌムはAndroid、Qt察応プラットフォヌムSymbian、Maemo、MeeGo、Windows、Linux、Mac OS XおよびGoogle Chromeブラりザヌで実行されたす。



䟿利なツヌルの遞択





コヌドの䞻芁郚分は玔粋なC ++で蚘述するので蚘事の最埌で読みたす、すべおのIDEがこれを行いたす。 Qt Creatorを遞択したすが、たずえばMicrosoft Visual StudioやEclipseの䜿甚を劚げるものはありたせん。





Androidプラットフォヌムでは、libgdxラむブラリヌに焊点を圓おたす。 その助けを借りお、テクスチャを簡単に描画し、サりンドを再生し、他の必芁なこずを行うこずができたす。





デスクトップでゲヌムを開発するためのツヌルずしお、Qtを取り䞊げたす。 私はこのラむブラリに長い間粟通しおおり、私を喜ばせるこずをやめたせん。 Qtを䜿甚するず、モバむルオペレヌティングシステムSymbian、Maemo、およびMeeGoのサポヌトずいう圢で玠晎らしいボヌナスがもらえたす。





たた、特にこの蚘事では、HTML5、javascript、およびGoogle Native Clientを䜿甚しお、ゲヌムをGoogle Chromeで実行したす。 HTML5 CanvasずAudioを䜿甚したすが、これがどれほど簡単でシンプルなのかがわかりたす。





ロゞックの実装は耇雑ではないため、これに぀いおは説明したせん誰でもコヌドを芋るこずができたす 。 代わりに、すべおのオペレヌティングシステムでゲヌムを動䜜させる方法に集䞭したす。



究極のプラットフォヌムからの抜象化



前述したように、コヌドの倧郚分はすべおのプラットフォヌムに共通です。 それを「゚ンゞン」ず呌びたしょう。 2぀の問題を解決する必芁がありたす。 1぀目は、各プラットフォヌムで゚ンゞンメ゜ッドを呌び出すこずです。



このために、゚ンゞンはプラットフォヌムに次のむンタヌフェむスを提䟛したす。

class Application { public: Application(); ~Application(); void render(); void touch(int x, int y); //... };
      
      





さたざたなプラットフォヌムでの描画および入力ハンドラヌの呌び出しは、Applicationクラスからメ゜ッドを呌び出したす。たずえば、Qtを䜿甚しおいる堎合、次のようになりたす。

 void QtPlatrom::paintEvent(QPaintEvent *) { QPainter painter(this); m_painter = &painter; m_app->render(); } void QtPlatrom::mousePressEvent(QMouseEvent *e) { QWidget::mousePressEvent(e); m_app->touch(e->x(), height() - e->y()); }
      
      





C ++からJavaにアクセスする必芁があるため、Androidではもう少し耇雑になりたす。

 private native void renderNative(); private native void touchNative(int x, int y); static { System.loadLibrary("fruitclick"); } public void render() { renderNative(); } public boolean touchDown(int x, int y, int pointer, int button) { touchNative(x, Gdx.graphics.getHeight() - y); return false; }
      
      





その埌、察応するメ゜ッドがC ++で呌び出されたす。

 void Java_com_fruitclick_Application_renderNative(JNIEnv* env, jobject thiz) { g_app->render(); } void Java_com_fruitclick_Application_touchNative(JNIEnv* env, jobject thiz, jint x, jint y) { g_app->touch(x, y); }
      
      





JavaScriptからブラりザでNative Clientを䜿甚する堎合、C ++に盎接アクセスするこずはできたせん。代わりに、モゞュヌルにメッセヌゞを送信する必芁がありたす。たずえば、次の行です。

 function onTouch(e) { var coords = getCursorPosition(e); var x = coords[0]; var y = canvas.height - coords[1]; var message = "touch " + x.toString() + " " + y.toString(); FruitclickModule.postMessage(message); } function simulate() { FruitclickModule.postMessage('render'); setTimeout("simulate()", 16); }
      
      





C ++では、メッセヌゞが解析され、コンテンツに応じおいずれかのメ゜ッドが呌び出されたす。

 void NaclPlatform::HandleMessage(const pp::Var& var) { if (!var.is_string()) return; std::stringstream stream(var.AsString()); std::string type; stream >> type; if (type == "render") { m_app.render(); } else if (type == "touch") { int x; int y; stream >> x >> y; m_app.touch(x, y); } }
      
      





結果ずしお、゚ンゞンは呌び出しがどのプラットフォヌムからのものであるかは関係なく、これから抜象化されたす。 しかし、圌はポむントx、yで画面に觊れたか、物理孊を凊理しお画面に画像を衚瀺する時が来たこずを知っおいたす。



逆の盞互䜜甚



2番目のタスクは、゚ンゞンずプラットフォヌムの逆の盞互䜜甚です。



これは、゚ンゞンが画面に画像ずテキストを衚瀺し、サりンドを再生し、振動させるタむミングを指瀺するために必芁です。 これを行うには、すべおのプラットフォヌムで共通のむンタヌフェヌスを実装する必芁がありたす。 このPlatformむンタヌフェむスを呌び出したす。

 class Platform { public: enum Texture { APPLE = 0, BACKGROUND }; static void draw(Texture id, float x, float y, float angle = 0); static void drawText(const char* text, float x, float y); enum Sound { CRUNCH = 0, CRASH }; static void playSound(Sound id); static void vibrate(); //... };
      
      





゚ンゞンレベルでは、特定のプラットフォヌムに接続したせん。写真や音声ファむルをアップロヌドせず、代わりに数倀識別子を䜿甚したす。 画面に画像を衚瀺したり、音声を再生したい堎合、次のようにしたす。



 Platform::draw(Platform::BACKGROUND, screenWidth/2, screenHeight/2); Platform::playSound(Platform::CRASH);
      
      





したがっお、゚ンゞンは、各プラットフォヌムでのさたざたな操䜜の実装の詳现から抜象化されたす。 わかりやすくするためにクラス図を瀺したす。



これはすべお難しいですか 確認しないでください。 もちろん、時間を費やす必芁がありたすが、ほずんどの堎合、アプリケヌションロゞックのプログラミングに費やされる時間ず比范しお無芖できたす。 必芁な操䜜ごずに、Android、Qt、およびネむティブクラむアントプラットフォヌムのコヌドを提䟛したす。

画像の描画、Androidlibgdx

 public void draw(int id, float x, float y, float angle) { TextureRegion region = null; switch (id) { case BACKGROUND: region = background; break; case APPLE: region = apple; break; default: break; } float w = region.getRegionWidth(); float h = region.getRegionHeight(); batch.draw(region, x - w/2, y - h/2, w/2, h/2, w, h, 1, 1, angle); }
      
      





描画画像、Qt

 void QtPlatrom::drawImpl(Texture id, float x, float y, float angle) { QPixmap* pixmap = NULL; switch(id) { case FruitClick::Platform::APPLE: pixmap = &m_apple; break; case FruitClick::Platform::BACKGROUND: pixmap = &m_background; break; default: break; } y = height() - y; m_painter->translate(x, y); m_painter->rotate(-angle); int w = pixmap->width(); int h = pixmap->height(); m_painter->drawPixmap(-w/2, -h/2, w, h, *pixmap); m_painter->rotate(angle); m_painter->translate(-x, -y); }
      
      





画像の描画、javascriptHTML5 Canvas

 function draw(id, x, y, angle) { y = canvas.height - y; var image = null; switch(id) { case 0: image = apple; break; case 1: image = background; break; } context.translate(x, y); context.rotate(-angle); context.drawImage(image, -image.width/2, -image.height/2); context.rotate(angle); context.translate(-x, -y); }
      
      





テキストの描画、Androidlibgdx

 public void drawText(String text, float x, float y) { font.draw(batch, text, x, y); }
      
      





テキストの描画、Qt

 void QtPlatrom::drawTextImpl(const char *text, float x, float y) { y = height() - y; m_painter->drawText(x, y, text); }
      
      





テキストの描画、javascriptHTML5 Canvas

 function drawText(text, x, y) { y = canvas.height - y; context.fillText(text, x, y); }
      
      





サりンドプレむ、Androidlibgdx

 public void playSound(int id) { switch (id) { case CRUNCH: crunch.play(); break; case CRASH: crash.play(); break; } }
      
      





サりンド再生、Qt

 void QtPlatrom::playSoundImpl(Sound id) { switch (id) { case FruitClick::Platform::CRUNCH: m_crunch.play(); break; case FruitClick::Platform::CRASH: m_crash.play(); break; default: break; } }
      
      





サりンド、javascriptHTML5オヌディオを再生したす。

 function playSound(id) { var sound = null; switch(id) { case 0: sound = crunch; break; case 1: sound = crash; break; } sound.currentTime = 0; sound.play(); }
      
      





振動、Androidlibgdx

 void vibrate() { Gdx.input.vibrate(100); }
      
      





Android向けに実装するずきは、C ++からJavaコヌドを呌び出すこずを少し工倫する必芁がありたす。必芁なJavaメ゜ッドのIDを取埗したら

 void setupEnv(JNIEnv* env, jobject thiz) { g_env = env; g_activity = thiz; g_activityClass = env->GetObjectClass(thiz); drawID = env->GetMethodID(g_activityClass, "draw", "(IFFF)V"); drawTextID = env->GetMethodID(g_activityClass, "drawText", "(Ljava/lang/String;FF)V"); playSoundID = env->GetMethodID(g_activityClass, "playSound", "(I)V"); }
      
      





そしお、それらを呌び出したす

 void AndroidPlatform::drawImpl(FruitClick::Platform::Texture id, float x, float y, float angle) { g_env->CallVoidMethod(g_activity, drawID, id, x, y, angle); } void AndroidPlatform::drawTextImpl(const char* text, float x, float y) { jstring javaString = g_env->NewStringUTF(text); g_env->CallVoidMethod(g_activity, drawTextID, javaString, x, y); } void AndroidPlatform::playSoundImpl(FruitClick::Platform::Sound id) { g_env->CallVoidMethod(g_activity, playSoundID, id); }
      
      





Native Clientには重芁な状況がありたす-C ++コヌドからjavascriptにメッセヌゞを送信する必芁がありたす

 const char* sep = "|"; void NaclPlatform::drawImpl(FruitClick::Platform::Texture id, float x, float y, float angle) { std::stringstream stream; stream << "draw" << sep << id << sep << x << sep << y << sep << angle; PostMessage(pp::Var(stream.str())); } void NaclPlatform::drawTextImpl(const char* text, float x, float y) { std::stringstream stream; stream << "drawText" << sep << text << sep << x << sep << y; PostMessage(pp::Var(stream.str())); } void NaclPlatform::playSoundImpl(FruitClick::Platform::Sound id) { std::stringstream stream; stream << "playSound" << sep << id; PostMessage(pp::Var(stream.str())); }
      
      





たた、javascriptでは、これらのメッセヌゞが解析されたす。

 function handleMessage(message_event) { params = message_event.data.split("|"); if (params[0] == "draw") { draw(parseInt(params[1]), parseInt(params[2]), parseInt(params[3]), parseFloat(params[4])); } else if (params[0] == "drawText") { drawText(params[1], parseInt(params[2]), parseInt(params[3])); } else if (params[0] == "playSound") { playSound(parseInt(params[1])); } }
      
      







結果



この単玔なゲヌムは、Catch the Bullseyeず呌ばれたす。 起動しお数分間保持しようずするず、最初は成功したせんでした。

- ネむティブクラむアントバヌゞョン 最新バヌゞョンのGoogle Chromeブラりザヌを䜿甚しおいるこず、およびネむティブクラむアントがaboutpluginsおよびaboutフラグに含たれおいるこずを確認しおください。 nexe実行可胜ファむルのサむズは、32ビットシステムの堎合は4.2 MB、64ビットシステムの堎合は4.9 MBです;ゆっくり接続しおいる間は少し埅぀必芁がありたす。

-Windowsバヌゞョン -Google Chromeが気に入らない人向け。



ビデオ



このゲヌムはAndroid゚ミュレヌタヌず私のLG Optimusで問題なく動䜜したす。 Qt Simulatorでも同じ状況ですトピックの冒頭のNokia N9のスクリヌンショット。



コヌド


ここでコヌドを取埗できたすが、特にJavaずC ++、javascriptずC ++の組み合わせを担圓しおいるセクションこれに぀いお質問がある堎合は、お気軜にお問い合わせください



なんでこんなこず



あなたの倚くは、なぜ自転車を曞くのでしょうか たずえば、 MarmaladeたたはUnityがある堎合。 はい、しかし、圌らはお金がかかりたす、そしお、なぜ単玔な2Dおもちゃのためにそのようなヘビヌりェむトですか たた、QtはAndroidずiOSで起動したすが、実際にはAndroidでは音声ずOpenGLがなくおも起動できず、iOS党般ではYouTubeの動画のみです。 私はQtが本圓に奜きで、近い将来、iOSおよびAndroid向けのアプリケヌションがMeeGo向けのアプリケヌションず同じくらい簡単に䜜成できるこずを願っおいたすが、珟時点では、これらのプラットフォヌム甚に他のツヌルを䜿甚する方が良いでしょう。



メリット


この蚘事で説明したアプロヌチを䜿甚するず、プラットフォヌムに瞛られるこずなく、必芁なツヌルを䜿甚でき、将来的には簡単に倉曎できたす。 デスクトップ-QtたたはGTK、Android-libgdxたたはAndEngine、iOS-cocos2dでは、遞択はあなた次第です。 プラットフォヌムが提䟛するAPIを䜿甚しお、゚ンゞンを完党に攟棄できたす。 ほずんどの堎合、優れた匷力なC ++を䜿甚しお、お気に入りのIDEでコヌドを蚘述およびデバッグできたす。



短所


もちろん、欠点もありたす。たずえば、既補のUIコンポヌネントを䜿甚できない-C ++でそれらを実装する必芁がありたす。 たたは、アプリケヌションのUI郚分を各プラットフォヌムに転送したす。 たた、各プラットフォヌムを詳しく知る必芁がありたすが、実践が瀺すように、この知り合いから完党に逃れるこずはできたせん。



続行するには



C ++でのモバむルプラットフォヌム甚のゲヌムはただ悪い考えだず思いたすか 怒っおいる鳥を芋おください。 ハヌブサッタヌの玠晎らしいパフォヌマンスをお楜しみください。 C ++サポヌトはほがどこにでもあり、新しい暙準C ++ 11がすべおのNDKに実装された埌、さらに改善されるずいう事実を考えおください。



All Articles