Mod:Coding Challenge #5: Space Invaders in Processing
スペースインベーダーのチュートリアル。マウスをクリックしてスタート。左右の矢印キーで母艦を動かしてスペースキーで射撃する。
ビデオではp5.jsでやってたけど気まぐれでProcessingでやった。クラスのプロパティも多少書き換えたりしてみた。ビデオの中では最低限動くところまでをやっていたので、スコアを計算して表示させた。その他、OpenProcessingで表示させるにあたってブラウザのタブに対してフォーカスをアクティブにする必要があったので、クリックさせて簡易的に画面遷移的な演出を加えた。この辺は有限ステートマシン(FSM: Finite State Machine)を使うと良いみたい。Processingだとこういうライブラリがある。
長いチュートリアルだったので思いの外時間がかかって、時間を決めて作業していたのでタイムアップ感がある。ゲームは作り出すと細かい所まで作り込みたくなるけど、まぁそういう目的でやってないので仕方がない。UnityでProcessingぽく書けるUnicessingをやりたいけどUnityのことをほぼ忘れているので復習してどこかのタイミングで触りたい。
Ship ship; Flower[] flowers = new Flower[10]; ArrayList<Drop> drops = new ArrayList<Drop>(); int score; float pastTime = 0; boolean isStart = false; // setup関数 : 初回1度だけ実行される void setup() { fullScreen(); colorMode(HSB,360, 100, 100); // HSBでの色指定にする ship = new Ship(); for (int i = 0; i < flowers.length; i++) { flowers[i] = new Flower(i*80+80, 60); } } // draw関数 : setup関数実行後繰り返し実行される void draw() { background(200, 80, 20); if(isStart == false){ textSize(64); textAlign(CENTER,CENTER); text("click to start!",width/2,height/2); }else{ ship.draw(); ship.move(); for (Drop d : drops) { d.draw(); d.move(); for (Flower f : flowers) { if (d.hits(f)) { f.grow(); d.evaporate(); score += 20; if (f.r > 40) { f.evaporate(); score += 200; } } } } boolean edge = false; for (Flower f : flowers) { f.draw(); f.move(); if ((f.x > width || f.x < 0) && f.isVisible ) { edge = true; } } if (edge) { for (Flower f : flowers) { f.shiftDown(); } } for (int i = drops.size()-1; i >= 0; i--) { Drop d = drops.get(i); if (d.toDelete) { drops.remove(d); } } } noStroke(); fill(0, 0, 100); textAlign(LEFT,CENTER); textSize(32); text("score: " + score, 20, height-70); textSize(16); text("LEFT-key,RIGHT-key:move ship", 20, height-40); text("SPACE-key:shoot", 20, height-20); } void keyReleased() { if (key != ' ') { ship.setDir(0); } } void keyPressed() { float now = millis(); if (key == ' ' && abs(now - pastTime) > 100 ) { drops.add(new Drop(ship.x, height)); pastTime = now; } switch(keyCode) { case RIGHT: ship.setDir(1); break; case LEFT: ship.setDir(-1); break; } } void mousePressed(){ isStart = true; } class Drop { float x, y, r; boolean toDelete; Drop(float _x, float _y) { x = _x; y = _y; r = 8; toDelete = false; } void draw() { noStroke(); fill(0, 80, 100); ellipse(x, y, r*2, r*2); } void evaporate() { toDelete = true; } boolean hits(Flower f) { float d = dist(x, y, f.x, f.y); if (d < r + f.r && f.isVisible) { return true; } else { return false; } } void move() { y = y -5; } } class Flower { float x, y, r, xDir; boolean isVisible; Flower(float _x, float _y) { x = _x; y = _y; r = 30; xDir = 1.5; isVisible = true; } void grow() { r = r + 2; } void evaporate() { isVisible = false; } void shiftDown() { xDir *= -1; y += r; } void move() { x = x + xDir; } void draw() { if (isVisible) { for (int i = 0; i < 3; i++) { float hue = map(r,30,50,220,360); stroke(hue, 80, 100); noFill(); strokeWeight(r/5); float r2 = r * (i+1)/3*2; ellipse(x, y, r2, r2); }; } } } class Ship { float x, xDir; Ship() { x = width/2; this.xDir = 0; } void draw() { fill(0, 0, 100); noStroke(); rectMode(CENTER); rect(x, height-20, 20, 60); } void setDir(float dir) { xDir = dir; } void move() { x += xDir * 5; } }