多角形の腹を持つ口ひげを生やしたシューティングゲーム。 パート2







プロジェクトの開発に関するストーリーはWebに似ています。関連のストリングはどこにでもあり、興味深いアイデアに関するストーリーです。 そして、時々、物語の糸は珍しいバグの周りにに包まれます。 そのため、今では大量の資料が蓄積されているため、最初の記事を公開する前に、記事の2番目の部分の作業を開始する必要があります。







そして今、2番目のパートが公開されたとき、3番目のパートには素材で十分です! :)

今日のプログラム:ビジュアルアーキテクチャとプロジェクトアーキテクチャの混合。 しかし、最初に、影についてさらにいくつかの詳細を説明します。

さあ、行こう!







記事





目次





レベル3.4 シャドウマネージャー。



ご存知のように、影はCPUですでに生成されており、多くの最適化が行われています。 しかし、それらのレンダリングを完成させる必要があります。 生成を扱っている間、最も簡単なレンダリング方法が必要だったため、次のように機能します。







  1. シャドウを投影する各オブジェクトには、異なるシェーダーでシャドウをレンダリングする2つの子孫があります(1つは背面のみ、もう1つは前面)。
  2. ステージ上には巨大なスプライトがあり、これは最後のスプライトによって描画され、ステンシルがゼロ以外の値を持っている場合は希望の色に着色されます。


これがどんな問題を引き起こすと思いますか?
  1. 最も単純なのは、オブジェクトの複製(各要素に2つの同一の子孫)です。 2パスシェーダーを作成してそれらを取り除くことはオプションではありません。 マルチパスシェーダーを持つオブジェクトは回転できません。
  2. さらに、影付きの光源を1つだけ作成する機能。
  3. 光を扱うための非常に貪欲な機会(実際、光はなく、影だけがあるため)。 したがって、色付きの影を作成できますが、色付きの照明-いいえ。
  4. ステンシルバッファは完全に使用されており、他の効果には使用できません。


アイデアは簡単です。シャドウレンダリングを別のパッセージでレンダリングし、任意の数の光源にシャドウを投影する機能を追加します(はい、fpsはたるみます)。







合計で、いくつかのクラスが必要でした:









コードが記述され、シェーダーがチェックされ、先に進むことができます。 そして、問題が始まりました。

影のある浅瀬

キャッチシャドウ。







上の画像には2つの問題があります。







第一に、影が長すぎて、オブジェクトが誤って重なることがあります。 これは、空のzバッファーの上に影がレンダリングされた場合に発生する可能性があります(他のオブジェクトは影とオーバーラップできますが、影自体はzバッファーに何も書き込みません)。







第二に、いくつかの奇妙なノイズの影。 これは、クリーニングされていないバッファを使用している場合に発生します。







そのため、問題は、作業中のzバッファーがカメラで使用されていないように見えることです。 フレームレンダリングは次のようになります。







  1. RenderTextureでのシーンのレンダリング。
  2. シャドウレンダリング、深度バッファは項目1から取得され、カラーバッファは独自のものです(詳細については後述)。
  3. 影とレンダリングされたシーンの構成。
  4. ポストエフェクト。


バッファの個別の使用について。

ポストエフェクトを使用する場合、多くの場合、シェーダーを使用してテクスチャを変換する必要があります。 Unity3Dには、このためのGraphics.Blitメソッドがあります。 ソーステクスチャをその中に渡し、ターゲットを指定します-描画する場所、マテリアル、さらにはシェーダーパスです。

実際、少なくとも3つの異なるバッファーを使用します。







  1. ピクセルの色を読み取る元のカラーバッファー。
  2. 色を書き込むターゲットカラーバッファー。
  3. 深さ+ステンシルバッファー。書き込み先(および深さおよびステンシルデータの読み取り元)。


また、 Graphics.Blitメソッドでは、ターゲットカラーバッファーと深度バッファーは分離できません。 つまり、たとえば、ソーステクスチャからシーンジオメトリの深さを読み取り、ターゲットにピクセルを書き込む必要がある場合-残念です。







または、シーンレンダリングをテクスチャに行ったときに、一部のシェーダーがデータをステンシルに書き込み、このデータを使用して新しいテクスチャを取得したい(そして元のテクスチャを保存します!)-残念です。







抜け道があり、Unity3Dのドキュメントにはこれが明示的に記載されています。







ソース(レンダー)テクスチャの一部である深度またはステンシルバッファーを使用する場合は、Blit機能と同等の操作を手動で行う必要があります-つまり、宛先カラーバッファーとソース深度バッファーでGraphics.SetRenderTargetを実行し、正投影を設定します(GL.LoadOrtho)、マテリアルパス(Material.SetPass)をセットアップし、クワッドを描画します(GL.Begin)。

一般に、スプリット転送バッファーを許可するBlitの修正バージョン:







static void Blit(RenderBuffer colorBuffer, RenderBuffer depthBuffer, Material material) { Graphics.SetRenderTarget(colorBuffer, depthBuffer); GL.PushMatrix(); GL.LoadOrtho(); for (int i = 0, passCount = material.passCount; i < passCount; ++i) { material.SetPass(i); GL.Begin(GL.QUADS); GL.TexCoord(new Vector3(0, 0, 0)); GL.Vertex3(0, 0, 0); GL.TexCoord(new Vector3(0, 1, 0)); GL.Vertex3(0, 1, 0); GL.TexCoord(new Vector3(1, 1, 0)); GL.Vertex3(1, 1, 0); GL.TexCoord(new Vector3(1, 0, 0)); GL.Vertex3(1, 0, 0); GL.End(); } GL.PopMatrix(); Graphics.SetRenderTarget(null); }
      
      





コードで使用:







 void RenderShadowEffect(RenderTexture source, RenderTexture target, LightSource light) { shadowEffect.SetColor("_ShadowColor", light.ShadowColor); shadowEffect.SetColor("_LightColor", light.LightColor); shadowEffect.SetTexture("_WorldTexture", source); shadowEffect.SetTexture("_ShadowedTexture", target); Blit(target.colorBuffer, source.depthBuffer, shadowEffect); }
      
      





それで問題は何ですか? 出力でカメラをレンダリングするRenderTextureが完全に空になっているのはなぜですか?







シャドウをオフにして、フレームデバッグが示す内容を確認します。











奇妙なレンダリングテクスチャ。







好奇心が強い。 明らかに、アンチエイリアシングのポストエフェクトは、カメラをそのテクスチャに強制的にレンダリングします。 同時に、このテクスチャにアクセスできません。Camera.activeTextureでのデバッグが空の場合。

ああ、アンチエイリアス! 描画シーケンスに登る? 次に、コードに入ります!







ポストエフェクトはMonoBehaviour.OnRenderImageメソッドを介して機能し 、IはMonoBehaviour.OnRenderImageを介して機能し

MonoBehaviour.OnPostRender 。 私は汚いハックをしています:OnRenderImageの名前をApplyに変更し、影をレンダリングした後、renderTextureで手で呼び出します。 現在、アンチエイリアスは影を妨げません。







新しい影を使用すると、色収差や滑らかな影など、あまり必要ではない面白いものを作成できます。



わずかな偏りのある普通の淡い影。



3色の影。







携帯電話では影が遅くなります(約10〜15 fpsを消費します)。 すべてが悲しい場合は、最後にすべてをシングルパスレンダリングに移行しますが、現時点では光源に頼りません。







ヒント:グラフィックのデバッグを即興で行います! 頂点シェーダーのデバッグは手間がかかるため、できる限りすべてのデータを視覚化します。法線に沿って頂点を引き出したり、色や透明度を追加したりします。



シェーダーとギズモによる視覚化のデバッグ。

設計の決定が失敗したために、新しいクラスを追加することが難しくなったことがわかりました。

Todo:きれいなアーキテクチャとプロジェクトコード







レベル4.1 アーキテクチャのリファクタリング。



覚えているように、私はプロトタイプを使用してプロジェクトを開発しています。 しかし、すべてのプロトタイプアーキテクチャをプルしたくはありません(2時間で書かれたプロトタイプのどのアーキテクチャを知っていますか?)。そのため、リファクタリングが必要です。







だから:







始めるために、 MonoBehaviourからScriptableObjectにできるだけ多くのデータを取り出します。 これらは、すべての種類のスタイル、設定、プレハブのライブラリです。









プロジェクト設定







BulletCasterMovableObjectなど、すべてのロジックを小さなクラスに分割します。 それぞれに必要な設定が含まれており、1つの目標のみを追求しています。







これらのクラスには、非常にシンプルなインターフェイスがあります。
 public class BulletCaster : MonoBehaviour { public void CastBullet(Vector2 direction); } public class MovingBody : MonoBehaviour { public Vector2 Direction {get; set;} public bool IsStopped {get; set;} }
      
      







マイクロクラスから、複雑なロジックを組み立てることができます。







私は、シングルトーン(Clock、ShadowManagerなど)への直接的な依存関係を削除し、サービスロケーターパターンを実装します(少し物議を醸すものですが、シングルトーンの散乱よりもはるかに正確です)。







レイヤーを介して衝突処理を実装し、それらを最適化して、不可能な衝突を明示的に削除します(例えば、静的<->静的)。







グローバルプールを記述することにより、オブジェクトの作成を最適化します。 これは別のバイクだと思いますが、自分の手で書きたかったのです。 プールは、プレハブキーを使用してオブジェクトを作成し、作成後にそれらを初期化し、作成/削除をオブジェクトに通知できます。







そして、プールができたら面白い出来事が起こりました。

私の弾丸には寿命があります(約10秒の「凍結されていない」時間)。 ある日、奇妙なバグが現れました。クールダウンが予定より早く進んでおり、弾丸がタイマーで消えたように、弾丸の一部が空中に消えました。







キャッチするのは困難でした。すべての弾丸が消えたわけではありませんが、少なくとも1つが消えることを期待して、それぞれの弾丸がデビューしました。







しかし、2つの奇妙な事実を見つけることができました。







  1. 弾丸は、レベルが再起動された後にのみ消え始めました。
  2. 箇条書きを削除するコードはまったく呼び出されませんでした。


最も重要なルールが再び失敗しました:







バグが見知らぬ人であるほど、愚かな人が原因です。

だからお楽しみください:







  1. レベルは、リブートせずに1つのシーンで再作成されます。
  2. レベルを作成するときに、古い壁を削除するのを忘れました(レベルが同じであるため、階層内以外のどこにも表示されませんでした。
  3. 弾丸がそのような二重壁に触れると、衝突ハンドラーが2回呼び出されました。
  4. 衝突ハンドラーで、弾丸が削除されます(プールに追加されます)。 したがって、プールデータには同じプールへの2つの参照が含まれていました。
  5. しばらくして、プレーヤーはこの弾丸を発射しました。
  6. プールから再び撮影しようとすると、すでにアクティブな飛行弾丸へのリンクが取られました。 彼女は再初期化し、座標を変更し、「前の」弾丸が空中に消えました。


もちろん、このような間違いは、古い壁がなくて、複雑な衝突があった場合にも起こり得ます。 そのため、衝突時のオブジェクトのアクティビティにチェックを追加し、もちろん古い壁を削除し始めました。







不便なタッチの問題を思い出し、TouchManagerを実装します。 彼は最後のタッチを覚えており、彼だけを追跡します。 短すぎる(指の揺れ)を無視して、最後のN個の動きを保存します。 指やマウスが画面に触れるのをやめた瞬間に、マネージャーはジェスチャーの方向と長さを計算します。 ジェスチャが短すぎる場合、マネージャーはそれを無視します。プレーヤーは明確な方向を選択せず​​に心を変えます。







これでコードが読みやすくなり、新しいクラスの追加が容易になり、リリースが消滅する前に最新の機能を追加するとプロジェクトのアーキテクチャがばらばらになるという感じがします。

アートとゲームのロジックに戻ることができます。 実際には、ゲームのロジックはきれいになります。







Todo:プレイヤーのゲームプレイ要素、ゲームプレイの明快さとシンプルさについて考えてください。







レベル4.2 ゲームオブジェクトのリファクタリング



ゲームプレイ機能を検討したとき、私は膨大な数の可能性に魅了されました。 自分で判断すると、すべてのオブジェクトには4つの直交特性があります。







  1. 弾丸は反射または吸収しますか?
  2. 弾丸でオブジェクトを破壊しますか?
  3. 動いているのですか、それとも静的ですか?
  4. オブジェクトの種類(プレイヤー/敵/民間人)は何ですか?


これらの特性はすべて組み合わせて、その場で変更することもできます。 しかし、これをプレーヤーにどのように見せるか? 最初、私のオブジェクトのリストは次のようになりました。







  1. 普通の壁 。 弾丸を吸収します。
  2. 鏡の壁 。 箇条書きを反映します。
  3. 多階建ての壁 。 各フロアは無地または鏡面です。 弾丸が当たると、下の階が破壊され、上の階が倒れます。 したがって、カウンターなどを行うことができます。
  4. ボックス 動的ですが、破壊できない弾丸を吸収します。
  5. ミラーボックス 。 動的ではあるが破壊できない弾丸を反映します。
  6. ニワトリ ダイナミックで破壊され、プレイヤーは死ぬとポイントを失います。
  7. ダイナミック、破壊された、あなたはレベルを完了するために全員を倒す必要があります。
  8. 敵をミラーリングします。 通常の敵が、敵を破壊する弾丸が反映されます。
  9. クリスタル 。 弾丸によって破壊されたダイナミック、プレーヤーがそれらに触れた場合、彼はボーナスを受け取ります。
  10. プレイヤー




利用可能なすべてのオブジェクト。







私は明らかに、このすべての美しさの明確な視覚化に問題があるでしょう。 低ポリバージョンの作業を開始したとき、単純なカラーコーディングを使用する予定でした。







  1. エッジの色によってオブジェクトのタイプが決まります。
  2. 天井の白い色は静的なオブジェクト(壁)を示し、天井の端の色で着色された天井は動的です。


たとえば、灰色のエッジは、オブジェクトが弾丸を吸収することを意味し、紫色-反射します。 しかし、エッジの色は多くのプロパティをエンコードする必要があることがわかりました。 複雑すぎます。 私は明らかな問題を持つオブジェクトの種類を見つけます:







  1. 多階建ての壁。 床が1つだけ残っている場合、通常の壁と変わりません。 しかし、彼は破壊されます。 かどうか? 非常に非論理的な機能。
  2. ミラーボックス。 弾丸は常に同じ速度で移動します。 箱から反射して、それらを無限に予測不可能に加速します。
  3. 敵をミラーリングします。 「敵」でも「鏡」でもない別の色を使用する必要があります。 このエンティティは、すべてのカードを混乱させるだけです。


残りの合計:







  1. 2種類の壁、普通の壁と鏡面の壁。
  2. プレイヤー
  3. 鶏;
  4. 結晶;
  5. ボックス。



    クリーニング後に残ったオブジェクト。


すべてが適切に色分けされており、オブジェクトはほとんどありませんが、レベル設計の余地があります。







ゲームエンティティを決定し、コードをゴミから削除したので、グラフィックを最終状態に戻すことができます。 これらはエフェクトと物理学であり、あらゆる種類のインターフェース要素です。 要するに、多くの仕事があります。







Todo:リリースエフェクトの開発を開始します。







レベル5.1。 死の影響。



正確で検証済みのジェスチャーと移動が行われました。 弾丸が飛んで、壁から反射し、プレーヤーから数ピクセル通過し、別の反射板の端に触れて、再び方向を変えます。 今、彼女はマップ上の最後の敵を目指しています。 動きは終わりました。 勝利を待っている新しい動き。 方向はもはや重要ではありません:過去の弾丸はその仕事をします。 したがって、ジオメトリの法則は容赦なく、シェルはそのターゲットを見つけます。 弾丸は敵に触れます...そして敵は消えます。 残念だ。

はい、弾丸が当たったときのファンが欲しいです。 ゲームに視覚的に言うには:









しかし、今では弾丸が当たると、要素は単純に消えます。 床に物がスムーズに浸るようにしています。 遅くて不自然に見えますが、オブジェクトがすでに破壊されていることは明らかではなく、操作することは不可能です。







さて、死の効果には何が必要ですか?







  1. 明るく、具体的で意味のあるものでなければなりません。
  2. 死の結果はレベル全体で目に見えるようにし、気を散らすのではなく、通過を計画するのに役立ちます。


破片! 弾丸で敵を粉々に粉砕しましょう! うーん、それは複雑ではないですか? いいえ、すべてのオブジェクトは凸型であり、凸型ポリゴンをカットするのは楽しいことです。







実際、敵を半分に切ることはできません。 いくつかのメッシュで構成されています。







  1. 内側の凸多角形は単純です。
  2. カラーリング。 凸面だけでなく、穴が開いています。 ただし、N個(Nは辺の数)の凸四角形で構成されます。 したがって、それらを配列に保存し、それぞれを切り取ります。
  3. 外。 実際、これは前の段落の四角形の外側です。 しかし、PolygonCollider2Dを介して側面と物理をレンダリングするために、大きな凸ポリゴンと同様にそれを使用します。




個別に切断する必要があるオブジェクトの部分。







その結果、アルゴリズムは次のようになります。







  1. オブジェクト(プレイヤー、敵など)を内側、リングの破片の配列、外側に変換します。 将来、この構造を「ピース」と呼びます。
  2. ピースの幾何学的中心を見つけます。
  3. ランダムな方向を選択し、幾何学的中心を通るこの方向に直線を「描画」します。
  4. この一片を切り取ります。 これを行うには、Pieceから各ポリゴン(リング、内側と外側のパーツ、またはそれらのピース)を取得します。

    4.1。 ポイントの左右の配列を作成します。

    4.2。 現在の配列を左に示します。

    4.3。 多角形の最初のポイントを現在の配列に追加します。

    4.4。 残りのすべてのポイントを確認します。

    4.5。 現在のポイントと前のポイントが線の同じ側にある場合、 現在の配列に現在のポイントを追加します。

    4.5。 現在のポイントと前のポイントが線の異なる側にある場合、交点を見つけ、 左右の配列の両方に追加します。 現在の配列を反対に切り替えます。 現在のポイントを新しい現在の配列に追加します。
  5. 左のすべてのフラグメントを新しい左のピースに追加し、 右のフラグメントを右に追加します。
  6. 繰り返しますが、結果のフラグメントを指定された深さまで再帰的にカットしました。


各カットで、オブジェクトを2つの部分に分割します。したがって、3つのカットで、8つのフラグメントが得られます。 少しだけ深みのある「遊び」をすることもできますが、とても美しくもできます。







ポリゴンのメッシュ、コライダー、シャドウを作成するためのコードを変更して、特定のポイントでフラグメントを作成できるようにします。









私はそのような断片について知ります。







最初はキャッシュされたパーティションを作成し、それだけを使用する予定でしたが、リアルタイムのパーティションはブレーキをかけませんが、より壮観に見えることがわかりました。



フラグメントは、影のある不快なバグを明らかにしました。ポリゴン上のシルエットポイントを誤って検索しました。 このため、前のビデオでは、フラグメントの一部が部分的にのみ影を落としています。 修正は、前の記事で既に利用可能です。

そのため、弾丸がオブジェクトに当たると、最後の部分をフラグメントに置き換えます。 オブジェクトがプールに入れられ、フラグメントがプールからロードされ、新しいフォームに従ってメッシュ/コライダーを更新します。 rigidBodyの速度は、破壊された要素の速度と弾丸の方向に基づいて計算されます。 フラグメントはisBulletフラグがオフになっており、壁と相互作用するだけです。 各フラグメントには特別なFloorHiderクラスがあり、床を通してz座標でオブジェクトを下げ、完全に消えた後、削除します(プールに移動します)。







小さな回顧展:







  1. 破片-非常に「物理的に」理解可能な画像。 したがって、停止時間の概念を理解するのに役立ちます。 破片はばらばらに飛び始め、時間は止まり、すべてがフリーズします。 ゲームは決定論的なターンベースの外観を停止しました。
  2. すべてのフラグメントは互いに戦っており、速度は低下しません。
  3. クリスタルは、効果なしでタッチすると削除されます。 たぶんバラバラになりますか?
  4. 破壊後の痕跡は十分ではありません。攻撃の結果がそのレベルにとどまることを望みます。
  5. ファンのように見えるので、撮影したい!




破片!







バラバラに散らばる敵は非常に壮観なタッチですが、破片が跡形もなく消えてしまうという事実に甘やかされています。 壮観さ以外にもいくつかの理由がありますが、なぜこれらのトレースを追加する必要があります。







Todo:断片の痕跡の効果を実現します。







レベル5.2.1。 スポットの効果。



ビジュアルについて考えていたとき、プレイヤーやNPCが死んだときに現れる「血痕」のアイデアがありました。 長いレベルでは、そのようなスポットはナビゲーションに便利なラベルになります。 また、損失が発生した場合、敵の初期位置を評価し、再通過の戦術を検討するのに役立ちます。

だから、スポット。 デカールとレンダリングされたテクスチャを含むオプションを破棄します。スタイルから抜け出すようです。 破片の痕跡や消失の場所を表示しようとしています。 悪く見える:











さまざまな染色オプション。







私はこれをプロトタイプ化するオプションの中で、本格的な塗りつぶしについて考えます:









三角形の束を作成するだけです。







はい、このオプションの方が好きでした。 しかし、ワイルドなオーバードローで多くの三角形を作成するのは悪い考えです。 ただし、結果はモザイクに似ているため、ボロノイ図に向かって考えます。







ただ、その場で生成するだけです。特に、その後のモバイルデバイスでのロイドの緩和(緩和によりセルのサイズが同様になります)では、痛みが大きすぎます。 事前計算が必要です。 ここに別の問題があります:スポットは互いに任意の距離にある可能性があり、無限大の図を計算できないことは明らかです。 タイリングとは何ですか? :)







まず、ボロノイ図を生成するのに適したライブラリを見つけます。







今、私はタイルを扱っています。 トーラス上にダイアグラムを直接作成することは理想的な解決策ですが、ライブラリのジャングルに入ったり、自分で書いたりすることは本当にしたくありません。 したがって、次のアルゴリズムを思い付きます。







  1. 座標{-0.5、-0.5、0.5、0.5}の正方形にN個のポイントを作成します。
  2. 間隔[-tiles、tiles]の各Yおよび間隔[-tiles、tiles]のXに対して、X = 0、Y = 0を除く:

    2.1。 オフセットX、Yでポイントをコピーします(タイル= 1の場合、開始センターで9タイルが取得されます)。
  3. すべてのポイント(置換されたクローンを含む)のボロノイ図を作成します。
  4. 必要に応じて、ロイドのリラクゼーションを適用します。
  5. 結果のポリゴンをすべて調べ、中心が元の正方形{-0.5、-0.5、0.5、0.5}にあるポリゴンのみを残します。


結果は、多角形のタイルです。このタイルでは、左側が右上、上から下(対角線も同様)に完全に収まります。 実際、すべてがそれほどスムーズではありません。

アイデアは、ボロノイ図は非常にローカルなものであるため、すべての方向にソースポイントのコピーをいくつか作成することでトーラスをエミュレートできます。 しかし、ロイドの緩和は確かにローカルではなく、反復回数が多いほど、より多くのコピーを作成する必要があります(タイルの値を増やします)。







そして、結局のところ、中心の座標は、浮動小数点をチェックするために常に正しく動作するとは限りません。 したがって、非常にまれに、モザイクの端にある要素が欠落している場合があります。



繰り返しを見つけますか?







ヒント







したがって、このモザイクのようなものが得られます。



ライブラリのテクスチャにレンダリングされたモザイク。







計算されたタイルの配列を格納する小さなScriptableObjectと、大きな「タイルの再計算」ボタンを備えたエディターを作成します。







間違ったタイルを再生成することで、ポリゴンをタイルに入れるためのテストで、フロートによるまれな穴の問題を解決することにしました。 なぜなら 事前計算を一度行います。エディターに手を入れて、余裕があります。 :)

次に、これらのタイルを画面に表示します!







Todo:モザイクタイルの三角形を生成します。







レベル5.2.2。 タイルレンダリング。



スポットが完全に丸く、その座標と半径がわかっているとします。 このスポット内にあるすべてのポリゴンを何らかの方法で取得する必要があります。 さらに、染色は任意の座標に表示される可能性があるため、モザイクを「仮想的に」タイル化する必要があります。







アルゴリズムを確認するために、このような「モザイク」を作成しました。問題を見つけやすくなります。



偽のモザイク







512ポイントから生成されたモザイクがあるとします。 そのため、出力は512ポリゴンになり、各ポリゴンと円との交差をチェックするのはコストがかかりすぎます。 したがって、モザイクを小さな長方形のブロックの形で保存します。



ブロック分割の視覚化。

モザイクの面積とポリゴンの数がわかれば、検索速度が最大になる最適なブロック数を取得できます。







したがって、検索ロジックは次のとおりです。







中心座標と半径radiusの円が与えられます。 円の中にあるすべてのポリゴンを見つける必要があります。







  1. AABB円から、AABBに入る最初のブロックと最後のブロックの座標を計算します(座標値は0未満または複数の行である場合があります-タイリングのため1)。

     var rectSize = size / (float)rows; int minX = Mathf.FloorToInt((center.x - radius) / rectSize); int minY = Mathf.FloorToInt((center.y - radius) / rectSize); int maxX = Mathf.CeilToInt((center.x + radius) / rectSize); int maxY = Mathf.CeilToInt((center.y + radius) / rectSize);
          
          



  2. min max;
  3. , ;
  4. :

     int innerX = ((x + rows) % rows + rows) % rows; int innerY = ((y + rows) % rows + rows) % rows;
          
          



  5. ;
  6. , ( ).


, , :



.







, — . :









, , . , , , {0, 0} {1, 1}, .







, :







  1. , ;
  2. ;
  3. .


:

:



Points = 500, Relax = 5







, .

:



Points = 500, Relax = 0







: , , , :



Triangles, Points = 500, Relax = 0







. , , , : , . - "", , :



.







, — . , : -:



.







: , . . , "" .







Todo: .







おわりに



, . — , . , ( ), .







, :







  1. . ( ) , ;
  2. : , , . , . :)
  3. Unity3D , . — frame debug, , Unity3D ( msaa).


, , .







, feedback'a!








All Articles