アイデアと実装
インスピレーションはルービックキューブとV.L.Rvachev の R関数でした。 アイデアは何ですか:競技場には、リストにリンクされている要素があります。 関連アイテムのチェーンは、これらのリスト内で移動できます。 実装とゲームプレイの重要な部分は、要素の幾何学的配置です。 ルービックキューブの場合、それらは3次元マトリックスに配置されます。
今注目。 リストノードは、長方形のマトリックスにある必要はありません。 それらを任意に配置したり、異なる長さのチェーンを作成したり、このグラフに異なる条件を課したり、移動したりできます。 そのようなモデルを視覚化しようとするまで、それは単純に聞こえます。 ここで、最初の興味深いタスクに直面します。
ゲームオブジェクトのサイズは明確で明確に定義されているという事実に慣れています。 ただし、オブジェクトの動きの任意の軌道を設定すると、パズル要素の視覚化時に衝突が避けられないため、衝突が互いに重なります。 解決策は、相対的な位置に応じて要素のサイズと形状を計算することです。
一般に、ドロップの模倣を取得することは興味深いでしょう、例えばこれ:
よさそうだ。 R関数ではそのような結果は得られませんが、私はそれらからインスピレーションを得ました。 次に、モデルを複雑にして、全体としてどのように見えるかを確認します。 滴をより「硬く」する必要がありました。そうしないと、画像全体が揺れ動きました。
次のようになりました。
そして再び、それはよさそうだ、さらにはよさそうだ。 開始モデルの確認が終了しました。ここから有用なアーティファクトを抽出する必要があります。 実験の後、彼はこのオプションに落ち着きました。
- メモリ消費を削減するために、グラフィック要素(私はそれらをBLOBと呼ぶようになりました)は、ポリゴンの形式でベクター形式で保存されます。
- 丸みを帯びた形状にするために、ベクターフォントのように、スプラインによる補間でポリゴンがレンダリングされます。
- 単一の競技場のすべての頂点とインデックスは、1つの連続したバッファー内にあり、レンダリングパスの数を減らします。
フレームを記述するためのサービス構造用のメモリはまだ少量です。 その結果、競技場ごとに0.5〜1 MBのサイズのデータ構造が作成されました。 サイズは、ブロブの幾何学的記述の望ましい精度とその動きの滑らかさに依存します。 モデルがあり、データがあり、それらを復活させることが残っています。 いくらかの努力の後、最初のアイデアはそのようなゲームプレイで実現しました:
アイデアから実装までの記事では、いくつかの段落が当てはまります。 実際には、移動距離はより長く、そしてもちろん、それはすでに何度も私に伝えられていました。 そのため、私が行った作業のいくつかのエピソードのみに焦点を当てます。
プラットフォームとSDKの選択
私は選択に非常に近づいた。 これはクロスプラットフォーム(または簡単に移植可能)なものである必要があり、最初の実装は確かにAndroidなどのモバイルプラットフォーム上にあるべきだと判断しました。
私の専門的な活動は、主に.netスタックでのエンタープライズ開発に関連しており、「モバイル」と「ゲーム」という言葉は私にとっては遠いものでした。 上記に基づいて、メイン開発ツールとしてXamarinまたは他の一般的なライブラリのモノベースの実装を選択することは論理的です。 したがって、少し考えてから、c ++で記述し、フレームワークとして使用することにしました。 その結果、プログラムコードの99.9%が書き込まれました。 開発およびデバッグは、ネイティブWindows OSのVisual Studioで実行されました。 モバイルプラットフォームでは、最終アセンブリとマイナーテストのみが実行されました。
主なことはあきらめないことです
Blobのスムーズな流れはゲームプレイの重要な部分です。FPS60と、デバイスの最大数でこのフレームレートを維持するための十分な最適化が必要でした。 実装中に発生したこと:OpenGL ES2 APIが使用され、アプリケーションのグラフィック設定が最小限(無効:アンチエイリアシング、選択されたblobチェーンの強調表示など)で、メイン画面は3回のglDrawElements呼び出しで描画され、最大設定は6から9呼び出しです。 一般に、それは非常に経済的に判明し、まだ最適化に取り組むことができましたが、ここでは失望が嘘をつきました。 どんな状況でも、古いGalaxy Aceから60 FPSを取得することはできませんでした(それで動作すると思っていました-どこでも動作します)。 多くの実験とかなりの時間を費やした後、ついに私に気づきました。 シェーダーとドローコールの数を最適化しようとするたびに、Galaxy Aceの問題の解決策はまったく別の面にありました。 このデバイスをチェックリストから除外しました。その後、パフォーマンスを最適化する作業を続ける必要がなくなりました。 主なものはあきらめることではなく、解決策があります!
インターフェイス上のボタンのペア
もう1つの興味深いタスクは、ユーザーインターフェイスです。 要するに、「ボタンのペア」の実装にそれほど時間がかかるとはまったく思っていませんでした。 非常に大きな驚きでした。 「ボタン」を機能させるために、いくつかのレンダーが実装されました。テキスト、アイコン、スプライト、長方形、画面に対する要素の配置、および相互の追加が追加されました。 より多くのプリミティブからのオブジェクトはより複雑にコンパイルされ、それらに動作が追加されます:テキストラベル、ボタン、ダイアログ、ポップアップなど。もちろん、パフォーマンスが心配で、「ボタン」を描くことができなかったので、すばやく実行する必要がありました。 迅速に行う必要がある場合、選択肢は1つだけです-draw-colaを最小化します。 結果として、同じグループ内のすべてのオブジェクトが1回の呼び出しで描画されるように、UI要素がグループ化されました。 自転車は、UIのマイクロライブラリのようでした。 たとえば、ボタンは次のように作成されます。
std::unique_ptr<IControl> button(new Icon( "render_group_for_all_controls_on_the_screen", IconType::MenuSolid, // SDFStyle::Default, // SDF vec2(0, 0.5f), // size, // rect::up, // rect::down, // parent_button->GetRenderObject(), // [](IControl*)->void {Locator::Resolve<IActivityManager>(true)->OpenActivity("Levels"); } ));
開発者のフィードバック
リリースが近づくと、ジャーナリングについて考えました。 アプリケーションはAndroidに移植され、物理デバイスとエミュレーターのペアでテストされました。 明らかに、良心を込めて「公開」ボタンを押すだけでは不十分です。 重要な問題を修正するには、リリース(または次の更新)後の最初の数日間に役立つ集中エラーログが必要です。 サーバーを上げることは、私が望んでいることではありません。 モバイルアプリケーション向けの既製のソリューションは、少なくとも無料ではありませんでした。 雲に行った。 一般的に、誰もが同じです。 偶然ですが、これはまったく基本的ではありませんが、AzureのBLOBリポジトリを使用しました。 私の予想によると、クラウド内のリモートログを使用したソリューションは安価であるはずです。 最小値はログに書き込まれ、アプリケーションの通常の操作中はログは空になります。 この記事を書いている時点では、チェックする時間がありませんでした。
メインについての結論
行われた作業と行われた決定を振り返ってみると、すでに限られたリソースは無秩序にひどく非効率的に費やされていたと断言できますが、一方で、それらは非常に正確でした。 プロジェクトが機能する場合、ビジネスモデル、計画、仮説とピボットを含む製品プラクティスは非常に優れたツールです。 しかし、突然、通常の何かを超えて何かを「作成」したい場合、メトリックは機能しなくなり、目標は結果ではなくプロセスになります。 そして、その中で最も難しいタスクは、この奇妙で不安定な動機を維持することです。 それだけで、最もアクセスしにくい場所に登ることができます。 これがプロジェクト中の私の主な挑戦でした。 おそらく、この道を歩いたすべての人のようです。
この間ずっと私は製品に取り組んでおり、これはコードやアイデア以上のものであり、その作業は止まることはありませんが、今日、もう1つのチェックマークを付けました:「ロックの準備ができました」。