|ハイブリッドOS|File System|ARM|Android|Java|制御システム|オープンシステム

 

技術者コラム

 
フォーム
 
参考画像1〜3
2014-10-12
 
第30回目:Processingでシューティング(Part2)
2014-10-05
筆者:村田
 
こんにちは。
 
前回、図形を一定方向に動かしたりくるくる回したりしました。これを応用して敵やプレイヤーの弾にしましょう。画面上の複数の弾を簡単に扱いたいので、配列やリストなどのコレクションを使いたいです。リファレンスを見てみましょう。固定配列のArrayと可変配列のArrayListがありました。今回は画面上の弾数が増減するのでArrayListを使います。弾のXY座標、進行角度などのデータをセットにして扱いたい時は構造体、クラス、ハッシュ、タプルといったユーザー定義のデータ型を作れる仕組みが必要です。Processingではクラスでデータ型を作ります。それでは弾(Bullet)クラスを作りましょう。
 
class Bullet {
}
 
XY座標と進行角度をBulletクラスの属性として持たせます。他にも進行角度の変化量である角速度や弾の移動速度も属性に追加しました。
 
class Bullet {
  float x;          // X座標
  float y;          // Y座標
  float angle;      // 進行角度
  float speed;      // 移動速度
  float angleSpeed; // 角速度
}
 
次に初期値を渡せるコンストラクタを作ります。Javaと同じく自インスタンスの参照にはthisを使います。なお所々...で記述を省略します。
 
class Bullet {
  ...
  Bullet (float x, float y, float angle, float speed, float angleSpeed) {
    this.x = x; 
    this.y = y;
    this.angle = angle;
    this.speed = speed;
    this.angleSpeed = angleSpeed;
  }
}
 
前回draw関数の中に書いた図形の移動処理をBulletクラスのメソッドにします。前回の処理とちょっと違うのは三角関数で得られたxとyの移動量をspeedで定数倍してスピードアップしている点です。
 
class Bullet {
  ...
  void move() {
    angle = (angle + angleSpeed) % 360; // 進行角度が角速度で変化する
    x += cos(radians(angle)) * speed;   // 進行角度のx成分のスピード倍
    y += sin(radians(angle)) * speed;   // 進行角度のy成分のスピード倍
  }
}
 
moveは移動だけで描画されません。描画するメソッドを別に作りましょう。
 
class Bullet {
  ...
  void draw() {
    ellipse(x, y, 10, 10); // XY座標を中心に幅と高さ10の円を描く
  }
}
 
これで弾が扱い易くなりました。試しに複数の弾をsetup関数で作成しdraw関数で描画してみましょう。
 
ArrayList<Bullet> bulletList; // 弾リスト(グローバル変数)
 
void setup() {
  size(500, 500);
  noStroke();
  
  bulletList = new ArrayList<Bullet>(); // 弾リスト生成
 
  // 画面の中央(cx,cy)
  float cx = width / 2;
  float cy = height / 2;
  
  // 0〜350°まで10°ずつずらして弾を生成
  for (float angle = 0; angle < 360; angle += 10) {
    Bullet bullet = new Bullet(cx, cy, angle, 2, 0);
    bulletList.add(bullet);
  }
}
 
void draw() {
  fill(0, 0, 0, 20);
  rect(0, 0, width, height);
 
  fill(255, 0, 0); // 弾の色(赤)
  for (Bullet bullet : bulletList) {
    bullet.move(); // 弾を移動
    bullet.draw(); // 弾を描画
  }
}
 
どうですか?参考画像1のように画面中央から放射状に弾が動けば成功です。次はこの放射弾を発射する敵を作りましょう。これもEnemyクラスとしてまとめます。なお今回は敵1体だけのゲームにします。Enemy vs Playerのゲーム。
 
class Enemy {
  float x = width / 2;  // 敵の初期位置(xは真ん中)
  float y = height / 3; // 敵の初期位置(yはちょっと上)
  int angle = 0;
  
  void move() {
    angle = (angle + 1) % 360;           // 角速度1
    x += cos(radians(angle)) * 2;        // 移動速度2
    y += sin(radians(angle*2 + 90)) * 3; // 移動速度3 縦は横の2倍の周期で動く
  }
  
  void draw() {
    rect(x-10, y-10, 20, 20); // 敵は四角図形。幅と高さは20
  }
}
 
Bulletクラスに似ています。どうせ敵1体だけなのでパラメータ付きのコンストラクタは用意していません。初期位置は画面中央のちょっと上に設定しました。まずdrawメソッドを見ていきます。敵は四角で描画することにしました。rect関数の第1,2引数は四角形の左上隅の座標を指定します。したがって(x,y)を四角形の中心とすると、幅と高さの半分を引いた位置が左上隅となります。
 
さてmoveの中身を順番に見ていきましょう。まずangleは0°から始まります。1°ずつ増えて360°で1周します。次はx方向(横)の移動です。コサインカーブを思い出しましょう。1(cos0°)から始まって0(cos90°)、-1(cos180°)、0(cos270°)、1(cos360°)と動きます。まず右に動き始めangleが90°になったところで右から左向きに動くように変わり、angleが270°になったところでまた右に動き始めます。左右の往復運動がイメージできますでしょうか。次はy方向(縦)の移動です。angleを2倍にしているので、横の2倍の周期で縦が移動します。左右に1往復する間に上下に2往復するというわけです。なお位相を90°ずらしています。サインカーブを思い出しましょう。通常は0(sin0°)から始まって、0 -> 1 -> 0 -> -1 -> 0と変化しますが、位相を90°ずらしているので、1 -> 0 -> -1 -> 0 -> 1というカーブになります。スタート時(angle=0)、下方向(1)にすぐに移動を始めて欲しかったのでこうしました。ではEnemyを実際に描画してみましょう。
 
Enemy enemy; // グローバル変数
 
void setup() {
  ...
  enemy = new Enemy(); // 敵1体を生成
}
 
void draw() {
  ...
  fill(167, 87, 168); // 敵は紫っぽい色
  enemy.move();
  enemy.draw();
}
 
参考画像2のように無限マークに似た感じで動きます。ここまで書いておいてなんですが、こんなことは頭で考えるより実際に手でパラメータをいじくりまわした方が楽しいですし、面白い動きを発見できると思います。ちょっといじってはRUNして、またいじくって・・・と、どんどん面白いビジュアルを追求する楽しさがProcessingの醍醐味かもしれません。
 
今度は敵の動きと先程の放射弾を組み合わせていきましょう。その前にやることがあります。弾をどんどん発射するので、画面から外れた弾はArrayListから削除しましょう。それとまだ先の話ですが、プレイヤーに当たった弾も画面から消してArrayListからも削除したいです。これらを判定するメソッドをBulletクラスに追加しましょう。
 
class Bullet {
  ...
  boolean hit = false; // プレイヤーに当たったかを示すフラグ (まだ使わないけど)
 
  boolean needRemove() {
    // 画面からはみ出るか、プレイヤーに当たったら削除してよい。
    return x < 0 || x > width || y < 0 || y > height || hit;
  }
}
 
draw関数内の弾リスト描画ループを修正しましょう。
 
void draw() {
  ...
  for (Bullet bullet : bulletList) {
    bullet.move();
    bullet.draw();
    if (bullet.needRemove()) bulletList.remove(bullet); // 画面外の弾を削除(?)
  }
  ...
}
 
上記は実行時エラーとなってしまいます。Java同様、拡張for文内のイテレータは途中で要素が削除されることを想定していません。普通のfor文に書き換えましょう。お尻から回します。
 
void draw() {
  ...
  for (int i = bulletList.size()-1; i >= 0; i--) {
    Bullet bullet = bulletList.get(i);
    bullet.move();
    bullet.draw();
    if (bullet.needRemove()) bulletList.remove(i); // 画面外の弾を削除
  }
  ...
}
 
では敵に放射弾を発射させましょう。先程setup関数内で試したロジックをEnemyクラスに持ってくるだけです。
 
class Enemy {
  ...
  void circleShot() {
    // 放射弾。敵の現在位置(x,y)を中心に360°発射してやんよ
    for (float angle = 0; angle < 360; angle += 10) {
      Bullet bullet = new Bullet(x, y, angle, 2, 0); // 弾速2
      bulletList.add(bullet);
    }
  }
}
 
というわけでsetup関数からお試しコードは削除します。残ったコードは以下の通り。
 
void setup() {
  size(500, 500);
  noStroke();
  
  bulletList = new ArrayList<Bullet>();
  enemy = new Enemy();
}
 
次はcircleShotを定期的に呼ぶようにします。どうやってタイミングを計るか。こんなときはProcessingのframeCountグローバル変数を利用します。描画フレーム数がカウントアップされています。frameCountが60になると大体1秒です。Enemyのdraw関数内でframeCountを参照し、適当なタイミングでcircleShotを呼びます。
 
class Enemy {
  ...
  void draw() {
    rect(x-10, y-10, 20, 20);     
    if (frameCount % 90 == 0) circleShot(); // ほぼ1.5秒間隔で放射弾発射
  }
  ...
}
 
さて動かしてみましょう。参考画像3ではちょっと分かりづらいですが、敵が無限の軌跡で動きながら放射弾を撃ってきます。別の種類の弾も作って発射させてみましょう。スロー弾です(テキトー)。
 
class Enemy {
  ...
  void draw() {
    rect(x-10, y-10, 20, 20);     
    if (frameCount % 90 == 0) circleShot();
    if (frameCount % 10 == 0) slowCurveShot(); // 1秒間に6発
  }
  ...
  void slowCurveShot() {
    Bullet bullet = new Bullet(x, y, angle, 1, 0.2); // スピードが遅く(1)ちょっとだけ曲がる(0.2)
    bulletList.add(bullet);
  }
}
 
動かすと参考画像4のようになります。やはり分かりづらい・・・遅い弾を発射しビミョ〜に曲がります。避けにくいかもしれません:P 今回はここまで。
 
p.s. 最近Breaking Badという海外ドラマを見始めまして。久々に海外ドラマシリーズを見ているのですが、人気シリーズのようで面白いです。
 
以上。
 
参考画像1〜2
2014-10-05
 
参考画像3〜4
2014-10-05
 
第29回目:Processingでシューティング(Part1)
2014-09-27
筆者:村田
 
こんにちは。
 
9月も終わりで涼しくなってきましたね。よく晴れた週末は少し近所を散歩するだけでもいいリフレッシュになります。先日、ライブフェスに友人と出かけて一日音楽を楽しんだのですが、2日後、下半身に謎の痛みが・・。しばらく考えて、ああ膝を使ってリズムを取っていたせいで筋肉痛になってしまったのだと気づき、唖然としました。まさかあの程度でスクワット扱いになるとは・・・
 
さて表題のProcessingです。先日社内でArduinoについての話題があったので、ちょっと気になっていました。Arduinoは小型のマイコンボードでオープンソースハードウェアってやつです。・・・えっと全然語れません。Arduinoを使った記事をここで書ければ良かったのですが残念ながら触ったことがありません。4〜5000円でキットを入手できるようなので、今度買ってみようかな:) ただ、ちらちらネットを見ているうちにArduino開発言語のベースとなっているProcessing言語に興味がでてきたので、今回はこれで遊んでみようと思いたった次第であります。それではしばらくお付き合い宜しくお願いいたします。
 
Processingはデザイン/アートのためのプログラミング言語としてMITで開発されました。Arduino向けの言語はProcessing派生言語と言えます。アーティスト向けの言語ということで、ネットでサンプルコードや作品を検索すると奇麗なグラフィックスが目を引きます。とにかくグラフィックスを簡単に扱えることが言語の特長です。Processingでのプログラミングを「スケッチする」と言うらしいです。Javaを単純化した言語なのでJavaを知っている方はすぐに開発を楽しむことができます。Javaを知らなくても特に問題ないと思います。クラスだのインタフェースだのをあまり気にせず、ガンガンと手続きをコーディングしていく感じです。Javaのクラスをインポートして利用することもできますが、今回はなるべくProcessingが提供している関数だけで進めたいと思います。公式サイトでリファレンスを見てみるとこれだけ?ってくらいコンパクトです。Processingの紹介はこのくらいにして、早速ダウンロードしてみましょう。ダウンロードを待っている間は公式Tutorialの動画でも見てみましょう。Danielさんがとても楽しくホットに基本を教えてくれます:-)
 
IDEを実行してみると、おおシンプル!感激!初心者の壁となりうるIDEの複雑さは全くありません。(参考画像1)
 
まずは2つの関数を覚えましょう。setup()とdraw()です。setupの中には起動時に一回だけ実行したいコードを書きます。drawの中には丸や四角などを描画するコードを書きます。デフォルトでは1秒間に60回drawが呼ばれます。では早速、丸と四角の図形を描いてみましょう。
 
void setup() {
  size(500, 500); // ウインドウのサイズを大きめ500x500にしておく
}
 
void draw() {
  ellipse(100, 200, 60, 50);  // 丸 x座標 y座標 幅 高さ
  rect(300, 50, 30, 60);      // 四角 x座標 y座標 幅 高さ
}
 
IDEのRUNボタンを押してみると丸と四角が描画できました。(参考画像2) 簡単ですね。次は色を変えて図形を動かしてみましょう。
 
int ellipse_x = 100, ellipse_y = 200; // グローバル変数だけどちっちゃいことは気にするな!それワカチ・
int rect_x = 300, rect_y = 50;
 
void draw() {
  fill(255, 0, 0);          // 赤に変える(red:255 green:0 blue:0)
  ellipse_x++; ellipse_y++; // 毎回ちょっとずつ動かす
  ellipse(ellipse_x, ellipse_y, 60, 50);
  
  fill(0, 255, 0);          // 緑に変える
  rect_x++; rect_y++;
  rect(rect_x, rect_y, 30, 60);
}
 
実行してみると、色の付いた絵がウインドウの右下に向かってゆっくり動いてくれました。よく見ると、図形の内側は設定した色で塗りつぶされるのですが、縁取りが黒いままなので動いた後がちょっと汚なくなります。(参考画像3) そこで縁取りを無くしてしまいましょう。setupの中でnoStroke()を呼びます。
 
void setup() {
  size(500, 500);
  noStroke(); // 縁取り(stroke)をずっと描画しない
}
 
次はちょっとした背景のトリックを使います。drawの中で毎回半透明のブラックで背景を塗りつぶすようにします。すると丸と四角が移動した後を少しずつ黒くすることになるので、移動の後に尾ヒレが付いたようになります。なお、以下のコードのwidth、heightはProcessingが用意しているグローバル変数で、ディスプレイの幅と高さを表しています。
 
void draw() {
  fill(0, 0, 0, 20);         // rgbは0(black) alphaは20
  rect(0, 0, width, height); // ディスプレイを塗りつぶす
  ・・・
}
 
参考画像4のように尾ヒレが付きました。次にマウスの動きに図形を追従させてみましょう。マウスイベントハンドラをうんたんうんたん・・なんてコーディングする必要はありません。Processingが用意しているmouseXとmouseYというグローバル変数にマウスの位置が入っているのでellipse関数にそのまま渡しましょう。こういうところがシンプルで気持ちいいです。
 
void draw() {
  fill(0, 0, 0, 20);
  rect(0, 0, width, height);
  
  fill(255, 0, 0);
  //ellipse_x++; ellipse_y++;
  ellipse(mouseX, mouseY, 60, 50); // マウスに追従
  
  fill(0, 255, 0);
  rect_x++; rect_y++;
  rect(rect_x, rect_y, 30, 60);
}
 
実行してぐりぐりマウスを動かすとちょっとだけ楽しいです。さて先程は右斜め下に移動させるだけでしたが、今度は三角関数を使って任意の方向に移動させてみましょう。
 
float x = 100, y = 400; // 三角関数の結果を扱うのでfloatにします
int degree = -30;       // 2時の方向
 
void draw() {
  fill(0, 0, 0, 20);
  rect(0, 0, width, height);
  
  fill(255, 0, 0);
  x += cos(radians(degree)); // ラジアンに変換して-30°のx成分を計算 
  y += sin(radians(degree)); // y成分
  ellipse(x, y, 30, 30);
}
 
基本的な三角関数なので頑張って思い出しましょう。ポイントはcos関数やsin関数には角度をラジアンで渡すというところです。ラジアンは180°をPI(3.14)で換算する単位です。-30°は-0.52ラジアンとなります。この単位変換はradiansという便利関数を使いましょう。なお、Processingの座標系について、x軸は右方向がプラスですが、y軸は下方向がプラスである点に注意しましょう。したがって角度を増やすと時計回りに回転します。上記例では-30°に設定しているので、時計の針で言うと4時の方向では無く2時の方向を指していることになります。
 
上記は角度を固定していましたが、今度は角度も少しずつ動かして円運動をさせてみましょう。
 
float x = 200, y = 200;
int degree = 0;
 
void draw() {
  fill(0, 0, 0, 5);
  rect(0, 0, width, height);
  
  fill(255, 0, 0);
  degree = (degree + 5) % 360; // 5°ずつ増やす。360°で0°に戻す。
  x += cos(radians(degree));
  y += sin(radians(degree));
  ellipse(x, y, 30, 30);
}
 
図形がくるくると動きましたか? それでは最後にくるくる回しながら一定方向に動かしてみましょう。(参考画像5)
 
float x = 200, y = 200;
int degree = 0;
 
void draw() {
  fill(0, 0, 0, 2);
  rect(0, 0, width, height);
  
  fill(255, 0, 0);
  degree = (degree + 5) % 360;
  x += cos(radians(degree)) + cos(radians(30)) / 3; // 一定方向(30°)ちょい(1/3)足し
  y += sin(radians(degree)) + sin(radians(30)) / 3; 
  ellipse(x, y, 30, 30);
}
 
今回ちょっとしか触りませんでしたが、次回はもう少し頑張ってシューティングっぽくしたいと思います。芸術の秋、絵の具ではなく、Processingでスケッチなんてのも楽しいかもしれません;-) 興味のある方は是非触ってみて下さい。
 
さて本題を切り上げたのに雑談がもう少し続きます。今月の日経ソフトウェア(2014年11月号)の「オブジェクト指向、関数型のウソ」という特集記事がとても面白かったという感想です。ポリモーフィズムの実装を様々な言語で紹介したり、OCamlのまとまった解説が読めたり(モナドも出てきます)、NTTDで運用されているHaskellコード規約(do構文禁止!?)に関する記事など、いづれも読み応えがありました。登場する言語がとても多い記事だったのですが、そこに意図があるように感じます。ポリモーフィズム(多態性)というプログラミングの概念を知っている人は多いと思いますが、その実現方法は多彩です。C++/Javaのような仮想関数による実現方法しか知らなければ、多態は型(クラス)に縛られた印象を持つと思います。特集記事ではPerlやJavaScriptのハッシュテーブルによる多態や、Smalltalk/ObjC/Rubyのメッセージ転送による柔軟な多態、そしてOCamlの強力な型推論を用いた多態について読むことができます。たくさんの言語に触れることで、言語に縛られない多態というもののイメージが湧いてきます。これは多態性に限らず「オブジェクト指向」や「関数型」という概念にも言えることだと思います。
 
ん〜今日もいい天気です:-D
 
以上。
 

連載記事のソースコード

連載記事のソースコード
 
・Haskellで問題を解く(Part1〜Part4) ソースコード
・Clojureで8クイーン問題にチャレンジ(Part1〜Part5) ソースコード
・OCamlでへびゲームを作る(Part1〜Part5) ソースコード
・Swiftでオセロを作る(Part1〜Part5) ソースコード
・Processingでシューティング(Part1〜Part4) ソースコード 
Haskellでテトリス(Part1〜Part9) ソースコード
・プチコン3号(BASIC)でさめがめ(Part1〜Part3) ソースコード
・Prologでさめがめを解く(Part1〜Part6) ソースコード