RismGame for Processing

Processingでリズムゲーを作ってみよう

この資料は、九州産業大学 理工学部のプチオープンキャンパス模擬講義用に作成した資料です。約1時間で簡単なゲームプログラミングに触れることを目的にしています。

今回作るゲーム

今回作るゲームはこんな感じです。

線と文字、四角形だけで作る簡素な見た目のゲームですね。下にある線(判定ライン)と四角形(ノーツ)が重なっている時にキーを押していくゲームです。

Processingをインストール

それでは、まずはProcessingをインストールしましょう。
下記のリンクをクリックして、Processingのダウンロードページを開いてください。

Download Processing
Processing is open source and is available for macOS, Windows, and Linux. Projects created with Processing are also cros...

Download Processing … for Windows (または Mac )のボタンをクリックしてインストーラーをダウンロードします。(寄付をすることが出来るページに移動しますが、無料で利用可能です)

ダウンロードしたインストーラー(processing-4.5.2-windows-x64など)を開いて、Processingをインストールしてください。

Processingを起動

インストールしたProcessingを起動すると、このような画面が表示されます。(Welcom to Processingの画面がでたら、[Get Started]をクリックしてください)

ライブラリインストール

今回、音を鳴らすプログラムを作るので、そのためのライブラリを追加します。ライブラリとは、標準の機能だけでは出来ないことをするために配布されている便利な部品のことです。

「スケッチ」→「ライブラリをインポート…」→「Manage Libralies…」とクリック。

検索窓に、「Sound」と入力します。
下の検索結果に、 Sound | Provides a simple way to work with audio. がでてくるので、それを探してクリック。

「Install」ボタンをクリックしてインストールします。

Installボタンの色が反転したら、インストール完了です。
「ライブラリをインポート」の画面は閉じて構いません。

プログラムをコピー&ペーストしてゲームを動かす

下記のコードをコピーして、Processingに貼り付けましょう。右上の[Copy] ボタンをクリックすると簡単にコピーできます。

import processing.sound.*;

SoundFile bgm, hitSound;

float lineY = 500;
float noteSpeed = 0.5;
int score = 0;

// 譜面データ 判定ラインに到着する時間を カンマ区切り ミリ秒で指定する
int[] targetTimes = { 1000, 2000, 3000, 4000, 5000, 
  6000, 7000, 8000, 9000, 10000 };


// 成功判定
boolean[] isHit;

// タイミング同期用の変数
int startTime = 0;
boolean gameStarted = false;

int keyPressTime = 0;

// 判定文字の表示用変数
String judgeText = ""; // 表示する文字(Perfect, Goodなど)
int judgeTimer = 0;    // 文字を表示し続ける残りフレーム数

float endTime = 10000; //強制終了する時間

void setup() {
  size(400, 600);
  rectMode(CENTER);

  // 音声ファイル読み込み
  bgm = new SoundFile(this, "bgm.mp3");
  hitSound = new SoundFile(this, "se.mp3");

  // 成功判定を初期化
  isHit = new boolean[targetTimes.length];
  for (int i = 0; i < isHit.length; i++) {
    isHit[i] = false;
  }
}

void draw() {
  // 背景を描画
  background(30);

  // 開始前の待機画面
  if (!gameStarted) {
    fill(255);
    textAlign(CENTER);
    text("Press SPACE to Start", width/2, height/2);
    return; // 開始前ならここで描画を止める
  }

  // ゲーム開始からの時間を計算
  int gameTime = millis() - startTime;
  
  // ゲーム終了画面の描画
  if (gameTime > endTime || !bgm.isPlaying()) {
    text("FINISH!", width/2, height/2 - 50);
    textSize(30);
    text("Final Score: " + score, width/2, height/2 + 20);
    textSize(15);
    return;
  }

  // 判定ラインを描画する
  stroke(255);
  // 1. 判定ラインを描画

  
  // ゲーム時間によってノーツのスピードを変更


  for (int i = 0; i < targetTimes.length; i++) {
    float y = lineY - (targetTimes[i] - gameTime) * noteSpeed;

    if (y > -50 && y < height) {
      // ノーツがヒットしていたら
      if (isHit[i]) {
        fill(255, 255, 0);
      } else {
        fill(100, 150, 255);
      }
      rect(width/2, y, 50, 20);
    }
  }

  fill(255);
  textAlign(LEFT);
  text("Score: " + score, 20, 40);

  // 判定文字の描画
  if (judgeTimer > 0) {
    fill(255, 255, 0); // 黄色で表示
    textSize(40);
    text(judgeText, width/2, height/2);
    judgeTimer--; // 1フレームごとにタイマーを減らす
  }
}

void keyPressed() {

  // スペースキーでゲーム開始
  if (!gameStarted && key == ' ') {
    startTime = millis(); // その瞬間の時間を「0」とする
    bgm.play();           // 同時に音楽スタート
    gameStarted = true;
    return;
  }

  if (gameStarted) {
    int gameTime = millis() - startTime;
    println(gameTime);
    for (int i = 0; i < targetTimes.length; i++) {

      if (!isHit[i]) {
        // ターゲットとの時間差
        int diff = abs(targetTimes[i] - gameTime);

        // 成功判定
        if (diff < 50) { // 時間差が 50ミリ秒未満ならパーフェクト
          isHit[i] = true;
          score += 10; // スコアを増やす
          judgeText = "PERFECT!";
          judgeTimer = 30;
          hitSound.play();
          break;
        } else if (diff < 80) { // 時間差が 80ミリ秒未満ならグッド
          isHit[i] = true;
          score += 10;  // スコアを増やす
          judgeText = "GOOD";
          judgeTimer = 30;
          hitSound.play();
          break;
        } else if (diff < 100) { // good以上, 100ミリ秒未満なら ヒット
          isHit[i] = true;
          score += 10;  // スコアを増やす
          judgeText = "HIT";
          judgeTimer = 30;
          hitSound.play();
          break;
        }
      }
    } // 繰り返しの終わり
  }
}

コードを貼り付けたら実行する前に音声ファイルをダウンロードして指定する必要があります(今実行してもエラーになります)。

プログラムを保存する

プログラムを貼り付けたら、一度保存しておきましょう。(保存はこの後の工程のために必須!)
「ファイル」→「名前をつけて保存…」とクリックして、名前をつけて保存してください。付ける名前は何でもいいですが、後で分かりやすい名前にしておきましょう。

一度保存したら、次に同じプログラムを開くときに「最近開いたファイル」から簡単に開き直すことが出来ます。

音を鳴らす準備

サウンドファイルを再生するためには、まず再生したい音声ファイルを探す必要があります。

BGMと効果音(ノーツがヒットした時に鳴らす音)をそれぞれ探してダウンロードしましょう。

ダウンロードした音声ファイルをProcessingで追加します。
「スケッチ」→「ファイルを追加…」

ダウンロードしたファイルを探して「開く」をクリック。

これでファイルを追加することが出来ました。

次に、再生する音声ファイルの名前をプログラム内で指定します。
bgm = new SoundFile(this, “bgm.mp3“);
bgm.mp3 をダウンロードして追加したファイルの名前に付け替えましょう。記号も含めて、1文字でも違う文字になってはいけません。

間違わないように、ファイル名をコピーして貼り付けるのが良いです。

「スケッチ」→「スケッチフォルダーを開く」

data フォルダを開く

音声ファイルを見つけたら、右クリックのメニューなどから名前を変更を選び、ファイル名をコピー。

ファイル名の箇所に貼り付けます。” ” を消さないように注意してください。


同じように、効果音のファイルも変更しましょう。
hitSound = new SoundFile(this, “se.mp3“);

実行

左上にある、右▲ボタンをクリックすると実行できます。

スペースキーを押すとゲーム開始です。
スペースキーが効かないときは、一度ゲームの画面をマウスでクリックしてみてください。
青い四角形が落ちてきてそのまま流れていきます。
確認できたら■ボタンを押してゲームを終了しましょう。

この青い四角形がノーツです。ただ、判定ラインがないので、どこでキーを押せばいいのか分かりませんね。

ファイル名を間違えたり、データの追加を忘れたりするとエラーがでます。
エラーが出た場合は、■ボタンで停止してからファイル名などを見直しましょう。

判定ラインを描画しよう

今のままだと、どのタイミングでキーを押せば成功になるのか分かりません。

判定ラインになる線を描画しましょう。判定ラインを描く位置は、 変数 lineY で設定しています。この位置に線を引けば、ノーツが流れてくるタイミングでキーを押せばいいことが分かるようになります。

プログラムの中から、 // 1. 判定ラインを描画 と書かれている箇所を探してください。

そこに、下記のプログラムを入力します。
line(0, lineY, width, lineY);

実行すると、線が一本引かれています。ここがノーツに合わせてキーを押す場所です。

実行してタイミングに合わせてキーを押してみましょう。
(キーボードのどのキーでも反応します)

ノーツの増やし方とタイミング

流れてくるノーツは、9行目あたりにある targetTimes で指定しています。
カンマ区切り、ミリ秒単位で判定ラインに到着する時間を指定していて、初期状態だと1000ミリ秒(1秒)ごとに10個流れてくるようになっています。

この時間を変えれば、ノーツが流れてくるタイミングを変えることが出来ます。

更に、数を増やしたいときは、カンマの後に数値を入力しましょう。
この時に、 } など他の記号を消してしまわないように気をつけてください。

キーを押したタイミングが、コンソールに表示されています。
これを利用すると、自分のタイミングでキーを押した時間が分かるようになるので、ノーツの設定が楽になります。

ゲームの終了時間

ゲームの終了は、音楽が再生終了するか、強制終了する時間まで経過した時になっています。

強制終了時間は、 endTime と言う名前の変数に入っています。(変数は値を入れておける箱のようなもの)

長い音楽を使っている場合などは、この数値を変えていいタイミングで終わるようにしてみましょう。

判定条件を見直す

適当にキーを押してもPerfectになると思いませんか?

今は判定条件をすごく甘くしていて、相当に遅れても、あるいは早くてもPerfectになってしまいます。この時間を自分好みに設定してみましょう。

// 成功判定 と書かれている箇所を探してください。

int diff = abs(targetTimes[i] – gameTime); 
は、各ノーツのタイミングからゲーム開始時の時間を引いた値の絶対値を計算しているコードです。
つまり diff が小さいほどジャストタイミングに近いということになります。

perfect, good, hit の判定をしているのはその下のプログラムです。

if (diff < 50)  がパーフェクトの条件です。
もしも、diff が 50ミリ秒未満ならパーフェクトになっています。

同様に、if (diff < 80) がgood、if (diff < 100) がhit の条件です。

この3つの数値を調整して、自分好みの難易度に変更しましょう。

判定に応じて点数を変える

表示は変わりましたが、点数は全部10点のままです。これだと頑張ってタイミングを合わせる意味がないですね。

perfectやgoodならもっとたくさん点数が入るようにしてみましょう。

perfect, good, hit での処理は、それぞれ枠で囲ったブロックで書かれています。
Processingでは、 { から } までを一つのプログラムのまとまりとして見ていて、これをブロックのように呼びます。


perfectの処理を書いているブロックは、この { から } の中になります。

if (diff < 50) { // 時間差が 50ミリ秒未満ならパーフェクト
  isHit[i] = true;
  score += 10; // スコアを増やす
  judgeText = "PERFECT!";
  judgeTimer = 30;
  hitSound.play();
  break;
}

それぞれの、 score += 10; の値を変えて、難しい条件のときにはより高い点数になるようにしてみましょう。

効果音を変えてみよう

パーフェクト、グッド、ヒットの時の効果音をそれぞれ別にしてみましょう。

まず、鳴らしたい効果音を探してダウンロード、ダウンロードした音声ファイルを、「スケッチ」→「ファイルを追加…」で追加しておいてください。

まず、追加した音声ファイルを入れておくための箱(変数)を作ります。

SoundFile bgm, hitSound; のコードが最初から用意されている音声ファイル用の変数で、 bgm は背景音楽、hitSoundがキーを押した時に鳴る効果音です。

これに、perfectとgoodの時の効果音用の変数を追加しましょう。

SoundFile bgm, hitSound, perfectSound, goodSound;
赤字のコードを追加してください(カンマを忘れない、 ; を消さないように注意)。

続いて、追加した音声ファイルを変数に読み込みます。
hitsoundのファイル名を書き換えた箇所の下の行にコードを追加します。

perfectSound = new SoundFile(this, “perfect.mp3”);
goodSound = new SoundFile(this, “good.mp3”);

追加するのはこの2行ですが、音声ファイルの名前は自分がダウンロードしてきたファイル名に合わせましょう。

最後に、成功判定箇所で音を鳴らしているコードを修正したら、それぞれ違う音が鳴るようになります。

hitSoundを、perfectSound、GoodSoundにそれぞれ書き換えましょう。

ゲームの難易度アップ

もう少しゲームを改良してみましょう。

ノーツが流れてくるスピードは、noteSpeed変数で決まっています。
初期状態だと0.5ですね。

この値を大きくすると、ノーツのスピードが早くなります。

// ゲーム時間によってノーツのスピードを変更 の箇所を探して、下記のプログラムを入力します。

if () は、条件分岐の構文で、このプログラムは、
もしも gameTimeが3000より大きければ noteSpeedを1.0にする、プログラムになります。

変更する時間や速度を変えて、ゲームの難易度を変えてみましょう。

リズムゲームを完成させよう

ノーツの配置、成功判定条件、難易度調整を行って、リズムゲームを完成させましょう。

タイトルとURLをコピーしました