lyrics-typing-engine
v2.0.0
Published
A typing engine designed for music-based typing games. Provides time-tagged typing map generation and kana/roma input evaluation synchronized with music playback.
Readme
lyrics-typing-engine
音楽に同期したタイピングゲーム用のエンジンライブラリ。歌詞データからタイピングマップを生成し、ローマ字入力・かな入力の判定を行います。
インストール
npm install lyrics-typing-engine機能
タイピング譜面データの構築
buildTypingMap()
rawMapLines - ビルド前のタイピング譜面データ
charPoint - ローマ字換算での1打鍵あたりのポイント
import { buildTypingMap, type RawMapLine } from 'lyrics-typing-engine';
/**
* @note 重要: タイピング譜面データはtime:0から始まり、最後の歌詞は"end"にする必要があります。
*/
const rawMapLines: RawMapLine[] = [
{ time: 0, lyrics: "こんにちは", word: "こんにちは" },
{ time: 3.5, lyrics: "世界", word: "せかい" },
{ time: 6.0, lyrics: "end", word: "" }
];
const builtMapLines = buildTypingMap({ rawMapLines, charPoint: 50 });
console.log(builtMapLines);
/**
* [
* {
* time: 0,
* duration: 3.5,
* lyrics: "こんにちは",
* kanaLyrics: "こんにちは",
* romaLyrics: "konnitiha",
* wordChunks: [
* { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
* { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
* { kana: "に", romaPatterns: ["ni"], point: 100, type: "kana" },
* { kana: "ち", romaPatterns: ["ti", "chi"], point: 100, type: "kana" },
* { kana: "は", romaPatterns: ["ha"], point: 100, type: "kana" }
* ],
* kpm: { kana: 85, roma: 154 },
* notes: { kana: 5, roma: 10 },
* },
* {
* time: 3.5,
* duration: 2.5,
* lyrics: "世界",
* kanaLyrics: "せかい",
* romaLyrics: "sekai",
* wordChunks: [
* { kana: "せ", romaPatterns: ["se", "ce"], point: 100, type: "kana" },
* { kana: "か", romaPatterns: ["ka", "ca"], point: 100, type: "kana" },
* { kana: "い", romaPatterns: ["i", "yi"], point: 50, type: "kana" }
* ],
* kpm: { kana: 72, roma: 120 },
* notes: { kana: 3, roma: 5 },
* },
* {
* time: 6,
* duration: 0,
* lyrics: "end",
* kanaLyrics: "",
* romaLyrics: "",
* wordChunks: [],
* kpm: { kana: 0, roma: 0 },
* notes: { kana: 0, roma: 0 }
* }
* ]
*/歌詞フレーズ切り替わり時のタイピングワード更新
createTypingWord(builtMapLine: BuiltMapLine, correct?: { kana: string; roma: string }) - builtMapLinesの行から次のフレーズのタイピングワードを作成
correct - 正解したローマ字・かな(デフォルトは空文字列)
import { createTypingWord } from 'lyrics-typing-engine';
const count = 0;
const timer = () => {
const currentTime = video.getCurrentTime();
const nextLine = builtMapLines[count + 1];
if (currentTime >= nextLine.time) {
count++;
const newTypingWord = createTypingWord(nextLine);
console.log(newTypingWord);
}
}
/**
* {
* correct: { kana: "", roma: "" },
* nextChunk: { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
* wordChunks: [
* { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
* { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
* { kana: "に", romaPatterns: ["ni"], point: 100, type: "kana" },
* { kana: "ち", romaPatterns: ["ti", "chi"], point: 100, type: "kana" },
* { kana: "は", romaPatterns: ["ha"], point: 100, type: "kana" }
* ],
* wordChunksIndex: 1,
* }
*/入力の判定
isTypingKey(event: KeyboardEvent) - イベント時の文字入力キー判定
evaluateRomaInput({ event, typingWord, isCaseSensitive? }) - ローマ字入力時の判定
evaluateKanaInput({ event, typingWord, isCaseSensitive? }) - かな入力時の判定
isCaseSensitive - 大文字小文字を区別するかどうか(デフォルトはfalse)
import { isTypingKey, evaluateRomaInput, evaluateKanaInput } from 'lyrics-typing-engine';
const inputMode = "roma";
document.addEventListener('keydown', (event) => {
if (!isTypingKey(event)) return;
const typingWord = readTypingWord();
const typingResult =
inputMode === "roma"
? evaluateRomaInput({ event, typingWord })
: evaluateKanaInput({ event, typingWord });
if (typingResult.successKey) {
// 正解時の処理
console.log(typingResult);
} else if (typingResult.failKey) {
// ミス時の処理
}
});
/**
* {
* nextTypingWord: {
* correct: { kana: "こ", roma: "co" },
* nextChunk: { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
* wordChunks: [
* { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
* { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
* { kana: "に", romaPatterns: ["ni"], point: 100, type: "kana" },
* { kana: "ち", romaPatterns: ["ti", "chi"], point: 100, type: "kana" },
* { kana: "は", romaPatterns: ["ha"], point: 100, type: "kana" }
* ],
* wordChunksIndex: 2
* },
* successKey: "o",
* failKey: undefined,
* chunkType: "kana",
* isCompleted: false,
* updatePoint: 100
* }
*/リプレイ再生用の入力判定関数
executeTypingInput({ inputChar, inputMode, typingWord, isCaseSensitive? })
isCaseSensitive - 大文字小文字を区別するかどうか(デフォルトはfalse)
import { executeTypingInput } from 'lyrics-typing-engine';
const replayData = [
startInputMode: "roma",
typeResults: [
{ time: 0, char: "c", isSuccess: true }
{ time: 1, char: "o", isSuccess: true }
{ time: 2, char: "a", isSuccess: false }
]
];
const typeResults = replayData[0].typeResults;
const inputMode = replayData[0].startInputMode;
const typingResult = executeTypingInput({
inputChar: typeResults[0].char,
inputMode,
typingWord
});
console.log(typingResult);
/**
* {
* nextTypingWord: {
* correct: { kana: "", roma: "c" },
* nextChunk: { kana: "こ", romaPatterns: ["o"], point: 100, type: "kana" },
* wordChunks: [
* { kana: "こ", romaPatterns: ["ko", "co"], point: 100, type: "kana" },
* { kana: "ん", romaPatterns: ["nn", "'n", "xn"], point: 100, type: "kana" },
* { kana: "に", romaPatterns: ["ni"], point: 100, type: "kana" },
* { kana: "ち", romaPatterns: ["ti", "chi"], point: 100, type: "kana" },
* { kana: "は", romaPatterns: ["ha"], point: 100, type: "kana" },
* ],
* wordChunksIndex: 1
* },
* successKey: "c",
* failKey: undefined,
* chunkType: "kana",
* isCompleted: false,
* updatePoint: 0
* }
*/
表示用タイピングワードの生成
createDisplayState(typingWord: TypingWord, options?: { remainWord: { maxLength: number } }) - タイピングワードから表示用の状態を生成
typingWord - 現在のタイピングワード
options.remainWord.maxLength - 未入力部分の表示文字数上限
入力済みのスペースは "ˍ" (U+02CD) に置換されます。 未入力半角スペースは " " (U+2004) に置換されます。
import { createDisplayState } from 'lyrics-typing-engine';
const displayState = createDisplayState(typingWord, { remainWord: { maxLength: 20 } });
console.log(displayState);
/**
* {
* correct: { kana: "こ", roma: "co" },
* nextChar: { kana: "ん", roma: "nn" },
* remainWord: { kana: "にちは", roma: "nitiha" }
* }
*
* // nextChar.roma は nextChunk.romaPatterns の最初のパターンが表示されます
*/タイピングワード再生成
かな入力 → ローマ字入力 の動的なモード切り替え時に使用します。
recreateTypingWord(typingWord: TypingWord) - タイピングワードを再生成します。
import { recreateTypingWord } from 'lyrics-typing-engine';
const newTypingWord = recreateTypingWord(currentTypingWord);カスタムオプション
ジェネリック型で独自のオプションを定義できます。
interface MyOptions {
changeCSS?: string;
changeVideoSpeed?: number;
}
const rawMapLines: RawMapLine<MyOptions>[] = [
{
time: 0,
lyrics: "歌詞",
word: "かし",
options: { changeCSS: "color: red;" }
}
];
const builtMapLines = buildTypingMap<MyOptions>({ rawMapLines, charPoint: 0 });import 可能な型
// ビルド前タイピング譜面データ型
interface RawMapLine<TOptions = unknown> {
time: string | number; // 時間(ミリ秒)
lyrics: string; // 歌詞
word: string; // ひらがなで記述されたタイピングワード
options?: TOptions; // オプション(カスタムオプション)
}
// ビルド済みタイピング譜面データ型
interface BuiltMapLine<TOptions = unknown> {
time: number; // 時間(ミリ秒)
duration: number; // 行の時間の長さ(秒) - 次の行のtimeとの差分
wordChunks: WordChunk[]; // ビルド済みタイピングワード
lyrics: string; // 歌詞
kpm: { kana: number; roma: number }; // フレーズの要求速度
notes: { kana: number; roma: number }; // フレーズの要求打鍵数
kanaLyrics: string; // かな表記の歌詞
romaLyrics: string; // ローマ字表記の歌詞(各chunkの最初のパターン)
options?: TOptions; // オプション(カスタムオプション)
}
// ビルド済みタイピングチャンク 型
interface WordChunk {
kana: string; // ひらがな
romaPatterns: string[]; // ローマ字パターン
point: number; // ポイント
type: "kana" | "alphabet" | "num" | "symbol" | "space" | undefined; // タイピングチャンクの種類
}
// タイピングモード 型
type InputMode = "roma" | "kana";
// タイピングワード 型
interface TypingWord {
correct: { kana: string; roma: string }; // 正解したローマ字・かな
nextChunk: WordChunk; // 次のタイピングチャンク
wordChunks: WordChunk[]; // 全タイピングワード
wordChunksIndex: number; // 現在のwordChunksの位置
tempRomaPatterns?: string[]; // 一時的なローマ字パターン
}
// タイピング入力時の判定 型
interface TypingInputResult {
nextTypingWord: TypingWord; // 更新後のタイピングワード (ミス時は実質更新されません)
successKey: string | undefined; // 正解時の入力キー
failKey: string | undefined; // ミス時の入力キー
chunkType: WordChunk["type"]; // 入力したタイピングチャンクの種類
isCompleted: boolean; // 打ち切り判定
updatePoint: number; // 加算ポイント
}
ライセンス
MIT
作者
toshi7878
