JSLを管理するプロジェクトを試してみましょう
ずっと前に私は横スクロールを書くことを提案しましたhttps://community.jmp.com/t5/Uncharted/Wild-Road-Map/bc-p/20996/highlight/true#M5 。 80年代にゲームをプレイしていなかった場合は、 https://en.wikipedia.org/wiki/Parallax_scrollingいくつかの例があります。 横スクロールは、キャラクターが左から右に移動する2Dゲームであり、背景には3Dの奥行き感を与えるために、さまざまな速度でスライドする複数のレイヤーが含まれる場合があります。 他のバリエーションとしては、垂直スクロール寸法のプラットフォームジャンパーもあります。
解決すべき問題があります:
- ゲーム開発(JMPはこれを書きやすくすることができますか?)
- ゲームグラフィックス(どのように見えますか?)
- ゲームオーディオ(あなたはそれを聞きましたか?)
- ゲーム入力(どのように移動しますか?)
- ゲームプレイ(何か起こりますか?)
1.ゲーム開発
私はJMPプロジェクトを使用しています(そして最近、JMP 16自己完結型プロジェクトに切り替えています)。5〜10個の独立したファイルを持つプロジェクトの自己完結型プロジェクトが好きになり始めています。 開いているファイルのタブとログウィンドウのドッキングポイントがあります。 自己完結型のプロジェクトは、すべてを1つのファイル(zip、カバーの下)に保存し、開いたり閉じたりすると自動的に解凍および再パックします。
グラフィック用のファイルとオーディオ用のファイルとゲームプレイ用のファイルは別々の比較的小さなファイルであるため、このゲームを機能させるのは簡単でした。 これにより、ゲームキャラクター間のゲームプレイを管理するJSLなどを簡単に見つけることができます。 プロジェクトでは、次のようにファイルがタブに表示されます。
A JMP project with several files.複数のファイルを含むJMPプロジェクト。
このプロジェクトが開くとすぐに9つのファイルが開きます。ゲーム内の吸血鬼のクラス定義を保持するファイルが現在表示されています。 ファイルがプロジェクトに保存される時期とプロジェクトがまだ保存される時期に完全に満足していないため、プロジェクトを閉じる前に左上にある強調表示されたアイコンを使用します。 また、左下にログをドッキングしました。 プロジェクトは他のJMPや相互に分離されているように見えるため、独自のログがあります。
MakeAddinファイルはゲームの一部ではありませんが、ゲームの開発の一部です。 そのタブが開いていて、それを^ R(実行)すると、アドインが再生成されます。 別のタブを実行するとどうなりますか? そのJSLが実行されますが、これはゲームロジックの開始ではない可能性があります。 しかし、私はそれをいつも間違って行っていたので、次のように修正しました:
- 他のすべてのファイルを適切な順序で含むメインファイルgame.jslがあります。
- ほとんどのファイルは独立しており、クラスまたは名前空間を作成するだけです。
- sidescroller.jslファイルには、他のすべてのファイルがロードされている必要があります(これがメインのゲームループです)。
独立したファイルすべて終わりと
try(include("game.jsl"),if(!contains(char(exception_msg),"includes itself through a closed path"),show(exception_msg)));これにより、現在のファイルの後にgame.jslファイルが実行されます。 game.jslファイルは、現在のファイルを再インクルードしようとし、無視される再帰的なインクルードエラー、または報告されるその他のエラーをスローします。 最後である必要があるsidescrollerファイルには、そのtryステートメントが最初ライン。 そうすれば、game.jslファイルには、sidescrollerファイルの残りの部分が実行される前に他のすべてのものが含まれます。
game.jslファイルは次のようになります
files = {"AUDIO.jsl", "RGBA.jsl", "ASSETS.jsl", "LAYERS.jsl", "VAMPIRE.jsl", "BEAR.jsl",
// currently SIDESCROLLER contains the game logic. All of the files include this file,
// at the end, except for SIDESCROLLER which include it at the start. SIDESCROLLER is
// dependent on all of the other files being loaded. The other files are not order-dependent.
"SIDESCROLLER.jsl"};
For( iFile = 1, iFile <= N Items( files ), iFile += 1,
Try(
Include( files[iFile] ), // this exception is EXPECTED if you start one of the other files; it is OK!
If( !Contains( Char( exception_msg ), "includes itself through a closed path" ),
Show( files[iFile], exception_msg )
)
)
);これで、編集したファイルを実行できます。 ただし...複数のファイルを編集する場合は、明示的に実行されていないファイルを必ず保存してください。 インクルードは、編集タブのコピーではなく、ディスクのコピーを取得します。 ただし、ファイルを1つだけ変更する場合は、上記のインクルードの設定方法により、保存せずに現在の編集タブのコピーが使用されます。
ウィッシュリスト:
- 実行する前にすべてを保存します(または、編集タブのコピーを使用してインクルードします)
- プロジェクトの実行時に開始ファイルを指定します(on-project-loadファイルではありません)
- タブの保存とプロジェクトの保存がどのように同じ/異なるかを文書化する
- JSLからプロジェクトのsave-allコマンドを実行する方法
2.ゲームグラフィックス
このプロジェクトでは、JSLで生成されたグラフィックとダウンロードされたビットマップグラフィックの両方を使用します。 以下は、JSLが作成した青い空、山、木、砂利道です。 岩壁の面はタイル張りのビットマップであり、吸血コウモリ、コウモリ、クマはアニメーション化されたビットマップシーケンスです。 最後にクレジット。
The little bear is idling; the vampires are releasing bats or disappearing.小さなクマはアイドリングしています。吸血鬼はコウモリを解放するか、姿を消しています。
RGBA.jslファイルは、JSLグラフィックの重要な部分です。 JMPは、グラフで透明度を使用して描画したり、グラフで透明度を使用してビットマップを使用したりできますが、たとえば、透明な背景に折れ線グラフを作成し、そのビットマップを透明な背景で取得する簡単な方法はありません。 (グラフを除くすべてを透明に保存するオプション、レポートPNG画像の透明な背景があります。 保存したSVGを編集して背景を削除することはできますが、それを透明な背景のPNGに戻すには別のプログラムが必要です。)
ウィッシュリスト
RGBAファイルは、何も描画されない透明なピクセルでビットマップを作成するためのクラスを作成します。 お気に入りのプリミティブを拡張する必要があるかもしれません。 完全に不透明ではない葉と葉の間の隙間がある木をチェックしてください。 Path()を使用したベジェツリー木を描くことについて説明しますが、アルファチャネルでは説明しません。 山は、さまざまなパラメータを持つ丘の描画アルゴリズムのいくつかのレイヤーであり、空は、不透明な青から黒の影付きの長方形です。 グラベルパスの生成は非常に遅い場合がありますが、実行すると十分に高速になります。砂利の各ビットはビットマップで描画されます。
ASSETS.jslファイルは、ゲームが使用するビットマップアセット、それらを開く方法、それらを正規化する方法、およびそれらに名前を付ける方法を認識しています。 アニメーションはさまざまな人によって描かれているため、同じように中央に配置するには正規化が必要です。 その情報はこのような構造で保持されます
Assets:vampire_images["hit"] = {{"attack/hit_1",.,.,0.80,0.5},これは、吸血鬼がコウモリを解放することをビットマップでシフトして(他の場合は.80対.5)、非常に広いアニメーションを処理することを示しています。 吸血鬼とクマは横スクロールで独自のレイヤーを取得し、それらの透明性により、より離れたレイヤーが透けて見えます。
ゲームは実際にはグラフになっているので、ズームツールを使ってゲームを覗いてレイヤーを理解することができます。 ここではズームアウトされており、空が窓よりも左から右に大きくないことがわかります。 スクロールしません。 しかし、より近いレイヤーはますます広くなるので、より多くスクロールすることができます。
Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.通常、空は左から右にディスプレイを埋めます。 ズームアウトして、吸血鬼がステージに入るところを見てください。
小さなクマが右に歩くと、ビットマップの位置が調整されます...
Not afraid.恐れることはありません。
真ん中を過ぎて...
The NPCs animate even when off the display.NPCは、ディスプレイから離れているときでもアニメートします。
そして遠端では...
The little bear has reached the right-hand end of his world.小さなクマは彼の世界の右端に到達しました。
吸血鬼はクマよりもゆっくり歩きます。 彼らは追いつくでしょう。 そして、ここでは、背景と前景の画像の一部が削除されています...
Without all the layers, the characters seem to float.すべてのレイヤーがないと、キャラクターは浮かんでいるように見えます。
3.ゲームオーディオ
音は没入型体験の重要な部分です。 私はLMMSを使用してこのゲームのサウンドを作成しましたが、適切なサウンドデザイナーの方がはるかに優れています。 しかし、音が互いに途切れることなく重なり合うことができるので、それでもかなり良いです。 Windowsの場合。 おそらくMacで、Macを持っている人がサウンドを再生できるコマンドラインプログラムを見つけた場合。 ありがとう@Jeff_Perkinson Macのオーディオコードを手伝ってくれて。 Windowsの場合、AUDIO.jslファイルはRunProgramを使用して、WindowsサウンドシステムにアクセスできるPowerShellを実行します。 Macでは、コマンドラインサウンドプレーヤー/ usr / bin/afplayを使用します。 すべての再生音が終了する前にJMPを終了しようとすると、JMPがクラッシュする可能性があります。開始したすべてのサウンドの再生を最後のプログレスバーで終了させます。
4.ゲーム入力
このゲームは、ゲームを保持するグラフボックスの周りにMouseBoxを配置することにより、キーボードを入力デバイスとして使用します。
New Window( "overlay",
mb = MouseBox(
V List Box(
hlistbox(
leftSpacer=spacerbox(size(5,windowy),color("black")),
gb = Graph Box(
suppressaxes,
framesize( windowx + 1, windowy + 1 ),
X Scale( -1, windowx ),
Y Scale( -1, windowy )
),MouseBoxは、キーストロークをキーと時間を記憶する小さな関数に転送します
lastkey=empty();
lastkeyexpire=tickseconds();
spriteRunner = function({key},{},
lastkey=key;
lastkeyexpire=tickseconds()+1;
);他の場所では、キーのアクションが1秒後に古くなるまで繰り返されます
While( go,
if( tickseconds() > lastkeyexpire,
lastkey="";
);
…キーは小さなクマを操縦します。 それらは、クマの有限状態マシン(FSM)に実装されています。 クマをアイドリングからウォーキングに変更する例は、BEAR.jslのcycle()関数です。
If( begin,
laststate=m_state;
if( keystroke=="LEFT" & ( pFaceLeft=="faceright" | (pFaceLeft=="faceleft" & pName=="idle") ),
m_state = m_stateWalkLeft;現在のアニメーションサイクルが最初にある場合、Beginはtrueです。これは、状態を変更するのに適した場所になります。 ジャンプアニメーションの途中をアイドリングアニメーションのままにしておくのは奇妙に見えます。 クマは左を向いてアイドル状態の場合にのみ左に動き始める必要があるというロジックを書いてからしばらく経ちましたが、右を向いている場合は...左に向かって始めますか? これにより、歩行時に瞬時に反転できるようになり、同じ状態に変化するのを防ぐことができると思います。これは、別の方法でも処理される可能性があります。
5.ゲームプレイ
魅力的なゲームを作ることは私にとって難しい部分です。 ストーリーを語り、目標を作成し、パズルを追加します。 このゲームは主に環境であり、ストーリーテラーを待っています。 しかし、私はゲームプレイを少し追加しました。
,/*else if*/ keystroke=="DOWN" & m_NumRocks<6 & (m_state == m_stateWalkLeft | m_state==m_stateIdleLeft),
m_state = m_statePickLeft;
m_NumRocks = min(6,m_NumRocks+1);
// use SPACE to HIT
,/*else if*/ keystroke==" " & (m_state == m_stateWalkRight | m_state==m_stateIdleRight),
if(m_NumRocks,
m_NumRocks = max(0,m_NumRocks-1);
m_state = m_stateHitRight;
m_RockThrownRightFrom = m_x;
);下矢印を押すと岩が拾われます。小さなクマは6人まで収容できます。 スペースバーを押すと、岩が向いた方向に投げられます。 クマは投げられた岩をメモしますRockThrownRightFromまたRockThrown左からこれは、吸血鬼とクマを見ることができるメインループのコードによってチェックされます。
// check for interaction between bear and vampire
if(!ismissing(bearPlayer:m_RockThrownRightFrom),
if(bearPlayer:m_RockThrownRightFrom<GETPOS></GETPOS>
吸血鬼を岩で叩くと、50ピクセル戻ります。 (実際、私が書いたように、1つの石で2つの吸血鬼を手に入れることができます。)吸血鬼はおそらく2つ以上をサポートできるリストに含まれているはずです。 実際には、すべてのNPC(ノンプレイヤーキャラクター)はリストに含まれている必要があり、共通の基本クラスからサブクラス化して、代わりにループでcycle()を呼び出す必要があります。
vampire1:cycle(stagepos);
vampire2:cycle(stagepos);
bearPlayer:cycle(stagepos,lastkey);
しかし、それはこの単純な例がどのように機能するかを曖昧にするだけです。
プロジェクトファイルとアドインファイルの2つの添付ファイルが必要です。 (現在、プロジェクトをコミュニティに添付することはできませんが、アドインをインストールすると、プロジェクトのコピーがアドインディレクトリにあります。)
アートクレジット
オープンゲームアートサイトには、さまざまな品質のコンテンツがたくさんあります。