トップ


大ボスをつける3

レーザーを発射させる

攻撃パターンがmiddleBossと同じでは物足りませんので、レーザーでも発射させて見ましょう。新しいオブジェクトを作成して、Laserと名前を付けてください。

Laser.tonyu
extends SpriteChar;

function atariHantei(){
  for( t in $chars ){
    if( (t is Enemy) && !(t is Boss) && crashTo(t) ) t.die();
    if( t is Tama && crashTo(t) ) t.die();
    if( t is myChar && crashTo(t) ) t.die();
  }
}
vy=5;
while(y<$screenHeight+100){
  y+=vy;
  atariHantei();
  update();
}

レーザーはTamaで破壊されない、Tamaが貫通しない、myCharを破壊でき、Enemyも破壊される、という条件にしてみました。
時に、Boss自身がLaserに当たって自滅してしまうかもしれません。BossはLaserに当たっても貫通するようにもしておきました。

それでは、オブジェクトは消して、Bossから発射させるようにしてみましょう。

Boss.tonyu
extends middleBoss;

function involute(sx,sy,ang,rate) {
  x=sx+cos(ang)+ang/rate*3.14156*sin(ang);
  y=sy+sin(ang)-ang/rate*3.14156*cos(ang);
}
function lemniscate(sx,sy,ang,rate) {
  if(1+sin(ang)==0) ang++;
  x=sx+rate*(cos(ang)/(1 +(sin(ang))*(sin(ang))));
  y=sy+rate*(sin(ang)*cos(ang)/(1 +sin(ang))*(1 +sin(ang)));
}
ang=5000;
while(ang>0){
  involute($screenWidth/2-15,100,ang,60);
  ang-=10;
  update();
}
life=500;
mark=1000;
sx=x;
sy=y;
ang=90;
circle=0;
while(1){
  r=rnd(3);
  ang++;
  if(circle==0 && (ang%360==90 || ang%360==270)&& r==0) {
    ang=270;
    circle=1;
  }
  if (circle==0) lemniscate(sx,sy,ang,150);
  if(circle==1) asteroid(sx,sy,ang,100,1);
  if(circle==1 && ang%720==630) {
    circle=0;
    ang=90;
  }
  appearShoot();
  if (rnd( 400 )==0) {
    p++;
    for(i=0;i<20;i++){
      atariHantei();
      update();
    }
    appear(new Laser(x,y,$pat_Sample+31));
    for(i=0;i<20;i++){
      atariHantei();
      update();
    }
    p--;
  }
  atariHantei();
  update();
}

だんだんとプログラムも長くて見づらくなってきました。400分の1の確率でLaserを発射するのですが、発射する前に20フレーム停止を行い、発射した後も20フレーム停止を行うようにしてみました。いきなりLaserが発射されたのではアニメーションを見ることも出来ないうえに、プレイヤーの方もLaserを避け切れない確率が高くなるからです。updateEx()という関数もありますが、atariHantei()をすり抜けてしまいます。それを避けるためにonUpdate()にatariHantei()をつけるとBossの登場場面でupdate()が呼ばれたときにatariHantei()も呼ばれてしまい、BossがTamaに当たるとBossがいきなり死んで、BossがmyCharに当たるとmyCharは死んでしまいます。

オーバーライド

Bossの登場場面をもう一度良く見てみましょう。BossにTamaが当たってもTamaは貫通してしまっていると思います。一応弾が当たった場合には、Tamaは消えるようにしてみましょう。

Boss.tonyu
extends middleBoss;

function atariHantei() {
  for (t in $chars) if ( t is Tama && crashTo(t) ) t.die();
}
function involute(sx,sy,ang,rate) {
  x=sx+cos(ang)+ang/rate*3.14156*sin(ang);
  y=sy+sin(ang)-ang/rate*3.14156*cos(ang);
}
function lemniscate(sx,sy,ang,rate) {
  if(1+sin(ang)==0) ang++;
  x=sx+rate*(cos(ang)/(1 +(sin(ang))*(sin(ang))));
  y=sy+rate*(sin(ang)*cos(ang)/(1 +sin(ang))*(1 +sin(ang)));
}
ang=5000;
while(ang>0){
  involute($screenWidth/2-15,100,ang,60);
  ang-=10;
  atariHantei();
  update();
}
life=500;
mark=1000;
sx=x;
sy=y;
ang=90;
circle=0;
while(1){
  r=rnd(3);
  ang++;
  if(circle==0 && (ang%360==90 || ang%360==270)&& r==0) {
    ang=270;
    circle=1;
  }
  if (circle==0) lemniscate(sx,sy,ang,150);
  if(circle==1) asteroid(sx,sy,ang,100,1);
  if(circle==1 && ang%720==630) {
    circle=0;
    ang=90;
  }
  appearShoot();
  if (rnd( 400 )==0) {
    p++;
    for(i=0;i<20;i++){
      super.atariHantei();
      update();
    }
    appear(new Laser(x,y,$pat_Sample+31));
    for(i=0;i<20;i++){
      super.atariHantei();
      update();
    }
    p--;
  }
  super.atariHantei();
  update();
}

新たにatariHantei()という関数を作りました。
しかし、atariHantei()はEnemyクラスにも作成してあります。ただ、atariHantei()と書いた場合には、Bossクラスに書いたatariHantei()が呼ばれるので、BossがmyCharと当たってもmyCharが死ぬことはありません。
しかし、今度はBossの登場場面が終わったときにBossのライフが減ったり、BossがmyCharと当たったりしたときの判定が必要になりました。同じatariHantei()では問題です。そこで、super.atariHantei()とすると、親クラスのatariHantei()メソッドを呼び出してくれます。これをオーバーライドと読んでいます。

実際には、BossクラスのatariHantei()をnewAtariHantei()などと名前を変えて、登場場面のときにnewAtariHantei()を呼び出せばよかっただけの話ではあります。しかし、プログラムが膨大な量になったときに、関連性のある名前をつけて後で見やすくしたいものです。膨大なソースからatariHantei()だったかnewAtariHantei()だったかと見直すよりも作成時にatariHantei()とsuper.atariHantei()と書いた方が見やすくなることが多々あります。今は、オーバーライドという方法があるんだという程度で結構だと思います。

コンストラクタにするか条件分岐にするか

レーザーが1種類ではつまらないので2種類作ってみましょう。赤いレーザーを作って、移動スピードの遅いレーザーにしてみましょう。
移動速度が異なるということで、コンストラクタで
appear(new Laser(x,y,$pat_Sample+32,vy));
のようにしてあげれば良いのではないのかと考えた人はコンストラクタをちゃんと理解できた証拠です。しかし、無理にコンストラクタを利用する必要性はどこにもありません。pで条件分岐して、vyをLaserクラスで設定してあげた方がプログラムは短く大して見難くもなさそうです。

Laser.tonyu
extends SpriteChar;

function atariHantei(){
  for( t in $chars ){
    if( (t is Enemy) && !(t is Boss) && crashTo(t) ) t.die();
    if( t is Tama && crashTo(t) ) t.die();
    if( t is myChar && crashTo(t) ) t.die();
  }
}
if(p==$pat_Sample+31) vy=5;  //水色のレーザー
else vy=2;                   //赤色のレーザー
while(y<$screenHeight+100){
  y+=vy;
  atariHantei();
  update();
}

これで、赤いレーザーはのろのろと落ちてくると思います。

Bossを出現させる

最後にオブジェクトを削除して、Bossを出現させることにしましょう。まず、3000点以上だったらBossを出現させUFOをまた右端に寄せておきましょう。音楽も変えておきましょう。

shootingsampleBoss.lzh

UFO.tonyu
extends SpriteChar;

function appearEnemy() {
   if (rnd( 400 )==0) appear(new Item(rnd(530)+15,y,22));
   if (rnd( 1600 )==0) appear(new Item(rnd(530)+15,y,17));
   if (rnd( 50 )==0) appear(new skyBlue(x,y,$pat_Sample+2));
   if (rnd( 100 )==0 && $score>100) appear(new Yellow(x,y,$pat_Sample+8));
   if (rnd( 200 )==0 && $score>200) appear(new Red(x,y,$pat_Sample+11));
   if (rnd( 800 )==0 && $score>300) appear(new Green(x,y+20,$pat_Sample+19));
   if (rnd( 1600 )==0 && $score>400) appear(new Blue(x,y+20,$pat_Sample+22));
   if ($score>1000 && stop==0) stop=1;
   if ($score>3000 && stop==3) {
     appear(new Boss(x+1000,y+1000,$pat_Sample+29));
     stop=2;
     $mplayer.stop();
     $mplayer.play($se_shootingsampleBoss,0,80);
   }
}
$score=0;
vx=2;
stop=0;
while(1) {
  if(! $myChar.isDead()) {
    if(!( (stop==1 || stop==2) && x>$screenWidth - 42 )) x=x+vx;
    else {
      if(stop==1){
        appear(new middleBoss(x,y,$pat_Sample+26));
        stop=2;
      }
    }
    if( stop!=1 && stop!=2 ) {
      appearEnemy();
    }
    if( x<40 || x>$screenWidth - 40 ) vx=-vx;
  }
  update();
}

今回はUFOが右端に退避する前にBossがでてきますが、Bossの出現に時間がかかりますのでそちらの方がむしろゲームをしているひとを飽きさせずに良いでしょう。

これで、Bossは全て終わりです。次は総仕上げとしてオープニングを作成してみましょう。

  トップ