日常の進捗

主に自分のための,行為とその習慣化の記録

Mod:Coding Challenge #5: Space Invaders in Processing

スペースインベーダーチュートリアル。マウスをクリックしてスタート。左右の矢印キーで母艦を動かしてスペースキーで射撃する。

ビデオではp5.jsでやってたけど気まぐれでProcessingでやった。クラスのプロパティも多少書き換えたりしてみた。ビデオの中では最低限動くところまでをやっていたので、スコアを計算して表示させた。その他、OpenProcessingで表示させるにあたってブラウザのタブに対してフォーカスをアクティブにする必要があったので、クリックさせて簡易的に画面遷移的な演出を加えた。この辺は有限ステートマシン(FSM: Finite State Machine)を使うと良いみたい。Processingだとこういうライブラリがある。

github.com

長いチュートリアルだったので思いの外時間がかかって、時間を決めて作業していたのでタイムアップ感がある。ゲームは作り出すと細かい所まで作り込みたくなるけど、まぁそういう目的でやってないので仕方がない。UnityでProcessingぽく書けるUnicessingをやりたいけどUnityのことをほぼ忘れているので復習してどこかのタイミングで触りたい。

eyln.hatenablog.com

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;
  }
}

リファレンス