マジックブラシプログラムの動作:
プログラムページ: http : //www.tarasov-mobile.com/magicbrush.html
計画されたもの、またはゲームのエンジン
すべては2010年2月に始まりました。 それから、最初のスマートフォンであるNokia 5800 Express Musicを購入しました。 新しいデバイスで約1か月遊んだ後、子供の頃の夢を思い出し、タッチフォン用のゲームを書くことにしました。 そして、私が望んでいたのは:
- 新しい経験を積む;
- 開発からファンを獲得します。
- 多分お金を稼ぐ。 :)
物理的な2Dパズルというジャンルを選びましたが、自分のおもちゃが既存のSymbianゲームの背景と好意的に比較できるようにしたかったのです。 Ovi Storeのコンテンツを調べた後、ゲーム内のオブジェクトのベクターグラフィックス、アンチエイリアシング、サブピクセルポジショニングを使用してゲームを作成することにしました。 そして、決定が下されるとすぐに、実装を始めました。 最初のステップは、エンジンを作成することでした。
Symbian SDKの知識は、最初は衝撃を受けました。 第一に、Symbian C ++は非常に奇妙で珍しいように思えました。 次に、Symbian ^ 1(5800、N97など)のスマートフォンは、グラフィックアクセラレータが搭載されていないかなり弱いハードウェアプラットフォーム上に構築されました。 SDKにはOpenGLライブラリがありましたが、プログラムで実装されていました。 第三に、スマートフォンのCPUの浮動小数点演算のサポートにもかかわらず、SDKコンパイラはハードウェアで浮動小数点数を使用して演算を実行するコードを生成できませんでした。
最初の問題はかなり迅速に解決されました。 幸いなことに、Symbian C ++だけでなく、標準C ++でもコードを書くことができます。 また、Symbian APIを使用した私のゲームは実際には機能しなかったため、Symbian C ++で作成されたのは初期化、センサーとの相互作用、タイマーのみでした。 それ以外はすべて、STLを使用してクラシックC ++で正常に記述されました。
Symbian C ++の魅力について少し整理して、画面にグラフィックを表示する問題に取り組み始めました。 私はOpenGLの経験がなく、Symbian ^ 1ではOpenGLはパフォーマンスを向上させませんでした(アクセラレーターがないため)。 しかし、Symbianで非常に優れた機能であるダイレクトスクリーンアクセス(DSA)が発見されました。 DSAを使用すると、デバイスのビデオメモリに直接アクセスできました。
さて、ビデオバッファへのアクセスが取得されました。これで、可能な限り最高の速度で、あなたの心が望むすべてをプログラムでレンダリングできます。 非常に迅速に、スプライトバッファメソッドが記述され、 Anti Grain Geometry(AGG)ライブラリが平滑化されたベクターグラフィックスオブジェクトに接続されました。 むしろ、その非常に軽いバージョン:AGG Lite。 AGGを使用して、簡単なグラフィックプリミティブ(線、円など)を表示するためのメソッドがすばやく作成されました。 すべてが十分にスマートに機能し、絵は目を楽しませていました。
コンテナクラスは、多くのカラフルな図形で構成される複雑なベクトルオブジェクトに関する情報を格納するために使用されました。 また、これらのオブジェクトを特定の座標、特定の角度、特定のスケールで画面に表示するためのメソッドも実装しました。 その後、 Box2Dはプロジェクトに接続され、画面上の画像は物理法則に従い始めました。
そして、問題が始まりました。 画面上の画像はきれいに見え、すべての線は滑らかで、出力のサブピクセル精度のため、オブジェクトのすべての動きは非常に滑らかに見えました...画面上に動くオブジェクトはほとんどありませんでした。 10〜12個のゲームベクトルオブジェクトが画面に追加された場合、fpsは壊滅的に低下しました。 状況を救うために何かをしなければなりませんでした。
まず、すべての浮動小数点演算が固定小数点演算に書き換えられました。 Box2DのFixedクラスがこれに役立ち、その後、三角関数、平方根と乱数を計算するための最適化されたメソッドを計算するために、事前に計算されたテーブルが追加されました。 プロジェクトに残っている浮動小数点数の用途はありません。 これによりパフォーマンスは向上しましたが、それでも十分ではありませんでした。 AGGで最適化できる場所をいくつか見つけることができました。 たとえば、色の混合を無効にし、オブジェクトの不透明な部分を表示する単純なコピーを有効にします。 これにより、fpsがさらに2つ追加されましたが、fpsを15以上に上げることができませんでした。 15 fpsでプレイすることは可能ですが、...ダイナミクスが少なく静的オブジェクトが多くなる別のゲームプレイについて考えなければなりませんでした。
新しいゲームプレイについての考えと並行して、エンジンの開発を続けました。AngelCodeコンバーターからのビットマップフォント、自家製のローカリゼーションサポート、ウィンドウシステム、および小さなウィジェットのセットのサポートを追加しました。
何が起こった、またはマジックブラシ
そして、Habréに関するこの記事に出会いました: Dmitry Ryzhkovと彼のMy Little Artistは、Intel Atomの最高のアプリケーションのコンテストで堅実な賞を受賞しました 。 職場でマイリトルアーティストを見て、mr.doobのハーモニーを見て、既存のエンジンに似たようなものを非常にすばやく実装できることに気付きました。 わずか15分で、最初のブラシが追加され、テストされました。 C ++のハーモニーブラシコードを分析し、いくつかの最適化を加えて書き直しました。 9つの新しいブラシも追加され、アプリケーションのUIが作成され、各ブラシには追加の設定(色、線の太さ、透明度など)がありました。
このビデオでは、さまざまなブラシの動作を見ることができます。
さて、例として、Smokeブラシの実装方法を説明します。
私のプログラムでは、SmokeBrushを含むすべてのブラシクラスが、画面に表示される基本クラスの3つの純粋に仮想的なメソッドを実装しています。
virtual void StrokeStart(Fixed x, Fixed y) = 0; virtual void Stroke(Fixed x, Fixed y) = 0; virtual void StrokeEnd(Fixed x, Fixed y) = 0;
それらは、画面に触れたとき、指を動かしたとき、および画面の表面から引き裂かれたときにそれぞれ呼び出されます。 さらに、基本クラスには、色、透明度、線の太さを管理する属性が含まれています。
agg::rgba8 color; Fixed opacity; Fixed width;
出力用の現在のバッファへのポインタ:
BufferGraphics* graphics;
Smokeブラシを実装するには、細い線(煙の「ジェット」)から太い線を構成して、太い線を構成する必要があります。 これを行うには、ユーザーの指の現在位置に対する各「ジェット」の現在位置を知る必要があります。 この目的のために、SmokeBrushはポイントベクトルポイントを使用します。
std::vector<Point> points;
また、指から現在の位置まで線を引くために、指の前の位置を覚えておく必要があります。
Point previousFingerPosition;
ブラシのロジックは、StrokeStartおよびStrokeメソッドで説明されています。
void SmokeBrush::StrokeStart(Fixed x, Fixed y) { points.clear(); for (int i = 0; i < (int)(width * Fixed(3)) + 16; ++i) { Fixed r = (fixrand() * Fixed(0.4f) + Fixed(0.1f)) * width; Fixed a = fixrand() * Fixed::PI2; points.push_back(Point(r * cos(a), r * sin(a))); } previousFingerPosition = Point(x, y); }
画面をタッチするときは、前回のブラシの使用後に画面に残っていた値からポイントベクトルをクリアする必要があります。 次に、新しい点群がこのベクトルに書き込まれます。 さらに、ブラシの太さが大きいほど、画面にブラシを表示するためにより多くの「ジェット」が必要になります。 個々の「ジェット」は、画面のタッチポイントからランダムな距離にありますが、ブラシの太さの半分以下です。 また、後で使用するために現在の連絡先を覚えておく必要があります。
前に、プロジェクトでは浮動小数点数の単一の使用はありませんでしたが、テキストにはフロートが見られることを書きました。 幸いなことに、コンパイラはFixedクラスのインラインコンストラクターを適切に処理できるほどスマートです。 その結果、実行段階では、作業は実際には整数値でのみ発生します。
Strokeメソッドは次を表示します。
void SmokeBrush::Stroke(Fixed x, Fixed y) { for (vector<Point>::iterator i = points.begin(); i != points.end(); ++i) { Fixed newX = i->x + (fixrand() - Fixed(0.5f)) * width * Fixed(0.25f); Fixed newY = i->y + (fixrand() - Fixed(0.5f)) * width * Fixed(0.25f); Fixed d = newX * newX + newY * newY; if (d > width * width * Fixed(0.25f)) { newX = i->first; newY = i->second; } graphics->DrawLine(previousFingerPosition.x + i->x, previousFingerPosition.y + i->y, x + newX, y + newY, width * Fixed(0.0625f), color); i->x = newX; i->y = newY; } previousFingerPosition = Point(x, y); }
ループはクラウド内のすべてのポイントを横断します。 各ポイントについて、現在の位置を基準にして小さなランダムオフセットが計算されます。 ポイントが画面のタッチポイントから離れすぎている場合(ブラシの厚さの半分以上)、古い場所に残ります。 次に、別の「ストリーム」が画面に描画され、別の「ストリーム」の太さはブラシ全体の太さに依存します。 ループの終わりに、オフセットポイントはポイントに書き戻されます。 これにより、太いブラシの構成で波状の細い線を得ることができます。
このような簡単な方法で、ブラシを作成できます。ブラシは、熟練した手で描画に役立つツールになります。
製品発売
そのため、Habréに関する記事を読んでから約1か月後、Magic Brushは完全に準備が整いました。 プログラムに取り組む過程でさえ、私はそれをどのように販売するかを考え、マーケティングには非常に長い時間がかかることを認識しました。 開発と並行して、Store'ahsでのアプリケーションのプロモーションに関するさまざまな資料を研究し、グーグルの過程でDmitry Tarasovの「ビジネスとしてのモバイルソフトウェア」という記事に出会いました。 これが私たちの協力の始まりであり、マジックブラシの開発における新たな、それほど面白くない段階でした。マーケティングマジックです。 しかし、それについては、ストーリーの次の部分で詳しく説明します。
そして最後に-MagicBrushで作成された図面へのリンク: http ://vk.com/album-19391365_127709331