Processingで2Dアクションゲーム
この資料は、九州産業大学 理工学部のプチオープンキャンパス模擬講義用に作成した資料です。約1時間で簡単なゲームプログラミングに触れることを目的にしています。
Processingは、Java言語をベースに開発されたプログラミング言語で、静止画や映像、ゲーム等のビジュアルプログラミングを少ないコードで記述することが出来ます。
今回は、Processingで簡単な2Dアクションゲーム(っぽいもの)を作ってみましょう。コードの大部分は最初から用意しています。少しコードを追加するだけで、プログラムの挙動が変わることを体験してみてください。
作るゲームの完成例
今回作るゲームはこんな感じです。

全部、四角形だけで作る簡素な見た目のゲームですね。
プレイヤーは左右移動とジャンプするだけ、ジャンプブロックやテレポートブロックを上手く配置して、面白いステージを作っていくチュートリアルです。
Processingをインストール
それでは、まずはProcessingをインストールしましょう。
下記のリンクをクリックして、Processingのダウンロードページを開いてください。
Download Processing … for Windows (または Mac )のボタンをクリックしてインストーラーをダウンロードします。(寄付をすることが出来るページに移動しますが、無料で利用可能です)


ダウンロードしたインストーラー(processing-4.5.2-windows-x64など)を開いて、Processingをインストールしてください。
Processingを起動、スケッチを保存
インストールしたProcessingを起動すると、このような画面が表示されます。(Welcom to Processingの画面がでたら、[Get Started]をクリックしてください)

プログラムをコピー&ペーストしてゲームを動かす
下記のコードをコピーして、Processingに貼り付けましょう。右上の[Copy] ボタンをクリックすると簡単にコピーできます。
import processing.sound.*;
float deltaTime;
long lastTime;
Player player;
ArrayList<BlockBase> blocks = new ArrayList<BlockBase>();
// プレイヤーの初期位置
float startX = 50;
float startY = 100;
void setup() {
size(960, 540);
lastTime = millis();
player = new Player(startX, startY, 100);
// --- ステージを構成する部分 ---
blocks.add(new NormalBlock(0, 450, 200, 20)); // 普通のブロック
// 左上から右に100, 下に450の位置に、 幅100, 高さ20 のブロックを配置する
// ----------------------------------------------
}
void draw() {
long currentTime = millis();
deltaTime = (currentTime - lastTime) / 1000.0;
lastTime = currentTime;
background(220);
// ブロックを全て描画する
for (BlockBase b : blocks) {
b.display();
}
// プレイヤーの更新処理
player.update(deltaTime, blocks);
player.xSpeed = 0;
if (keyPressed) { // もしもキーが押されたら
if (key == 'a') { // 押されたキーが a なら
// playerのスピードを -100 にする(左に移動するようにする)
}
if (key == 'd') { // 押されたキーが d なら
// playerのスピードを 100 にする(右に移動するようにする)
}
}
if (mousePressed) { // もしもマウスが押されたら
// playerのy方向のスピードを -500 にする(上に移動するようにする)
}
// 画面外に落ちたらリスタート
if (player.y > height) {
player.reset(startX, startY);
}
}
// プレイヤー クラス
class Player {
float x, y, xSpeed, ySpeed = 0, gravity = 1200, size = 32;
boolean isCleared = false;
Player(float x, float y, float speed) {
this.x = x;
this.y = y;
this.xSpeed = speed;
}
void update(float dt, ArrayList<BlockBase> blocks) {
if (isCleared) {
fill(255, 0, 0);
textSize(64);
textAlign(CENTER, CENTER);
text("GOAL!!", width/2, height/2);
return;
}
x += xSpeed * dt;
ySpeed += gravity * dt;
y += ySpeed * dt;
// ブロックとの当たり判定
for (BlockBase b : blocks) {
if (checkCollisionRect(x, y, size, size, b.x, b.y - 2, b.w, b.h)) {
b.onTouch(this);
}
}
fill(50, 100, 255);
rect(x, y, size, size);
}
// 当たり判定メソッド
boolean checkCollisionRect(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2) {
return (x1 + w1 > x2) && (x1 < x2 + w2) &&
(y1 + h1 > y2) && (y1 < y2 + h2);
}
void reset(float rx, float ry) {
this.x = rx;
this.y = ry;
this.ySpeed = 0;
}
}
// 親クラス:BlockBase
abstract class BlockBase {
float x, y, w, h;
color col = color(100);
BlockBase(float x, float y, float w, float h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
void display() {
fill(col);
rect(x, y, w, h);
}
void onTouch(Player p) {
if (p.ySpeed > 0) {
// 落下中のみ:ブロックの「上」に着地させる
p.y = this.y - p.size;
p.ySpeed = 0;
} else if (p.ySpeed < 0) {
// 上昇中(ジャンプ中)のみ:ブロックの「下」で頭をぶつける
p.y = this.y + this.h;
p.ySpeed = 100; // 少し下に弾き返す
}
}
}
// ブロック型の子クラス群
class NormalBlock extends BlockBase {
NormalBlock(float x, float y, float w, float h) {
super(x, y, w, h);
col = color(150);
}
}
// ジャンプブロック
class JumpBlock extends BlockBase {
JumpBlock(float x, float y, float w, float h) {
super(x, y, w, h);
col = color(255, 200, 0);
}
@Override
void onTouch(Player p) {
super.onTouch(p);
p.ySpeed = -700; // ジャンプ
}
}
// サウンドブロック
class SoundBlock extends BlockBase {
SoundFile sfx;
SoundBlock(float x, float y, float w, float h, String fileName, PApplet p) {
super(x, y, w, h);
col = color(255, 50, 50, 120);
sfx = new SoundFile(p, fileName);
}
@Override
void onTouch(Player p) {
if (!sfx.isPlaying()) sfx.play();
}
}
// テレポートブロック
class TeleportBlock extends BlockBase {
float tx, ty;
TeleportBlock(float x, float y, float w, float h, float tx, float ty) {
super(x, y, w, h);
this.tx = tx;
this.ty = ty;
col = color(200, 50, 255);
}
@Override
void onTouch(Player p) {
p.x = tx;
p.y = ty;
}
}
// サイズ変更ブロック
class SizeBlock extends BlockBase {
float targetSize;
SizeBlock(float x, float y, float w, float h, float targetSize) {
super(x, y, w, h);
this.targetSize = targetSize;
col = color(50, 255, 100);
}
@Override
void onTouch(Player p) {
p.size = targetSize;
}
}
// ゴールブロック
class GoalBlock extends BlockBase {
GoalBlock(float x, float y, float w, float h) {
super(x, y, w, h);
col = color(255, 0, 0);
}
@Override
void onTouch(Player p) {
p.isCleared = true;
}
}コードを貼り付けたら実行しましょう。

左上にある、右▲ボタンをクリックすると実行できます。
青い四角形が落ちてきて、足場に乗るはずです。
確認できたら■ボタンを押してゲームを終了しましょう。

この青い四角形がプレイヤーです。しかし、今はまだ落ちて止まるだけで動くことは出来ません。
プログラムを保存する
動くことを確認したら、プログラムを保存しておきましょう。
「ファイル」→「名前をつけて保存…」とクリックして、名前をつけて保存してください。付ける名前は何でもいいですが、後で分かりやすい名前にしておきましょう。


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

プレイヤーを左右に動かそう
47行目あたり、 // playerのスピードを -100 にする(左に移動するようにする) と書かれている箇所を探してください。
if (keyPressed) { // もしもキーが押されたら
if (key == 'a') { // 押されたキーが a なら
// playerのスピードを -100 にする(左に移動するようにする)
}
if (key == 'd') { // 押されたキーが d なら
// playerのスピードを 100 にする(右に移動するようにする)
}
}このコードは、ユーザ(あなた)がキーボードのキーを押した時に何をするのかをプログラムするためのブロックです。
プログラミングでは、処理の単位ごとに「ブロック」があり、そのブロック内にプログラムを書いていくことになります。Processingでは、 { がブロックの開始で } がブロックの終了です。算数の ( ) のようなものですね。
今回の場合は、if (key == ‘a’) { } が一つのブロックになっていて、キーボードの a キーを押した時に何をするかを { } の中に書いていきます。
if (key == ‘a’) { } のブロック内に、
player.xSpeed = -100;
と書いて実行してみましょう。

a キーを押すと、青い四角形が左に動くようになりましたね。
-100 は移動するスピードです。 -150のように小さくするとより早く動き、 -50 のように大きくするとより遅く動くようになります。
自分が操作しやすいスピードになるように調整してみましょう。
同じようにして、右にも動くようにコードを追加してみましょう。

ジャンプするようにしてみよう
左右移動のコードのすぐ下に、マウスクリックで何をするかを書くブロックがあります。

このブロックの中に、
player.ySpeed = -500;
と書いて実行してみましょう。

実行するとマウスクリックでジャンプするようになっているはずです。
これで、プレイヤーがある程度自由に動けるようになりましたね。
空中でクリックすると何度もジャンプしてしまったり、挙動がおかしいところはありますが、簡単に動かすだけならこれで十分です。
ブロックを追加しよう
動けるようにはなりましたが、今はまだ落ちること以外何も出来ませんね。
まずは、右にもう一つ足場ブロックを追加してみましょう。
blocks.add(new NormalBlock(0, 450, 200, 20)); // 普通のブロック
このプログラムをコピーして、下記の場所に貼り付けましょう。

これでブロックが一つ増えます・・・が実行してもさっきと変わらないように見えます。
ブロックを置く場所を変えてみましょう。
new NormalBlock() には、 カンマで区切られて4つの数値が入っています。
それぞれ、ブロックを置くx座標, y座標, ブロックの幅, 高さ を意味しています。ブロックを置くx座標を 0 から 250に変更してみてください。
blocks.add(new NormalBlock(0, 450, 200, 20));
blocks.add(new NormalBlock(250, 450, 200, 20));

コード中の // はそれ以降をコメントにするコードです。
// が付いているコードは、プログラムとしては認識されなくなり、ただの説明文になります。

ブロックが移動して、2つになりましたね。ジャンプすると右の足場に移動できるようになっています。
同じように、ブロックのコードをコピーして増やすだけで足場を増やしていくことが出来ます。
Processingでの座標指定
Processingでの座標指定は、左上を原点 ( x : 0, y : 0 ) として、右方向にいくつ、下方向にいくつといった形で図形の場所などを指定します。

ブロックを右に移動させたいときは、x座標(横方向の座標)を増やす、左に移動させたければx座標(横方向の座標)を減らすと良いです。
同じように、下に移動させたいときは y座標(縦方向の座標)を増やす、上に移動させたければy座標(縦方向の座標)を減らします。
ゴールブロックを配置
次は、ゴールを追加しましょう。
GoalBlockは、プレイヤーが触れたらGoalと表示する機能を持ったBlockです。
blocks.add(new GoalBlock(900, 100, 40, 150));
のコードを先程のブロック配置の部分に追加してください。
このとき、 } を削除するなどしないように注意してください。
// ゴールブロック
blocks.add(new GoalBlock(900, 100, 40, 150));
// 左上から右に900, 下に100の位置に、 幅40, 高さ150 のブロックを配置する
これでゴールが表示されました。
普通ならたどり着けませんが、今は空中ジャンプが出来るようになっているので、ゴールに触れることが出来るはずです。空中ジャンプは後で出来なくしますが、コード作成中のテスト(デバッグと言います)をする際には楽ですね。

ジャンプブロックを配置
プレイヤーが乗ると、強制的にジャンプさせるブロックも用意しています。
下記のコードを追加してみましょう。置く場所や大きさは自分で調整してください。
// 乗ったらジャンプするブロック
blocks.add(new JumpBlock(200, 450, 80, 20));
// 左上から右に200, 下に450の位置に、 幅80, 高さ20 のジャンプブロックを配置するテレポートブロック
プレイヤーが触れると、指定した場所にテレポートするブロックもあります。
// テレポートブロック(指定した場所にテレポート)
blocks.add(new TeleportBlock(550, 400, 100, 20, 800, 100));
// 左上から右に550, 下に100の位置に、 幅100, 高さ20 のテレポートブロックを配置する. 触れたら 左から800, 下に100の位置にテレポート.テレポートブロックは、触れた時にどこに移動するのかを指定します。幅と高さの次にテレポート先の座標を入力しましょう。
サイズ変更ブロック
ちょっと変わったブロックで、触れるとプレイヤーのサイズが変わるブロックも用意しています。
// サイズ変更(触れるとサイズが変わる)
blocks.add(new SizeBlock(500, 280, 50, 50, 10));
// 左上から右に500, 下に280の位置に、 幅50, 高さ10 のサイズ変更ブロックを配置する. 触れたら 10 のサイズに変化する. サウンドブロック
SoundBlockは、触れると音が出るブロックです。
// 触れると音が鳴るブロック
blocks.add(new SoundBlock(200, 300, 50, 50, "se_jump.wav", this));
// 左上から右に200, 下に300の位置に、 幅50, 高さ50 のサウンドブロックを配置する.触れると se_jump.wav の音声ファイルを再生する.このブロックについては、コードを書くだけではなく、再生する音声ファイルをデータとして追加する必要があります。
ちょっと扱いが難しいので、使う場合には注意してください。
サウンドファイルを再生するためには、まず再生したい音声ファイルを探す必要があります。
ここでは、「FC音工場」さんからファイルをダウンロードして追加する例を紹介します。
まずは気に入った音を探してダウンロードします。(下矢印のボタンがダウンロード)

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

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

これでファイルを追加することが出来ました。
次に、再生する音声ファイルの名前をプログラム内で指定します。
blocks.add(new SoundBlock(200, 300, 50, 50, “se_jump.wav“, this));
se_jump.wav をダウンロードして追加したファイルの名前に付け替えましょう。記号も含めて、1文字でも違う文字になってはいけません。
間違わないように、ファイル名をコピーして貼り付けるのが良いです。
「スケッチ」→「スケッチフォルダーを開く」

data フォルダを開く

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

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

配置を工夫すれば、色んな音を出すことが出来るようになります。
空中ジャンプを出来なくしよう
空中でスペースキーを押すと何度もジャンプ出来てしまうのを直しましょう。
ジャンプのプログラムを書いた場所で、playerの縦方向のスピードが0の時だけジャンプするようにコードを修正します。
player.ySpeed = -500; のコードを、
if (player.ySpeed == 0) { } の条件分岐で囲みます。
こうすることで、ジャンプ中や落下中にマウスを押しても次のジャンプが出来ないようになります。
{ と } の位置に気をつけて修正しましょう。

ブロックを配置してステージを作り込もう
ブロックを配置して、アクションゲームのステージを完成させましょう。
サウンドブロックを他のブロックと組み合わせると、色んな音が鳴るゲームになります。
サイズ変更ブロックやテレポートブロックを組み合わせると、道順を考えないとクリアできないパズル的な要素も加えられそうですね。
自分だけのステージを作って、ゲームを完成させてみましょう!
blocks.add(new NormalBlock(300, 400, 100, 20)); // 普通の足場
// 左上から右に300, 下に400の位置に、 幅100, 高さ20 のブロックを配置する
blocks.add(new JumpBlock(200, 450, 80, 20)); // 乗ったらジャンプするブロック
// 左上から右に200, 下に450の位置に、 幅80, 高さ20 のジャンプブロックを配置する
blocks.add(new SoundBlock(200, 300, 50, 50, "se_jump_001.wav", this)); // 音が鳴るブロック(音が鳴るだけで乗れない)
// 左上から右に200, 下に300の位置に、 幅50, 高さ50 のサウンドブロックを配置する
// ゴールブロック
blocks.add(new GoalBlock(900, 100, 40, 150));
// 左上から右に900, 下に100の位置に、 幅40, 高さ150 のブロックを配置する
blocks.add(new JumpBlock(400, 450, 100, 20)); // 乗ったらジャンプするブロック
// 左上から右に400, 下に450の位置に、 幅100, 高さ20 のジャンプブロックを配置する
blocks.add(new SizeBlock(500, 280, 50, 50, 10)); // サイズ変更(触れるとサイズが変わる)
// 左上から右に500, 下に280の位置に、 幅50, 高さ10 のサイズ変更ブロックを配置する
blocks.add(new TeleportBlock(550, 400, 100, 20, 800, 100)); // テレポートブロック(指定した場所にテレポート)
// 左上から右に550, 下に100の位置に、 幅100, 高さ20 のテレポートブロックを配置する
// プレイヤーが当たると、 左上から右に800, 下に100の位置にプレイヤーが移動する

