すでに回答を検索しましたが、高価な関数や計算を処理するための最良のアプローチを見つけることができませんでした。
私の現在のゲーム(2dタイルベースの都市の建物)では、ユーザーは建物を配置したり、道路を構築したりできます。すべての建物は、ユーザーがマップの境界に配置する必要があるジャンクションへの接続を必要とします。建物がこのジャンクションに接続されていない場合、「道路に接続されていません」の標識が影響を受ける建物の上にポップアップ表示されます(それ以外の場合は、削除する必要があります)。ほとんどの建物には半径があり、互いに関連している可能性があります(たとえば、消防署は半径30タイル以内のすべての家を助けることができます)。これは、道路の接続が変更されたときにも更新/確認する必要があることです。
昨日、パフォーマンスの大きな問題に遭遇しました。次のシナリオを見てみましょう。ユーザーはもちろん、建物や道路を消去することもできます。したがって、ユーザーがジャンクションの直後に接続を切断した場合、同時に多くの建物を更新する必要があります。最初のアドバイスの1つはネストされたループを避けることです(これは間違いなくこのシナリオの大きな理由です)が、私は確認する必要があります...
- 道路タイルが削除された場合に建物がまだジャンクションに接続されている場合(私はその道路によって影響を受けた建物に対してのみそれを行います)。(このシナリオでは小さな問題である可能性があります)
半径タイルのリストと、半径内の建物を取得します(ネストされたループ-大きな問題!) 。
// Go through all buildings affected by erasing this road tile. foreach(var affectedBuilding in affectedBuildings) { // Get buildings within radius. foreach(var radiusTile in affectedBuilding.RadiusTiles) { // Get all buildings on Map within this radius (which is technially another foreach). var buildingsInRadius = TileMap.Buildings.Where(b => b.TileIndex == radiusTile.TileIndex); // Do stuff. } }
これはすべて、私のFPS を1秒間で60からほぼ10に分解します。
そうすることができます。私の考えは:
- このスレッドのメインスレッド(更新関数)を使用せず、別のスレッド。マルチスレッドの使用を開始すると、ロックの問題が発生する可能性があります。
- キューを使用して多くの計算を処理する(この場合、どの方法が最適ですか?)
- より多くの計算を回避するために、オブジェクト(建物)により多くの情報を保持します(たとえば、半径内の建物)。
最後のアプローチを使用して、代わりにこのforeachの形式で1つのネストを削除できます。
// Go through all buildings affected by erasing this road tile.
foreach(var affectedBuilding in affectedBuildings) {
// Go through buildings within radius.
foreach(var buildingInRadius in affectedBuilding.BuildingsInRadius) {
// Do stuff.
}
}
しかし、これで十分かどうかはわかりません。Cities Skylinesのようなゲームは、プレイヤーが大きな地図を持っている場合、はるかに多くの建物を処理する必要があります。それらはそれらをどのように処理しますか?すべての建物が同時に更新するわけではないため、更新キューが存在する可能性があります。
あなたのアイデアやコメントを楽しみにしています!
どうもありがとう!