跳到內容

Cassiopeia 譜面嵌入

完整示例 chart.json引用的原創音訊 original-tone.wav已提供。下載後保持這兩個檔案相鄰;頁面原創示例的簡圖由示例 rendererAdapter 繪製,使用遊戲主題的入口見下文字站譜面。

mountChart 預設組合 Cassiopeia kernel、Vue ChartPlayer、Our Notes plugin、Three renderer 和 Web 音訊/輸入宿主。它擁有掛載 DOM、媒體、input、renderer 與資料 loader。自定義 renderer 可沿用 kernel/Web host 並呈現作者自己的圖形。候選產物安裝與 CDN 方法見 JavaScript 嵌入。

將完整 watch HTML另存為 index.html,再把 chart.json和original-tone.wav儲存到同一個資料夾。JSON 是完整的八秒原創 ChartEmbedDocument,WAV 是音量較低的原創測試音。

my-chart/
index.html
chart.json
original-tone.wav

在 my-chart 資料夾中開啟終端,執行 python3 -m http.server 8000(Windows 用 py -m http.server 8000),再開啟 http://localhost:8000/。Python 安裝和編輯器 HTTP 預覽方法見快速開始。完成後在終端按 Ctrl+C 關閉服務。

點選載入我的 chart.json,等待已就緒,再點播放。你會看見原創示例的音符以預設遊戲畫面呈現,測試音提供實際媒體時鐘。暫停停止播放,進度滑塊按秒跳轉。關閉播放器停止媒體並移除畫面,之後可以重新載入。載入或播放失敗會在畫面上方顯示原因,修正檔案或連線後重試。

託管模組提供預設播放器和配套樣式;模組與公開畫面素材需要聯網,自己的 JSON 和 WAV 則由本地 HTTP 提供。初始例子關閉按鍵音效,配樂仍正常播放。

在同一網頁中保持歌曲 ID 100070、難度 expert,點選載入本站歌曲。等已就緒後點播放。source 讀取當前歌曲,解析它返回的譜面檔案,使用返回的配樂 URL。其他歌曲 ID 可以從API 入門示例取得;歌曲必須實際提供所選難度。換來源前先關閉當前播放器。

資料 source 和畫面 theme 是兩個獨立 factory。完整 HTML 使用公開瀏覽器入口:

import { mountChart, haneokaChartSource, haneokaChartTheme } from "https://haneoka.org/embed/cassiopeia.js";
const chart = mountChart(container, {
source: haneokaChartSource({ songId: "100070", difficulty: "expert" }),
theme: haneokaChartTheme(), server: "intl", locale: "zh-CN", mode: "watch",
});
await chart.ready;
// 播放按鈕中呼叫 await chart.play();元件關閉時 await chart.dispose()。

這段展示接入點;上面的完整 HTML 已提供 container、按鈕、載入狀態、取消、錯誤和清理。它使用輕量素材 quality 2,伺服器與文字語言獨立,無需帳號或 release ID。

在完整 HTML 的 mountChart 引數中,把 volume: 0.3 改為 0–1 的音量,或把 rate: 1 改為 0.8,重新整理網頁後重新載入。mode: "watch" 在點選播放後自動演奏;mode: "play" 開啟手動譜面輸入。加入 settings: { noteSpeed: 9 } 可以調整滾動速度。後面的引數、skin、settings、source 和 handle 表保留完整公開預設值、範圍和限制。自帶 JSON 使用標準 ChartDocument;其他遊戲的原始譜面應先透過其 parser/converter 轉換。

後文包匯入供已有打包應用參考,npm 釋出另行進行。最初的普通 HTML 使用上述託管 ESM;打包應用單獨 import @haneoka/embed-cassiopeia/style.css,官方 facade 會掛載匹配樣式,並在最後一個例項銷燬時移除。

ChartEmbedDocument 包含 chart,可選 audio、background、bgmOffsetMs 和 assets。chart 使用公開 ChartDocument 欄位:version、bpmChanges、signatureChanges、timeScaleChanges、notes、lines、timeline、durationMs。選擇 document 或 source 其中一個。

const handle = mountChart(element, {
document: { chart: myChartDocument, audio: "musique/été.wav", bgmOffsetMs: 250 },
assetsBase: "https://my.example/project/",
theme: haneokaChartTheme(),
locale: "fr-CA",
labels: { player: "Partition", pause: "Pause", loading: "Chargement" },
});
await handle.ready;

myChartDocument 是呼叫方實際準備的標準譜面。音訊為時鐘:seek(seconds) 使用媒體/呈現秒數,譜面時間減去 bgmOffsetMs,正偏移產生前導。沒有音訊時為暫停視覺預覽。

HTTP 透過 httpDataSource({url,decode}),本地透過 fileDataSource(jsonFile) 與 fileResourceResolver(Map)。作者可使用單語言和 Unicode 素材名。原生 theme 自己還請求公開 API/素材;嚴格本地 Map 應讓合法的公開絕對路徑透過,或提供完整自帶 theme。自帶素材可用 document.assets: OurNotesAssetManifest 或 theme({loader,signal,skin}) factory。

const handle = mountChart(element, {
document: { chart: myChartDocument, audio: "audio.ogg" },
assetsBase: "https://my.example/project/",
rendererAdapter: {
async create({ element, chart, loader, signal }) {
const canvas = document.createElement("canvas");
element.append(canvas);
const context = canvas.getContext("2d");
const artwork = await loader.resourceUrl("notes.svg", { signal });
return {
render(snapshot, options) { drawMyChart(context, chart, snapshot, options, artwork); },
resize(width, height, ratio) { canvas.width = width * ratio; canvas.height = height * ratio; },
dispose() { canvas.remove(); },
};
},
},
});

drawMyChart 由作者實現。adapter 路徑載入 kernel/Web host;自己的 render、resize、dispose 負責圖形與 SE。可選 laneAtClientPoint(x,y) 返回 0–23 連續原生 lane,外部為 -1,以接收 play-mode input。createSession 為另一個源遊戲提供自己的規則;預設使用當前 Cassiopeia session。

await chart.setOptions({ rate: 0.8, volume: 0.6, noteSoundVolume: 0.4, settings: { noteSpeed: 9 } });
await chart.setSkin({ noteSkin: "skin003", noteEffectSkin: "effect001Simple", noteSeGroup: 4 });
chart.seek(12);
await chart.play();
chart.pause();
await chart.dispose();

play、setOptions、setSkin、dispose 返回 Promise,pause/seek 同步。ready 表示視覺初始化,不表示媒體完整下載;在使用者點選後呼叫 play 以解鎖音訊。預設 theme 更換皮膚時在當前媒體位置重新構建並暫停。skin001/002/003 為 Our Notes/Garupa/Honeycomb,effect001/effect001Simple 為效果版本,SE 組 1–4 為 Our Notes/Solid/Wood/Typing。

loadTimeoutMs 預設 30000,只覆蓋構造。cancel 和外部 signal 終止 handle;dispose 冪等,載入中也能呼叫,拆除 DOM、停音訊並釋放 loader URL。重試先銷燬再掛載。snapshot 提供 phase、playing、time、duration。

onEvent/subscribe 提供 load、state、player、error,load 保留 core 每個資源的進度,player 將既有播放器事件及引數轉交宿主。subscribe 返回取消監聽函式,監聽錯誤與操作結果隔離。

assetsBase 解析作者資料資源;theme 的 apiBase/publicBase 獨立指定公開 API/原素材根。core 預設 cors/omit,可配置 fetcher、headers、credentials 與每項預設 64 MiB 預算。預設播放器的媒體/貼圖/SE 使用既有原生載入器及 anonymous CORS,未經過注入 fetcher/private headers;私人素材可由宿主供應可訪問 URL 或 Blob。

把全部延遲 chunk 與模組一起釋出,單獨連結 style.css。本站影象消費在外站遇到 Referer 403 時,可讓資料傳輸使用 new Request(request,{referrerPolicy:"no-referrer"});原生貼圖/音訊請求仍由其載入器控制,需按部署的 origin/載入方式確認訪問。CSP 應允許模組、素材、媒體以及使用的 blob URL。

宿主沿用已有 Material 控制元件和載入狀態,保持舞臺尺寸及觸控按鈕。Cassiopeia 預設顯示原圖示與Haneoka連結,圖示半透明懸浮,畫面保持全尺寸,dispose自動清理;作者素材保持原許可。

預設八秒原創ChartDocument、輕音量WAV Blob和作者Canvas adapter,透過真實Cassiopeia session/MediaClock驅動。它展示作者整合,不冒充遊戲皮膚。HTTP讀取實際譜面JSON,file用你選的File;本站模式改用真實song source/native theme。修改options後載入,再play/pause/seek/setOptions,預設theme支援setSkin。

1. 選擇譜面

從完整原創檔案開始。載入後點擊播放讀第一句。

開啟完整 JSON · 下載 JSON

正在準備範例…

進階參數與完整輸入(修改後重新載入)
內容語言
載入紀錄與錯誤詳情
檢視頁面範例程式碼

點選才import/create,同頁另一個例項先dispose。input/publicSource/nativeTheme/url為示例欄位,會轉成真實factory;callback不能寫JSON,實際adapter在原始碼連結中。五語顯示另保留作者locale輸入。

import { mountChart, haneokaChartSource, haneokaChartTheme } from "https://haneoka.org/embed/r-22bfe102f7deb73f/cassiopeia.js";
const chart = mountChart(element, { source: haneokaChartSource({songId:"100070",difficulty:"expert"}), theme:haneokaChartTheme(), server:"intl",locale:"en",mode:"watch" });
await chart.ready;
await chart.play(); // 使用者按鈕
await chart.dispose();

實際示例使用穩定官方alias。官方facade在mount注入原包CSS、最後dispose移除;包/bundler方式單獨import style.css。r22沒有nonce引數,後續nonce介面需取該版本真實receipt/interface。CDN檔案為部署build輸出,倉庫保留原始碼。

使用本地音樂創作可編輯音符,見譜面創作器教程。其 Project JSON 和帶音訊備份與下面的播放 ChartDocument 分別使用對應入口。

container必填瀏覽器HTMLElement且有效尺寸,非法TypeError、同容器重複Error。runtime嚴格source/document二選一,否則TypeError。預設composition還需theme或document.assets,自帶renderer管理自己的圖形。

欄位型別/預設作用/錯誤/組合
documentChartEmbedDocument,替代source必填notes/lines array和finite durationMs,非法ready TypeError。
sourceDataSource<ChartEmbedDocument>,替代document必填memory/http/file/custom,load失敗拒絕ready。
serverstring/undefined,本站intlregistry,與locale獨立。
localestring/en作者/UI語言。
assetsBase可選HTTP(S)目錄/sourcebase無query/fragment,非法TypeError。
resolveResource(key,DataContext)=>URL/string/Blob/Promiseliteral鍵,預設原key;缺資源失敗。
fetcher(Request)=>Promise<Response>,fetchcore/nativebytes;公開Haneoka assets/runtime/embed no-referrer,作者/API保留策略;音樂獨立anonymous CORS。
credentialsomit/same-origin/include,omitcore傳輸,需要origin/credential允許。
headersHeadersInit,無core頭,音樂不繼承私有header。
maxBytes正safe integer/67108864每decodedresponse,非法RangeError;URL/媒體元素另計。
onListenerError(unknown)=>void,可選observer隔離。
theme(ChartThemeContext)=>manifest或Promise,可選預設呈現,優先於document.assets;factory失敗拒絕construct,setSkin需要。
skinChartSkin/{}下表四欄位;作者renderer不消費native皮膚。
rendererAdapterChartRendererAdapter,可選選kernel/Webhost作者呈現,自己擁有SE。
createSession(chart,playbackOptions)=>CassiopeiaSessionPort,可選作者renderer替代規則,預設ChartSession;不傳給預設Vue driver。
signalAbortSignal,可選終止handle,ready後也適用。
loadTimeoutMs正safe integer/30000檔案與每構造階段期限,不是player生命週期;非法RangeError/超時TimeoutError。
labels{player?,pause?,loading?}可選string,預設Chart player/Pause/Loading,僅預設Vue。
onEvent(ChartEmbedEvent)=>void,可選載入前登記。
modechart/watch/play,watch預覽/自動/手動input,變更沿driver reset/pause。
settingsPartial<RenderSettings>+judgementOffsetMs,{}下表完整;setOptions合併。
volumenumber/.80–1,初始media超界拒絕,更新clamp,需finite。
ratenumber/1正數規範化 .25–4;非法/非正用fallback。
loopboolean/false音樂loop。
noteSoundEnabledboolean/true預設SE,作者renderer自管SE。
noteSoundVolumenumber/.7預設SE gain,使用finite0–1。

core共同引數見core。無autoplay,ready後使用者操作play;construct deadline按階段,不表示ready已下載完整音樂。

skin取值/本站預設含義
noteSkinskin001/002/003,001Our Notes/Garupa/Honeycomb。
noteEffectSkineffect001/effect001Simple,前者native效果。
noteSeGroup1–4,1Our Notes/Solid/Wood/Typing。
currentQuality0/1/2,2high/middle/low素材,與graphicsQuality獨立。

typed union由theme解析,壞/缺素材拒絕構造。labels.player/pause/loading全為可選string,不選擇server或語言。

haneokaChartSource({songId,difficulty,apiBase?})的前兩項必填,difficulty為easy/normal/hard/expert/special/master且該曲提供;apiBase 預設 https://haneoka.org/api/v1/。讀取返回譜面file並parse/build,再用musicUrl;缺difficulty Error,data/budget/parser保留原因。server從mount。

haneokaChartTheme({apiBase?,publicBase?,server?}={}):API預設上述root,publicBase預設其origin根,server override預設loader.server→intl;與作者assetsBase獨立。真實descriptor缺失/歧義Error。ChartThemeContext完整為loader、signal、skin;可以按signal讀loader bytes並返回manifest。

以下是public OurNotes外掛RenderSettings預設,mount本身只給partial{};固定舊SDK仍保留其plugin版本。未被mount顯式校驗的項原樣傳遞,使用finite和建議區間,不偽稱SDK逐項驗證。

欄位型別/預設呈現/規範化
noteSpeednumber/5scroll速度。
noteSpeedCurvenumber/1.30999994offset curve常量。
mirrorboolean/false原24lane映象。
effectsboolean/truenote效果。
noteSizenumber/1.1–2 clamp。
longAlphanumber/.6.1–1。
guideAlphanumber/.6.1–1。
graphicsQualitynumber/1原0/1/2高/中/低。
guidelineCountnumber/6lane分隔。
guidelineOpacitynumber/.25建議0–1 alpha。
laneOpacitynumber/.8建議0–1。
showJudgementLineboolean/false判定線。
showSimultaneousLineboolean/true同時線。
showEaseNoteboolean/falseease/critical強調。
backgroundBrightnessnumber/.7建議0–1。
judgeResultPositioncenter/lane/none,center位置。
noteOverlapLaneBuffernumber/0判定lane buffer。
showFastSlowboolean/true時機提示。
showPerfectFastSlowboolean/true包括Perfect。
showJudgeOffsetMsboolean/falsems顯示。
alwaysShowFastSlowboolean/false常駐偏好。
scoreRankScoresreadonly number[]/無C/B/A/S/SS閾值。
judgementOffsetMsnumber/0ms session offset,作者adapter用setOffset更新。

ChartEmbedDocument.chart必填;audio/background?:string預設無,資源key/URL;bgmOffsetMs?:number預設0,chartTime=mediaMs-offset;assets?:OurNotesAssetManifest在無theme時提供預設圖形,rendererAdapter可省略;無audio為暫停預覽。

ChartDocument root必填欄位:version、bpmChanges、signatureChanges、timeScaleChanges、notes、lines、timeline、durationMs。BPM行tick/beat/timeMs/bpm;signature行tick/beat/timeMs/numerator/denominator;scale行timeMs/scale;timeline skills行index/tick/timeMs,fever行index/startTick/endTick/startTimeMs/endTimeMs,callChanges行index/tick/timeMs/rhythms。line為id/kind:long或guide/critical/noteIds。

note全部欄位:id/tick/timeMs/beat/pos/size/laneX/width;operateType/judgementType/judgementAreaOffsetType/direction;critical/judged/visible;lineIds;slideAlong;indexInLine:number/null;easeL/easeR:enum/null。使用公共converter/enums,原創例子包含全部必填。

native manifest root為id、source{game,noteSkin,laneSkin,noteEffectSkin,authoredProfile}、liveQuality{quality,effectRenderingScale,backgroundCameraFps,live2dFps}、noteAtlas、arrowGradient?、lane、particles、slideLineStyle、hud、tmpSdfFont?、noteSeGroup、noteSounds、palette、tiltThresholds[{distance,value}]。它是provider DTO;sprite/particle/audio詳細descriptor見完整公開schema,資源交給loader。

rendererAdapter.create({element,chart,loader,signal})同步/Promise返回render(snapshot,options)、resize(width,height,pixelRatio)、dispose()、可選laneAtClientPoint(clientX,clientY)。snapshot複用,只在當次消費;lane連續0–23或外部-1。作者adapter用真實MediaClock/input/session;createSession可以提供自己的publicsession規則,遵循reset/update/input/finish。

成員簽名生命週期/錯誤
readyPromise<void>視覺準備,requiredSE失敗拒絕,音訊可還buffer。
snapshotphase/playing/time/durationphase為loading/ready/cancelled/error/disposed,秒。
play()=>Promise<void>需ready,返回瀏覽器audio拒絕。
pause()=>void需ready,state暫停。
seek(seconds)=>voidfinite非負否則RangeError,已知durationclamp,需ready。
setOptionsChartPlaybackOptions=>Promise<void>partialsettings合併,其它交driver。
setSkinChartSkin=>Promise<void>ready+預設theme factory,作者/no factory Error;當前位置重建並暫停。
subscribelistener=>unsubscribe未來事件,disposed Error。
cancel()=>voidboot/ready都終止,停止player/input/資源。
dispose共用Promise<void>冪等,包括loading;移DOM/fullscreen監聽,停media/renderer/revoke;失敗AggregateError。

events為load{event}、state{snapshot}、player{name,args}、error{error}。預設轉發playing/media-playing/timeupdate/duration/judgement/skill/fever/callchange;timeupdate/duration為秒,judgement含note/judgement/timing/diffMs/judgedAtMs/combo/maxCombo/score/scoreDelta/life。observer異常由onListenerError隔離,source/transport/theme/decoder保留原Error,沒有新的錯誤envelope。同容器重新mount前先dispose。

當前原始碼的署名背景透明,圖示透明度55%,48px角落觸控區域懸浮在完整畫面上。穩定alias隨匹配構建部署採用該行為;固定r22保留舊的預留行佈局,顯式固定舊版時以該版行為為準。