讓我們嘗試一個項目來管理一些 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 獨立項目。)我開始喜歡具有五到十個獨立文件的項目的獨立項目。 它具有用於打開文件的選項卡和用於日誌窗口的停靠點。 這個獨立的項目將所有內容存儲在一個文件中(zip,在幕後),並在打開和關閉時自動解壓和重新打包。
讓這個遊戲運行起來更容易,因為圖形文件、音頻文件和遊戲文件是獨立的、相對較小的文件。 這使得查找內容變得很容易,例如管理遊戲角色之間的遊戲的 JSL。 該項目在選項卡中顯示文件,如下所示:
A JMP project with several files.A JMP project with several files.A JMP project with several files.A JMP project with several files.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 文件將嘗試重新包含當前文件並引發遞歸包含錯誤(該錯誤將被忽略)或其他錯誤(該錯誤將被報告)。 sidescroller 文件需要放在最後,該 try 語句作為第一的線。 這樣,在 sidescroller 文件的其餘部分運行之前,game.jsl 文件就包含了所有其他內容。
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 運行項目的 save-all 命令的方法
2. 遊戲畫面
該項目使用 JSL 生成的圖形和下載的位圖圖形。 下面的藍天、山巒、樹木、碎石路都是JSL創造的。 岩壁表面是平舖位圖,吸血鬼、蝙蝠和熊是動畫位圖序列。 學分在最後。
The little bear is idling; the vampires are releasing bats or disappearing.The little bear is idling; the vampires are releasing bats or disappearing.The little bear is idling; the vampires are releasing bats or disappearing.The little bear is idling; the vampires are releasing bats or disappearing.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 vs 0.5)以處理超寬動畫。 吸血鬼和熊在橫向捲軸中擁有自己的圖層,它們的透明度讓更遠的圖層顯示出來。
因為遊戲實際上是在圖表中,所以您可以使用縮放工具查看它以了解各個圖層。 在這裡,它被縮小了,你可以看到天空從左到右並不比窗戶大。 它不滾動。 但是越來越近的層變得越來越寬,因此它們可以滾動更多。
Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.通常,天空從左到右充滿顯示屏。 拉近鏡頭,看到吸血鬼即將登場。
隨著小熊向右走,位圖的位置也隨之調整……
Not afraid.Not afraid.Not afraid.Not afraid.Not afraid.不怕。
剛過了中間...
The NPCs animate even when off the display.The NPCs animate even when off the display.The NPCs animate even when off the display.The NPCs animate even when off the display.The NPCs animate even when off the display.即使不在顯示器上,NPC 也會有動畫。
而在遠端...
The little bear has reached the right-hand end of his world.The little bear has reached the right-hand end of his world.The little bear has reached the right-hand end of his world.The little bear has reached the right-hand end of his world.The little bear has reached the right-hand end of his world.小熊已經到達了他世界的右端。
吸血鬼比熊走得慢。 他們會趕上的。 這裡刪除了一些背景和前景圖像......
Without all the layers, the characters seem to float.Without all the layers, the characters seem to float.Without all the layers, the characters seem to float.Without all the layers, the characters seem to float.Without all the layers, the characters seem to float.沒有了所有的層次,人物似乎漂浮起來。
3. 遊戲音頻
聲音是沉浸式體驗的重要組成部分。 我使用 LMMS 來製作這個遊戲中的聲音,一個合適的聲音設計師可以做得更好。 但它仍然相當不錯,因為聲音可以重疊而不會相互切斷。 在 Windows 上。 如果使用 Mac 的人找到可以播放聲音的命令行程序,則可能是在 Mac 上。 謝謝@傑夫·珀金森幫助我編寫一些 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="";
);
…鑰匙控制小熊的方向。 它們在 Bear 的有限狀態機 (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;
);按向下箭頭可以撿起岩石;小熊最多可以容納六個。 按下空格鍵會將岩石朝正面的方向扔出。 熊記下扔出的石頭向右扔石頭或者扔石頭左邊從這是由主循環中的代碼檢查的,可以看到吸血鬼和熊。
// 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);
但這只會掩蓋這個簡單示例的工作原理。
應該有兩個附件:一個項目文件和一個加載項文件。 (我現在無法將項目附加到社區,但如果您安裝了加載項,加載項目錄中就會有該項目的副本。)
藝術作品
開放遊戲藝術網站有更多質量參差不齊的內容。