きっかけ
バイトでやってるプログラミング教室で教材として作り、せっかくなので公開します。
縦
上からノーツが落ちてくる感じのやつです。

// ノーツが落ちてくる感じの音ゲー
// 画面設定
int daten = 900; // 打つタイミングの位置(Y座標)
final int windowX = 300; // ウィンドウサイズ
final int windowY = 1000;
int [] commentX = {80,180}; // 各列のコメントの位置(X座標)
int commentY = 950; // コメントの位置(Y座標)
// ノーツの設定
// ノーツのタイミングの設定 notuN[列][行]/60秒 のタイミングにdatenで打つようにノーツが現れる
int[][] notuN = {
{100, 200, 300, 450, 550, 1000}, // 1列目
{400, 500, 600, 700 ,800, 900} // 2列目
};
int notuMaxLength = 0; // 長い配列の番号 1列目なら0、2列目なら1
int[] notuX = {100, 200}; // それぞれの列の位置
class Notu { // ノーツのクラス
int t; // ノーツのタイミング(フレーム数)
int x; // ノーツの位置
int v = 5; // ノーツの速さ 1フレームあたりのyの変化量
boolean exist = true; // 存在するときはtrue
int size = 50; // ノーツの大きさ
int move(int nowT) { // ノーツを描画する
int notuToDaten = (t - nowT) * v; // 打つタイミングとノーツの距離
if (notuToDaten < daten + size/2 && exist) { //画面の範囲内か?
ellipse(x, daten - notuToDaten, size, size); // ノーツの円を書く
}
if (windowY + size/2 - daten < -notuToDaten && exist) { // 画面の範囲外になったとき
exist = false; // 消える
return -1; // miss
}
return 0;
}
int click(int nowT) { // ノーツと打点の距離のチェック
int notuToDaten = (t - nowT) * v; // 打つタイミングとノーツの距離
if (abs(notuToDaten) < size && exist) { // 距離がノーツの直径よりも小さい時(半径分の猶予がある)
exist = false; // 消える
return notuToDaten; // 距離を返す
}
return windowY;
}
}
Notu[][] notu = new Notu[notuN.length][notuN[notuMaxLength].length]; // ノーツの配列を作成
void setup() {
size(10,10); // ウィンドウの仮作成
windowResize(windowX, windowY); // ウィンドウのサイズ変更
PFont myfont = createFont("MS Gothic", 20); // 文字サイズの変更
textFont(myfont); // 文字サイズの変更
// 各ノーツの設定
for (int i = 0; i < notuN.length; i++) { // 列の選択
for (int j = 0; j < notuN[i].length; j++) { // 行の選択
notu[i][j] = new Notu(); // ノーツのインスタンスを作成
notu[i][j].x = notuX[i]; // ノーツの列を設定
notu[i][j].t = notuN[i][j]; // ノーツのタイミングを設定
}
}
}
int t = 0; // 現在のフレーム
int score = 0; // 得点
int[] comment = {0, 0}; // コメント識別番号&点数、3:Great,2:Good,1:Bad,-1:miss
int[] commentCount = {0, 0}; // コメントを表示する時間のカウント、0の間はコメント非表示、1フレームで1つ減る
void draw() {
background(255); // 背景を描画
line(0, daten, windowX, daten); // 打つタイミング用の線を描画
for (int i = 0; i < notuN.length; i++) { // ノーツの行
for (int j = 0; j < notuN[i].length; j++) { // ノーツの列
if (notu[i][j].move(t) == -1) { // ノーツを描画する
comment[i] = -1; // miss
commentCount[i] = 60; // 1秒(60コマ)の間表示
}
}
}
fill(0);
text(score, 10, 20); // スコアの表示
// 上の行のノーツの点数の表示
for (int i = 0; i < comment.length; i++) {
if (0 < commentCount[i]) { // 表示させる時間内か?
fill(0);
if (comment[i] == -1){ // Missのとき
text("Miss", commentX[i], commentY); // Missを表示
} else if (comment[i] == 3){
text("Great", commentX[i], commentY);
} else if (comment[i] == 2){
text("Good", commentX[i], commentY);
} else if (comment[i] == 1){
text("Bad", commentX[i], commentY);
}
commentCount[i] -= 1; // 表示時間を一つ減らす
}
}
t++;
}
// スコアの計算
void calscore(int retu) {
int point;
for (int i = 0; i < notuN[retu].length; i++) {
point = notu[retu][i].click(t); // pointは打点とノーツの距離(=小さいほど近い)
if (point < windowY) { // 有効なpointか?
if (point < notu[retu][i].size / 4) {
comment[retu] = 3; // Great
} else if (point < notu[0][i].size / 2) {
comment[retu] = 2; // Good
} else {
comment[retu] = 1; // Bad
}
score += comment[retu]; // スコアに点数を足す
commentCount[retu] = 60; // 1秒(60コマ)の間表示
break;
}
}
}
// マウスがクリックされたとき
void mousePressed() {
if (mouseButton == LEFT) { // 左
calscore(0); // 1列目のスコアを計算
}
if (mouseButton == RIGHT) { // 右
calscore(1); // 2列目のスコアを計算
}
}
座標の関係はこんな感じです。

横
太鼓の達人てきな感じで右から左にノーツが流れてくるやつです。

// 太鼓の達人的な
// 画面設定
int daten = 100; // 打つタイミング(Y座標)
final int windowX = 1000; // ウィンドウサイズ
final int windowY = 300;
int commentX = 10; // コメントの位置(X座標)
int [] commentY = {80,180}; // 各行のコメントの位置(Y座標)
// ノーツの設定
// ノーツのタイミングの設定 notuN[行][列]/60秒 のタイミングにdatenで打つようにノーツが現れる
int[][] notuN = {
{100, 200, 300, 450, 550, 1000}, // 1行目
{400, 500, 600, 700 ,800, 900} // 2行目
};
int notuMaxLength = 0; // 長い配列の番号 1行目なら0、2行目なら1
int[] notuY = {100, 200}; // それぞれの行の位置
class Notu { // ノーツのクラス
int t; // ノーツのタイミング(フレーム数)
int y; // ノーツの位置
int v = 5; // ノーツの速さ 1フレームあたりのxの変化量
boolean exist = true; // 存在するときはtrue
int size = 50; // ノーツの大きさ
int move(int nowT) { // ノーツを描画する
int notuToDaten = (t - nowT) * v; // 打つタイミングとノーツの距離
if (notuToDaten < windowX - daten + size/2 && exist) { //画面の範囲内か?
ellipse(notuToDaten + daten, y, size, size); // ノーツの円を書く
}
if (daten + size/2 < -notuToDaten && exist) { // 画面の範囲外になったとき
exist = false; // 消える
return -1; // miss
}
return 0;
}
int click(int nowT) { // ノーツと打点の距離のチェック
int notuToDaten = (t - nowT) * v; // 打つタイミングとノーツの距離
if (abs(notuToDaten) < size && exist) { // 距離がノーツの直径よりも小さい時(半径分の猶予がある)
exist = false; // 消える
return notuToDaten; // 距離を返す
}
return windowX;
}
}
Notu[][] notu = new Notu[notuN.length][notuN[notuMaxLength].length]; // ノーツの配列を作成
void setup() {
size(10,10); // ウィンドウの仮作成
windowResize(windowX, windowY); // ウィンドウのサイズ変更
PFont myfont = createFont("MS Gothic", 20); // 文字サイズの変更
textFont(myfont); // 文字サイズの変更
// 各ノーツの設定
for (int i = 0; i < notuN.length; i++) { // 行の選択
for (int j = 0; j < notuN[i].length; j++) { // 列の選択
notu[i][j] = new Notu(); // ノーツのインスタンスを作成
notu[i][j].y = notuY[i]; // ノーツの行を設定
notu[i][j].t = notuN[i][j]; // ノーツのタイミングを設定
}
}
}
int t = 0; // 現在のフレーム
int score = 0; // 得点
int[] comment = {0, 0}; // コメント識別番号&点数、3:Great,2:Good,1:Bad,-1:miss
int[] commentCount = {0, 0}; // コメントを表示する時間のカウント、0の間はコメント非表示、1フレームで1つ減る
void draw() {
background(255); // 背景を描画
line(daten, 0, daten, windowY); // 打つタイミング用の線を描画
for (int i = 0; i < notuN.length; i++) { // ノーツの行
for (int j = 0; j < notuN[i].length; j++) { // ノーツの列
if (notu[i][j].move(t) == -1) { // ノーツを描画する
comment[i] = -1; // miss
commentCount[i] = 60; // 1秒(60コマ)の間表示
}
}
}
fill(0);
text(score, 10, 20); // スコアの表示
// 上の行のノーツの点数の表示
for (int i = 0; i < comment.length; i++) {
if (0 < commentCount[i]) { // 表示させる時間内か?
fill(0);
if (comment[i] == -1){ // Missのとき
text("Miss", commentX, commentY[i]); // Missを表示
} else if (comment[i] == 3){
text("Great", commentX, commentY[i]);
} else if (comment[i] == 2){
text("Good", commentX, commentY[i]);
} else if (comment[i] == 1){
text("Bad", commentX, commentY[i]);
}
commentCount[i] -= 1; // 表示時間を一つ減らす
}
}
t++;
}
// スコアの計算
void calscore(int gyou) {
int point;
for (int i = 0; i < notuN[gyou].length; i++) {
point = notu[gyou][i].click(t); // pointは打点とノーツの距離(=小さいほど近い)
if (point < windowX) { // 有効なpointか?
if (point < notu[gyou][i].size / 4) {
comment[gyou] = 3; // Great
} else if (point < notu[0][i].size / 2) {
comment[gyou] = 2; // Good
} else {
comment[gyou] = 1; // Bad
}
score += comment[gyou]; // スコアに点数を足す
commentCount[gyou] = 60; // 1秒(60コマ)の間表示
break;
}
}
}
// マウスがクリックされたとき
void mousePressed() {
if (mouseButton == LEFT) { // 左
calscore(0); // 1行目のスコアを計算
}
if (mouseButton == RIGHT) { // 右
calscore(1); // 2行目のスコアを計算
}
}
座標の関係はこんな感じです。

改造とか
音楽の挿入
ProcessingのSoundライブラリを使い、setup()の最後にplay()を呼ぶのが良いと思います。
譜面の作成
notuN[][]の2次元配列が譜面を意味しています。中身はノーツが打たれるタイミングのフレーム数です。そのため、/60したものが、秒となります。
行・列を増やす
ノーツの列・行数を増やすのは割と簡単にできるようにできています。はじめの方の定義のところの各配列の要素数を増やして、入力方法の部分を書き加えれば増やせると思います。
入力方法の変更
ディフォルトでは、マウスの左右クリックになっています。キーボードとかにすると良いと思います。
音ズレ
処理が遅れると音ズレが起きます。30fpsに減らしたりすることで回避できると思います。