カテゴリー
Processing

Processing(Step5)

複雑な図形を描く

普段使っているrect()や、ellipse()で四角形や円は描画できます。しかし、星型や多角形などの図形を描画するにはどうしたらいいしょう。

そこで、使えるのが、vertexです。合わせて、beginShape()endShape()を使い、図形を描くことができます。使い方はこうです。

  1. beginShape()と書き、図形を定義していきます。
  2. vertex(x座標,y座標,z座標)を指定し、図形の頂点を指定する。
  3. endShape()と書き、図形を完成させましょう。

正六角形

以下のコードを見てみましょう。vertexを使う前にこの2つを覚えておきましょう。

pushMatrix() → 現在の座標を保存する。
popMatrix() → 保存した座標を再展開する。

上記の2つをbeginShape()とendShape()で描画した図形を挟むように記述することで、座標を維持したまま描くことが出来ます。また、三角関数もプロセシングでは、扱うことが出来ます。

三角関数

  • sin(a)…サイン(正弦)を求める
  • cos(a)…コサイン(余弦)を求める
  • tan(a)…タンジェント(正接)を求める
  • degrees(a)…角度の単位をラジアンから「度」へ変換する
  • radians(a)…角度の単位を「度」からラジアンへ変換する

この場合は、vertexで頂点の位置を指定する時に使用しています。図形を使う時には良く使うので、覚えておきましょう。

int r = 150;

void setup() {
  size(1000, 1000);
}

void draw() {
  background(255);
  
  pushMatrix(); // 正六角形を描画
  translate(width/2,height/2);
  fill(255,0,0);
  strokeWeight(2);
  stroke(255, 0, 0);
  beginShape();
  for (int i = 0; i < 6; i++) {
    vertex(r*cos(radians(360*i/6)), r*sin(radians(360*i/6)));
  }
  endShape(CLOSE);
  popMatrix();
}

実行すると、以下のようになります。

星型

次は、星型を描いてみましょう。

int r = 150;
int R = 0;
int inR = 0; //中心点から谷までの距離
int outR = 0; //中心点から先端までの距離

void setup() {
  size(1000, 1000);
}

void draw() {
  background(255);
  
  pushMatrix(); //星を描画
  translate(width/2,height/2);
  rotate(radians(-90));
  fill(255,0,0);
  strokeWeight(2);
  stroke(255, 0, 0);
  
  outR = r;
  inR = outR/2;
  
  beginShape();
  for (int i = 0; i < 10; i++) {
   if (i%2 == 0) {
      R = outR;
    } else {
      R = inR;
    } 
    
    vertex(R*cos(radians(360*i/10)), R*sin(radians(360*i/10)));
  }
  endShape(CLOSE);
  popMatrix();
}

星形の難しいところは、尖っている所、へこんでいる所が存在することです。そのため、頂点の位置の角度に気をつけなければなりません。尖ってる→へこみ→尖ってる…と連続した規則性を持っているので、それをコードとして表現できれば、簡単です。

肝となるのが、以下のコードです。偶数、奇数で角度をずらす処理をしています。これをすることで、星の規則性を再現できます。

beginShape();
  for (int i = 0; i < 10; i++) {
   if (i%2 == 0) {
      R = outR;
    } else {
      R = inR;
    } 

関数を作る

上で、複雑な図形を描きましたが、これをいくつか描画したいとなったら、面倒くさいですよね。そこで、自分で星を描画する関数を作れば、いつでも簡単に描画できます。以下のコードを見てみましょう。

void setup() {
  size(1000, 1000);
}

void star(int x, int y, int r, int num){ //星を描画する関数
  int p = num*2;
  int R;
  
  int outR = r; //中心点から先端までの距離
  int inR = r/2; //中心点から谷までの距離
  
  pushMatrix(); 
  scale(0.5); //星のサイズを変更
  translate(x,y);
  rotate(radians(-90));
  
  beginShape();
  for (int i = 0; i < p; i++) {
   if (i%2 == 0) {
      R = outR;
    } else {
      R = inR;
    } 
    
    vertex(R*cos(radians(360*i/p)), R*sin(radians(360*i/p)));
  }
  endShape(CLOSE);
  popMatrix();
}

void draw() {
  background(255);
  fill(255,0,0);
  strokeWeight(2);
  stroke(255, 0, 0);
  
  for(int i = 0; i < 5; i++){
   
    star(width/3+i*300,3*height/4,150, 5);
  }
}

void star()を新たに作っています。()内には引数として取得したい値を設定しています。

void star(int x, int y, int r, int num){ //星を描画する関数
}

その後、draw関数内で呼び出す際に、値を入れてあげると、star関数に値が引き渡されるようになっています。

star(width/3+i*300,3*height/4,150, 5);

繰り返し処理で、関数を呼び出してあげることで、何度も描画できるようになり、簡略化できます。

ゲームを作ってみよう

今まで習ったことを生かして、 シューティングゲームを作ってみましょう。

自機を作成

自機は、マウスを追従するようにしたいので、マウスの座標を引数に取る関数を作成します。

また、画面外から出ないように条件を付けています。

void machine(int x, int y){ //自機を描画する関数
  rectMode(CENTER);
  noStroke();
  fill(0,0,255);
  
  if( 50 > x ){   //画面内から出ないように描画
    x = 50;  
  }
  if( width - 50 < x ){
    x = width - 50;  
  }
  if( height/2 + 50 > y ){
    y = height/2+50;  
  }
  if( height - 50 < y ){
    y = height - 50;  
  }
  
  rect(x,y,100,100);
  
}

ビームを作成

シューティングといえば、ビームが発射されますよね。上から下に移動していくビームを作成しましょう。

ビームの座標、スピードを保持する変数、さらに、状態を遷移するようにフラグを立てておきましょう。

//ビーム関数用の変数
int beam_sp = 0;
int flag = 2;
int beam_posX = 0;
int beam_posY = 0;

ビームを発射するときには、マウスを押したときの処理とフラグで管理しています。マウスを押したとき、ビームの初期位置をマウスの位置に変更し、1度、スピードも0となります。その後、上に向かって移動していきます。画面外に到達したときは、フラグを2にして発射されていない最初の状態と同じになります。

void beam(int x,int y){ //ビーム描画用の関数

  if(mousePressed){ //マウスを押したときの処理
    flag = 0;
    beam_posX = x;//ビームの位置座標をマウスの位置にする
    beam_posY = y;
  }
  if(flag == 0){ //マウスを押したときにビームを初期位置に変更
    beam_sp = 0;
    flag = 1;
  }
  if(flag ==1){ //ビームの移動処理
    fill(0,255,150);
    rect(beam_posX,beam_posY-10-beam_sp,10,50);
    beam_sp += 20;
  }
  if(y-10-beam_sp <= 0){ //画面外にビームが出たときの処理
    beam_sp = 0;
    flag = 2;
  }
  
}

敵の作成

敵は複数ほしいので、自機とは違い、配列で変数を組み、個々の座標、スピードを管理します。

//敵の関数用の変数
int enemy_x[]=new int[10];
int enemy_y[]=new int[10];
int enemy_sp[]=new int[10]; 

敵の動きはx軸と水平に動き、画面端で折り返すように設定しています。また、ビームが衝突したときは、敵が消えるという動きを画面外に座標を変えることで実装しています。

void enemy(int x, int y, int sp){ //敵の描画
  rectMode(CENTER);
  fill(255,0,0);
  noStroke();
  for(int i=0;i<10;i++){
    
    rect(enemy_x[i],enemy_y[i],50,50);
    
    enemy_x[i] += enemy_sp[i]; //敵にスピードを付加
  
      if(enemy_x[i] >= width - 50){ //画面端に行ったら敵が折り返す
        enemy_sp[i]--;  
      }
      if(enemy_x[i] <= 50){
        enemy_sp[i]++;
      }
      //ビームが衝突した時の処理
      if((x < enemy_x[i]+25 && x > enemy_x[i]-25 && y-25-sp <= enemy_y[i]+25)
      || (x < enemy_x[i]+25 && x > enemy_x[i]-25 && y-25-sp <= enemy_y[i]+25)){
        enemy_sp[i] = 0;
        enemy_x[i] = -500;
        enemy_y[i] = -500;
        beam_sp = 0;
        flag = 2;
      }
  }
}

最終的なコードと実行結果が以下のようになります。

//ビーム関数用の変数
int beam_sp = 0;
int flag = 2;
int beam_posX = 0;
int beam_posY = 0;

//敵の関数用の変数
int enemy_x[]=new int[10];
int enemy_y[]=new int[10];
int enemy_sp[]=new int[10]; 

void setup(){
  size(1000,1000);
  background(255);
  
  for(int i = 0;i < 10;i++){
  enemy_x[i]=int(random(width));
  enemy_y[i]=int(random(50,400));
  enemy_sp[i]=int(random(3,5));
  }
}

void machine(int x, int y){ //自機を描画する関数
  rectMode(CENTER);
  noStroke();
  fill(0,0,255);
  
  if( 50 > x ){   //画面内から出ないように描画
    x = 50;  
  }
  if( width - 50 < x ){
    x = width - 50;  
  }
  if( height/2 + 50 > y ){
    y = height/2+50;  
  }
  if( height - 50 < y ){
    y = height - 50;  
  }
  
  rect(x,y,100,100);
  
}

void beam(int x,int y){ //ビーム描画用の関数

  if(mousePressed){ //マウスを押したときの処理
    flag = 0;
    beam_posX = x;//ビームの位置座標をマウスの位置にする
    beam_posY = y;
  }
  if(flag == 0){ //マウスを押したときにビームを初期位置に変更
    beam_sp = 0;
    flag = 1;
  }
  if(flag ==1){ //ビームの移動処理
    fill(0,255,150);
    rect(beam_posX,beam_posY-10-beam_sp,10,50);
    beam_sp += 20;
  }
  if(y-10-beam_sp <= 0){ //画面外にビームが出たときの処理
    beam_sp = 0;
    flag = 2;
  }
  
}

void enemy(int x, int y, int sp){ //敵の描画
  rectMode(CENTER);
  fill(255,0,0);
  noStroke();
  for(int i=0;i<10;i++){
    
    rect(enemy_x[i],enemy_y[i],50,50);
    
    enemy_x[i] += enemy_sp[i]; //敵にスピードを付加
  
      if(enemy_x[i] >= width - 50){ //画面端に行ったら敵が折り返す
        enemy_sp[i]--;  
      }
      if(enemy_x[i] <= 50){
        enemy_sp[i]++;
      }
      //ビームが衝突した時の処理
      if((x < enemy_x[i]+25 && x > enemy_x[i]-25 && y-25-sp <= enemy_y[i]+25)
      || (x < enemy_x[i]+25 && x > enemy_x[i]-25 && y-25-sp <= enemy_y[i]+25)){
        enemy_sp[i] = 0;
        enemy_x[i] = -500;
        enemy_y[i] = -500;
        beam_sp = 0;
        flag = 2;
      }
  }
}

void draw(){
  background(255);
  stroke(40);
  
  beam(mouseX,mouseY);//ビームを描画する関数呼び出し
  enemy(beam_posX,beam_posY,beam_sp);//敵を描画する関数呼び出し
  machine(mouseX,mouseY);//自機を描画する関数呼び出し
  
}

この段階では、点数や時間を実装していません。ゲームでは、そういった要素もありますので、改良してみてください。