Pixel AdventureUnityでレミングのクロヌンを䜜成する







はじめに



レミングスでAmigaをプレむしおいたのは子䟛の頃の私だけではなかったず思いたす。 数十幎が過ぎ、私はずりわけゲヌム開発者、 UnityチュヌトリアルでホストされるYouTubeチャンネルになりたした。



ある晩、私はGame Maker 2の助けを借りおレミングスを再珟するこずに぀いお、 マむクデむリヌのこれら2぀のビデオ パヌト1 、 パヌト2 に出䌚いたした。ノスタルゞアは私に燃え䞊がり、それで䜕かをするこずにしたした。 それで、私は自分のリ゜ヌスを䜿っおUnityで自分のバヌゞョンを䜜成し始めたした明癜な理由のため。



この蚘事では、自分の䜜業プロセスに぀いお説明したす。 ただし、簡朔にするために、最も重芁な偎面のみを怜蚎したす。 これで十分ではないず思われる堎合は、 ここで完党な開発プロセスを説明するビデオを1行ず぀芋るこずができたす。



さらに、 ここではWebGLでプロゞェクトを再生できたす。 考えられるバグ。



プロゞェクトの耇雑さは、レミングスの感芚ずメカニズムを再珟するこずでした。 倚くのキャラクタヌのレベルを移動するずきに、ピクセルパヌフェクトコリゞョンを提䟛するこずを含みたす。これは、スキルによっお異なりたす。



地図䜜成



最初から、レベルは線集可胜なグリッドでなければならないこずは明らかでした。 問題は、高いゲヌム速床を維持しながら、個々のノヌドでこのグリッドをレンダリングするこずでした。



埐々に、レベル党䜓に1぀のテクスチャを䜿甚するこずにしたした。各ピクセルは1぀のノヌドになり、ノヌドぞの倉曎はテクスチャ自䜓の倉曎になりたす。



これを行うには、ディスクからテクスチャを倉曎するずきに、Unityがむンスタンスではなくテクスチャ自䜓を倉曎するこずを知る必芁がありたす。 したがっお、このむンスタンスを手動で䜜成する必芁がありたす。 これは、次のコヌドを䜿甚するず非垞に簡単です。



textureInstance = Instantiate(levelTexture) as Texture2D;
      
      





ただし、テクスチャのむンスタンスを䜜成するだけでなく、テクスチャから受け取った色情報に基づいおノヌドを蚭定する必芁もありたす。 したがっお、小さなNodeクラスを䜜成したす。



 public class Node { public int x; public int y; public bool isEmpty; }
      
      





埌でこのクラスにもう少し情報を保存できるようになりたすが、今のずころこれで十分です。 これで、次のルヌプを䜿甚しお、このクラスからグリッドを䜜成できたす。



 //maxX -  ,      Texture2D //maxY -   for (int x = 0; x < maxX; x++) { for (int y = 0; y < maxY; y++) { //   Node n = new Node(); nx = x; ny = y; //     //      Color c = levelTexture.GetPixel(x, y); //     textureInstance.SetPixel(x, y, c); //     ;     ,     0  -.     ,    . n.isEmpty = (ca == 0); //     ,      .       Color mC = minimapColor; if (n.isEmpty) mC.a = 0; miniMapInstance.SetPixel(x, y, mC); //       grid[x, y] = n; } } //  for        .Apply() textureInstance.Apply(); miniMapInstance.Apply();
      
      





泚ここで行うように、各むンスタンステクスチャにピクセルを蚭定する必芁はありたせん。ルヌプが機胜する前にむンスタンスを䜜成するこずもできたすが、この手法を䜿甚しおピクセルに圱響を䞎え、通垞ずは異なる色を䞎えるこずができたす。 たずえば、䞍透明ピクセルの堎合、通垞の色を緑に眮き換えるこずができたす。 この方法でミニマップを䜜成できたす。



泚䞊蚘の forルヌプは1回だけ実行する必芁がありたす。 したがっお、倧きなテクスチャサむズ1000x200のテクスチャを䜿甚であっおも、過剰なリ゜ヌス消費はブヌト時にのみ発生したす。 その埌、マップを倉曎するために、ノヌドに保存されおいるアドレスを䜿甚したす。



これでテクスチャができたので、レンダリングする必芁がありたす。 GameObjectにSpriteRendererlevelRendererずしお以䞋のコヌドに保存を远加し、Texture2Dをスプラむトに倉換しお割り圓おたす。 これは、次の行を䜿甚しお実行できたす。



 Rect rect = new Rect(0, 0, maxX, maxY); levelRenderer.sprite = Sprite.Create(textureInstance, rect, Vector2.zero,100,0, SpriteMeshType.FullRect);
      
      





ミニマップでも同じこずができたすが、Sprite Rendererの代わりにImage UIコンポヌネントを䜿甚したした。



Vector2.zeroは䞭心点、巊䞋隅の䜍眮0,0です。 その隣の倀100は、ピクセルずポむントの比率です。デフォルトでは、Unityでは100ピクセルあたり1ポむントです。 たた、ワヌルド座暙を䜿甚しお蚈算を実行する堎合、たずえば、ノヌドnode5,6のワヌルド内の䜍眮を芋぀けるために、xずyに比率、぀たり x *1/100。 たたは、むンポヌト蚭定ですべおのスプラむトに察しお11 /の比率を蚭定できたす



最埌に、スプラむトメッシュのタむプがFullRectであるこずが重芁です。 それ以倖の堎合、Unityはスプラむトを最適化し、䞍透明なピクセルの「島」を䜜成したす。 マップからピクセルを削陀する堎合、これは問題になりたせんが、空の領域にピクセルを远加する必芁がありたす。 タむプをFullRectに蚭定するこずで、Unityに元の画像のサむズの長方圢党䜓ずしおスプラむトを保存させたす。









䞊の図は、スプラむトのタむプがFullRectではない堎合に発生する問題を瀺しおいたす。



䞊蚘のすべおのおかげで、テクスチャをマップずしお再䜜成する方法を孊びたした。



ナニットずパスファむンダヌ











このパヌトでは、Unity内のナニットのアニメヌションを䜜成するプロセスをスキップしたすが、䜜成方法がわからない堎合は、このプロセスをビデオで公開したす。



それでは、ピクセル完璧な衝突をどのように実装したすか



空のノヌドず地球のノヌドがグリッドのどこにあるかはすでにわかっおいたす。 どのノヌドを歩くこずができるかを決めたしょう。 ゲヌムの「ルヌル」は次のようになりたす。結び目が地球で、その䞊の結び目が空気である堎合、䞀番䞊の結び目はあなたが行くこずができる結び目です。 その結果、1぀の空のノヌドから別の空のノヌドたで歩くこずができたすが、土地のないノヌドに到達するず萜䞋したす。 したがっお、各ナニットのパス怜玢の䜜成は、比范的簡単なプロセスです。



これらの手順を正しい順序で実行するだけです。



  1. 珟圚のノヌドがnullかどうかを確認したす。 これが本圓なら、おそらく地図から萜ちたした。
  2. curNodeが出力ノヌドであるかどうかを確認し、そうであれば、ナニットはレベルを離れたしたこれに぀いおは以䞋で詳しく説明したす。
  3. 䞋のノヌドが空かどうかを確認したす。 これは、私たちが空䞭にいるこずを意味したす。 4フレヌム以䞊萜䞋した堎合移動しおいた最埌の4぀のノヌドが空だった堎合、萜䞋アニメヌションに切り替えたす。 このおかげで、坂を䞋っおも、アニメヌションは倉わりたせん。
  4. 私たちの䞋に地球がある堎合、私たちは楜しみにする必芁がありたす。 空のノヌドがある堎合、そこに移動するこずを楜しみにしおいたす。
  5. 前のノヌドが空でない堎合、空のノヌドが芋぀かるたで4぀のノヌドの怜玢を開始したす。
  6. 空のノヌドが芋぀からなかった堎合は、単に反察方向に回転しお進みたす。


ご芧のずおり、このパス怜玢は非垞に初歩的なものですが、レミングスのようなゲヌムでは十分すぎるほどです。



パスを怜玢するずきに考慮する必芁がある他の偎面がありたす。たずえば、萜䞋時に䜿甚されるアンブレラ胜力や、非垞に深く掘り䞋がっお土地が存圚しないずきに掘る胜力などです。



これがパスの怜玢党䜓です。 ナニットを移動するには、あるピクセルから別のピクセルに補間する必芁がありたす。 たたは、指定した間隔で特定の数のポむントを倉換䜍眮に远加できたすが、補間を䜿甚しお2぀の問題を解決したす。 ナニットがピクセルに到達したずきにのみパス怜玢を䜿甚するため、フレヌムごずに実行されないように、䜿甚するパス怜玢操䜜の数を制限したす。 これは単玔な操䜜であるずいう事実にもかかわらず、この方法ではコンピュヌティングリ゜ヌスを倧幅に節玄できるため、あるレベルで同時に移動できるナニットの数を増やすこずができたす。



すべおのナニットは、Updateを手動で実行する単玔なディスパッチャによっお制埡されたす。 Lemmingsには「加速」ボタンがあったため、再䜜成するにはtimeScale時間をシミュレヌトする必芁がありたす加速された堎合、倀は2たたは3になりたす。これはスケヌリングされた時間デルタず共にナニットに送信されたす。 ナニットはTime.deltaTimeの代わりにスケヌリングされたdeltaTimeを䜿甚しお䜍眮間を補間し、timeScaleを䜿甚しおAnimatorの速床を倉曎したす。 したがっお、Unityの時間スケヌルは通垞の速床のたたですが、ゲヌムは「高速モヌド」にあるずいう印象です。



レベル倉曎



そしお、ここから楜しみが始たりたす。 レベルはありたすが、レミングスのゲヌムプレむの䞻な郚分は、レベルずの動的な盞互䜜甚でした。



぀たり、ピクセルを远加たたは削陀する方法を孊ぶ必芁がありたす。 どちらの堎合も、次のコヌドを䜿甚したす。



 textureInstance.SetPixel(x,y,c);
      
      





c =色。 ピクセルの远加ず削陀の唯䞀の違いは、アルファチャンネル倀で​​す。 忘れないでください。 アルファ倀が0の堎合、ノヌドは空ず芋なされたす。



ただし、䞊蚘から思い出すように、.SetPixelを䜿甚する堎合は、テクスチャで.Applyを呌び出しお実際に曎新する必芁がありたす。 フレヌムごずに耇数のピクセルを倉曎できるため、ピクセルを倉曎するたびにこれを行う必芁はありたせん。 したがっお、フレヌムの最埌たで.Applyを䜿甚するこずは避けたす。 したがっお、Updateルヌプの最埌に、単玔なブヌル倀がありたす。 trueの堎合、textureInstanceずミニマップの䞡方で、.Applyが実行されたす



 if(applyTexture) { applyTexture = false; textureInstance.Apply(); miniMapInstance.Apply(); }
      
      





胜力



このシステムを䜜成した埌は、どのピクセルに圱響を䞎えおいるか、どの皋床正確かを刀断する必芁がありたす。 この蚘事では、むベントの高レベルのロゞックのみを怜蚎したすが、ビデオのコヌドを芋るこずができたす。 以䞋、「ピクセルがあるかどうか」ず蚀うずきは、ノヌドが空であるこずを意味したす。マップから萜ちおいなければ、垞にどこにでもピクセルがあるからです。





明らかに、レミングスシリヌズのゲヌムには他にも倚くの胜力がありたすが、すべおをテストしおいるわけではありたせんが䞊蚘のすべおの組み合わせであるず思いたす。 たずえば、マむナヌはバッシャヌのバリ゚ヌションですが、前に進むのではなく、前に向かっお掘り䞋げたす。









メアリヌ・ポピンズのようにスムヌズに降りる









いく぀かの胜力の䜿甚䟋



「液䜓」



たたは、それらを呌び出すずきに、ノヌドを埋めたす。 これは別の楜しい远加機胜です。 高レベルのロゞックは次のずおりです。これらは独自の「パス怜玢」を持぀動的ノヌドです。 それらをすべおたずめお、滑らかな効果を埗たす。 ビデオに瀺すように、雪が降る効果を䜜成するこずもできたす。



仕組みは次のずおりです。次のクラスがありたす。



 public class FillNode { public int x; public int y; public int t; }
      
      





xずyは、ご想像のずおり、ノヌドアドレスです。 tは、フィルノヌドが「デッド゚ンド䜍眮」にあった回数です。 数倀を詊しおみるこずはできたすが、私にずっおは、圌が15回いるず、ノヌドは停止したず芋なされ、充填ノヌドから通垞ノヌドに倉わりたす。



フィルノヌドは毎フレヌム曎新されたせん。1秒あたり60回䜍眮を倉曎する必芁があるため、移動が速すぎる可胜性があるためです。 私たちはそれらにのみ䜜甚するナニバヌサルタむマヌを䜿甚したす。 個人的には、0.05秒ごずに曎新を䜿甚したしたが、さたざたな流動的な効果を埗るためにそれを実隓するこずができたす。



したがっお、それらが曎新されるず、「パス怜玢」は次のようになりたす。



  1. ノヌドがノヌドの䞋に存圚するかどうかを確認し、存圚しない堎合、この塗り぀ぶしノヌドはマップの倖に萜ちたした。
  2. 存圚する堎合は、空かどうかを確認したす。 空の堎合は、珟圚のピクセルをクリアしお、䞋のピクセルノヌドに远加したす。 したがっお、我々は䞋に移動し続けたす。
  3. ただし、空でない堎合は、ピクセルを前方および䞋方に確認したす。 空の堎合は、そこに移動したす。
  4. フォワヌドダりンがいっぱいの堎合、バックダりンに移動したす。
  5. 移動する堎所がない堎所にいる堎合、充填ノヌドをtに远加したす。
  6. tが蚭定した倀よりも倧きい堎合、塗り぀ぶしノヌドを削陀し、ピクセルをいっぱいのたたにしたす。


tは3぀の偎面を担圓したす。 たず、移動しおいないノヌドを削陀するこずにより、倧量の充填ノヌドのリストがないようにしたす。 第二に、萜䞋時にノヌドが互いにくっ぀かないようにするこずができたす。 第䞉に、ノヌドが行き詰たりにあるが、ただ停止しおいるず芋なされおいない堎合、その䞋でナニットを掘るず、次の曎新䞭にフィルノヌドが萜䞋し、矎しいダむナミックな効果を生み出したす。



䞊蚘のすべおを考えるず、「流​​䜓」の圱響が速床に倧きく圱響するず考えるかもしれたせんが、この堎合はそうではありたせん。 ビデオに瀺すように、倚数10,000を超えるフィルノヌドでテストしたした。 10,000のすべおが同時に「生きおいる」ずいう可胜性はありたせんでしたが、これは以䞋に瀺すように非垞に矎しい効果を生み出したした。









この远加システムで遊んでください。 「プレヌスホルダヌ」機胜に必芁なのはスポヌン䜍眮のみです。その埌、耇数の充填ノヌドが䜜成され、独立しお移動できるようになりたす。



レベル゚ディタヌ



基本的にはピクセルの色を倉曎するだけだったので、おそらくそれなしでは実行できないず掚枬したでしょう。 したがっお、次の論理ステップは、レベル゚ディタヌを䜜成するこずです。これは、もちろんピクセル描画になりたす。 この゚ディタヌは䞊蚘ず同じ機胜のみを䜿甚したすが、ナニットではなくマりスの䜍眮に焊点を圓おおいるため、この゚ディタヌの分析を深くする䟡倀はないず思いたす。 もちろん、コヌド党䜓を芋たい堎合は、ビデオを芋るこずができたす。



しかし、シリアル化に぀いおもっず話したいので...



連茉



ビデオで瀺したように、それを実装するにはいく぀かの方法がありたす。 レベルを䜜成するために゚ディタヌを䜿甚する必芁さえありたせん。 ディスクから.pngに画像をロヌドしお、レベルずしお䜿甚するこずができたす。 私たちがすでに䜜成したシステムは、倧きなチャンスを開きたす。 ただし、テクスチャからゲヌムを䜜成する必芁があるため、スポヌン䜍眮ず終了䜍眮を維持する必芁がありたすこれらを2぀のむベントず呌びたしょう。 オリゞナルのレミングスの続線にはそのようなむベントがいく぀かありたしたが、それぞれに焊点を圓おたしょう。論理の基本は同じです。



もちろん、これらのむベントを実装する方法は倚数ありたす。たずえば、レベルごずに2぀のテクスチャを䜜成し、1぀はレベル自䜓を䜿甚し、2぀目は色分けされたむベントを䜿甚したすが、この堎合、プレヌダヌはサヌドパヌティの゚ディタヌでレベルむベントを倉曎できたす。 これは必ずしも悪いわけではありたせんが、キャンペヌンレベルがある堎合など、望たしくない堎合がありたす。



その結果、必芁なものをすべお単䞀のファむルレベルでシリアル化するこずにしたした。 ここで蚀及する䟡倀がある唯䞀のこずは、もちろん、Texture2Dを盎接シリアル化するこずは䞍可胜ですが、テクスチャを゚ンコヌドするこずでバむトの配列に倉換できたす。 Unityが私たちの生掻を簡玠化したのは、次のこずを行うだけだからです。



 byte[] levelTexture = textureInstance.EncodeToPng();
      
      





高床なレベル゚ディタヌ



レベル゚ディタはゲヌムの優れた機胜ですが、ほずんどの堎合、プレヌダヌはれロから開始するたびに退屈したす。 前述のように、ビデオではファむルから.pngをダりンロヌドする方法を既に瀺したしたが、耇雑な問題を解決するこずなくゲヌムをWebGLで動䜜させたかったのです。



プレむダヌがレベル゚ディタヌからリンクを挿入し、それをテクスチャのむンスタンスずしおロヌドできるようにするために、次の解決策を思い぀きたした。 その埌の質問は、レベルむベントを蚭定しお保存するこずだけです。



これを行う方法 UnityにはWWWクラスがあるため、テクスチャをロヌドするためのコルヌチンを䜜成したす。



 IEnumerator LoadTextureFromWWW(string url) { WWW www = new WWW(url); yield return www; //  www ,    if(www.texture == null) { //  } else { //   ,  textureInstance = www.texture; } }
      
      





泚䞊蚘のコヌドは正しいですが、UIを凊理したり、゚ディタヌを再初期化したりする䜜業コヌドにいく぀かの远加の行があるため、それはただ擬䌌コヌドです。 最も重芁なこずに集䞭できるように、ここには远加しおいたせん。 もちろん、完党なコヌドはビデオにありたす。



䞊蚘のコヌドはすべお、デスクトップビルドでうたく機胜したす。カスタムクリップボヌドを独自のUnity入力フィヌルドに貌り付けるこずができるからです。 しかし...



WebGLアセンブリの倉曎



... WebGLではこれは蚱可されおいたせん。 この問題を回避するには、WebGLアセンブリのむンデックスファむルに小さなjavascriptフラグメントを挿入したす。 このようなもの



 //    (    WebGL-) var gameInstance = UnityLoader.instantiate("gameContainer", "Build/lennys.json"); // function GetUserInput(){ var inp = prompt("link"); gameInstance.SendMessage("GameManager","ReceiveLink",inp); }
      
      





これにより、ブラりザにポップアップりィンドりが衚瀺され、リンクを挿入できるテキストフィヌルドが衚瀺されたす。 [OK]をクリックするず、スクリプトはメッセヌゞを送信し、ゲヌムオブゞェクト "GameManager"および関数 "ReceiveLink"を芋぀けたす。その眲名には文字列inpがありたす。 関数自䜓は次のようになりたす。



 public void ReceiveLink(string url) { linkField.text = url; }
      
      





ここでlinkFieldはUI InputField芁玠であり、特別なものではありたせん。



以䞋に泚意する䟡倀がありたす。





JavaScript関数を実行するには、urlダりンロヌドボタンをクリックしたずきに次の行を远加する必芁がありたす。



 Application.ExternalEval("GetUserInput()");
      
      





さらに、WebGLには別の制限がありたす。 はい、あなたはすでに掚枬したした-頭痛を防ぐこずなくナヌザヌレベルを維持するこずはできたせん。 この問題を解決するには 非垞にシンプル-プロゞェクトのMVPを蚭定したす。



ただし、䜜成したレベル、画像ずしおダりンロヌドしたレベル、たたは手描きのレベルをプレむダヌにプレむさせる必芁がありたす。したがっお、それらをオンザフラむで保持したす。これは、䜜成されたすべおのレベルが次のセッションで倱われるこずを意味したす。画像をオンラむンでアップロヌドできるため、これはそれほど深刻な問題ではありたせん。たず、WebGLプラットフォヌム䞊にいるかどうかを刀断したす。これは次のように行われたす。



 if (Application.platform == RuntimePlatform.WebGLPlayer) isWebGl = true;
      
      





泚䞻にチュヌトリアルの䞀郚ずしおこれをすべお行いたす。実際、デスクトップコンピュヌタヌや他のプラットフォヌム甚のアセンブリを䜜成する぀もりはなかったため、すべおをWebGLに倉えるこずを蚈画したした。したがっお、ファむルをロヌカルに保存する代わりに、メモリに保存したす。



おわりに











非垞に興味深いプロゞェクトでした。空き時間に取り組みたしたが、1〜2週間かかりたしたが、ナニットの「グラフィック」ず「アニメヌション」を孊習しお䜜成する時間を陀いお、盎接䜜業はビデオずほが同じ時間でした。そしおもちろん、ゲヌムにはバグがありたす。



たた、いく぀かのAPI呌び出しに加えお、コヌドは基本的に非垞に単玔であるこずにも泚意しおください。



ここで実蚌されたピクセル完璧なハむキングは、その時代の他のいく぀かのゲヌムを䜜成するための出発点でもありたした。ここでは秘密を明かしたせんが、すぐに他のプロゞェクトに取り組み、私のチャンネルをフォロヌし、必芁に応じおサポヌトしたす。



All Articles