processingにおけるシーン遷移(画面遷移)を実現する一般的なテクニック

はじめに

ここでは,processingのシーン遷移について解説します.
シーン遷移を知っていれば,様々な方面に活用できます.具体的には,スタート画面,クリア画面,操作説明の画面などを簡単に作ることができます.他にも,ステージクリア制のゲーム制作,アート作品のポートフォリオアプリの制作などにも応用が利きます.

この記事では,まず基本アイデアとして,ステージ遷移の考え方について書きます.
その後,その実装方法を簡略化して3通り述べます.
最後に,単純なゲームを作成してみて,実践的な使い方を解説します.

基本アイデア

まずは概要として,基本アイデアを示します.

まずは,全てのシーンに番号を振ります(頭の中で).例えば,スタートシーンは0,ゲーム画面は1にするかー,という感じです.そして,プログラム上で番号を管理する変数を一つ持っておいて,その番号によって実行するプログラムを変えます.例えば,番号を管理する変数が0なら,スタート画面のプログラムを実行するようにします.このような実装はif文で書けます.

さて,次からは具体的な実装面を見ていきますが,シーンや,それぞれに対応する番号を次のように決めることにします(頭の中で).
まず,シーンはスタート,ゲーム,クリアの3種類を想定します.また,それぞれに対応する番号は0:スタートシーン,1:ゲームシーン,2:クリアシーン だと思うことにします.さらに,シーン遷移するためには,シーン遷移する「きっかけ」が必要です.今回は,スタート→ゲームの遷移は「スタートボタンを押す」,ゲーム→クリアの遷移は「ゲームをクリアする」ことがきっかけになるとします.

実装例

ここからは実装例を書きます.
なお,processingのテンプレである以下の形から付け加えていくことにします.

void setup(){
  // 最初の1回だけ実行される
}

void draw(){
  // 何回も実行される
}

1. draw()に直書き

一番愚直な方法です.ある意味わかりやすいかもしれません.

int scene = 0;  // シーンを指定する番号
void setup(){

}

void draw(){
  if(scene == 0){
    // スタートシーンの処理
    if(スタートボタンを押したら) scene = 1;
  }else if(scene == 1){
    // ゲームシーンの処理
    if(クリアしたら) scene = 2;
  }else if(scene == 2){
    // クリアシーンの処理
  }
}

各シーンについて,次のシーンに移れるようにsceneの値を書き換える処理を入れています.

また,この例では,全ての処理をdraw()に直書きします.一見分かりやすそうですが,例えば,ゲームシーンの処理が200行とかになった場合,プログラム上で処理の流れが追いにくくなります.

2. 関数を呼び出す

int scene = 0;  // シーンを指定する番号
void setup(){

}

void draw(){
  if(scene == 0){
    start_scene();
  }else if(scene == 1){
    game_scene();
  }else if(scene == 2){
    clear_scene();
  }
}

void start_scene(){
  // スタートシーンの処理
  if(スタートボタンを押したら) scene = 1;
}

void game_scene(){
  // ゲームシーンの処理
  if(クリアしたら) scene = 2;
}

void clear_scene(){
  // クリアシーンの処理
}

各シーンでの処理を,関数によって記述しました.ここでは,具体的な処理は全てstart_scene(), game_scene(), clear_scene()に記述します.

この実装で嬉しいことは,各シーンの処理によらず,draw()の中身は変わらないことです.とりあえずdraw()さえ見ればシーンと番号の対応が一目で分かるので,コードの可読性がぐっと上がります.

ちなみに,シーンによらず常に実行したい処理もあるかもしれません.例えば,background()や,BGMに関する処理です.このような時は,もう一つ専用の関数を作って,if文の影響を受けないところで呼び出します.

int scene = 0;
void setup(){

}

void draw(){
  common();  // if文の影響を受けないところで呼ぶ
  if(scene == 0){
    start_scene();
  }else if(scene == 1){
    game_scene();
  }else if(scene == 2){
    clear_scene();
  }
}

void common(){
  // 共通の処理
}

void start_scene(){
  // スタートシーンの処理
  if(スタートボタンを押したら) scene = 1;
}

void game_scene(){
  // ゲームシーンの処理
  if(クリアしたら) scene = 2;
}

void clear_scene(){
  // クリアシーンの処理
}

3. シーンを文字列で指定する

1.および2.では,シーンを番号で指定していました.でもこの方法は,「0番はスタート」という情報を自分が覚えておく必要があります.これでは,より複雑なゲームでは,何番が何のシーンだったかを忘れるかもしれません.「キャラ選択画面は5で,魔法使いの能力確認画面は49で..」みたいなのは,できるだけやりたくないです.

このような時,シーンの指定に文字列を使えば良いかもしれません.例えば,"start"はスタートシーン,"select_character"はキャラ選択画面, "magic_ability"が魔法使いの能力確認画面,みたいな要領です.これだと,非常に直感的に記述できます.

String scene = "start";  // シーンを指定する文字列
void setup(){

}

void draw(){
  if(scene == "start"){
    start_scene();
  }else if(scene == "game"){
    game_scene();
  }else if(scene == "clear"){
    clear_scene();
  }
}

void start_scene(){
  // スタートシーンの処理
  if(スタートボタンを押したら) scene = "game";
}

void game_scene(){
  // ゲームシーンの処理
  if(クリアしたら) scene = "clear";
}

void clear_scene(){
  // クリアシーンの処理
}

文字列はString型で扱うことができます.数字で指定していた時よりも,少しわかりやすくなった気がしますね.

単純なゲーム制作で理解するシーン遷移

最後に,具体的なサンプルを見て,実践的な感覚を掴みましょう.ここでは,3.の方法を用いて,文字列でシーンを制御します.
ここでは単純な例として,赤い四角をクリックしたらクリアできるようなゲームを考えましょう.
もちろん,スタート画面とクリア画面も作ります.

f:id:gotutiyan:20200116225907g:plain

まずはテンプレです.

void setup(){
  size(500, 500);
}

void draw(){

}

共通部分の追加

まずは共通部分です.今回は,画面更新のたびに画面を塗りつぶすことくらいです.

void setup(){
  size(500, 500);
}

void draw(){
  common();  // 呼び出し
}

// 共通部分
void common(){
  background(255); 
}

スタートシーンの追加

スタートシーンを作ります.これに伴って,シーンを指定する文字列も作成します. この文字列には,最初"start"を代入しているので,最初はスタートシーンが表示されます.

また,何かしらのキーをクリックしたらゲームが始まるようにします.これはvoid keyPressedの中で,sceneの文字列を書き換えれば良いです.

String scene = "start";
void setup(){
  size(500, 500);
  textSize(50);
}

void draw(){
  if(scene == "start") start_scene(); //呼び出し
}

void common(){
  background(255); 
}

// スタートシーンの処理
void start_scene(){
  fill(0);
  text("Start", width/5, height/2);
  text("Press any key", width/5, height/2+60);
}

void mousePressed(){ 
  // ゲームシーンへ遷移
  if(scene == "start") scene = "game"; 
}

ゲームシーンの追加

次にゲームシーンを作成します.
ゲームの内容的には,赤い四角が描画できて,クリックしたらシーン遷移すれば良いです.

String scene = "start";
void setup(){
  size(500, 500);
  textSize(50);
}

void draw(){
  common();
  if(scene == "start") start_scene();
  else if(scene == "game") game_scene();
}

void common(){
  background(255); 
}

void start_scene(){
  fill(0);
  text("Start", width/5, height/2);
  text("Press any key", width/5, height/2+60);
}

// ゲームシーンの処理
void game_scene(){
  fill(255,0,0);
  rect(30, 50, 70, 90);
}

void mousePressed(){
  if(scene == "start")scene = "game";
  else if(scene == "game"){
     // クリックできたらクリア画面に遷移
     if(get(mouseX,mouseY) == color(255,0,0)){
        scene = "clear"; 
     }
  }
}

get(x,y)は点(x,y)の色を取得する関数で,get(mouseX, mouseY)でマウス座標の色を取得できます.color(255,0,0)は赤色を表します.

クリアシーンの追加

最後にクリア画面を追加します.ここでは単純に,Clearの文字を出すことにします.

String scene = "start";
void setup(){
  size(500, 500);
  textSize(50);
}

void draw(){
  common();
  if(scene == "start") start_scene();
  else if(scene == "game") game_scene();
  else if(scene == "clear") clear_scene();  // 呼び出し
}

void common(){
  background(255); 
}

void start_scene(){
  fill(0);
  text("Start", width/5, height/2);
  text("Please click", width/5, height/2+60);
}

void game_scene(){
  fill(255,0,0);
  rect(30, 50, 70, 90);
}

// クリアシーンの処理
void clear_scene(){
  fill(0);
  text("Clear", width/5, height/2);
}

void mousePressed(){
  if(scene == "start")scene = "game";
  else if(scene == "game"){
     if(get(mouseX,mouseY) == color(255,0,0)){
        scene = "clear"; 
     }
  }
}

このようにして,スタートシーン→ゲームシーン→クリアシーンの遷移を実装できました.

おわりに

今回は,processingにおけるシーン遷移について説明し,具体的なサンプルと共に使い方を説明しました.いろいろなシーンで使えるので,ぜひ使ってみてください.

以上です.ありがとうございました.