Processingとは
Javaをベースにした言語で、グラフィックを使ったプログラミングが簡単に作成できます。
私は、Javaのグラフィックを使うのが苦手だったので、Processingに感動を覚えました。
ここでは、私が作成した簡単なタイピングゲームの作り方を説明していきます。
できるだけ細かく説明していくので、プログラミングが苦手な人でも作れると思います。
(if文やfor文など最低限のプログラミングの知識はあるものとして書きました。)
作るものはこれです!
↓ ↓ ↓
Processingでタイピングゲームを作りました! pic.twitter.com/fF56CFCQdF
— まゆ (@Mayu_snba19) May 8, 2020
事前に読んだ本
田中賢一郎著『ドリル形式で楽しく学ぶ Processing-Java』を半分くらい読みました!
サクサクゲームが作れるのでかなりおすすめです。(2020/5/8現在 kindle unlimitedで読めます。)
Processingのインストール方法
私はこちらの記事を参考にさせて頂きました。
Processingの導入方法 - Qiita
データ
私の下手な絵で申し訳ないのですが、こちらの4枚を保存してください。
こんなダサいイラスト使いたくないよ!!と言う人は適当に画像を探してくるか、自分で描いてください。
Processingの起動
開いたら、ファイル→名前を付けて保存を押して保存してしまいましょう。
名前はなんでも良いです。私はtypingと名前を付けました。
typingフォルダが作られ、その中にtyping.pdeが作られます。(このtyping.pdeを編集していくことになります。)
この隣に新たにdataフォルダを作って、その中に先ほどの4枚のイラストを保存しましょう!
基本的な命令
まず以下のように入力してください。(//以下の一文はコメントなので書かなくても大丈夫です。)
//最初に呼ばれる関数
void setup(){
size(900,600); //900×600pxのウィンドウを作成する
}
//1秒間に60回呼び出される関数
void draw(){
}
//キーボードが押されたら呼ばれる関数
void keyPressed() {
}
- size(横の長さ,縦の長さ)
これを実行してみると(▶︎っぽいボタンを押してください!)ウィンドウが出てくると思います。setup関数は実行した時に最初に一度だけ呼び出される関数です。その中にウィンドウを生成するsizeを書きました。
その下に書いたdraw関数は、1秒間に60回呼び出されるものです。この関数の中に表示させる位置を変えながらオブジェクトを表示していくことでアニメーションになります!
一番下のkeyPressed関数はキーボードが押されると反応して呼び出されます。
イメージを読みこもう!
次にこのようにしてください。
//ここで変数名を宣言することによって、どの関数でも使えるようになります。
PImage back; //<--new 背景
PImage cat; //<--new 下の方でちょこちょこ動いてる猫ちゃん
PImage fish; //<--new ミスタイプしたときに表示される予定の魚
PImage cat2; //<--new 最後の結果の画面で出てくる猫ちゃん
//最初に呼ばれる関数
void setup(){
size(900,600);
//イメージを読み込んで、変数にいれていきます。
back=loadImage("back.PNG"); //<--new
cat=loadImage("cat.PNG"); //<--new
fish=loadImage("sakana.PNG"); //<--new
cat2=loadImage("endneko.PNG"); //<--new
}
//1秒間に60回呼び出される関数
void draw(){
image(back,0,0); //<--new 背景を表示する
image(cat,frameCount*5-100,400); //<--new 移動する猫ちゃん
if(frameCount>=180) frameCount=0; //<--new 猫が右に到達したら左から再スタートする
}
//キーボードが押されたら呼ばれる関数
void keyPressed() {
}
画像はPImage型で宣言します。
setup関数の中で、loadImage関数を用いて画像ファイルを読みこんで代入しています。
実際に画像を表示しているのは、draw関数の中のimage関数となります。
image(表示する画像,x座標,y座標)
で画像を表示することができます。
移動する猫の画像以外は900×600pxで作成したので、表示位置は0,0でOKです!
移動する猫はimage(cat,frameCount*5-100,400)と書きました。
frameCountというのは、drawが呼び出されるたびに数字が1ずつ増えていくものです。
これが180以上になったら0にする、とすることで、猫が右に到達したらリスタートされます。
文字列を表示する
ここでは、タイピングする文字を表示します。
かなり増えましたが、次のように記述してください。
PImage back;
PImage cat;
PImage fish;
PImage cat2;
//<--new タイピングに使う文字
String letters[]={"Java","PHP","Javascript","Python","C++",
"C#","COBOL","Swift","Brainfuck","Go","Haskell","Julia",
"Kotlin","Mathematica","Perl","Processing","R","Ruby","Rust"};
String letter; //<--new 現在の文字列
int now=0; //<--new 現在の文字の位置
//最初に呼ばれる関数
void setup(){
size(900,600);
back=loadImage("back.PNG");
cat=loadImage("cat.PNG");
fish=loadImage("sakana.PNG");
cat2=loadImage("endneko.PNG");
letter=letters[(int)random(letters.length)];
textSize(50);
textAlign(CENTER); //<--new テキストを中央揃えにする
}
//1秒間に60回呼び出される関数
void draw(){
image(back,0,0);
image(cat,frameCount*5-100,400);
text(letter,450,200); //<--new 文字列を表示する
//<--new これから打つ一文字を表示する
text("now : "+letter.charAt(now),450,260);
if(frameCount>=180){ //3秒毎に文字列を変える
letter=letters[(int)random(letters.length)]; //<--new lettersの中のランダムな一文字を取得する
now=0; //<--new 文字位置を0に戻す
frameCount=0;
}
}
//キーボードが押されたら呼ばれる関数
void keyPressed() {
if(key==letter.charAt(now)){ //<--new 正しいタイピングだったら
now++; //<--new 文字位置を増やす(次に進める)
if(now==letter.length()){ //<--new 現在の文字列を打ち終わったら
letter=letters[(int)random(letters.length)]; //<--new lettersの中のランダムな一文字を取得する
now=0; //<-- new 文字位置を0に戻す
frameCount=0; //<-- new 猫が左側に戻るようにする
}
}
}
letter=letters[(int)random(letters.length)];
で、letters配列に格納したランダムな1文字を取り出してletterに入れています。randomはfloat型なので、int型に変換してあげましょう。
random(数字)
・・・ 0〜数字-1をランダムに1つ取得する
テキストを表示するには、text関数を使います。
text(文字列,x座標,y座標)
nowという変数で現在の文字位置を管理します。
KeyPressed関数の中で正しいタイピングだったらnowを1増やすようにしましょう。
nowが文字列の長さと等しくなったら、random関数で文字を変えます!
KeyPressedの中でもframeCountを0にしているのは、猫ちゃんの位置で現在の文字列の残り時間がわかるようにしたからです。(猫ちゃんが右に到達すると次の文字列になる)
ゲームっぽくする
ここまでのコードでタイピングはできるようになりました。
ここからスコアや制限時間、ミスタイプ数などを表示させたいと思います!
PImage back;
PImage cat;
PImage fish;
PImage cat2;
String letters[]={"Java","PHP","Javascript","Python","C++",
"C#","COBOL","Swift","Brainfuck","Go","Haskell","Julia",
"Kotlin","Mathematica","Perl","Processing","R","Ruby","Rust"};
String letter;
int now=0;
int time=1800; //<--new 時間
int missCount=0; //<--new ミスタイプ数
int cnt=0; //<--new 連続タイプ(10ごとに0になる)
boolean error=false; //<--new 間違えたかどうか
int errorCount=0; //<--new 間違い画像を表示する回数
int score=0; //<--new スコア
boolean continuous=false; //<--new 連続タイプができたかどうか
int continuousCount=0; //<--new タイム延長のテキストを表示する回数
//最初に呼ばれる関数
void setup(){
size(900,600);
back=loadImage("back.PNG");
cat=loadImage("cat.PNG");
fish=loadImage("sakana.PNG");
cat2=loadImage("endneko.PNG");
letter=letters[(int)random(letters.length)];
textSize(50);
textAlign(CENTER);
//<--new 日本語のフォントにする
PFont font = createFont("Osaka",50);
textFont(font);
}
//1秒間に60回呼び出される関数
void draw(){
image(back,0,0);
image(cat,frameCount*5-100,400);
if(error){//<--new ミスタイプをしたら呼び出される。
image(fish,0,0); //<--new ミスタイプ時に呼び出される画像
errorCount++; //<--new この部分が呼び出される回数を数える
if(errorCount>=30) { //<--new 0.5秒間画像を表示する
error=false; //<--new ミスはなかったことに...
errorCount=0; //<--new 回数は0に戻しておきましょう
}
}
fill(255,255,255);
text(letter,450,200);
text("now : "+letter.charAt(now),450,260);
if(time<=0) {//<--new タイムが0になったら
image(back,0,0); //<--new 背景を読み込み直す
fill(255,255,255); //<--new テキストの色を白にする
text("SCORE:"+score+" ミスタイプ数 :"+missCount,450,200); //<--new結果のテキストを表示する
image(cat2,0,0); //<--new 大きい方の猫を表示する
noLoop(); //<--new draw関数の動きが止まる
}
// 残り時間を四角形で表示する
fill(141,217,163); //<--new 色を設定
noStroke(); //<--new 縁の色はなしにする
rect(0,0,time/2,20); //<--new 四角形を書く命令
if(continuous){
fill(0,212,166); //<--new テキスト色の変更
text("PLUS",800,80); //<--new テキストを表示
continuousCount++; //<--new 何回表示されたかを記録
if(continuousCount>=50){ //<--new PLUSを表示するのは50回まで!
continuousCount=0; //<--new 回数を0に戻す
continuous=false; //<--new 呼び出されなくする
}
}
if(frameCount>=180){
letter=letters[(int)random(letters.length)];
now=0;
frameCount=0;
}
time--; //<--new 残り時間を減らす
}
//キーボードが押されたら呼ばれる関数
void keyPressed() {
if(key==letter.charAt(now)){
cnt++; //<--new 連続タイプ数を増やす
score++; //<--new スコアを増やす
now++;
if(now==letter.length()){
letter=letters[(int)random(letters.length)];
now=0;
frameCount=0;
}
if(cnt>=10){ //<--new 連続タイプが10以上になると
cnt=0;
time+=60; //<--new タイムが1秒増える
continuous=true; //<--new 連続タイプできたことを伝える
}
}else if(keyCode==SHIFT){//<--new シフトキーはミスではない
cnt++; //<--new 正しいタイピングなので数える
}else{ //<--new ミス
missCount++; //<--new ミスタイプを数える
cnt=0; //<--new ミスすると連続タイプ数が0に戻る
error=true; //<--new 間違えたことを伝える
}
}
間違えたときには、最初に読み込んだ魚の画像を使いたいので、error(boolean型)をtrueにして、間違ったことをdraw関数のif(error)の部分に伝えます。
draw関数の一回呼び出しだけでは、魚の画像が一瞬で消えてしまうので、しばらく残るようにしましょう。ここでは、30回呼び出されるまで消えないようにしました!(30回までは呼び出される。その後errorをfalseにして間違いがなかったことになる。)
連続タイプも、ミスタイプと同じようにしばらく表示されるようにします。
テキストや、文字の色を変えるには、fill関数を使います。fill関数の引数はRGBです。
fill関数は一度設定すると、もう一度設定するまで同じ色が使われます。
fill(red,green,blue)
また、日本語の文字列が表示できるようにしたいので、setup関数でPFont font = createFont("Osaka",50);と記述しました。
サウンドを設定しよう
残念ながらサウンドについては、このゲームを作り始めてから知ったのでまだあまり知識がありません...
という訳でProcessing で音を扱う - 小学校教員のためのプログラミング入門
を参照してください。
ここにも書いてあるのですが、minimを使えるようにするためにはスケッチ→ライブラリをインポート→ライブラリを追加からminimをインストールする必要があります!
音源は、
The circus - フリーBGM こちらをBGMに、
猫の鳴き声(3種) - フリーBGM こちらを間違えた時の効果音に、
生活(1)- 効果音ラボ のレジスターで精算を連続タイプの効果音に使用させて頂きました。
これらの音源をダウンロードして、dataフォルダにいれると読み込めるようになります!
完成!!
import ddf.minim.*; //<--new
Minim minim; //<--new
AudioPlayer nekosound; //<--new
AudioPlayer backsong; //<--new
AudioPlayer money; //<--new
PImage back;
PImage cat;
PImage fish;
PImage cat2;
String letters[]={"Java","PHP","Javascript","Python","C++",
"C#","COBOL","Swift","Brainfuck","Go","Haskell","Julia",
"Kotlin","Mathematica","Perl","Processing","R","Ruby","Rust"};
String letter;
int now=0;
int time=1800;
int missCount=0;
int cnt=0;
boolean error=false;
int errorCount=0;
int score=0;
boolean continuous=false;
int continuousCount=0;
//最初に呼ばれる関数
void setup(){
size(900,600);
back=loadImage("back.PNG");
cat=loadImage("cat.PNG");
fish=loadImage("sakana.PNG");
cat2=loadImage("endneko.PNG");
//サウンドの読み込み
minim = new Minim( this ); //<--new
backsong = minim.loadFile( "back.mp3" ); //<--new
nekosound=minim.loadFile("nekosound.mp3"); //<--new
money=minim.loadFile("money.mp3"); //<--new
letter=letters[(int)random(letters.length)];
textSize(50);
PFont font = createFont("Osaka",50);
textFont(font);
textAlign(CENTER);
//BGMを流す
backsong.play(); //<--new
}
//1秒間に60回呼び出される関数
void draw(){
image(back,0,0);
image(cat,frameCount*5-100,400);
//ミスタイプをしたら呼び出される。
if(error){
image(fish,0,0);
errorCount++;
if(errorCount>=30) {
error=false;
errorCount=0;
}
}
fill(255,255,255);
text(letter,450,200);
text("now : "+letter.charAt(now),450,260);
if(time<=0) {
image(back,0,0);
fill(255,255,255);
text("SCORE:"+score+" ミスタイプ数 :"+missCount,450,200);
image(cat2,0,0);
noLoop();
backsong.close(); //<--new
}
//残り時間を四角で表示する
fill(141,217,163);
noStroke();
rect(0,0,time/2,20);
if(continuous){
fill(0,212,166);
text("PLUS",800,80);
continuousCount++;
if(continuousCount>=50){
continuousCount=0;
continuous=false;
}
}
//3秒経ったら文字列を変える
if(frameCount>=180){
letter=letters[(int)random(letters.length)];
now=0;
frameCount=0;
}
time--;
}
//キーボードが押されたら呼ばれる関数
void keyPressed() {
if(key==letter.charAt(now)){
score++;
now++;
cnt++;
if(now==letter.length()){
letter=letters[(int)random(letters.length)];
now=0;
frameCount=0;
}
if(cnt>=10){
cnt=0;
time+=60;
continuous=true;
money.rewind(); //<--new
money.play(); //<--new
}
}else if(keyCode==SHIFT){
cnt++;
}else{
missCount++;
cnt=0;
error=true;
nekosound.rewind(); //<--new
nekosound.play(); //<--new
}
}
//<--new
void stop(){
nekosound.close(); //<--new
backsong.close(); //<--new
minim.stop(); //<--new
super.stop(); //<--new
}
これで完成です!!
(コンソールにJavaSound Minim Errorが出ていると思います。再生自体には問題はないのですが、気になる方は音楽の再生準備を行う(minim編)を参照してください。)
lettersの中にたくさん文字列を追加したらもう少しタイピングゲームっぽくなると思います。(日本語には対応していません💦)
Processingは使い始めたばかりなので間違っているところ等ありましたらコメントお願いします!