08-03 たくさんのオブジェクトを管理する

練習問題8-3-1

ランダムな位置に星を20個つくり、夜空のようにキラキラさせましょう。
Star.js をプロジェクトに入れたうえで、次の雛形の ??? を埋めて完成させなさい。

let stars = []; 
function setup() {
  createCanvas(400, 400);
  noStroke(); 
  for (let i = 0; i < 20; i++){ 
    let star = new Star(
      random(width),
      random (height), 
      random(20, 40), 255, 230, 160
    );
    star.offset = floor(random(60));
    // 明滅のずれ
    stars.push(star)
  } 
} 
function draw() {
  background(18, 24, 60);
  // すべての星を点滅・表示させる
  for(let i=0; i<stars.length; i++){
  stars[i].blink() 
  stars[i].display() 
  }
}

まず最初に、問題文を読む能力が低すぎて、どうすればいいのか少し戸惑い、とりあえず星を表示してみようとしたら、大苦戦をし、AIに助けを求めて、どうにかこうにかランダムに星を配置して、それぞれの星を動かすことに成功しました。ですが、AIに聞いたおかげで、クラスの理解が深まったので、いい経験だったと思います。

練習問題8-3-2

練習問題8-3-1 では、星をランダムに置くと偏って見えました。
今度は 6×6 のマス目 を考え、市松模様 になる位置だけに星を置きましょう。

  • 色は R=255、G=230 に固定し、B だけ random(128, 255) にする
  • 大きさや明滅のずれは 8-3-1 と同様でよい
// Star.js をプロジェクトに入れておく

let stars = [];

function setup() {
  createCanvas(400, 400);
  noStroke();

  let cols = 6;
  let rows = 6;
  let cellW = width / cols;
  let cellH = height / rows;

  for (let row = 0; row < rows; row++) {
    for (let col = 0; col < cols; col++) {
      // 市松の位置だけ、星をつくって配列に入れる(03-05)
      if((col+row)%2==0){
        let star =new Star(cellW*col+cellW/2, cellH*row+cellH/2, 30, 255, 230, random(128, 255))
        star.offset = floor(random(60))
        stars.push(star)
      }
    }
  }
}

function draw() {
  background(18, 24, 60);
  for(let i=0; i<stars.length; i++){
    stars[i].blink()
    stars[i].display()
  }
  // すべての星を点滅・表示させる
}

今までやってきたことを正確に実行し、以前やったことも再現しなければいけなかったので、なかなかに難しかったです。

練習問題8-3-4

例題8-3-3 を改造し、粒が 動く ようにしなさい。まだ消さなくてよいです。

  • constructor に速さ vx / vy を足す
  • this.vx = random(-1.5, 1.5);
  • this.vy = random(-2.5, -0.5);
  • update() メソッドで、位置を vx / vy だけ動かす
  • draw のループで、display の前に update を呼ぶ

動かしたあと、少し待ってみましょう。粒が画面にどんどん溜まっていくはずです。

let particles = [];

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  background(18, 24, 60);

  // 毎フレーム、マウス位置に1粒生む
  particles.push(new Particle(mouseX, mouseY));

  // すべての粒を描く
  
  for (let i = 0; i < particles.length; i++) {
    particles[i].update()
    particles[i].display();
  }
}

class Particle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.size = random(4, 10);
    this.vx = random(-1.5, 1.5);
    this.vy = random(-2.5, -0.5);
  }

  update(){
    this.x+=this.vx
    this.y+=this.vy
  }
  
  display() {
    fill(255, 230, 160);
    ellipse(this.x, this.y, this.size, this.size);
  }
}

ここまで動きが激しいと見ていて気持ちいです。

練習問題8-3-5

練習問題8-3-4 では、粒が消えないまま増えていきました。
そこで 寿命 を持たせ、尽きた粒は配列から外しましょう。

let particles = [];

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  background(18, 24, 60);

  particles.push(new Particle(mouseX, mouseY));

  // うしろから前へ調べる(なぜかは次の練習問題で考えよう)
  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].update();
    particles[i].display();
    if (particles[i].isDead()) {
      // 配列から外す(04-02 の splice)
      particles.splice(i, 1)
    }
  }
}

class Particle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.vx = random(-1.5, 1.5);
    this.vy = random(-2.5, -0.5);
    this.life = 255;  // 残り寿命(透明度にも使う)
    this.size = random(4, 10);
  }

  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.life -= 5;
  }

  display() {
    fill(255, 230, 160, this.life);
    ellipse(this.x, this.y, this.size, this.size);
  }

  // 寿命が尽きたか判定(尽きた:true、尽きていない:false)
  isDead() {
    if (this.life<=0) {
      return true;
    } else {
      return false;
    }
  }
}

ちゃんと消えているのかは、調べ方がいまいちわからなかったので、ちゃんと消えているかわかりませんが、多分消えていると思います。

練習問題8-3-6

練習問題8-3-5 では、配列を うしろから前へ 調べています。
なぜ前からではなく、うしろから調べるのですか?

具体例を使って自分の言葉で説明し、先生に話しなさい。
(例:粒が3つ並んでいるとき、途中の1つを消すと番号がどうずれるか)

例で表すと、「0,1,2」ということになると思います。
そしてそこから真ん中の0が消えない、1が消えるとなると、2だったものが1になるため、もう一度1を認識しなければいけなくなるが、もう一週回ってくるまで、認識されなくなってしまうと考えました。
それと、増やす方式だとどうなるのか気になったため、足していく方式にしてみましたが、自然に動いているように見えたので、先ほどの仮説は少し信ぴょう性が高くなったと思います。

練習問題8-3-7

練習問題8-3-5 を改造し、次のうち どれか1つ以上 を変えなさい。

  • 粒の色
  • 飛ぶ速さ(vx / vy)
  • 1フレームに生まれる数(8-3-5 では1個。for で3個や5個まとめて push すると密度が上がる)
  • 消える速さ(life の減り方)
let particles = [];

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  background(18, 24, 60);

  for(let i=0; i < 5; i++){
    particles.push(new Particle(mouseX, mouseY));
  }


  // うしろから前へ調べる(なぜかは次の練習問題で考えよう)
  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].update();
    particles[i].display();
    if (particles[i].isDead()) {
      // 配列から外す(04-02 の splice)
      particles.splice(i, 1)
    }
  }
}

class Particle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.vx = random(-3, 3);
    this.vy = random(-4, -1);
    this.life = 255;  // 残り寿命(透明度にも使う)
    this.size = random(4, 10);
  }

  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.life -= 3;
  }

  display() {
    fill(255, random(255), 160, this.life);
    ellipse(this.x, this.y, this.size, this.size);
  }

  // 寿命が尽きたか判定(尽きた:true、尽きていない:false)
  isDead() {
    if (this.life<=0) {
      return true;
    } else {
      return false;
    }
  }
}

とりあえず書かれていたこと全部変えてみました。とてもきれいな感じになってよかったです。

let particles = [];

function setup() {
  createCanvas(400, 400);
  noStroke();
}

function draw() {
  background(18, 24, 60);

  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].update();
    particles[i].display();
    if (particles[i].isDead()) {
      particles.splice(i, 1)
    }
  }
}

function mousePressed(){
  let r=random(255);
  let g=random(255);
  let b=random(255);
  for(let i=0; i < 60; i++){
    switch(frameCount%3){
      case 0:
        r = random(255);
        break
      case 1:
        g = random(255);
        break
      case 2:
        b = random(255);
    }
    particles.push(new Particle(mouseX, mouseY, r, g, b));
  }
}

class Particle {
  constructor(x, y, r, g, b) {
    this.x = x;
    this.y = y;
    this.vx = random(-3, 3);
    this.vy = random(-3, 3);
    this.life = 255;
    this.size = random(4, 10);
    this.r = r;
    this.g = g;
    this.b = b;
  }

  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.life -= 3;
  }

  display() {
    fill(this.r, this.g, this.b,this.life);
    ellipse(this.x, this.y, this.size, this.size);
  }

  isDead() {
    if (this.life<=0) {
      return true;
    } else {
      return false;
    }
  }
}

花火作れそうだなと思って作りました。めちゃくちゃ時間かかりました。
特に大変だったのが、一発の花火を似たような色合いにする方法です。RGBの一つだけをランダムにしても、すべての花火が、同じような色になってしまうので、一発ずつ色合いを変えたかったので、結構試行錯誤しました。p5jsで私が作ってきた中で一番の力作です。
ただ、日本の花火のようにきれいな丸は作ることができませんでした。何か方法はあるかもしれませんが、思いつきませんでした。

コメントする