アむ゜メトリックワヌルドの䜜成方法

画像

初めおのディアブロ、゚むゞオブ゚ンパむア、コマンドなど、私たちは皆玠晎らしいアむ゜メトリックゲヌムをプレむしたした。 等尺性ゲヌムずの最初の䌚議で、 2次元 、 3次元、たたはたったく異なる䜕かを尋ねるこずができたす。 アむ゜メトリックゲヌムの䞖界自䜓は、開発者にずっお魅力的な魅力がありたす。 等角投圱の秘密を明らかにし、簡単な等尺性レベルを䜜成しおみたしょう。

このため、JSコヌドでPhaserを䜿甚するこずにしたした。 結果は、むンタラクティブなHTML5アプリケヌションです。

これはPhaser開発のチュヌトリアルではないこずに泚意しおください。アむ゜メトリックシヌンを䜜成するための基本的な抂念を簡単に理解するために䜿甚したす。 Phaserには、 Phaser Isometric Pluginなどの等尺性コンテンツを簡単に䜜成する方法もありたす。

シヌンの䜜成を簡玠化するために、タむルを䜿甚したす。

1.タむルベヌスのゲヌム


2次元タむルゲヌムでは、各芖芚芁玠はタむルず呌ばれる暙準サむズの小さな断片に分割されたす。 これらのタむルのうち、レベルデヌタ通垞は2次元配列に基づいお、ゲヌムワヌルドが圢成されたす。

ほずんどの堎合、タむルゲヌムでは、 トップ ビュヌたたはサむドビュヌが䜿甚されたす。 図に瀺すように、2぀のタむル 草 タむルず壁タむル を備えた暙準の2次元の䞊面図を想像しおみたしょう。



これらのタむルは䞡方ずも同じサむズの正方圢の画像です。぀たり、タむルの高さず幅は同じです。 ゲヌムのレベルは、すべおの偎面が壁で囲たれた芝生であるず想定したす。 この堎合、レベルデヌタは次のような2次元配列です。

[ [1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,0,0,0,1], [1,0,0,0,0,1], [1,0,0,0,0,1], [1,1,1,1,1,1] ] 

ここで、 0は草のタむル、 1は壁のタむルです。 レベルデヌタに埓っおタむルを配眮したら、図に瀺すようにフェンス付きの芝生を䜜成したす。



別の手順を実行しお、コヌナヌタむルず、垂盎壁ず氎平壁の個別のタむルを远加できたす。 これには、さらに5぀のタむルが必芁です。さらに、レベルデヌタを倉曎する必芁がありたす。

 [ [3,1,1,1,1,4], [2,0,0,0,0,2], [2,0,0,0,0,2], [2,0,0,0,0,2], [2,0,0,0,0,2], [6,1,1,1,1,5] ] 

以䞋の画像を参照しおください。レベルデヌタの倀に察応する番号でタむルをマヌクしたした。



タむルレベルの抂念を理解したので、2次元グリッドの単玔な擬䌌コヌドを䜿甚しおレベルを構築する方法を芋おみたしょう。

 for (i, loop through rows) for (j, loop through columns) x = j * tile width y = i * tile height tileType = levelData[i][j] placetile(tileType, x, y) 

䞊蚘の幅ず高さが同じタむルの画像を䜿甚するず、同じレベルの寞法が埗られたす。 この䟋のタむルの幅ず高さが50ピクセルの堎合、レベルの合蚈サむズは300 x 300ピクセルになりたす。

前述のように、通垞、タむルは䞊面たたは偎面ビュヌのゲヌムで䜿甚されたす。 等角投圱ビュヌの堎合、等角投圱を実装する必芁がありたす。

2.等角図


等角投圱法の最良の技術的説明は、Clint Bellengerによるこの蚘事で説明されおいるように思えたす。
カメラを2぀の軞に沿っお傟けたすカメラを暪に45床、次に30床䞋に回転させたす。 これにより、セルの幅が高さの2倍の菱圢グリッドが䜜成されたす。 このスタむルは、戊略ゲヌムずアクションRPGのおかげで人気になりたした。 このフォヌムのキュヌブを芋るず、その3぀の偎面䞊偎ず2぀の偎面が芋えたす。

これは少し耇雑に聞こえたすが、この皮の実装は非垞に簡単です。 2次元空間ず等尺性空間がどのように関連しおいるか、぀たりレベルデヌタずビュヌの関係を理解する必芁がありたす。 トップビュヌのデカルト座暙を等角座暙に倉換する必芁がありたす。 以䞋の図は、グラフィック倉換を瀺しおいたす。



等尺性タむルの配眮


2次元配列に保存されたレベルデヌタず等角投圱ビュヌの関係、぀たりデカルト座暙を等角投圱に倉換するプロセスを単玔化しおみたしょう。 フェンスで囲たれた芝生の等角図を䜜成したす。 このレベルの2次元実装は、幅ず高さでオフセットされた正方圢タむルを䜿甚した2サむクルの単玔な反埩でした。 アむ゜メビュヌの堎合、擬䌌コヌドは同じたたですが、 placeTile()関数は倉曎されたす。

元の関数は、枡されたxおよびyにタむルの画像を描画するだけです。アむ゜メビュヌの堎合、察応するアむ゜メ座暙を蚈算する必芁がありたす。 この方皋匏を以䞋に瀺したす。 isoXおよびisoYは等尺性のXおよびY座暙であり、 cartXおよびcartYはデカルトのXおよびY座暙です。

 //     : isoX = cartX - cartY; isoY = (cartX + cartY) / 2; 

 //     : cartX = (2 * isoY + isoX) / 2; cartY = (2 * isoY - isoX) / 2; 

はい、それだけです。 これらの単玔な方皋匏は、等角投圱の魔法を䜜成したす。 非垞に䟿利なPointクラスを䜿甚しお、あるシステムから別のシステムに倉換するために䜿甚できるPhaserのヘルパヌ関数を次に瀺したす。

 function cartesianToIsometric(cartPt){ var tempPt=new Phaser.Point(); tempPt.x=cartPt.x-cartPt.y; tempPt.y=(cartPt.x+cartPt.y)/2; return (tempPt); } 

 function isometricToCartesian(isoPt){ var tempPt=new Phaser.Point(); tempPt.x=(2*isoPt.y+isoPt.x)/2; tempPt.y=(2*isoPt.y-isoPt.x)/2; return (tempPt); } 

したがっお、 cartesianToIsometricヘルパヌメ゜ッドを䜿甚しお、 placeTileメ゜ッド内で入力2D座暙をアむ゜メトリックに倉換できたす。 これ以倖は、衚瀺コヌドは同じたたですが、新しいタむル画像を䜜成する必芁がありたす。 トップビュヌから叀い正方圢のタむルを䜿甚するこずはできたせん。 䞋の図は、草ず壁の新しい等尺性タむルず完成した等尺性レベルを瀺しおいたす。



信じられないよね 通垞の2次元䜍眮がどのように等尺性に倉換されるかを芋おみたしょう。

 2D point = [100, 100]; //      isoX = 100 - 100; // = 0 isoY = (100 + 100) / 2; // = 100 Iso point == [0, 100]; 

぀たり、入力デヌタ[0, 0] [0, 0]に、 [10, 5] [5, 7.5]倉換されたす。

フェンスで囲たれた芝生の堎合、目的の座暙で配列芁玠の倀が0に等しいかどうかを確認するこずで、通過可胜な領域を決定できたす。 等しい堎合、これは草です。 これを行うには、配列の座暙を決定する必芁がありたす。 この関数を䜿甚しお、デカルト座暙からレベルデヌタでタむルの座暙を芋぀けるこずができたす。

 function getTileCoordinates(cartPt, tileHeight){ var tempPt=new Phaser.Point(); tempPt.x=Math.floor(cartPt.x/tileHeight); tempPt.y=Math.floor(cartPt.y/tileHeight); return(tempPt); } 

ここでは、ほずんどの堎合ず同様に、タむルの高さず幅が同じであるず想定しおいたす。

぀たり、䞀察の画面等尺性座暙がわかっおいるので、関数を呌び出すこずでタむル座暙を芋぀けるこずができたす。

 getTileCoordinates(isometricToCartesian(screen point), tile height); 

画面䞊のこのポむントは、たずえば、マりスカヌ゜ルたたは遞択したアむテムの䜍眮です。

登録ポむント


Flashでは、任意のグラフィックポむントをベヌスポむントたたは[0,0]ずしお遞択できたす。 Phaserのこの類䌌物はPivotです。 たずえば、ポむント[10,20]にグラフを配眮するず、このPivotポむントは[10,20]察応したす。 デフォルトでは、 [0,0]たたはPivotは巊䞊のポむントず芋なされたす。 このコヌドを䜿甚しお䞊蚘のレベルを䜜成しようずするず、目的の結果が埗られたせん。 代わりに、以䞋に瀺すように、壁のない平らな地面になりたす。



これは、タむル画像のサむズが異なるため、壁タむルの高さ属性を考慮しおいないためです。 䞋の図は、私たちが䜿甚するタむルのさたざたな画像ず、デフォルトで配眮されおいる癜い円[0,0]を瀺しおいたす。



基点ピボットを䜿甚する堎合、デフォルトではヒヌロヌは間違った堎所にいるこずに泚意しおください。 たた、デフォルトの基点を䜿甚しお壁を描画するず、壁の高さが倱われるこずに泚意しおください。 右の図は、壁タむルの高さを考慮し、ヒヌロヌが草のタむルの真ん䞭にいるように、正しく配眮する方法を瀺しおいたす。 この問題はさたざたな方法で解決できたす。

  1. すべおのタむルの画像サむズを同じにし、グラフィックを画像に正しく配眮したす。 同時に、倚くの空の領域が各タむル画像に䜜成されたす。
  2. タむルが正しく配眮されるように、各タむルの基点を手動で蚭定したす。
  3. 特定のオフセットでタむルを描きたす。

このチュヌトリアルでは、ベヌスポむントを倉曎できないフレヌムワヌクでも機胜するため、3番目の方法を遞択したした。

3.等尺性座暙の動き


等尺性座暙の文字やオブゞェクトを盎接移動しないでください。 代わりに、デカルト座暙でゲヌムワヌルドのデヌタを管理し、䞊蚘の関数を䜿甚しお画面䞊の䜍眮を曎新したす。 たずえば、キャラクタヌをY軞に沿っお正の方向に前方に移動する堎合、2次元座暙でyプロパティを単玔に増やし、最終䜍眮を等角座暙に倉換できたす。

 y = y + speed; placetile(cartesianToIsometric(new Phaser.Point(x, y))) 

私たちが孊んだすべおの新しい抂念を芁玄し、等尺性の䞖界で動くオブゞェクトの実䟋を実装しおみたしょう。 git゜ヌスコヌドリポゞトリの assetsフォルダヌから必芁なグラフィックリ゜ヌスを䜿甚できたす 。

深さ゜ヌト


フェンスで囲たれた庭でボヌルのむメヌゞを移動しようずするず、 深さによる゜ヌトに問題がありたす。 等尺性の䞖界に移動する芁玠がある堎合、通垞の堎所に加えお、 depthによる䞊べ替えの䞖話をする必芁がありたす。 適切な䞊べ替えにより、画面に近いオブゞェクトがより遠いオブゞェクトの䞊に描画されたす。 この蚘事で説明したように、最も単玔な゜ヌト方法はデカルトY座暙を䜿甚するこずです。画面䞊のオブゞェクトが高ければ高いほど、早く描画されるはずです。 これは単玔な等尺性シヌンではうたく機胜したすが、配列内のタむルの座暙に埓っお移動するプロセスで等尺性シヌン党䜓を再描画する方が良いでしょう。 レベルレンダリングに擬䌌コヌドを䜿甚しお、このアプロヌチを詳しく説明したす。

 for (i, loop through rows) for (j, loop through columns) x = j * tile width y = i * tile height tileType = levelData[i][j] placetile(tileType, x, y) 

オブゞェクトたたはキャラクタヌがタむル[1,1] 、぀たり等角図の䞀番䞊の緑のタむルの䞊にあるず想像しおください。 レベルを正しく描画するには、図に瀺すように、コヌナヌの壁タむル、巊右の壁タむル、地面を描画した埌にキャラクタヌを描画する必芁がありたす。



擬䌌コヌドに埓っおレンダリングサむクルを実行するず、最初に䞭倮のコヌナヌの壁が描画され、その埌、右䞊隅のすべおの壁が右コヌナヌに到達するたでサむクルが描画され続けたす。 次のサむクルで、圌はキャラクタヌの巊偎に壁を描き、次にキャラクタヌが立぀草のタむルを描きたす。 これがキャラクタヌが占めるタむルであるず刀断したため、芝生タむルの埌にキャラクタヌを描画したす。 したがっお、キャラクタヌのタむルの隣にある無料の3぀の草のタむルに壁がある堎合、これらの壁はキャラクタヌず重なり、深さによる適切な゜ヌトを保蚌したす。

4.グラフィックの䜜成


等尺性グラフィックスは、ピクセルアヌトである堎合がありたすが、そうである必芁はありたせん。 等尺性ピクセルアヌトを䜿甚する堎合、必芁なものがすべお含たれおいるRhysDマニュアルを孊習するず圹立ちたす 。 理論はりィキペディアで孊ぶこずができたす。

アむ゜メ図を䜜成するずきは、次の芏則に埓う必芁がありたす。


単䞀のタむルサむズより倧きい等尺性タむルは、深さで゜ヌトするずきに問題を匕き起こしたす。 このような問題は、次の蚘事で察凊されおいたす。

関連する投皿



5.等尺性文字


たず、ゲヌム内でどの方向に移動できるかを決定する必芁がありたす。 通垞、ゲヌムでは4぀たたは8぀の方向に移動できたす。 次の図を芋お、2次元空間ず等尺性空間の関係を理解し​​おください。



トップビュヌのゲヌムでは、䞊キヌを抌すずキャラクタヌが垂盎に䞊に移動したすが、等尺性ゲヌムでは右䞊の角に向かっお45床の角床で移動したす。

トップビュヌの堎合、䞀方向に芋えるキャラクタヌアニメヌションのセットを1぀䜜成し、すべおのアニメヌションを回転させるだけです。 アむ゜メトリックキャラクタヌのグラフィックの堎合、蚱容可胜な各方向のアニメヌションを䜜成する必芁がありたす。぀たり、8方向の移動の堎合、アクションごずに8぀のアニメヌションを䜜成する必芁がありたす。

理解を容易にするために、方向は通垞「北」、「北西」、「西」などず呌ばれたす。 文字フレヌムでは、図は南東から時蚈回りに固定䜍眮のフレヌムを瀺しおいたす。



キャラクタヌをタむルず同じように配眮したす。 キャラクタヌの移動は、デカルト座暙を蚈算し、それらを等尺性に倉換するこずにより実行されたす。 キヌボヌドを䜿甚しおキャラクタヌを制埡するずしたす。

2぀の倉数dXずdYを割り圓おたす。その倀は、抌されたコントロヌルキヌに䟝存したす。 デフォルトでは、これらの倉数は0であり、以䞋の衚に埓っお倀が割り圓おられたす。ここで、 、 、 、およびは、それぞれ、 侊例 、 巊右の方向キヌを意味したす。 キヌの䞋の倀1はキヌが抌されおいるこずを意味し、 0抌されおいないこずを意味したす。

       dX dY ================ 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 0 -1 0 0 1 0 1 0 0 0 0 1 -1 0 1 0 1 0 1 1 1 0 0 1 -1 1 0 1 1 0 1 -1 0 1 0 1 -1 -1 

dXずdY倀を䜿甚しお、次のようにデカルト座暙を曎新できたす。

 newX = currentX + (dX * speed); newY = currentY + (dY * speed); 

したがっお、 dXずdYは、抌されたキヌに応じお、XずYでのキャラクタヌの䜍眮の倉化を衚したす。 䞊蚘のように、新しい等角座暙を簡単に蚈算できたす。

 Iso = cartesianToIsometric(new Phaser.Point(newX, newY)) 

新しい等尺性の䜍眮を受け取ったら、キャラクタヌをこの䜍眮に移動する必芁がありたす。 dXずdY倀に基づいお、キャラクタヌがどの方向を向いおいるかを理解し、適切なアニメヌションを䜿甚できたす。 キャラクタヌを移動した埌、キャラクタヌのタむル座暙が倉曎される可胜性があるため、深床による適切な䞊べ替えでレベルを再描画するこずを忘れないでください。

衝突認識


衝突の認識は、オブゞェクトの新しい蚈算された䜍眮のタむルが通過できないかどうかをチェックするこずによっお実行されたす。 したがっお、新しい䜍眮を芋぀けた埌、すぐにキャラクタヌを移動するこずはできたせん。たず、この堎所がどの皮類のタむルを占有しおいるかを確認する必芁がありたす。

 tile coordinate = getTileCoordinates(isometricToCartesian(current position), tile height); if (isWalkable(tile coordinate)) { moveCharacter(); } else { //  ; } 

isWalkable()関数では、指定された座暙のレベルデヌタ配列の倀がトラバヌスタむルかどうかを確認したす。 たた、キャラクタヌが芋えないタむルに遭遇した堎合に備えお、キャラクタヌが動いおいない堎合でも 、キャラクタヌが芋おいる方向を曎新する必芁がありたす。

これは正しい解決策のように思えたすが、ボリュヌムのないオブゞェクトに察しおのみ機胜したす。 衝突を蚈算するために、1぀のポむントキャラクタヌの䞭心点のみを考慮したす。 実際、特定の2次元の䞭心点から4぀の角床をすべお芋぀け、これらすべおの角床の衝突を蚈算する必芁がありたす。 コヌナヌのいずれかが貫通できないタむルに萜ちた堎合、キャラクタヌを移動するこずはできたせん。

文字による深さ゜ヌト


どんなに䞍自然に芋えおも、同じ画像サむズの等尺性の䞖界のキャラクタヌずツリヌタむルを考えおみたしょう。

深さによる゜ヌトの理解を深めるために、文字のX座暙ずY座暙がツリヌの座暙よりも小さい堎合、ツリヌが文字ず重なるこずを理解する必芁がありたす。 キャラクタヌのX座暙ずY座暙がツリヌの座暙よりも倧きい堎合、キャラクタヌはツリヌに重なりたす。 X座暙が等しい堎合、決定はY座暙でのみ行われたす。Y座暙が倧きいオブゞェクトは別のオブゞェクトず重なりたす。 Y座暙が䞀臎する堎合、決定はXでのみ行われたす。Xが倧きいオブゞェクトは他のオブゞェクトず重なりたす。

䞊蚘のように、アルゎリズムの単玔化バヌゞョンは、離れたタむル぀たり、 tile[0][0] から隣接するレベルたで、行ごずにレベルを単玔に描画するこずにありたす。 キャラクタヌがタむルを占有する堎合、最初に土地タむルを描画し、次にキャラクタヌタむルを描画したす。 キャラクタヌが壁のタむルを占有できないため、これはうたく機胜したす。

6.デモの時間です


Phaserのデモはこちらです。 クリックしおむンタラクティブ領域に切り替えおから、矢印キヌでキャラクタヌを制埡したす。 斜めに移動するには、2぀のキヌを抌したす。



7.収集されたアむテム


収集されたアむテムは、レベルで拟うこずができるアむテムであり、通垞それらを螏むだけです。 たずえば、コむン、クリスタル、匟薬などです。

以䞋に瀺すように、アむテムデヌタはレベルデヌタに盎接保存できたす。

 [ [1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,8,0,0,1], [1,0,0,8,0,1], [1,0,0,0,0,1], [1,1,1,1,1,1] ] 

このレベルのデヌタでは、草のタむル䞊のオブゞェクトを瀺すために8を䜿甚したす前述のように1ず0は草ず壁を瀺したす。 これは、オブゞェクトの画像が重ねられた草タむルのタむル画像です。 このロゞックによれば、アむテムを配眮できるタむルごずに2぀の異なる状態のタむルが必芁になりたす。1぀はアむテムあり、もう1぀はアむテムなしで、アむテムを受け取った埌に衚瀺されたす。

通垞、アむ゜メトリックグラフィックスには、倚くの通行可胜なタむルがありたす。 䞊蚘のアプロヌチを䜿甚する堎合、持ち䞊げるN個のオブゞェクトがある堎合、既存の30個のタむルに察しおN x 30が必芁になりたすが、これはあたり効果的ではありたせん。 したがっお、このような組み合わせを動的に䜜成する必芁がありたす。 この問題を解決するには、䞊蚘で䜿甚したのず同じ方法でキャラクタヌを配眮したす。 アむテムのあるタむルに到達したら、たず草のタむルを描画し、次にその䞊にアむテムを配眮したす。 したがっお、30個の通過可胜なタむルに加えお、N個のアむテムタむルだけが必芁ですが、レベルデヌタの各組み合わせを瀺す数倀が必芁です。 N x 30の倀を入力しないようにするために、 pickupArrayずは別に、アむテムデヌタを保存するための個別のpickupArrayを保存できたす。 アむテムの完成したレベルを以䞋に瀺したす。



この䟋では、よりシンプルにし、オブゞェクトに別の配列を䜿甚したせん。

アむテムを拟う


オブゞェクト認識は、衝突認識ず同じ方法で実行されたすが、キャラクタヌを移動した埌です。

 if(onPickupTile()){ pickupItem(); } function onPickupTile(){//,        return (levelData[heroMapTile.y][heroMapTile.x]==8); } 

関数onPickupTile()ではlevelData、座暙の配列の倀がheroMapTileオブゞェクトを含むタむルかどうかを確認したす。levelDataこのタむルの座暙の配列内の番号は、アむテムのタむプを瀺したす。キャラクタヌを移動する前に衝突をチェックしたすが、アむテムは埌にチェックされたす衝突の堎合、キャラクタヌはすでに通過できないタむルで占められおいる堎合、ポむントを取るこずができたせん。オブゞェクトの堎合、キャラクタヌはタむルに自由に移動できたす。

たた、衝突デヌタは通垞倉曎されるこずはなく、アむテムをピックアップするずアむテムデヌタが倉曎されるこずにも泚意しおください。 これは通垞、配列の倀を、levelData䟋えばから8に倉曎するだけ0です。

これは問題に぀ながりたす。レベルを再起動する必芁がある堎合、぀たり、すべおのオブゞェクトを元のポむントに埩元する必芁がある堎合はどうなりたすかlevelDataアむテムが発生するず配列が倉化するため、これに関する情報はありたせん。解決策は、ゲヌム䞭にレベル配列のコピヌを䜿甚し、配列を倉曎しないこずlevelDataです。たずえば、levelDataand を䜿甚しおlevelDataLive[]、レベルの最初の最初の最埌のクロヌンを䜜成し、ゲヌム䞭にのみ倉曎しlevelDataLive[]たす。

たずえば、各アむテムを収集した埌、無料の芝生タむルにランダムなアむテムを䜜成し、倀を増やしたすpickupCount。機胜はpickupItem次のずおりです。

 function pickupItem(){ pickupCount++; levelData[heroMapTile.y][heroMapTile.x]=0; //    spawnNewPickup(); } 

キャラクタヌがタむル䞊にあるずきはい぀でもアむテムをチェックするこずに気づいたでしょう。これは1秒間に数回発生する可胜性がありたすナヌザヌが移動したずきのみチェックしたすが、1぀のタむルで䜕床も繰り返すこずができたすが、䞊蚘のロゞックは正しく実行されたす。我々は、アレむデヌタ割り圓おたらlevelData倀を0オブゞェクトを持ち䞊げるの最初の怜出時に、埌続のすべおのチェックがonPickupTile()タむルに戻りたすfalse。このむンタラクティブな䟋をご芧ください。



8.トリガヌタむル


名前が瀺すずおり、トリガヌタむルは、プレヌダヌが螏むかキヌを抌すずアクションをトリガヌしたす。プレむダヌを別の堎所にテレポヌトしたり、ゲヌトを開いたり、敵を䜜成したりできたす。ある意味では、収集されるアむテムは単に特殊なタむプのトリガヌです。プレむダヌがコむンでタむルを螏むず、コむンが消えおカりンタヌが䞊がりたす。

プレむダヌを別のレベルに導くドアを実装する方法を芋おみたしょう。ドアの暪のタむルがトリガヌになりたす。プレヌダヌがxキヌを抌すず、別のレベルに移動したす。



レベルを倉曎するには、珟圚の配列levelDataを新しいレベルの配列に眮き換えおから、新しい䜍眮ず方向を割り圓おるだけです。heroMapTileキャラクタヌ。通過できるドアのある2぀のレベルがあるずしたす。ドアの隣の地面のタむルは䞡方のレベルでアクティブなタむルになるため、レベルに衚瀺される新しいキャラクタヌの䜍眮ずしお䜿甚できたす。

ここでの実装のロゞックは、収集されたアむテムのロゞックず同じです。トリガヌタむルの倀を保存するには、再床arrayを䜿甚しlevelDataたす。この䟋で2は、ドアのあるタむルを意味し、その隣の倀がトリガヌになりたす。私が䜿甚しおいる101ず102、100を超える倀の倧きい持぀任意のタむルがタむルアクティブになるず刀断し、マむナス100の倀は、それがリヌドしたレベルです。

 var level1Data= [[1,1,1,1,1,1], [1,1,0,0,0,1], [1,0,0,0,0,1], [2,102,0,0,0,1], [1,0,0,0,1,1], [1,1,1,1,1,1]]; var level2Data= [[1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,8,0,0,1], [1,0,0,0,101,2], [1,0,1,0,0,1], [1,1,1,1,1,1]]; 

トリガヌむベントを確認するためのコヌドを以䞋に瀺したす。

 var xKey=game.input.keyboard.addKey(Phaser.Keyboard.X); xKey.onUp.add(triggerListener);//  Signal listener   up function triggerListener(){ var trigger=levelData[heroMapTile.y][heroMapTile.x]; if(trigger>100){//  trigger-=100; if(trigger==1){//   1 levelData=level1Data; }else {//   2 levelData=level2Data; } for (var i = 0; i < levelData.length; i++) { for (var j = 0; j < levelData[0].length; j++) { trigger=levelData[i][j]; if(trigger>100){//         heroMapTile.y=j; heroMapTile.x=i; heroMapPos=new Phaser.Point(heroMapTile.y * tileWidth, heroMapTile.x * tileWidth); heroMapPos.x+=(tileWidth/2); heroMapPos.y+=(tileWidth/2); } } } } } 

この関数triggerListener()は、指定された座暙でトリガヌデヌタ配列の倀が100より倧きいかどうかを確認したす。もしそうなら、タむル倀から100を匕くこずで、どのレベルに行く必芁があるかを決定したす。この関数は、新しいトリガヌでタむルトリガヌを芋぀けlevelDataたす。これは、キャラクタヌ䜜成の座暙になりたす。xキヌが攟されたずきにトリガヌを起動したした。抌されたキヌを単玔に読み取るず、キヌが抌されおいる間にレベル間で移動するサむクルで自分自身を芋぀けるこずができたす。これは、キャラクタヌが垞にアクティブなタむルの新しいレベルで䜜成されるためです。これが実際のデモです。螏みながらアむテムを収集し、ドアの前に立っおxを抌しおレベルを倉曎したす。





9.シェル


匟䞞、魔法、ボヌルなど、特定の速床で特定の方向に移動するシェルを呌び出したす。キャラクタヌに関連するものはすべお、シェルに適甚されたす。ただし、高さは䟋倖です。シェルは通垞、地面では転がりたせんが、特定の高さで飛行したす。匟䞞はキャラクタヌのり゚ストレベルで飛ぶため、ボヌルはゞャンプするこずさえできたす。

興味深いこずに、等尺性の高さは2次元偎面図の高さに察応しおいたすが、サむズは小さくなっおいたす。耇雑な倉換はありたせん。デカルト座暙のボヌルが地面から10ピクセル䞊にある堎合、アむ゜メトリック座暙では、10ピクセルたたは6ピクセルで地面より䞊になりたす。 この堎合、察応する軞はY軞になりたす。

フェンスで囲たれた庭の呚りを疟走するボヌルを実珟しおみたしょう。リアルさを远加するには、ボヌルに圱を远加したす。必芁なのは、バりンスの高さの倀をボヌルの等尺性倀Yに远加するこずです。リバりンドの高さの倀は、重力に応じおフレヌムごずに倉化し、ボヌルが地面に觊れるずすぐに、Y軞に沿っお珟圚の速床の笊号を倉曎し

たす。倉数によっおボヌルの跳ね返りの匷さを瀺したすzValue。たず、ボヌルのバりンス力が100、぀たりであるずしzValue = 100たす。

私たちは2぀の倉数を䜿甚したす。incrementValue最初は重芁な倉数0、gravityもう1぀は重芁な倉数です-1。各フレヌムで枛算したすincrementValueアりトzValue及び枛算gravityからincrementValue枛衰効果を䜜成したす。にzValue達する0ず、ボヌルが地面に到達したこずを意味したす。この時点で、signを倉曎しincrementValue、それに乗算-1し、正の数に倉換したす。これは、次のフレヌムからボヌルが䞊に移動し始める、぀たりバりンドするこずを意味したす。

コヌドでは次のようになりたす。

 if(game.input.keyboard.isDown(Phaser.Keyboard.X)){ zValue=100; } incrementValue-=gravity; zValue-=incrementValue; if(zValue<=0){ zValue=0; incrementValue*=-1; } 

アむ゜メビュヌの堎合、コヌドも同じたたで、わずかな違いがありたすzValue。より小さな初期倀を䜿甚したす。以䞋はzValue、yレンダリング䞭にボヌルのアむ゜メトリック座暙の倀にどのように远加されるかを瀺しおいたす。

 function drawBallIso(){ var isoPt= new Phaser.Point();//       var ballCornerPt=new Phaser.Point(ballMapPos.x-ball2DVolume.x/2,ballMapPos.y-ball2DVolume.y/2); isoPt=cartesianToIsometric(ballCornerPt);//           gameScene.renderXY(ballShadowSprite,isoPt.x+borderOffset.x+shadowOffset.x, isoPt.y+borderOffset.y+shadowOffset.y, false);//     gameScene.renderXY(ballSprite,isoPt.x+borderOffset.x+ballOffset.x, isoPt.y+borderOffset.y-ballOffset.y-zValue, false);//     } 

むンタラクティブな䟋を参照しおください。



圱が挔じる圹割は非垞に重芁であり、この幻想にリアリズムを加えたす。たた、2぀のスクリヌン座暙xおよびyを䜿甚しお、アむ゜メトリック座暙で3次元を衚すこずに泚意しおください。スクリヌン座暙のY軞はアむ゜メトリック座暙のZ軞でもありたす。これはわかりにくいかもしれたせん。

10.パスを芋぀けおその䞊を移動する


パスを芋぀けおそれに沿っお移動するのは、かなり耇雑なプロセスです。2぀のポむント間のパスを芋぀けるには、さたざたなアルゎリズムを䜿甚したさたざたな゜リュヌションがありたすlevelDataが、2次元配列であるため、すべおが可胜な限り単玔です。プレヌダヌが占有できる䞀意のノヌドを明確に定矩しおおり、それらを通過できるかどうかを簡単に確認できたす。

関連する投皿



この蚘事では、パス怜玢アルゎリズムの詳现な抂芁は倧きすぎたすが、最も䞀般的な方法、最短パスアルゎリズム、その最も有名な実装はA *およびダむクストラのアルゎリズムです。

私たちの目暙は、開始ノヌドず終了ノヌドを接続するノヌドを芋぀けるこずです。開始ノヌドから、8぀のすべおの隣接ノヌドを蚪問し、蚪問枈みずしおマヌクしたす。このプロセスは、蚪問された新しいサむトごずに再垰的に繰り返されたす。

各スレッドは、蚪問したノヌドを远跡したす。隣接ノヌドに行くずき、すでに蚪れたノヌドはスキップされたす再垰は停止したす。プロセスは最埌のノヌドに到達するたで続きたす。最埌のノヌドでは再垰が完了し、移動したパス党䜓がノヌドの配列ずしお返されたす。゚ンドノヌドに到達できない堎合がありたす。぀たり、パスの怜玢が倱敗したす。通垞、ノヌド間にいく぀かのパスがありたす。この堎合、最小数のノヌドを持぀それらの1぀を遞択したす。

道を探す


明確に定矩されたアルゎリズムに関しおは、車茪を再発明するのは愚かなこずなので、既存の゜リュヌションを䜿甚しお方法を芋぀けたす。PhaserではJavaScript゜リュヌションが必芁なので、EasyStarJSを遞択したした。パス怜玢゚ンゞンは次のように初期化されたす。

 easystar = new EasyStar.js(); easystar.setGrid(levelData); easystar.setAcceptableTiles([0]); easystar.enableDiagonals();//  ,      easystar.disableCornerCutting();//         

配列は以降levelDataのみを含む0ず1私たちはノヌドの配列に盎接枡すこずができたす。0通過可胜なノヌドを瀺した倀。たた、斜めに移動する機胜をオンにしたしたが、移動が通過できないタむルの角に近づくずオフにしたした。

これは、斜めの動きでキャラクタヌが通過できないタむルに衝突する可胜性があるためです。この堎合、衝突認識システムはキャラクタヌの通過を蚱可したせん。さらに、この䟋ではキャラクタヌが人工知胜ずずもに移動するため、衝突認識が完党に削陀されおいるこずに泚意しおください。これは必須ではありたせん。

レベル内の空きタむルのクリックを認識し、関数を䜿甚しおパスを蚈算したすfindPath。コヌルバックメ゜ッドplotAndMoveは、䜜成されたパスのノヌドの配列を取埗したす。芋぀かったパスをにマヌクしたす。

 game.input.activePointer.leftButton.onUp.add(findPath) function findPath(){ if(isFindingPath || isWalking)return; var pos=game.input.activePointer.position; var isoPt= new Phaser.Point(pos.x-borderOffset.x,pos.y-borderOffset.y); tapPos=isometricToCartesian(isoPt); tapPos.x-=tileWidth/2;//       -  tapPos.y+=tileWidth/2; tapPos=getTileCoordinates(tapPos,tileWidth); if(tapPos.x>-1&&tapPos.y>-1&&tapPos.x<7&&tapPos.y<7){//    if(levelData[tapPos.y][tapPos.x]!=1){//   isFindingPath=true; //    easystar.findPath(heroMapTile.x, heroMapTile.y, tapPos.x, tapPos.y, plotAndMove); easystar.calculate(); } } } function plotAndMove(newPath){ destination=heroMapTile; path=newPath; isFindingPath=false; repaintMinimap(); if (path === null) { console.log("No Path was found."); }else{ path.push(tapPos); path.reverse(); path.pop(); for (var i = 0; i < path.length; i++) { var tmpSpr=minimap.getByName("tile"+path[i].y+"_"+path[i].x); tmpSpr.tint=0x0000ff; //console.log("p "+path[i].x+":"+path[i].y); } } } 



道に沿った動き


ノヌドの配列の圢匏でパスを受け取ったら、キャラクタヌを匷制的にそれに沿っお移動させる必芁がありたす。

クリックしたタむルにキャラクタヌを移動させたいずしたす。たず、キャラクタヌが占めおいるノヌドずクリックしたノヌドの間のパスを探したす。パスが芋぀かったら、文字をノヌド配列の最初のノヌドに移動し、それを宛先ずしおマヌクする必芁がありたす。宛先ノヌドに到達したら、ノヌドの配列にさらにノヌドがあるかどうかを確認し、ある堎合は、最終ノヌドに到達するたで次のノヌドを宛先ずしおマヌクしたす。

たた、ノヌドに到達するたびに、珟圚のノヌドず新しい宛先ノヌドに基づいおプレヌダヌの方向を倉曎したす。ノヌド間では、目的のノヌドに到達するたで正しい方向に歩きたす。これは非垞に単玔なAIであり、この䟋では、aiWalk以䞋に郚分的に瀺すメ゜ッドで実装されおいたす。

 function aiWalk(){ if(path.length==0){//  if(heroMapTile.x==destination.x&&heroMapTile.y==destination.y){ dX=0; dY=0; isWalking=false; return; } } isWalking=true; if(heroMapTile.x==destination.x&&heroMapTile.y==destination.y){//   ,  ,   //  ,         stepsTaken++; if(stepsTaken<stepsTillTurn){ return; } console.log("at "+heroMapTile.x+" ; "+heroMapTile.y); //     heroMapSprite.x=(heroMapTile.x * tileWidth)+(tileWidth/2)-(heroMapSprite.width/2); heroMapSprite.y=(heroMapTile.y * tileWidth)+(tileWidth/2)-(heroMapSprite.height/2); heroMapPos.x=heroMapSprite.x+heroMapSprite.width/2; heroMapPos.y=heroMapSprite.y+heroMapSprite.height/2; stepsTaken=0; destination=path.pop();//     if(heroMapTile.x<destination.x){ dX = 1; }else if(heroMapTile.x>destination.x){ dX = -1; }else { dX=0; } if(heroMapTile.y<destination.y){ dY = 1; }else if(heroMapTile.y>destination.y){ dY = -1; }else { dY=0; } if(heroMapTile.x==destination.x){ dX=0; }else if(heroMapTile.y==destination.y){ dY=0; } //...... } } 

正しいマりスクリックポむントを陀倖する必芁がありたす。これを行うには、壁タむルや他の貫通できないタむルではなく、歩行可胜な領域をクリックしたず刀断したす。

AIのコヌディングのもう1぀の興味深い点珟圚のノヌドに到達するずすぐに、ノヌドの配列内の次のタむルにキャラクタヌが盎面するのは望たしくありたせん。代わりに、キャラクタヌがタむルに数歩行くのを埅っおから、次の目的地を探し始める必芁がありたす。たた、タヌンの盎前に珟圚のタむルの䞭倮にキャラクタヌを手動で配眮しお、すべおが最高に芋えるようにするこずをお勧めしたす。

動䜜デモを芋るこずができたす。



11.等尺スクロヌル


レベル領域が画面領域よりもはるかに倧きい堎合、スクロヌルする必芁がありたす。



衚瀺可胜な画面領域は、レベル領域党䜓の倧きな長方圢の䞭の小さな長方圢ず芋なすこずができたす。スクロヌルずは、単に内偎の長方圢を内偎に移動するこずです。通垞、スクロヌル凊理䞭、画面の長方圢内のキャラクタヌの䜍眮は䞀定のたたです。ほずんどの堎合、画面の䞭倮にありたす。興味深いこずに、スクロヌルを実装するには、内偎の四角圢のコヌナヌポむントを远跡するだけです。

デカルト座暙で指定するこのコヌナヌポむントは、レベルデヌタのタむルの1぀に該圓したす。スクロヌルするには、デカルト座暙のX軞ずY軞に沿っおコヌナヌポむントの䜍眮を増やしたす。これで、このポむントを等角座暙に倉換し、それらを䜿甚しお画面を描画できたす。

等角空間での新しい倉換倀は、画面の角床、぀たりnewである必芁がありたす(0, 0)。したがっお、レベルデヌタを解析およびレンダリングするずき、この倀を各タむルの等角䜍眮から枛算したす。新しいタむルの䜍眮が画面内にあるかどうかを刀断できたす。

たたは、サむズX x Yの等尺性タむルのグリッドを画面䞊に描画しお、レンダリングサむクルが倧きなレベルで有効になるようにするこずができたす。

これらのすべおの手順は、次のように衚珟できたす。


 var cornerMapPos=new Phaser.Point(0,0); var cornerMapTile=new Phaser.Point(0,0); var visibleTiles=new Phaser.Point(6,6); //... function update(){ //... if (isWalkable()) { heroMapPos.x += heroSpeed * dX; heroMapPos.y += heroSpeed * dY; //     cornerMapPos.x -= heroSpeed * dX; cornerMapPos.y -= heroSpeed * dY; cornerMapTile=getTileCoordinates(cornerMapPos,tileWidth); //    heroMapTile=getTileCoordinates(heroMapPos,tileWidth); //       renderScene(); } } function renderScene(){ gameScene.clear();//  ,    var tileType=0; //    var startTileX=Math.max(0,0-cornerMapTile.x); var startTileY=Math.max(0,0-cornerMapTile.y); var endTileX=Math.min(levelData[0].length,startTileX+visibleTiles.x); var endTileY=Math.min(levelData.length,startTileY+visibleTiles.y); startTileX=Math.max(0,endTileX-visibleTiles.x); startTileY=Math.max(0,endTileY-visibleTiles.y); //   for (var i = startTileY; i < endTileY; i++) { for (var j = startTileX; j < endTileX; j++) { tileType=levelData[i][j]; drawTileIso(tileType,i,j); if(i==heroMapTile.y&&j==heroMapTile.x){ drawHeroIso(); } } } } function drawHeroIso(){ var isoPt= new Phaser.Point();//       var heroCornerPt=new Phaser.Point(heroMapPos.x-hero2DVolume.x/2+cornerMapPos.x,heroMapPos.y-hero2DVolume.y/2+cornerMapPos.y); isoPt=cartesianToIsometric(heroCornerPt);//        2D- gameScene.renderXY(sorcererShadow,isoPt.x+borderOffset.x+shadowOffset.x, isoPt.y+borderOffset.y+shadowOffset.y, false);//     gameScene.renderXY(sorcerer,isoPt.x+borderOffset.x+heroWidth, isoPt.y+borderOffset.y-heroHeight, false);//     } function drawTileIso(tileType,i,j){//    var isoPt= new Phaser.Point();//       var cartPt=new Phaser.Point();//    . cartPt.x=j*tileWidth+cornerMapPos.x; cartPt.y=i*tileWidth+cornerMapPos.y; isoPt=cartesianToIsometric(cartPt); //         . if(tileType==1){ gameScene.renderXY(wallSprite, isoPt.x+borderOffset.x, isoPt.y+borderOffset.y-wallHeight, false); }else{ gameScene.renderXY(floorSprite, isoPt.x+borderOffset.x, isoPt.y+borderOffset.y, false); } } 

コヌナヌポむントの増分は、キャラクタヌが移動するずきにキャラクタヌの䜍眮を曎新する方向ずは反察の方向に発生するこずに泚意しおください。これにより、キャラクタヌは画面に察しお同じ堎所に残りたす。この䟋を芋おください矢印キヌを䜿甚しおスクロヌルし、マりスをクリックしお衚瀺されおいるグリッドを拡倧したす。



いく぀かのメモ


おわりに


このチュヌトリアルは、䞻に等尺性ゲヌムの䞖界を孊ぶ初心者を察象ずしおいたす。ここで玹介する抂念の倚くには、他のやや耇雑な゜リュヌションがあり、私は意図的に最も単玔なものを遞択したした。

このマニュアルでは、すべおの問題を解決できるわけではありたせんが、受け取った情報により、これらの抂念を開発しおより耇雑な゜リュヌションを䜜成できたす。たずえば、実装された深さによる単玔な䞊べ替えは、耇数のフロアずプラットフォヌムタむルがあるフロアから別のフロアに移動するレベルの堎合には圹に立ちたせん。しかし、これは別のチュヌトリアルのタスクです。

Source: https://habr.com/ru/post/J332922/


All Articles