音のワンキーゲーム
SFCaveというワンキーゲームがある。その音バージョンを作った。
現在の音高の位置を黄色のドットで表しているが、これはイージーモード。本当はこのドットは描かずに聴音だけでやるべきである。が、そうすると自分の場合、開始直後に位置感覚を喪失して全くゲームにならないので仕方ない。
比較。大体こんな感じ。
開発言語は今回もChucKとProcessing。
ChucKのプログラム。一回起動したら裏で動かしっぱなし。
BeeThree b => Gain g => dac; .3 => g.gain; 1 => b.noteOff; OscRecv recv; 12000 => recv.port; recv.listen(); recv.event("/pitch, f") @=> OscEvent oe; false => int isPlaying; float pitch; while (oe => now) { while (oe.nextMsg() != 0) { oe.getFloat() => pitch; if (pitch < 0) { if (isPlaying) { false => isPlaying; 1 => b.noteOff; } else { true => isPlaying; 1 => b.noteOn; } } else { pitch => Std.mtof => b.freq; } } }
Processingのプログラム。操作方法:Enterでスタート、↑キーで上昇。
import oscP5.*; import netP5.*; static int FPS = 20; static float GRAVITY = -0.02; static int MAX_DELTA = 5; static int MAX_DIFF = 3; static int MIN_SPACE = 9; static int UPPER_BOUND = 95; static int LOWER_BOUND = 36; static int NUM_NOTES = 40; static int NOTE_WIDTH = 15; static int NOTE_HEIGHT = 10; static float SPEED_UP = 0.15; boolean isEasy = true; boolean isPlaying; float pitch, dp; float time, dt; int current, oldest; int uppers[], lowers[]; float center; int score; OscP5 osc; NetAddress addr; int port = 12000; PFont gfont, sfont; PImage piano; void setup() { noLoop(); isPlaying = false; uppers = new int[NUM_NOTES]; lowers = new int[NUM_NOTES]; size(NUM_NOTES * NOTE_WIDTH, (UPPER_BOUND - LOWER_BOUND + 1) * NOTE_HEIGHT); frameRate(FPS); osc = new OscP5(this, port); addr = new NetAddress("127.0.0.1", port); gfont = loadFont("EstrangeloEdessa-60.vlw"); // 事前に用意 sfont = loadFont("EstrangeloEdessa-20.vlw"); // 事前に用意 piano = loadImage("piano.png"); // 事前に用意 background(150); } float midi2y(float midi) { return height - (midi - LOWER_BOUND + 1) * NOTE_HEIGHT; } int setCaveAt(int idx, int preLower, int preUpper) { center = constrain(random(center - MAX_DIFF, center + MAX_DIFF), LOWER_BOUND + 2 * MIN_SPACE, UPPER_BOUND - 2 * MIN_SPACE); uppers[idx] = (int)constrain( random(preUpper - MAX_DIFF, preUpper + MAX_DIFF), center + MIN_SPACE, UPPER_BOUND - MIN_SPACE); lowers[idx] = (int)constrain( random(preLower - MAX_DIFF + 1, preLower + MAX_DIFF + 1), LOWER_BOUND + MIN_SPACE, center - MIN_SPACE); return (idx + 1) % NUM_NOTES; } void draw() { if (!isPlaying) return; dp = constrain(dp + GRAVITY, -MAX_DELTA, MAX_DELTA); pitch = constrain(pitch + dp, LOWER_BOUND, UPPER_BOUND); background(150); noStroke(); int idx; for (int i = 0; i < NUM_NOTES; i++) { idx = (oldest + i) % NUM_NOTES; color uc = (idx == current ? color(255, 0, 0) : color(102, 102, 0)); color lc = (idx == current ? color(255, 0, 0) : color(0, 102, 102)); fill(uc); rect((i + 1) * NOTE_WIDTH - time, midi2y(uppers[idx]), NOTE_WIDTH, NOTE_HEIGHT); fill(lc); rect((i + 1) * NOTE_WIDTH - time, midi2y(lowers[idx]), NOTE_WIDTH, NOTE_HEIGHT); } image(piano, 0, 0); for (int i = 0; i < UPPER_BOUND - LOWER_BOUND + 1; i++) { stroke(125); line(piano.width, i * NOTE_HEIGHT, width, i * NOTE_HEIGHT); } if (isEasy) { noStroke(); fill(color(255, 255, 0)); ellipse(width / 2 + NOTE_WIDTH, midi2y(pitch), 4, 4); } fill(color(255, 255, 0)); textAlign(RIGHT); textFont(sfont); text("Score: " + score, width - 30, 20); if (pitch > uppers[current] || pitch < lowers[current]) { fill(color(255, 255, 0)); textAlign(CENTER); textFont(gfont); text("Game Over", width / 2, height / 2); isPlaying = false; noLoop(); } time += dt; if (time > NOTE_WIDTH) { oldest = setCaveAt(oldest, lowers[oldest - 1 < 0 ? NUM_NOTES - 1 : oldest - 1], uppers[oldest - 1 < 0 ? NUM_NOTES - 1 : oldest - 1]); current = (current + 1) % NUM_NOTES; time = 0; if (score++ % 10 == 0) dt += SPEED_UP; } OscMessage msg = new OscMessage("/pitch"); msg.add(isPlaying ? pitch : -1.0); osc.send(msg, addr); } void keyPressed() { if (!isPlaying && key == ENTER) { isPlaying = true; pitch = 60; dp = 0.5; time = 0; dt = 1.4; for (int i = 0; i < NUM_NOTES; i++) { if (i < NUM_NOTES * 2 / 3) { uppers[i] = UPPER_BOUND - MIN_SPACE; lowers[i] = LOWER_BOUND + MIN_SPACE; center = (UPPER_BOUND + LOWER_BOUND) / 2; } else { setCaveAt(i, lowers[i - 1], uppers[i - 1]); } } current = NUM_NOTES / 2; oldest = 0; score = 0; loop(); OscMessage msg = new OscMessage("/pitch"); msg.add(-1.0); osc.send(msg, addr); } if (key == CODED) { if (keyCode == UP) { dp = constrain(dp - 1.6 * GRAVITY, -MAX_DELTA, MAX_DELTA); } } }
変数isEasyをtrueにするとイージーモードになる。