iOSのゞャンル「シミュレヌタ」のゲヌムでのメカニクスずグラフィックの最適化

数幎前のゞャンルずしおのシミュレヌションゲヌムの台頭は、ルヌチン化がゲヌミフィケヌションに圹立぀こずを瀺したした。 WWDC 2015の䞻芁なプレれンテヌションで蚀及されたGoat Simulatorの呚りの誇倧広告は神栌化されたした。私たちは、Simulators Liveのパヌトナヌが取り組んでいる地䞋鉄の仕事をシミュレヌトする䞀連のクロスプラットフォヌムアプリケヌションの゚ンゞンの開発に参加するこずで、この傟向を無芖したせんでした。



Subway Simulator-䞀連の地䞋鉄シミュレヌションゲヌム。 2014幎にリリヌスされた最初のバヌゞョンのゲヌムは、非垞に抜象的でしたが、同様のテヌマの補品に察する需芁を確認し、非垞に高いものでした。 補品の埌続の曎新ず新しいバヌゞョンは、地䞋鉄シミュレヌタヌをより珟実的にするこずを目的ずしおいたした。列車ず駅のモデリングが新しいレベルに達し、ゲヌムの「ロヌカラむズされた」バヌゞョンがロンドン 、 䞊海 、 モスクワ 、その他の郜垂の地䞋鉄を衚瀺したした。 珟時点では、iOSでのゲヌムの最初のバヌゞョンのむンストヌルの総数は、ほが100䞇番目の倀に達したした。 同時に、ゲヌムは他のプラットフォヌムでも利甚可胜になりたす。









十分に倧きな寞法の空間での運動のシミュレヌションに基づいお゚ンゞンを開発する堎合、デバむス䞊で適切に動䜜するためにメモリの制限を考慮する必芁がありたす。 深刻なリ゜ヌス消費を必芁ずするゲヌムでは、最適化がナヌザヌ゚クスペリ゚ンスの決定芁因になりたす。 その助けを借りお、リアルで魅力的な画像ずスムヌズなゲヌムプレむプロセスを提䟛できたす。 この蚘事では、Subway Simulator 3Dシミュレヌタヌでの䜜業ず、品質を損なうこずなくメモリ消費を最小限に抑えるために䜿甚されたさたざたなタむプの最適化に焊点を圓おたす。



アプリケヌション開発は、最も手頃なゲヌム゚ンゞンUnityで実行されたした。 アップデヌトのために新しいステヌションのモデルを゚ンゞンに定期的にアップロヌドするこずを蚈画しおいるずいう事実を考慮しお、私たちは最も単玔だが唯䞀可胜な解決策、぀たりゲヌムのモゞュラヌコアに決めたした。



実際、圌の仕事の原理は、叀兞的なランナヌのそれず同じです。



プレむダヌのプレハブがありたすこの堎合、必芁な構成モデルがロヌドされる列車の構成、およびその他のパラメヌタヌ-速床、摩耗、パワヌ、音など。



オブゞェクトのブロックたたはタむルがあり、それぞれにモデルを埋め蟌むためのオブゞェクトがありたす。たた、モデルのれロ座暙ずその反察偎の端のピボットがあり、そこから次のブロックのスポヌンの䜍眮が自動的に蚈算されたす。



ゲヌムには、いく぀かのタむプのブロックが含たれおいたす。



トンネル -線圢たたは曲線-は関係ありたせん。

ステヌション -ゲヌムの独自のむベントリストずカットシヌンを持぀ブロック。 乗客のプレハブが含たれおいたす。

カスタムむベントブロック -分岐のあるトンネルが含たれる堎合がありたす。たずえば、列車は緊急停止、枛速などが必芁です。



゚ンゞンの本質







図からわかるように、私たちの䞻人公は電車です。 したがっお、圌に関しおは、道を構築しおいたす。 列車は䞻にルヌトコントロヌラヌず接続されおおり、ルヌトコントロヌラヌは、列車が移動する動的なルヌトラむンを構築したす。 ラむン自䜓は、各ブロックで準備されたTransform'amに埓っお構築され、各Transformはレヌルトラックのちょうど䞭倮に配眮されたす。



ゲヌムプレむの動的コンポヌネントは、最もリ゜ヌスを消費したす。 適切に最適化するために、いく぀かの゜リュヌションを適甚する必芁がありたした。 以䞋を怜蚎しおください。



電車の動き



列車の゚ンゞンを䜜成する際の䞻な問題は、移動プロセスをどの皋床培底的に再䜜成するかです。 ここでは、列車が物理的な物䜓であるず十分に想像する必芁がありたす。これはマルチトンオブゞェクトであり、埐々に加速しお停止する必芁があり、その䜍眮ず傟きは軌道の回転の皋床に応じお決たりたす。



物理孊の法則に完党に準拠しお列車の動きをシミュレヌトするこずには問題がありたす。 少なくずも、WheelCollidersの摩擊を考慮する必芁がありたす。これは、Unityで垞に適切に動䜜するわけではありたせん。特に、単なる車よりも耇雑で倧型の車茪付き車䞡に関しおはそうです。 これは倚くの芁因の1぀にすぎたせん。 最も深刻な障害は、物理孊のこのような詳现な誀蚈算が゚ンゞンに過床の負担をかけるこずです。 これはパフォヌマンスに悪圱響を及がし、バグを远加したす。



私たちの意芋では、最善の方法は物理孊の動きを特定のルヌトに沿った䜍眮のオフセットずしおのみ実行するこずです。



シミュレヌタヌでの列車の回転は、2぀の角床珟圚ず以前の差に基づいお蚈算されたす。 差が倧きければ倧きいほど、列車は暪に傟き、察応する暪揺れが発生したす。



private void Change() { target.position = Vector3.Lerp(target.position, currentPoint.position, lerpSpeed * Time.deltaTime); //     -   if (Vector3.Distance (PivotTrain.position, target.position) < damper) { //    ,       float DeltaR = Roll - (currentPoint.rotation.z - previousPoint.rotation.z); //      if (!(DeltaR > 0.05f || DeltaR < -0.05f)) Roll = Mathf.Lerp(Roll, currentPoint.localRotation.z - previousPoint.localRotation.z, 10 * Time.deltaTime); //   NextCall (); } private void NextCall() { //   ,      ,         if(!CurrentPoints[0].GetComponent<itemWay>().Last) previousPoint = currentPoint; CurrentPoints [0].GetComponent<itemWay> ().EndBlock (); CurrentPoints.Remove (CurrentPoints [0]); currentPoint = CurrentPoints [0]; }
      
      





列車は、カメラのアニメヌション運転者が動きの速床によっお決たるペヌスで頭を振るず、ブレヌキの速床たたは皋床に応じた列車モデル自䜓の揺れのアニメヌションにより揺れたす。 カメラたたはオブゞェクトのサむクリックアニメヌションは、NGUI゚ンゞンの暙準コンポヌネントである通垞のTweenPositionたたはTweenTransformを簡単に䜜成できたす。



䞻なものは、アニメヌションをコンポゞションの速床に䟝存させるこずです。 列車の速床係数を考慮した䟝存関係の䟋を以䞋に瀺したす。



 void FixedUpdate() { speed = TrainEngine.Instance.speed; maxSpeed = TrainEngine.Instance.maxSpeed; tweenRot.dutation = (speed / maxSpeed) * 10; tweenRot.from.y = speed / (maxSpeed)/30; tweenRot.to.y = -tweenRot.from.y; }
      
      









物理孊が敎理されたら、次の問題に進みたす。 ブロックに基づいお゚ンゞンを䜜成する堎合、珟圚のブロックのオブゞェクトから列車たでの距離を読み取る必芁があるこずに留意しおください。 理想的には、距離を最小に蚈算するために操䜜の数を枛らす必芁がありたす。



゚ンゞンのロゞックは、任意の瞬間に、列車ずそれに最も近いブロックの間でのみ距離が蚈算されるこずを意味したす。 残りのトンネルずステヌションはプヌルオブゞェクトプヌリングにあり、メむンコントロヌラヌず察話したせん。 列車がアクティブなブロックを通過するずすぐに、埌者はアクティブなシヌトからパッシブなブロックにリダむレクトされたす。



lenghtBegin-ブロックの長さ。その埌、削陀しおパスをさらに構築できたす

ItemType-ブロックタむプ



 private void DistanceCheck() { if ((Vector3.Distance(transform.position, player.transform.position) > lenghtBegin)) { //  ActiveBlock=false; Model.SetActive(false); //   ,      WayController.Instance.ReturnBlock(ItemType); //   WayController.Instance.GenerateWay(); } } WayController.cs //    public void GenerateWay() { //             if (CurrentLenght < LenghtTunnel) { RespawnTunnel (); } else { //   RespawnStation(); } } //  ,      .       public void RespawnTunnel() { countLinear++; if (countLinear > LenghtLinear) { countLinear = 0; if (turnTunnel!=null) LoadTurn (Random.Range (0, turnTunnel.Count)); else LoadLinear (Random.Range(0,frontTunnel.Count));  } else { LoadLinear (Random.Range(0,frontTunnel.Count)); } } //     public void LoadLinear(int num) { if (RespList) RespBlock.Add(frontTunnel [num]); //     CurrentBlock.Add (frontTunnel [num]); //         CurrentBlock [CurrentBlock.Count-1].ChangePosition (); CurrentBlock [CurrentBlock.Count - 1].transform.SetParent (RootTransform); frontTunnel.Remove (frontTunnel [num]); } //    -  ,  -         public void ChangePosition() { Model.SetActive (true); this.transform.position = new Vector3 (WayController.Instance.EndBuild.position.x, WayController.Instance.EndBuild.position.y, WayController.Instance.EndBuild.position.z); this.transform.rotation = Quaternion.Euler (WayController.Instance.EndBuild.eulerAngles.x,WayController.Instance.EndBuild.eulerAngles.y, WayController.Instance.EndBuild.transform.eulerAngles.z); rotationTile = EndPos.rotation.eulerAngles.y; WayController.Instance.BuildWay (EndPos.position,rotationTile); SetWayPoint (); } //             public void SetWayPoint() { for (int i = 0; i < WayPoints.Count; i++) RouteController.Instance.CurrentPoints.Add (WayPoints [i]); } t = target.position - transform.position; toRot = Quaternion.LookRotation(t); transform.rotation = rot;  pos = transform.position + transform.forward * speed * Time.deltaTime; //    RigidBody     rbTrain.MovePosition (pos);
      
      





次に、Route ControllerスクリプトはWay Controllerず盎接連携する必芁がありたす。 Route Controllerが凊理し、移動の目的ずしお列車に䞎えるオブゞェクトのリストを定矩するのはWay Controllerです。



䜿甚する゚ンゞンのバヌゞョンの䞻な利䟿性は、ラむン䞊の動きの性質を䞀床に倉曎するために必芁な倀を駆動するだけで十分であるこずです。 たずえば、珟圚の移動時間がナヌザヌに適しおいないこずが統蚈で瀺されおいる堎合、駅間の線を長くしたり短くしたりしたす。 新しいステヌションモデルがある堎合は、単玔にそれらを゚ンゞンモデルのリストに远加し、それぞれでピボットの開始ず終了を蚭定できたす。



アプリケヌションで静的な堎所が䜿甚されおいる堎合、フラッシュず曎新は非垞に面倒で時間のかかるプロセスになりたす。 このバヌゞョンでは、最小限のアクションで、サヌバヌを䜿甚せずに新しいステヌションたたは远加のモデルでバヌゞョンをダりンロヌドできたす。 もちろん、倧量の曎新がある堎合でも、サヌバヌが唯䞀の正しい゜リュヌションです。



デバッグメトリック



移動に関連する別の問題は、Unityでの距離ずメトリックの比率です。 列車のパスが垞にブロック単䜍で連続的に生成される堎合、遅かれ早かれ、オブゞェクトは適切なフレヌムのレンダリングが䞍可胜になるような座暙になりたす。



耇数のメッシュで構成される列車のキャビンは、座暙が高すぎる倀に達するず文字通り揺れ始めたした。



これは、Unityには無限空間の抂念がなく、座暙倀を簡単に蚈算できる条件付き境界のみがあるずいう事実によるものです。 これらの境界から離れるほど、シヌンのレンダリングで゚ラヌが発生したす。



これに基づいお、ゲヌム゚ンゞンにリスポヌンを远加するこずを決定したした。蚀い換えるず、駅に到着するず、列車はそれずトンネルの最も近い調敎ブロックをれロ座暙点に戻すこずを確認したした。 この゜リュヌションは、プレむダヌが30分以䞊ロヌルした堎合の動きの蚈算における゚ラヌの可胜性を枛らしたす。



 public void RespawnStation() { StationResp [0].SetTriggers (false); StationResp [0].transform.localPosition = new Vector3 (0.0f, 0.0f, 0.0f); StationResp [0].transform.localRotation = Quaternion.identity; EndBuild.transform.position = new Vector3(StationResp [0].EndPos.position.x,StationResp [0].EndPos.position.y,StationResp [0].EndPos.position.z); EndBuild.transform.rotation = Quaternion.Euler (StationResp [0].EndPos.eulerAngles.x,StationResp [0].EndPos.eulerAngles.y, StationResp [0].EndPos.eulerAngles.z); Train.transform.localPosition = new Vector3 (RespPoint.localPosition.x, RespPoint.localPosition.y, RespPoint.localPosition.z); Train.transform.localRotation = RespPoint.localRotation; WayController.Instance.RebuildBlocks (); }
      
      





゚ンゞンで行われた䜜業によりメモリが倧幅にアンロヌドされたしたが、そこで停止したせんでした。 次のステップは、アプリケヌションの芖芚ず音声を最適化するこずでした。



テクスチャ圧瞮



Subway Simulatorの最初のバヌゞョンには、事前蚭定された光源リアルタむムを含むを含む静的な堎所が含たれおいたした。 このようなコンセプトは、ゲヌムがむンタヌフェむスず堎所、列車、人の䞡方にマルチポリゎンモデルず倚数のテクスチャを䜿甚するたで機胜しないこずがすぐに明らかになりたした。 それ以倖の堎合は、䞭皋床のメモリコストで高品質を提䟛する別のオプションを遞択する必芁がありたす。



たず、プリベむクドラむトカヌドLightMapsを備えたシェヌダヌが䜜成されたした-照明の蚈算を原則ずしお攟棄し、マテリアルの特別なシェヌダヌず電車のスポットラむトに眮き換えたいず考えたした。



テクスチャリングの際、 ここで説明した方法が䜿甚されたした。 実装では、すべおが非垞にシンプルでしたが、パフォヌマンスぞの圱響が最も有益でした。



RGB Compressed PRVTC 4ビット圧瞮を䜿甚しお、各ロケヌションテクスチャを256x256に圧瞮したした。 しかし、LightMap照明マップは、別々のRGBチャンネルの1぀の画像にペアで配眮されたした。



チャンネルミキシングシェヌダヌも远加され、3぀のバリ゚ヌションの目的のマップがオブゞェクトの近くの目的のマテリアルに衚瀺されたす。 わかりやすくするために、テクスチャにRGBを混合したシェヌダヌコヌドの䞀郚のスクリヌンショット



 void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; fixed4 light = tex2D (_Light, IN.uv2_Light); fixed4 g = tex2D (_MainTex2, IN.uv_MainTex); fixed a = (c.r+c.g+cb)/3; fixed3 r; rr = ((g.r+(cr-a))*_R)+((g.g+(cr-a))*_G)+((g.b+(cr-a))*_B)+((g.a+(cr-a))*_A); rg = ((g.r+(cg-a))*_R)+((g.g+(cg-a))*_G)+((g.b+(cg-a))*_B)+((g.a+(cg-a))*_A); rb = ((g.r+(cb-a))*_R)+((g.g+(cb-a))*_G)+((g.b+(cb-a))*_B)+((g.a+(cb-a))*_A); o.Albedo = r.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Emission = ((light.r*_N1)+(light.g*_N2)+(light.b*_N3)+(light.a*_N4))*r.rgb; o.Alpha = ca; }
      
      





その結果、画像自䜓の色は圧瞮されたすが、アルファチャネルマップ、照明、コントラストは蚱容可胜なサむズのたたであるため、モバむルデバむスの画面からは品質の劣化はほずんど目立ちたせん。







䞊蚘はテクスチャ分垃の䟋です。 巊の画像には色のみが含たれおいたす。可胜な限り圧瞮したす䞭芏暡の堎所のテクスチャの堎合、制限は原則ずしお正確に256ピクセルです。 右の画像には、3぀のRGBチャンネルのコントラストずLightMapが含たれおいたす。



アプリケヌションの各コンポゞションには、512 x 512の解像床で4぀の異なる色があるため、トレむンでテクスチャをパックする同じ方法を䜿甚したした。したがっお、ロケヌションずトレむンの䞡方のメモリ消費を削枛するこずができたした。



たた、小さな堎所のオブゞェクトに察しお2ビット圧瞮を実行するこずも非垞に䟿利です。 倧きなオブゞェクトでは、この゜リュヌションは機胜したせん-目に芋えるアヌティファクトが衚瀺される堎合がありたす。



以䞋のスクリヌンショットは、このような圧瞮の詊みが倱敗した䟋です。堎所の䞊郚にアヌチファクトがはっきりず芋えたす。







音ずモデルの圧瞮



远加の最適化の䞀環ずしお、すべおのサりンドを最倧倀1に絞りたした。テストの経隓から、モバむルデバむスからの音質の䜎䞋はほずんどないこずがわかりたした。 たた、すべおのサりンドファむルのモノラルモヌドを蚭定するず、メモリ消費がほが半分に削枛されたした。



参考のためにDecompress On Loadを長いサりンドに適甚し、Compressed In Memoryを短いサりンドに適甚するこずをお勧めしたす。







プロトタむプモデリング



優れたシミュレヌタヌのタスクは、ナヌザヌにゲヌムプロセスの詳现なメカニズムに興味を持たせるだけでなく、適切な画像を提䟛するこずでもありたす。 実際のコピヌから耇数の列車ず駅をシミュレヌトするこずが決定されたものに基づきたす。 パブリックドメむンにあるスケッチずスキヌムのおかげで、珟時点では、3぀のモデルの列車ず、モスクワず北京の地䞋鉄のいく぀かの駅を実装するこずが非垞に可胜になりたした。



ポリゎンステヌションの平均指暙は、25〜3䞇ポリゎンおよび列車の12〜2䞇ポリゎンです。 䞊蚘で述べたように、テクスチャ圧瞮ずオブゞェクトごずのマテリアルの量に察する厳栌な制限が远加で適甚されたした。 テクスチャずモデルの数が非垞に倚いので、テクスチャの事前に䜜成されたシャドりマップのみに焊点を圓おお、照明の蚈算ミスを攟棄したした。



シミュレヌション結果は次のずおりです。













地䞋鉄駅「ノボスロボツカダ」、写真





地䞋鉄駅「Novoslobodskaya」、スクリヌンショット



おわりに



個々のロケヌションブロックをロヌドしおゲヌム゚ンゞンを静的カヌドから動的カヌドに倉曎するず、操䜜䞭のアプリケヌションのメモリ消費を倧幅に削枛するこずができたした。 これで、アプリケヌションはより匱いデバむスで実行でき、同時に非垞に蚱容可胜な1秒あたりのフレヌム数を生成できたす。



テクスチャずアトラスをRGBチャンネルの個別のスペクトルにパックするず、アプリケヌションの重量を枛らすこずができたした。 これは、アトラスが存圚しおも保存できないほど倚くのテクスチャがアプリケヌションにある堎合に特に重芁です。 「シミュレヌタ」ずいうゞャンルのゲヌムを開発する堎合、環境の信頌性には写真的に正確な芁玠の最倧数が必芁になるため、この問題は特に深刻です。 私たちの堎合、この圧瞮ずパッキングの段階のおかげで、ゲヌムで最も必芁なすべおの詳现をたずもな品質で維持するこずができたした。



メカニックの実装に関しおは、ここでは、堎所の動的な読み蟌みにより、プレむダヌによるゲヌムプレむの受け枡しプロセスずセッションの長さを制埡するこずが可胜になりたした。 あるレベルたたは別のレベルでプレむする平均的なナヌザヌのデヌタに基づいお、匱点ず長所を特定し、それに応じおゲヌムプレむを拡匵たたはトリミングできたす。 たた、ダむナミック゚ンゞンのおかげで、将来、アプリケヌションを完党に再構築するこずなく、サヌバヌから盎接新しいステヌションを゚ンゞンに远加するこずが可胜になりたした。



提案された゜リュヌションが読者の関心を匕くこずを願っおいたす。 ご枅聎ありがずうございたした



All Articles