讓我們嘗試一個項目來管理一些 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 遊戲,其中角色從左向右移動,背景可能有多個以不同速率滑動的層,以提供 3-D 深度感。 其他變體也可能是具有垂直滾動尺寸的平台跳線。
有問題需要解決:
- 遊戲開發(JMP 可以讓這更容易編寫嗎?)
- 遊戲圖形(它看起來像什麼?)
- 遊戲音頻(你聽到了嗎?)
- 遊戲輸入(如何移動?)
- 玩遊戲(有什麼事嗎?)
1. 遊戲開發
我正在使用一個 JMP 項目(並且就在最近,切換到一個 JMP 16 獨立項目。)我開始喜歡一個有五到十個獨立文件的項目的獨立項目。 它具有用於打開文件的選項卡和用於日誌窗口的停靠點。 自包含項目將所有內容存儲在一個文件中(zip,在封面下),並在打開和關閉時自動解包和重新打包。
讓這個遊戲運行起來更容易,因為圖形文件、音頻文件和遊戲文件是獨立的、相對較小的文件。 這使得查找東西變得很容易,例如管理遊戲角色之間遊戲的 JSL。 該項目在選項卡中顯示文件,如下所示:
A JMP project with several files.一個包含多個文件的 JMP 項目。
這個項目一打開就有九個文件打開;保存遊戲中吸血鬼類定義的文件當前可見。 我對文件何時保存回項目以及何時保存項目並不完全滿意,因此我在關閉項目之前使用左上角突出顯示的圖標。 我還將日誌停靠在左下方。 項目似乎與 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 文件將嘗試重新包含當前文件並拋出一個遞歸包含錯誤,該錯誤被忽略,或者報告其他一些錯誤。 需要放在最後一個的側滾輪文件將 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 )
)
)
);現在,無論哪個文件被編輯,都可以運行。 除非...如果編輯了多個文件,請務必保存未明確運行的文件! 包含獲取磁盤副本,而不是編輯選項卡副本。 但是,如果您只更改一個文件,由於上面設置包含的方式,它將使用當前的編輯選項卡副本,而無需保存。
願望清單:
- 在運行之前保存所有內容(或者,使用編輯選項卡副本進行包含)
- 運行項目時指定啟動文件(不是項目加載文件)
- 記錄保存選項卡和保存項目的相同/不同之處
- 從 JSL 運行項目的全部保存命令的方法
2. 遊戲畫面
該項目使用 JSL 生成的圖形和下載的位圖圖形。 下面,藍天、山脈、樹木和碎石路是由JSL創造的。 岩壁表面是平舖位圖,吸血鬼、蝙蝠和熊是動畫位圖序列。 學分在最後。
The little bear is idling; the vampires are releasing bats or disappearing.小熊閒著;吸血鬼正在釋放蝙蝠或消失。
RGBA.jsl 文件是 JSL 圖形的重要組成部分。 JMP 可以在圖形中以透明度進行繪製,並且可以在圖形中使用具有透明度的位圖,但沒有簡單的方法可以例如在透明背景上製作折線圖並抓取具有透明背景的位圖。 (有一個選項,報告 PNG 圖像的透明背景,它可以保存除具有透明度的圖形之外的所有內容。 您可以編輯保存的 SVG 以刪除背景,但您需要另一個程序將其轉換回透明背景上的 PNG。)
願望清單
RGBA 文件創建了一個類,用於創建沒有繪製任何內容的透明像素的位圖。 它可能需要擴展您最喜歡的原語。 檢查樹葉不太透明的樹木,以及樹葉之間的縫隙。 使用 Path() 的貝塞爾樹描述了繪製樹木,但不使用 alpha 通道。 山脈是不同參數的山丘繪製算法的幾層,天空是不透明的藍黑色陰影矩形。 礫石路徑的生成可能很慢,但一旦運行就足夠快了;每一塊礫石都繪製在位圖中。
ASSETS.jsl 文件了解遊戲使用的位圖資源、如何打開它們、如何規範化它們以及如何命名它們。 因為動畫是由不同的人繪製的,所以它們需要標準化以使它們以相同的方式居中。 該信息保存在這樣的結構中
Assets:vampire_images["hit"] = {{"attack/hit_1",.,.,0.80,0.5},這表明吸血鬼釋放蝙蝠在位圖中移動(0.80 對 0.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.遊戲輸入
該遊戲通過在容納遊戲的圖形框周圍放置一個鼠標框來使用鍵盤作為輸入設備。
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;
);在其他地方重複密鑰的操作,直到一秒鐘後它變得陳舊
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 為真,這是更改狀態的好地方。 將跳躍動畫的中間部分留給空閒動畫看起來很奇怪。 我已經有一段時間沒有寫過這樣的邏輯了 我認為這允許在行走時立即反轉,並且可能防止更改為相同的狀態,這也可能以另一種方式處理。
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;
);按向下箭頭撿起石頭;小熊最多可以容納六個。 按下空格鍵會朝正對的方向扔石頭。 熊記下了扔在裡面的石頭RockThrownRightFrom或者搖滾樂剩下從這由可以看到吸血鬼和熊的主循環中的代碼進行檢查。
// check for interaction between bear and vampire
if(!ismissing(bearPlayer:m_RockThrownRightFrom),
if(bearPlayer:m_RockThrownRightFrom<GETPOS></GETPOS>
用石頭擊中吸血鬼會將其擊退 50 像素。 (實際上,你可以按照我寫的方式用一塊石頭得到兩個吸血鬼。)吸血鬼應該在一個可以支持兩個以上的列表中。 實際上,所有 NPC(非玩家角色)都應該在一個列表中,並且應該從一個公共基類中繼承,以便在循環中調用 cycle() 而不是
vampire1:cycle(stagepos);
vampire2:cycle(stagepos);
bearPlayer:cycle(stagepos,lastkey);
但這只會掩蓋這個簡單示例的工作原理。
應該有兩個附件:一個項目文件和一個加載項文件。 (我現在無法將項目附加到社區,但是如果您安裝插件,插件目錄中有該項目的副本。)
藝術學分
開放式遊戲藝術網站有更多不同質量的內容。