おもちつき技術共有所

Last-modified: 2020-02-22 (土) 22:11:01

おもちつきの発展に伴い、技術格差が拡大しました。
その格差を縮めるために、先進おもちつきは発展途上おもちつきを支援しなければなりません。
発展途上おもちつきが先進おもちつきの要素を自由に取り入れれるようにこのページは作られました。
プログラム初心者の方でも分かるように書いていただけるとありがたいです。
おもちつきの製作者でなくても、プログラムが解読出来るなら、このページを編集して頂いても構いません。
しかし、やはり出来るだけそのおもちつきを作った人が加筆するのが、正確性という観点では良いかと思われます。
2月22日より、統合版の技術の情報開示を許可します。

SPの技術 Edit

変数とその詳細 Edit

変数詳細
HISCP[曲番号,内容]過去バージョンからのデータの引き継ぎに利用される。
HISC[難易度,曲番号,内容]ハイスコアを保存する
難易度は0がかため、1がやわらかめ(使われてない)
内容は0→おもち%、1→○、2→△、3→×、4→使われていない
例えばHISC[0,0,1]なら最初の曲のかための○の数を表す
BTIPE[0]ボタン設定
BGM_TIT$[曲番号]曲のタイトル
BGM_HARD[難易度,曲番号]曲の難易度
BGM_VOL[曲番号]曲の音量
TROPHY[難易度,曲番号]トロフィーの有無
CMPS$[曲番号]提供者名
FMT[3000]音符の判定を行うフレーム数
音符が3000個を超えるとエラーになってしまう。
また、FMT[1023]までしか初期化されていないため実質1024個しか使えない。*1*2
FMC[3000]赤音符を流すか、青音符を流すかの判定。赤は0、青は1
FMK[3000]音符のスプライトの管理番号
FMR[3000]音符の判定結果。と書かれているが、使われていない。
FMF[3000]1→おもちが飛んでいる。0→飛んでいない。おもちを飛ばす処理は@FLY
FMX[3000]
FMY[3000]
FMV[3000]
FMXV[3000]
おもちを飛ばす処理で使用。

いろいろな制限、仕様 Edit

  • 曲数…64曲
    • HISCやBGM_TIT$などのサイズ
    • 91行目のFOR Q=0 TO 128-1(あまり関係ないが)
    • 146行目のFOR Q=0 TO 63
  • 音符数…1024個
    • FM~で始まる変数のサイズ
    • 2023行目のFOR W=0 TO 1023
  • 譜面の行数…129行
    • 2232行目のFOR W=0 TO 128
  • その他
    • 最後の曲(BGM 1)を選んでオプション画面などに行った後選曲画面に戻ると、曲名の表示が間違っている
      • 1535行目IF QQ+Q>=BGM_MAX+1 THEN QQ=QQ-(BGM_MAX+1)

32分音符とかを使うには? Edit

  1. テンポを倍にする
    • MMLには元のテンポをT○○と書いておき、その前に書くテンポはその倍にする。
    • オプションのテンポ変更が使えなくなる。
  2. タイミング合わせ用MMLを変える
    • 「BGMSET 150,」を検索すれば見つかるはずです。
    • デフォルトでは"[$0=1 R16 $1=1 R16]"となってますが、ここの16を32とかに変えると32分音符とかになります。
    • ただのMMLなので、[$0=1R16$1=1R16]16[$0=1R32$1=1R32] みたいに途中から変えたりもできます。
    • これを譜面によって変える方法は各自で実装してください。
    • このままでは曲の開始がずれてしまいます。IF Q==4*4+1 THEN BGMPLAY ...っていう箇所があると思いますが、このQの値を(1小節の音符数)+1にすると正しいタイミングで曲が流れます。
    • UNTIL Q==の後ろは無理に1小節の音符数に合わせる必要はありません。Q==1とかでも動きます。
      • Qの値を大きくすると、譜面と曲の開始が遅くなります。逆に小さくすると早く始まります。1が最速。
      • ただし、1文字あたりの音符の長さによっては永遠に始まらないのでQ>0が良いと思われます。
    • ちなみに音遊戯等ではこのずれを防ぐため最初の1小節は必ず16分になってるみたいです。
    • この方法だと細かい音符がちらついて見えてしまいますが、2箇所にあるSPANIM 100+I,"Z",1,0,-((120/TMP*120)*2),-50,1の「-50」を「-250」などに変えることで改善できます。
  3. 全く別の方法にする
    • MILLISECを使う方法
      • NewSPやTG、KRなど。
    • VSYNC回数を数える or MAINCNTを使う方法
      • 太シとTJAS+
      • 3DSとWiiUでは1フレームの長さがわずかに違うため、タイミングがあわないようです。

音遊戯の技術 Edit

RYの技術 Edit

RCBMPの技術 Edit

SZの技術 Edit

謎の計算式たち Edit

意味不明な計算式をつかって数値だしたりしてるだけなので技術とかありません
計算式とかはここに → リズミカルおもちつきSZ
もし実装するのならおもちSZからぱくるより自分で作ったほうがわかりやすくなりますよ

NSPの技術 Edit

テンポ自動読み込み Edit

MML文字列を1文字ずつ調べ、テンポ変更(Tまたはt)を探します。
見つかったらそのテンポを配列TMPCHGに追加し、「$7=配列の番号」を追加します。
例えば「T120」を見つけたらTMPCHG[0]=120になり、MMLは「T120$7=1」にします。
(再生直後にBGMVARが0を返すため、$7の値は配列のインデックスより1多くしています)
こうすることで再生時にBGMVARで$7の値を調べ、配列からテンポが取得できます。

MILLISEC Edit

プレイ開始時のMILLISECの値をLASTMSに入れます。
譜面読み込み時に、音符の長さ(ミリ秒)を計算します。(60000/テンポ*4/L値)
現在のMILLISECとLASTMSの差が上の値より大きくなったら、音符を1つ読み込み、LASTMSに上の値を加算します。

テンポ自動変更 Edit

BGMVARでMMLのテンポの変更を検知したとき、
テンポが前の何倍になったかを計算し、
現在流れているすべての音符の流れる速度を一度に変更しています。
Hコマンドもこれとほぼ同じことをしています。

譜面ファイル読み込み Edit

以下、「曲番号」「譜面ID」「ファイル番号」は別物なので注意
曲数が変わっても対応できるように、すべて1次元配列にしてPUSHしています。

変数詳細
BGM_TIT$[曲番号]曲のタイトル
BGM_HARD[曲番号*5+難易度]曲の難易度
1次元配列で、1曲目軟→普→硬→カチ→黒→2曲目軟→...
BGM_VOL[曲番号]曲の音量
BGM_EXIST[曲番号*5+難易度]譜面が存在すれば1、なければ0
これも1次元配列
BGM_FILE0[曲番号]現在使用されていない
BGM_ID0[曲番号]
BGM_FILE[曲番号]曲番号に対応するファイル番号を返す。
BGM_ID[曲番号]曲番号に対応する譜面ID(譜面ファイルで設定したID)を返す。
CMPS$[曲番号]曲の作曲者
RDTMP[7]読み込むときにちょっと使っただけです
F$[ファイル番号]FILESの戻り値
ファイル名を返す。

FILESの戻り値F$を1つずつ調べて読み込みます。
なのでファイルの読み込み順はABC順になります。
曲番号はNewSPが読み込んだ順に0,1,2,...と自動でつきます。
譜面やMMLを読み込むときだけ曲番号→ファイル番号、譜面IDに変換して使っています。

譜面読み込み1(DEF FUMENREAD$()) Edit

NewSPモード Edit

そのまま。

TJASモード Edit

NewSPモードに変換する。

#BPM,数値→T数値

#HS,数値→S数値*100

#HB,数値→H数値*100

#MEA,数値→とりあえず変数MEAに入れる
コンマを読み込んだとき→L(文字数/MEA)とその小節の譜面データ

譜面読み込み2 Edit

FORで譜面を1文字ずつ調べる。
コマンドは飛ばす。
ただしLコマンドは最大値を変数MAXLに入れて記憶しておく。
音符の記号(ONP_D$とONP_K$にすべて一覧になって入っている)の個数を数えてMAXCMBに入れる。

譜面読み込み2.5 Edit

譜面先頭にT数値があった場合はそれを曲のテンポとする

譜面読み込み3(メインループ直前) Edit

BEF=1にする。(確かbeforeの略だった気がする)
ONPLIMITは読み込む小節数。(旧3DSでは重くなるためMAXLによって変更する。)
STARTMS(曲を流し始めるMILLISEC)とLASTMS(タイミング合わせ)をここで設定。
FOR文でCNTとMSを少しずつ変えながら@HUMEN2を呼び出して、(ONPLIMIT-1)小節を読み込む。(その後もう1小節追加された)
このあたりの実装は動けばいいやで試行錯誤したので自分でもよくわかってない。
終わったらBEF=-1にする。

譜面読み込み4(@HUMEN) Edit

CNTに開始からのフレーム数、MSにミリ秒を入れる。
BGMVARでテンポが変わったか確認。
テンポまたはHBが変わった場合は、今流れているすべての餅の速度を変更する。
STARTMSが経過したら曲を流し始め、BEF=0
そのまま@FUMEN2へ行く。

譜面読み込み5(@HUMEN2) Edit

BEF=-1の間の1小節分は開始前に読み込んでしまったので、スルーする。
LASTMSでタイミングを合わせる。
各種コマンドを読み込む。
L,Sの値は変数ONPL,ONPSに入れる。
Hは1小節後に変化させるため配列HBCHGF,HBCHGVに入れ、後で@HUMENで読み込む
IFを並べる気にはならないのでINSTRN(INSTR)とMID$,VALを使って音符の種類、速度を読み込む。
音符位置(SPOF)を計算する。この式もよくわからない。
SPでは3000の配列に音符の情報を入れていたが、NewSPではPUSHで追加していき、判定が終わったものはPOPで削除している。

TGの技術 Edit

複数のデータ作成 Edit

SPTG
HISC[難易度(かため),曲番号,内容]SVDT[難易度,曲ID,内容,データ番号]
BTIPE[内容(ボタン)]SETTING[内容,データ番号]

要素数を1つ増やしています。
SPから引き継ぐことも可能です(その場合はDATA 1のかために引き継がれる)。

移植元表示 Edit

ver.4.10以前 Edit

配列概要
BGM_TITB$[曲ID]元の曲名です。
移植元表記をオフにした場合に表示されます。
移植元表記をオンにしてても、演奏画面で表示されます。
BGM_TIT$[難易度,曲ID](移植)と表示する際の曲名です。
移植元表記を(移植)にした場合に表示されます。
BGM_TITA$[難易度,曲ID](SP)等と移植元表記する場合の曲名です。
移植元表記を(SP)にした場合に表示されます。
BEFORE$[難易度,曲ID]移植元です。

難易度によって移植元が違ったりするが場合があるので、移植元や、移植元表記を付けたあとの曲名は、難易度別になっています。
BGM_TIT$は、配列BEFORE$の中身がヌル文字じゃない場合のみ、(移植)と表記するようにしています。

ver.4.20以降 Edit

BEFORE$を曲名の下に表示するようになった為、BGM_TIT$,BGM_TITA$が消えました。
(そもそもよく考えれば、BGM_TITB$[BGMN]+BEFORE$[HARD,BGMN]とすればよかったので、元々必要なかった。)

コマンド Edit

NSPのLコマンドとSコマンドが使えます。また、l16やs100など、小文字でもいけます。

MILLISEC Edit

NSPとは違い、SMSという変数のみを使っています。
ゲーム開始時に、MILLISECをSMSに代入します。
それ以降音符を読み込む時間*3をSMSに加算していきます。
SMSがMILLISEC+{(1000/60)*(120*4)/TMP}*ONPLIMIT*4以上になったら音符を読み込み、加算します。
これの繰り返しです。

UEの技術 Edit

KRの技術 Edit

MILLISEC Edit

譜面読み込み時に、音符が判定枠に来るタイミング(240000/_ONP/BPM*文字目[ミリ秒])を求め、そこから(240000/BPM/HS)引いたのが音符を右端から出すタイミングになる。
最初の音符が判定枠にくる2秒前*5にSTARTCNTをセットし、更に現在のMILLISECからSTARTCNTを引いたものをSTARTCNTに代入する。演奏中のループごとにCNTという変数に現在のMILLISECからSTARTCNTを引いたものを代入し、それが音符を出すタイミング値以上になったら音符を画面右端に出す。
※_ONP…1文字が何分音符を表すかを格納した変数

NSP SODAの技術 Edit

画面レイアウト Edit

SODAでは、たしか320x180の解像度(4倍サンプリング)で作りましたが、640x360(2倍サンプリング)で作る方がオススメです(とても苦労しました)。
詳しいことは忘れてしまいましたが、画面幅が変わっても、ノーツが見えている時間が同じになるようにかなり調整しました。

セーブデータコピーツール Edit

何も難しいことはなく、プロジェクトをまたいでセーブデータを見に行くだけのものです。
セーブデータファイルの先頭か末尾に、適当なラベルを置いてDATA$、TIME$(プチコン4ではどちらも関数)を順に書き出させて、比較しただけです。

文字列同士も不等号で比較ができます。およそ、辞書で引いたときに先に出てくる方が「小さい」という順序になります(大文字だけとか小文字だけの比較の場合)。

AOTの技術 Edit

プリセット原曲の使用 Edit

※名無しさんから意見を頂いての実装です
選曲画面でMMLを読み込んで変数MMLD$に入れたあと、BPLAY命令(下で書く)でユーザーMMLかプリセット原曲かを判断して再生する。
おもちをつく時のBGMPLAYではBRET関数を使っているが、その前のBGMSETのところでプリセット原曲だった場合に「READ H」であらかじめ番号を変数Hに入れているのがポイント(?)。直後にその関数DEF中で使用する。
(DEF中のREADよりも確実な気がした)

DEF2つ Edit

BPLAY C$...MML文字列をC$に渡し、それが「#PS」ならその次の番号をREADで読み込んで再生する。「#PS」以外ならそのまま「BGMPLAY C$」。再生直後に音量を設定。
BRET(G$)...BGMSET後の再生番号の設定。MML文字列をG$に渡し、それが「#PS」ならあらかじめ読み込んで代入しておいた変数Hを返し、「#PS」でなければ128を返す。

汎用技術 Edit

ここでは汎用的な技術について紹介する。

これらの技術は統合版に使われているかもしれないが、普遍的、一般的な技術であり、とくに機密とするよりは公開して皆で共有するのが、おもちつきに限らないプログラミング技術向上に役に立つと考えるし、またそのように考えられているようである(オープンソースなど)。

処理速度問題 Edit

処理落ちが発生する場合はネックとなっている個所、とくに実行時間の大部分を占める処理がある一種類の繰り返し処理ということもあり、その繰り返される部分を改善すれば少ない労力で処理落ちを抑えることができます。
もし全体的に実行時間が散らばっている場合は、よく繰り返される少し重い処理から順に手を付けるとよいでしょう。
(参考)パレートの法則 - Wikipedia

データ圧縮 Edit

収録曲がかなり多い作品が増えてきているため、圧縮について提案します。

リズミカルおもちつき作品で最もデータが肥大化する原因は譜面データ本体にあります。その譜面データを思い切って圧縮してみるのはどうでしょうか。

筆者が思い当たる圧縮方法は次のようなものがあります:

  • DATA文を一行にする(文字列合成なども行うとよい)
  • 記号文字を英数字のみにする。不足する場合はヨーロッパ系の文字から使い、日本語文字を後回しにする
  • 既存の圧縮ツールを使用して圧縮する

現在のところあまり圧縮に関して知見が集まっていないようですが、ここで先陣を切って圧縮に取り組むと、今後の圧縮関連のデファクトスタンダードとなりうるかもしれません。

コメント Edit

  • 技術を共有する意味ある? -- 2019-11-10 (日) 12:49:58
  • ときどき新しいページできてるけど誰が作ってるんだろう(数週間の疑問) -- すず 2019-11-10 (日) 12:55:42
  • ↑↑初見さんが参入しやすくなるでしょ -- 2019-11-10 (日) 13:01:11
  • SPについていろいろ書き足しました -- Na 2019-11-10 (日) 15:12:42
  • すでにAOT作成で書き換えてますが、譜面の行数はWHILE 1を使えば理論上無制限に出来ますかね? -- アオタク 2019-11-10 (日) 17:18:49
  • ...というかSPでWHILEやREPEATが一切使われてないんですよね(FORばかり)。このFORを出来るだけWHILEに置き換えるだけでもコンピュータに優しくなりそう。 -- アオタク 2019-11-10 (日) 17:24:44
  • ...あ、もちろんINCもループ内に入れますよ -- アオタク 2019-11-10 (日) 17:25:30
  • http://plsk.net/omochinsp2 貼って大丈夫なのかは分からない -- アオタク 2019-11-10 (日) 17:32:23
  • ↑譜面読み込みのところは、別にINCなくても正常に動きます(むしろINC入れた方が遅延が発生する) -- 2019-11-10 (日) 17:51:48
  • INC使うなら、Q=Q+1って感じに書いたほうが遅延が少ないし。 -- 2019-11-10 (日) 17:52:35
  • ↑2のはそもそもループ内でその変数を使うところがないので。後で使うということもないし。 -- 2019-11-10 (日) 17:54:07
  • つまりWって変数使ってるけど、FOR分を使うためであって、Wっていうのはどこでも使ってないからINCを使う必要はない。(そもそもその後に最大コンボ取得するのにW使ってるし) -- 2019-11-10 (日) 17:56:14
  • ↑7 一応REPEATは使われてるはず(ループじゃなくてAボタン押されたかの判定のとこだが) -- 2019-11-10 (日) 18:01:31
  • ↑5 あ、例のループでINCが不要なのはURLを貼った後から気付いてカッコでくくりました。 -- アオタク 2019-11-10 (日) 18:05:22
  • ↑5 あ、INCよりそのほうが速いんですね、初耳です。 -- アオタク 2019-11-10 (日) 18:06:06
  • ↑3 確かに1行間のループでありますね、忘れてました。 -- アオタク 2019-11-10 (日) 18:06:55
  • NSPの三連符って、どうやって実装されているんですか? -- 駿ノ嘴? 2019-11-10 (日) 21:19:33
  • MILLISECで音符のタイミングをとってます たぶんMILLISECを使ってるおもちつきはNewSPだけですかね←違ったようです -- Na 2019-11-10 (日) 21:39:21
  • それの実装方法を是非とも書いていただきたいです!(ちなみにこのページの作成者は私です) -- 駿ノ嘴? 2019-11-11 (月) 00:18:11
  • ↑2 一応TGもです -- ☆Tatsukin★ 2019-11-11 (月) 07:51:53
  • ↑3 KRでも使っています。 -- やりのめ 2019-11-11 (月) 08:54:56
  • MILLISECについて書きました -- Na 2019-11-11 (月) 20:50:10
  • SZでの自動難易度計算とpp計算のわたしの考え方を書いておきました -- すず 2019-11-11 (月) 21:41:11
  • あ、普通にUNTIL Q>0でも問題ありませんでした、ありがとうございます(元のSPの表示でこれも調整がいるのかと思ってしまった) -- アオタク 2019-12-26 (木) 14:50:00
  • メトロノーム鳴らす場合にズレます。 -- 2019-12-26 (木) 15:12:31
  • (メトロノームとか鳴らさない場合は們台ありません) -- 2019-12-26 (木) 15:12:49
  • 問題(どういう誤字だよ) -- 2019-12-26 (木) 15:13:06
  • ↑3 鳴らす場合はUNTILの後を変える必要が出てくるわけなんですね -- アオタク 2019-12-26 (木) 16:25:02
  • おもちSZのところの内容を7割削除しました -- すず 2020-02-22 (土) 17:55:13
  • 統合版が正式リリースされてるけどまだ機密事項なのかな -- すず 2020-02-22 (土) 21:10:58

URL B I U SIZE Black Maroon Green Olive Navy Purple Teal Gray Silver Red Lime Yellow Blue Fuchsia Aqua White

*1 対処法1:2023行目のFOR文を書き換える。というかFILL命令のほうが速い。
*2 対処法2:2612行目のFOR文の終わりをONPC-1にする。この場合初期化は不要になる
*3 {(120/TMP)*30}/(ONPL/4)/60*1000
*4 {(1000/60)*(120*4)/TMP}*ONPLIMITは誤差埋め。
*5 それが-17より大きい場合は-17。CNTの絶対値が17未満の時にMMLを再生するため。