FreeJ2ME-Plus 是一個適用於 RetroArch 的 J2ME 核心,由 FreeJ2ME Fork 而來。
相比於 J2ME-Loader 等成熟的專案,FJ-Plus 的相容性依然有待提升。但因 AShiningRay 先生對這個專案的忠實維護,使得這個專案幾乎每隔一段時間,就有令人驚嘆的改進。
在 V1.50 版本中,不僅有近乎完美的 MIDP 2D 相容,更有 Doja、KDDI、SKT 等新生的支援,儘管這些日韓 J2ME 規範並不能很好的在 Libretro 下工作(AWT 前端可工作),但隨著時間的推移,我相信這些問題會被一點點解決。
現在,我決定寫下這篇貼文,來介紹如何在 RetroArch 上執行 J2ME 核心。


確認需求

在正式開始前,請先確認你是否真的適合這個核心。
如果您只是想簡單體驗 J2MEGAME,那麽 Android 上的 J2ME-Loader 將是更好的選擇。
如果您只是想在 PC 端遊玩 J2MEGAME。那麽 KEnnmod 或是 VM + JL 將是你的不二選擇。

缺陷

  • 3D 支援極弱
  • 無 AMR 媒體支援
  • 無中文輸入法[1]
  • 不支援網路[2]
  • ……

優勢

  • 最棒的著色器支援
  • 靈活的配置檔管理
  • 自訂 SF2 音源
  • 自訂字型[3]

準備環境

系統
Windows / Linux[4]
環境
Adoptium OpenJRE 8[5]
硬體:
RetroArch 支援的遊戲手把[6]


編譯核心

您不需要學習如何編譯核心,因為該專案提供了自動編譯檔。
造訪 FJ-Plus 的 Releases,點擊『HERE』即可下載自動編譯的 main jarslibretro cores

現在,我們獲得了兩個 ZIP 壓縮檔,這在下個章節將會使用到。

在本節,我們將學習如何手動編譯核心——這不是必須,但卻很有益處。
本節内容主要面對 Windows 用戶。Linux 用戶……我不相信你不會編譯 XD

編譯 FreeJ2ME Jar

為了完成編譯,除了 Java 環境,您仍需 Apache Ant

  1. 下載 Apache Ant,並解壓至 C 盤下;
  2. 添加系統變數:【變數名稱:ANT_HOME】【變數值:C:\Apache Ant 的資料夾名】;
  3. 在系統變數的『PATH』中新增:【C:\Program Files\Apache Ant 的資料夾名\bin】;
  4. 在 CMD 中鍵入 ant -version,如果安裝成功,會彈出 Apache Ant(TM)......
    現在,我們可以正式使用編譯命令。

在編譯之前,我們需要將原始碼下載到本地並解壓【Code → Download Zip】。

打開命令提示字元,執行以下操作(freej2me/替換為專案的絕對根路徑):

1
> cd freej2me/
1
> ant

編譯完成後,其結果在根路徑下出現的 build 資料夾:

freej2me.jar -> 獨立的 AWT 可執行檔,目前主要的獨立版本。

freej2me-lr.jar -> Libretro 核心依賴,需放置在 RetroArch 根路徑的 system 資料夾下。

我們主要使用 RetroArch,因此只保留 freej2me-lr.jar 這個核心依賴即可。
不過如若想在進入 RetroArch 前測試遊戲兼容度,那麽保留 freej2me.jar 也無妨。

編譯 Libretro 核心

若想在 Windows 上編譯核心,您需要使用 mingw 或 MSYS2 64 模擬 Linux 環境。
本指南使用  MSYS2-x86_64。常規情況下,您的所有編譯工作將在 C:\msys64\home\UserName 下完成。[7]

不過在此之前,我們需要在 MSYS2 中安裝編譯的依賴:

1
> pacman -S mingw-w64-ucrt-x86_64-gcc
1
> pacman -S make

我們將下載好的Zip專案解壓到上述的編譯工作路徑下,如 C:\msys64\home\UserName\freej2me-plus(專案根路徑):

1
> cd freej2me-plus/src/libretro
1
> make

編譯完成後,我們可以在 src/libretro/ 下找到 freej2me_libretro.dll 檔案。[8]


裝載核心

現在,解壓上文中的兩個 ZIP 檔,我們會得到核心。

FreeJ2ME-Plus 的核心分為三部分:

核心檔案:freej2me_libretro.dll / freej2me_libretro.so
核心依賴:freej2me-lr.jar
核心描述:freej2me_libretro.info

在解壓出的核心中,freej2me_libretro.so 是 Linux 核心檔,按需選擇即可[9]
至於核心描述的 freej2me_libretro.info 檔,則在原始碼的 src\libretro 路徑下。

我們將上述三個檔案放入 RetroArch 對應的資料夾中:

核心檔案:cores
核心依賴:system
核心描述:info

現在,我們打開 RetroArch,便可使用 FreeJ2ME-Plus 核心了。


匯入遊戲

JAR 檔因其易於修改的性質,導致同一個遊戲可能有十幾種變種。[10]
『掃描資料夾』這種方法依賴資料庫的健壯,RetroArch 現有的 J2ME 殘缺資料庫顯然無法滿足我們的需求——當然,因為其變種繁多的原因,構建一個事無巨細的資料庫也幾近不可能……

因此——我們匯入 JAR 檔的主要方式是『手動掃描』。

管理 JAR 檔

既然要掃描,那麽我們就必須 在外部嚴謹管理自己的 JAR 檔
按照『熒幕分辨率』的方式進行分類是最優的方式,這是一個例子:

1
2
3
4
5
6
7
8
Mobile - J2ME
├── 128
├── 128SQ
├── 176_208
├── 176_220
├── 208_320
├── QVGA
└── 360

在此以外,因為 RetroArch 的限制,我們匯入遊戲的命名也必須非常講究。

  • 若命名中有中文,則不允許出現連字符號『-』
  • 命名之中允許出現底線『_』
  • 中文一般不可與英文一同出現
  • ……
    當然,異常情況不可避免。如果遇見的話,主要表現就是 RetroArch 進程凍結。[11]
    這時,我們把檔名修改為純英文即可,如『傲世奇俠傳.jar』修改為『Legend of the Proud Hero.jar』。

開始掃描

如果你整理好了自己的 JAR 檔,那我們便開始 初次手動掃描 吧。

  1. 指定資料夾:選擇『Mobile - J2ME』路徑後,點擊『從此處開始掃描全部檔案』;
  2. 設定列表名稱:捲動列表,點擊『Mobile - J2ME』。若您的資料夾名稱本來就是『Mobile - J2ME』,則直接進行下一步;[12]
  3. 指定核心副檔名:點擊『Mobile - J2ME(FreeJ2ME-Plus)』。若您指定的路徑内僅有 JAR 檔,則直接進行下一步;
  4. 包含所有資料夾:勾選即可;
  5. 取代現有列表:勾選即可;
  6. 開始掃描:確認以上步驟完成後,點擊即可開始掃描。

現在,我們會發現 RetroArch 之中多了一串 Game 列表,這便是我們所擁有的 J2ME 檔。

如果後續在外部刪除或是新增了 JAR 檔,則重複上述步驟即可。
唯一不同的是,第五步的『取代現有列表』要替換為『驗證現有條目』。[13]


參數配置

如果使用預設參數進行遊戲,那麽我們的遊戲體驗不會獲得任何的提升。
在本章節,我們將對 RetroArch 的各項參數進行配置,以獲得良好的遊玩體驗。

控制配置

FreeJ2ME-Plus 把 控制器參數寫入了 RetroArch 的前端中。

現在,隨意打開一個 JAR 檔,緊接著按下 F1 鍵呼出 RetroArch 系統選單。

1
2
3
4
5
6
7
8
系統選單
├── 繼續
├── 重開
├── 關閉
├── 核心選項
├── 控制器(核心預設)
├── 熒幕覆蓋
└── 專用主題

打開『控制器(核心預設)』:

1
2
3
4
5
6
控制器(核心預設)
├── 管理控制器
├── 連發設定
├── 連接埠 1 控制器
├── 連接埠 2 控制器
└── ……

選擇『連接埠 1 控制器』進入,根據自我手感習慣兼喜好配置即可。

配置完成後,返回『控制器(核心預設)』,打開『管理控制器』:

1
2
3
4
5
6
7
管理控制器
├── 已載入控制器
├── 載入控制器
├── 儲存為項目控制器(該配置僅將被目前打開的遊戲讀取)
├── 儲存為路徑控制器(目前打開的遊戲所處的路徑下的全部遊戲都將應用該配置)
├── 儲存為核心控制器(FreeJ2ME 所運行的全部遊戲將使用該配置)
└── ……

選擇『儲存為核心控制器』後,全部的 JAR 檔將會應用剛才配置的控制器鍵值。

如果在未來遇見鍵值要求苛刻別扭的遊戲,可以打開這個遊戲,并按照以上步驟配置。[14]
配置完成後,選擇『儲存為項目控制器』。這樣,便可在不影響全域的情況下,從而為個別遊戲的控制器鍵值進行單獨設定。

『儲存為路徑控制器』在控制器配置中是一個無用的選擇。因為在上文中,我們是按照 螢幕分辨率 對遊戲分類的。

根據遊戲的不同種類,為其配置不同的控制器也是可行的,但如此反而不利於我們接下來的其他配置。

這是我使用的控制器鍵值配置,可以作為參考:

手機 按鍵 XBox360 按鍵 手機 按鍵 XBox360 按鍵
OK A # RB
RSoft B UP LJoy UP
LSoft Back DOWN LJoy DOWN
0 Start LEFT LJoy LEFT
1 LT RIGHT LJoy RIGHT
3 RT 2 D-pad UP
7 X 4 D-pad LEFT
9 Y 6 D-pad RIGHT
* LB 8 D-pad DOWN

Tip:離題:個人喜歡把 F1 鍵設定為『按壓左搖桿』,這點視個人習慣而來吧。


核心配置

FreeJ2ME-Plus 可在 RetroArch 前端中配置遊戲的啟動參數。
現在,依舊是隨意打開一個 JAR 檔,然後按下 F1 鍵呼出 RetroArch 系統選單。

1
2
3
4
5
6
7
8
系統選單
├── 繼續
├── 重開
├── 關閉
├── 核心選項
├── 控制器(核心預設)
├── 熒幕覆蓋
└── 專用主題

打開『核心選項』:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
核心選項
├── 管理核心選項
├── 系統
│ ├── 解析度
│ ├── 熒幕旋轉
│ ├── 鍵值佈局
│ ├── LCD 背光
│ ├── FPS 限制
│ ├── 模擬手機聲音
│ ├── MIDI 音源
│ ├── 文本字體
│ ├── 字體尺寸
│ └── 使用搖桿模擬鍵盤
├── 高級
│ ├── 光標類型
│ └── ……
├── 優化
│ └── 無 Alpha 空白圖形
└── 兼容
├── 不對 Null 圖形抛出異常
└── 圖像重置時使用 clipRect 替代 setClip

解析度:選擇遊戲分辨率
熒幕旋轉:根據需求打開/關閉
鍵值佈局:預設為 Nokia,根據需求選擇
LCD 背光:為單色遊戲設計,運行非單色遊戲時必須關閉
FPS 限制:建議限定為 60,一些老遊戲可設定為更低
模擬手機聲音:建議打開,以模擬未受支援的媒體格式(如 .AMR),如果遇見錯誤可關閉
MIDI 音源:打開後,在 system/freej2me_system/customMIDI 下放置 SF2 音源
自訂字體: 打開後,在 system/freej2me_system/customFont 下放置 TTF OTF TTC 字體
使用搖桿模擬鍵盤:建議關閉,但如果遊戲使用數字全鍵盤控制角色,那麽建議打開[15]
光標類型:建議選擇『觸摸模式』,需要如果遊戲需要縮放,可選擇『鼠標模式』
優化&兼容:建議全部關閉,如果遇見渲染異常,可酌情打開

配置完成後,返回『核心選項』,打開『管理核心選項』:

1
2
3
4
5
管理核心選項
├── 已載入核心選項
├── 儲存為項目核心選項
├── 儲存為路徑核心選項
└── ……

選擇『儲存為路徑核心選項』後,該 JAR 檔所處路徑下的其他 JAR 檔,在啟動時也均會自動載入此配置。

舉例:如果你打開了 QVGA 路徑下的某一遊戲,在完成配置後將其『儲存為路徑核心選項』,那麽,整個 QVGA 路徑下的遊戲便基本無需再配置了。

這亦是上文中 使用分辨率分類遊戲 的主要原因。

在開始下文前,讓我們再打開其他路徑下的代表性遊戲,逐一配置其參數吧。


縮放配置

在一般情況下,RetroArch 會根據 FJ-Plus 核心提供的解析度自動對畫面進行等比例縮放。
等比例縮放的做法是正確的,但如果您的 PC 分辨率過高,可能會造成畫面乃至字體的失真

在這一章節中,我們需要去 手動調節 每一個分辨率顯示在熒幕上的尺寸
本教程將以個人 PC 使用的 1920x1080 解析度去講述,請根據自己實際情況設定。

QVGA

QVGA,即 240x320,是 JAR 檔最常見的解析度。

打開一個隨意的 QVGA 遊戲,按下 F1 鍵呼出 RetroArch 系統選單。
按下控制器的『返回』鍵(XBox 360 手把為 B 鍵),來到『設定』選單:

1
2
3
4
5
6
設定
├── 選單
├── 顯示
├── 音效
├── 輸入
└── ……

選擇『顯示』-『縮放』-『寬高比』:

1
2
3
4
5
6
7
8
9
10
顯示
├── 縮放
│ ├── 整數縮放
│ ├── 寬高比
│ ├── 自訂寬度
│ ├── 自訂高度
│ └── ……
├── 同步
├── 全螢幕模式
└── ……

寬高比一般為 FreeJ2ME-Plus 核心提供,現在,我們將其設為『自訂』。
在 1920x1080 的解析度的範圍内,QVGA 的最佳縮放倍數為 2 倍(480x640)。

選擇『自訂寬度』,設定其值為『480』,『自訂高度』的值則設為『640』。

返回『設定』選單,選擇你所進入的 QVGA 遊戲:

1
2
3
4
5
6
7
8
系統選單
├── 繼續
├── 重開
├── 關閉
├── 核心選項
├── 控制器(核心預設)
├── 熒幕覆蓋
└── 專用主題

打開『專用主題』,選擇『儲存為路徑專用主題』。
現在打開其他 QVGA 路徑下的遊戲時,也會自動載入該配置。

其他解析度

其他解析度的整體流程也均以 QVGA 為准,這裡簡單寫下寬高值。
這些配置以 1920x1080 解析度為准,請根據自己的實際情況來設定。

128x128:『自訂寬度:384』『自訂高度:384』
128x160:『自訂寬度:384』『自訂高度:480』
176x208:『自訂寬度:528』『自訂高度:624』
360x640:『自訂寬度:1280』『自訂高度:720』


著色器配置

著色器,又名濾鏡,可透過改變畫面的特徵,來形成特殊的視覺效果。

J2ME-Loader 的分支 JL-Mod 也支援這個功能,不過僅兼容 PPSSPP的著色器,且效果難言。
在透過『模擬』的手段 懷舊 時,設法 還原在真機上的視覺 效果絕對不是一件壞事。

在這一章節,因為個人對著色器參數屬實一竅不通,所以我僅會推薦一款最喜歡的點陣著色器。

推薦著色器
脑浆油条 開發的 『荧光点滤镜』
相關影片:https://www.bilibili.com/video/BV16s4y1T7Qa
下載連結在影片簡介處,安裝方式請參考連結内的 Readme_v3.3.txt

現在,隨意打開一個 JAR 檔,然後按下 F1 鍵呼出 RetroArch 系統選單。

1
2
3
4
5
6
7
8
系統選單
├── 繼續
├── 重開
├── 關閉
├── 核心選項
├── 控制器(核心預設)
├── 熒幕覆蓋
└── 著色器

選擇『著色器』,打開『開啟著色器』,並『載入配置』:
配置路徑:shaders\shaders_slang\phosphor-dot v3.3\Dot-clear.slangp

配置載入完成後,選擇『儲存預設』,並『儲存為核心著色器』。
在『應用變化』後,回到遊戲即可發現畫面蒙上一層立體感點陣,但是畫面並為因此變暗失真。


覆蓋層配置

覆蓋層(遮罩)是一種提供 視覺增强 & 触控操作 的方式。
在 FreeJ2ME-Plus 的使用场景中,则以『视觉增强』为主。[16]

在本章節,我們將學習 使用制作自己的覆蓋層。
在開始前,我會提供一些自己製作的 半成品 覆蓋層以供練習。

下載覆蓋層

128x128FreeJ2ME_7260.png FreeJ2ME_7260.cfg
128x160:未製作
176x208FreeJ2ME_N72.png FreeJ2ME_N72.cfg
240x320FreeJ2ME_5310XM.png FreeJ2ME_5310XM.cfg
640x360FreeJ2ME_E700.png FreeJ2ME_E700.cfg

覆蓋層由兩部分構成:CFG配置檔 和 PNG貼圖層
下載完成後,為了方便管理,我們將其移動至 overlay/FreeJ2ME 路徑下,你也可以根據自己的習慣來分配路徑。

Tip: 請注意:它們僅無限適配 16:9 的熒幕,若需其他,請自行改圖。

使用覆蓋層

本貼文依舊以最常見的 QVGA 遊戲為例。
打開一個隨意的 QVGA 遊戲,按下 F1 鍵呼出 RetroArch 系統選單。

选择『螢幕覆蓋』:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
系統選單
├── 繼續
├── 重開
├── 關閉
├── 核心選項
├── 控制器(核心預設)
├── 熒幕覆蓋
│ ├── 開啟熒幕覆蓋
│ ├── 載入覆蓋元件
│ ├── 在選單後顯示覆蓋
│ ├── 橫向縮放大小
│ ├── 橫向寬度調整
│ ├── 橫向水平偏移
│ └── 橫向垂直偏移
└── ……

開啟熒幕覆蓋:覆蓋層開關。在外部修改覆蓋層配置時,可透過此刷新覆蓋層狀態
載入覆蓋元件:載入覆蓋層配置檔
在選單後顯示覆蓋:預設下,覆蓋層不在選單開啟時出現。開啟此選項有助於微調覆蓋層的位置
橫向縮放大小:控制覆蓋層的整體縮放
橫向寬度調整:控制覆蓋層的寬度縮放
橫向水平偏移:設定覆蓋層的左右位置
橫向垂直偏移:設定覆蓋層的上下位置[17]

使用覆蓋層只需『載入覆蓋元件』,我們打開的是 QVGA 遊戲,所以需要載入 FreeJ2ME_5310XM.cfg
載入完成后,開啟『在選單後顯示覆蓋』,使用『縮放大小』+『寬度調整』微調其位置即可——因為即使比例正確,RetroArch 加載的覆蓋層出現拉伸形變也是常態。

RetroArch 的窗口大小會在打開/關閉遊戲的時候變化,即便是直接修改配置檔,系統也會强行修正或者出現窗口位移……我的心得是不要輕易變動這些設定。當然,如果你喜歡全螢幕遊戲,那麽這將不是問題。

如果覆蓋層移動精度過低,無法和遊戲畫面嚴絲合縫,那麽可以嘗試位移遊戲畫面:

F1 呼出選單,『返回』主選單,選擇『顯示』-『縮放』:

1
2
3
4
5
6
7
8
顯示
├── 縮放
│ ├── 整數縮放
│ ├── 自訂水平位置
│ ├── 自訂垂直位置
│ └── ……
├── 同步
└── ……

使用『自訂水平/垂直位置』可以像素級的去微調遊戲畫面的位置。
這個設定不可被濫用,會破壞遊戲畫面和 PC 熒幕的對稱視覺觀感。

使覆蓋層與遊戲畫面緊密貼合後,返回當前運行遊戲的選單,將其存儲為『路徑專用主題』。

1
2
3
4
5
6
7
8
系統選單
├── 繼續
├── 重開
├── 關閉
├── 核心選項
├── 控制器(核心預設)
├── 熒幕覆蓋
└── 專用主題

現在,QVGA 路徑下的遊戲均可享受到 5310XM 的覆蓋層。

在將其存儲為『路徑專用主題』時,在 config 下會生成一個 QVGA.cfg 的配置檔。

我們在遊戲之中 修改 + 保存 各種配置時,修改的參數會自動完善該路徑配置檔。這非常重要,請妥善備份該路徑。

製作覆蓋層

覆蓋層分為兩部分:CFG 配置檔 和 PNG 貼圖層

我們先從 PNG 貼圖層開始。

PNG 貼圖層

如果你有設計功底,可以自己繪製外殼;
如果你有建模功底,可以自己建模並出渲染圖。

這樣製作出的仿真覆蓋層是高精度的。

如果你不想花費這個時間,可以在網路上以『Nokia』為關鍵詞檢索圖像。[18]
我們需要的圖像需要滿足『正視圖』與『非實拍』兩個條件。

獲得圖像後,打開 PhotoShop,新建一個和你 RetroArch 窗口大小一致的畫布,將素材拖進去、摳除熒幕、打光、增强材質,最後快速導出 PNG 檔。

這不是 PhotoShop 教程,所以非常簡略。如果你有相關經驗,歡迎留言。

CFG 配置檔-簡要

我們可以使用隨意的文本編輯器編輯 CFG 檔。

這是一個 CFG 檔的最簡形態:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
overlays = 1
# 覆蓋層數量為『1』

overlay0_overlay = FreeJ2ME_N72.png
# 載入同路徑下的『FreeJ2ME_N72.png』為底圖

overlay0_full_screen = true
# 拉伸覆蓋層至全熒幕,必須使用,否則覆蓋層只會在遊戲畫面中顯示

overlay0_descs = 0
# 覆蓋層中有『0』個可觸控按鈕,默認即可

overlay0_opacity = 1.0
# 覆蓋層的不透明度為『1.0』,即『100%』

這個配置檔的主要作用是幫助我們 找到覆蓋層的位置
在下文中,我們將深入編輯該覆蓋層,為其添加觸控支援。

CFG 配置檔-觸控

如果你使用支援觸摸的 Windows / Linux 設備,那麽觸控覆蓋層會十分有用。

唯一需要注意的是,既然已經使用了支援觸控的設備,那麽手把所帶來的『按鍵感』將不再是這種模擬手段的優勢……我相信你明白這一點。

觸控參數的詳細寫法在 Libretro Docs,而大部分參數均可透過一個開源專案自動生成:

RetroArch Overlay EditorGithub | Online

這裡將以 Github 的發行版為例,先簡要介紹主要的選單功能。

選單簡介

Files
New:新建配置檔
Open:打開配置檔
Save:保存配置檔
Save As:另村配置檔

Actions
Set Canvas Size:設定畫布尺寸,單位為 Px
Set / Remove Layer image:設定/清除 底圖,在參數中表示為 overlay0_overlay = XXX.png
Add/Delete Layer:添加/刪除圖層,這基本不會在教學中使用
Add Button:添加按鈕,按鈕名對應下表中的『配置按鍵』,前提是使用和我相同的映射
Add Joystick:添加搖桿,搖桿的鍵值未知,這基本不會在教學中使用
Delete Object:刪除選中的 按鈕 / 搖桿
Set Object Name/Image:設定 按鈕 / 搖桿 的 按鈕名 和 顯示圖像
Remove Object Image 清除 按鈕 / 搖桿 的圖像
Set Circular/Square Object:設定 按鈕 / 搖桿 的觸控範圍為 圓形 / 方形

第一個按鈕
  1. 『Open』一個已有的配置檔;
  2. 『Add Button』,創建一個按鈕層;
  3. 使用游標選中按鈕層,『Set Object Name』為『y』;
  4. 『Set Object Image』,為該按鈕設定顯示圖像;
  5. 使用游標選中按鈕層至適合的位置,至此一個 OK 鍵創建完成。

這個按鈕的參數如下:

1
2
3
4
5
6
7
overlay0_desc0 = "y,0.234671,0.453938,rect,0.05,0.05"

# overlay0_desc0:該圖層上的第一個按鍵
# y:鍵位名,對應了 Xbox360 上的『A』,按鍵手機上的『OK』
# 0.234671,0.453938:按鈕位置
# rect:矩形判定
# 0.05,0.05:按鈕大小
其他參數和心得

過去玩 JAR 基本使用按鍵手機,學會如何編輯按鈕便已足夠。

虛擬按鈕不僅可以觸控,在實體按鈕按下時,也會有對應的視覺反饋。
按鈕的視覺反饋本質上是『圖像的切換』,當你按下代表『左軟鍵』的按鈕時,其貼圖會閃爍,從而露出貼圖下『底圖上的左軟鍵』。所以——在設計時,可以考慮使按鈕貼圖的透明度降低至一個合理的閾值,或是在不影響全局的情況下降低其色澤。這樣在手把上按下對應的鍵時,熒幕上虛擬按鈕的反饋會更有力量感。

使用 Alpha 參數也可控制覆蓋層按鈕在按下時的發光:

1
2
3
overlay0_alpha_mod = 2
# 『2』代表按鈕的 Alpha 值乘以 2。
# 一張圖的 Alpha 值越大,圖片在視覺上便越『亮』。
鍵值對應表

這是一份鍵值對應表,如果你想寫配置檔,那麽它將是一個不錯的的參考。

如果將按鈕映射為『2-4-6-8』,那麽,沒有一個參數可用於配置檔——換言之,FreeJ2ME-Plus 中『2-4-6-8』的鍵值并不被 RetroArch 的覆蓋層參數所支援!

如果你有興趣,研究並檢查 input_config_bind_map 變數中的按鈕名:
游戲手把/光槍Github/RetroArch/blob/master/configuration.c
PC 鍵盤Github/RetroArch/blob/master/input/input_keymaps.c

手機 按鍵 XBox360 按鍵 配置 按鍵
OK A y
RS B start
LS Back select
0 start x
1 LT l1
3 RT r1
5 A l3
7 X b
9 Y a
* LB l2
# RB r2
UP LJoy UP up
DOWN LJoy DOWN down
LEFT LJoy LEFT left
RIGHT LJoy RIGHT right
2-4-6-8 D-Pad

音源/字體

自訂音源

J2ME 標准的 JAR 檔普遍使用『MID』為背景音樂、『WAV』或『AMR』為音效。

相較於『WAV』或『AMR』,『MID』不包含任何聲音流,只包含播放指令。如:在何時 / 使用何種樂器 / 演奏何種旋律。
為了播放此類文件,SoundFont 成為了『MID』的實際發聲體。這是一種包含各種樂器音色的樣本庫,決定了每一種樂器在 MIDI 下所發出的聲音質感與風格。

SoundFont 在各個品牌,乃至各種型號之間的音色都各不相同,這也是我們在現今設備上模擬 JAR 時會發覺音色『古怪』的原因。

Tip:不過——現今 Android 設備所擕帶的 SoundFont 屬實糟糕。

在 FreeJ2ME-Plus 中,我們可以自訂 MIDI 的 .SF2 音源。
J2ME-Loader 的 Fork JL-Mod 也支援自訂 .SF2,這裡便不再細講。

音源推薦

使用方式

在運行 FreeJ2ME-Plus 核心後,RetroArch 的 system 下會生成 freej2me_system/customMIDI 路徑,我們將下載好的 .SF2 檔放在此處,並在核心設定中將『自訂 MIDI 音源』設為『自訂』即可。

Tip:SF2 音源不宜過大,最好能夠限制在 5MB 之内,超過這個值將對游戲的流暢性造成影響。

自訂字體

現有的模擬器普遍存在字型縮放問題。
即便是使用點陣字型,也無法還原實機的視覺體驗。

使用方式

支援的字體格式:ttf otf ttc
在運行 FreeJ2ME-Plus 核心後,RetroArch 的 system 下會生成 freej2me_system/customFont 資料夾,將外掛字體放在其中即可。

使用心得

不論使用何種字體,在遊戲中都會有極大概率失真。
想要在海量遊戲中獲得良好體驗,唯一的方式是『嘗試』。

在專案的 Font.java 中,規定了 Java 程式顯示的字型大小:

1
2
3
4
5
6
7
private static final int[] fontSizes = 
{
8, 10, 12, // < 128 minimum px dimension
10, 12, 14, // < 176 minimum px dimension
12, 14, 16, // < 220 minimum px dimension
14, 16, 18, // >= 220 minimum px dimension
};

Tip:該處的參數根據個人視覺習慣微調,僅供參考。

我們可以得知:

  • 在 240 的螢幕下,字體渲染的大小為 14(小) 16(中) 18(大)
  • 在 176 的螢幕下,字體渲染的大小為 12(小) 14(中) 16(大)
  • 在 128 的螢幕下,字體渲染的大小為 10(小) 12(中) 14(大)
  • ……

因此在遊玩 QVGA 的遊戲時,我們應選擇以 14px16px 為基準設計的字體,這樣可以 最大程度 確保其不會過於失真。[19]

如果出現失真狀況,請考慮使用『核心選項-系統』中的『字體尺寸』微調字體的尺寸。
調整完成後,在選單中『重開』該遊戲,檢查字體的大小情況、微調、檢查、微調……直至字型的邊緣不再失真。

如果字體在反復調整後依舊失真,那麽我們可以考慮換字體。

字體推薦

  • 蘋方中等體【非公有】
    特點:字型筆劃粗細均匀,在全解析度下有非常均衡的表現
  • 寒蟬圓體【OFL-1.1】
    特點:圓潤平滑,在 240+ 的解析度下有不错的表現
  • Unifont【GNU】
    特點:像素字型的巔峰,在 240+ 的解析度下有不错的表現
  • Fusion Pixel Font【MIT】
    特點:10px 和 12px 的字型,在 128 和 176 解析度的情景下接近完美!
  • 文泉驛點陣【GPL-2.0】
    特點:縮放不佳,但卻是唯一使用 14Px 基準設計的字體,且支援繁體
  • 寒蟬點陣體【OFL-1.1】
    特點:16Px 的點陣體在部分 240 的遊戲上有優秀的表現
  • 俐方體11號【OFL】
    特點:在部分 176 的遊戲上有著接近完美的表現,形似 Fusion Pixel Font 10Px
  • 點點像素體【GPL 2.0】
    特點:一種風格化推薦,適合在 360+ 解析度上使用
  • 青鳥華光淡古印【非公有】
    特點:一種風格化推薦,適合在古典場景下使用,适合 240+ 解析度

Tip:非公有領域的字體需商業授權,如果想使用這些字體製作影片,望斟酌再行。

列表美化

我不喜歡 RetroArch 的列表機制,至少對於 J2ME 如此。
因為 RetroArch 不支援鍵入中文,所以無法檢索,尋找 Game 的方式只有卷动列表。
不過,我們依舊可以在這個限度内,最大化的美化我們的列表:

這需要使用兩個功能:預覽縮圖 和 Icon Thumbnail。

我們先在 RetroArch 的 thumbnails 路徑下創建以下結構:

1
2
3
4
thumbnails
└── Mobile - J2ME
├── Named_Logos
└── Named_Titles

Named_Logos: 存放『商標圖示』
Named_Titles: 存放『標題預覽縮圖』

標題預覽縮圖的獲得需要辛苦一點:
打開遊戲時,使用 RetroArch 内建的截圖工具截圖,截圖會保存至 RetroArch 的 screenshots 路徑下。

這樣獲得的截圖是帶有時間戳的,如 光明傳説_N72-250430-141303
使用 Powershell 指令可快速清理時間戳:

1
2
3
4
5
6
7
8
9
Get-ChildItem -Path . -Filter *.png | ForEach-Object {
$oldName = $_.Name
$newName = $oldName -replace '-\d{6}-\d{6}(?=\.png$)', ''

if ($oldName -ne $newName) {
Rename-Item -Path $_.FullName -NewName $newName
Write-Host "Renamed:`n $oldName -> $newName"
}
}

商標圖示的話,可透過 Powershell 指令獲得。
該指令可以快速提取同路徑下 JAR 檔中的圖標,如果遇見無圖標的遊戲,則會使用同指令路徑下的 icon.png 替代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 載入 ZIP 函式庫(必要)
Add-Type -AssemblyName System.IO.Compression.FileSystem

# 腳本所在目錄
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$imgDir = Join-Path $scriptDir "img"

# 建立輸出資料夾
if (!(Test-Path $imgDir)) {
New-Item -ItemType Directory -Path $imgDir | Out-Null
}

# 日誌時間戳
Write-Host "===== 開始圖示提取:$(Get-Date -Format 'MM/dd/yyyy HH:mm:ss') ====="

# 遞迴找所有 .jar 檔案
Get-ChildItem -Recurse -Filter *.jar | ForEach-Object {
$jarFile = $_.FullName
$relPath = $jarFile.Substring($scriptDir.Length + 1).Replace("\", "/").ToLower() # 相對路徑 + 正規化
$jarName = $_.BaseName

try {
$tempDir = Join-Path $env:TEMP "jar_extract_$([guid]::NewGuid().ToString())"
[System.IO.Compression.ZipFile]::ExtractToDirectory($jarFile, $tempDir)

$manifestPath = Join-Path $tempDir "META-INF\MANIFEST.MF"
$iconPath = $null

if (Test-Path $manifestPath) {
$manifest = Get-Content $manifestPath
$midletLine = $manifest | Where-Object { $_ -match "^MIDlet-1:" }
if ($midletLine) {
$parts = $midletLine -split ","
if ($parts.Length -ge 2) {
$iconPath = $parts[1].TrimStart()
}
}

# Fallback 1: 嘗試找 MIDlet-Icon
if (-not $iconPath) {
$iconLine = $manifest | Where-Object { $_ -match "^MIDlet-Icon:" }
if ($iconLine) {
$iconPath = $iconLine -replace "^MIDlet-Icon:\s*", ""
}
}
}

# Fallback 2: 直接找 jar 裡有沒有 icon.png
if (-not $iconPath) {
$found = Get-ChildItem -Path $tempDir -Recurse -Include "icon.png" | Select-Object -First 1
if ($found) {
$iconPath = $found.FullName.Substring($tempDir.Length + 1).Replace("\", "/")
}
}

# Fallback 3: 使用腳本目錄下的預設 icon.png
$outputIcon = Join-Path $imgDir "$jarName.png"
$srcIcon = Join-Path $tempDir $iconPath.Replace("/", "\")
if (Test-Path $srcIcon) {
Copy-Item -Path $srcIcon -Destination $outputIcon -Force
} elseif (Test-Path (Join-Path $scriptDir "icon.png")) {
Copy-Item -Path (Join-Path $scriptDir "icon.png") -Destination $outputIcon -Force
Write-Host "[$(Get-Date -Format 'HH:mm:ss')] 使用預設圖示:$relPath"
} else {
Write-Host "[$(Get-Date -Format 'HH:mm:ss')] 找不到圖示也無預設:$relPath"
}

Write-Host "[$(Get-Date -Format 'HH:mm:ss')] 完成提取:$relPath"

} catch {
Write-Host "[$(Get-Date -Format 'HH:mm:ss')] 錯誤處理 $relPath : $_"
} finally {
if (Test-Path $tempDir) {
Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
}

把圖標和截圖放在對應的資料夾後,在 RetroArch 中進行如下配置:
進入『設定』-『選單』-『外觀』:

  • 主要的預覽縮圖:標題
  • 次要的預覽縮圖:關閉
  • Icon Thumbnail:商標圖示

至此,列表美化結束。
該列表美化為後文補充,行文風格可能有所改變,海涵。

結尾

這篇貼文終於結束了,是的,終於結束。
現在反而沒有什麽想説的,因為精力都放在貼文的内容上。
貼文的創作動機……大概是推薦一種玩 J2ME 的新的方式。

我接觸 RetroArch 大約有三個月了,初衷只是想玩 SFC 和 GBA。
兩個月前,在 Emulation Wiki 閑逛時,無意間發現了一款 J2ME 核心,沒錯,就是本文介紹的 FreeJ2ME-Plus。

對於 J2ME 核心,RetroArch 的官方核心列表中其實有一個:SquirrelJME。這是一個相當龐大的專案,所以開發進度緩慢。現在,我期待其進展……

『哇哦,FreeJ2ME-Plus 居然可以工作耶!?』大概這就是發現這款核心時的心情吧(笑)。

這款核心并不完美,但 AShiningRay 先生的活躍維護,總能令人看到未來有著無窮的希望。

貼文的編輯已經許多時間,許多内容已經碎片到無法記清……如果有謬誤,請多多指正,謝謝你。

畫廊

我不喜歡在貼文中使用圖像,這樣會使版面變得臃腫。
這會降低閲讀體驗……我明白。但對教程向的貼文而言,這是一種雙向選擇。

閑話少叙。
這是畫廊,我會在這裡貼上一些模擬的效果展示 : )

參見

FreeJ2ME-Plus:
https://github.com/TASEmulators/freej2me-plus
Emulation Wiki - J2ME:
https://emulation.gametechwiki.com/index.php/Cellphone_emulators#Java_2_Micro_Edition_.28J2ME.29
Libretro Docs:
https://docs.libretro.com/guides/libretro-overlays/
https://docs.libretro.com/development/retroarch/input/overlay/


  1. 1.為了確保 FJ-Plus 的跨軟體性(AWT/Libretro),未來可能也不會支援。
  2. 2.為了安全性,未來可能也不會支援。
  3. 3.在 2025.04.22 的提交後,已原生支援自訂字型。字體的大小支援微調(2025.04.24)。
  4. 4.現有的核心依賴 Java 環境,故不可在其他端使用。
  5. 5.OpenJDK 值得被推薦,Azul Zulu 也是一個不錯的選擇。
  6. 6.非必須,但這能為大部分遊戲提供良好的控制體驗。
  7. 7.如果不改動配置,那麽 C:\msys64\home\UserName 就是預設路徑。
  8. 8.Linux 編譯後,獲得的并非 .dll 檔,而是 .so 檔。
  9. 9.在 Linux 編譯下,獲得的是 so 核心。
  10. 10.現有的 Mobile - J2ME 資料庫為 68650 KB,遠比其他資料庫龐大,但這依舊貧瘠。
  11. 11.在打開遊戲時的『遊戲名橫幅』處凍結。
  12. 12.RetroArch 為 JAR 設計了專屬圖示,僅有該名字可使其顯示。
  13. 13.使用『取代現有列表』亦可,但這將使遊戲運行時間等資料清空。
  14. 14.如音遊使用 456 打鍵、RPG 使用 1379 移動。
  15. 15.如 Glu 的《Stranded(孤島餘生)》。
  16. 16.正常情況下,極少有人使用觸摸設備運行該核心。
  17. 17.熒幕是橫向時,調整『竪向』參數是無效的。
  18. 18.在網路上尋找 3D 模型並自主渲染是最好的。
  19. 19.一些遊戲中,會為字體設計描邊效果,這類字體最容易失真。