top of page
Search
  • cedarcantab

Danmaku using Phaser 3 (lesson 27 part 2): Stop & Go Bullets

Updated: Nov 26, 2021

In the previous post I started to write about my attempt to code bullets that moves for a specific period, then stops for a prescribed period, then starts moving again. However, never even got to start the code as I worked on other "preparatory" code.


In this post I will definitely get around to writing the main meat of the code.



As a starter, we write code to make bullets that start moving after a certain fixed period, which I call "DELAY" bullets.


Bullets start starts moving only after a prescribed prescribed period

As with other bullets that "change" behaviour during its life, I will use bulletTransform paramter, and call it "DELAY".


But why do I need such a bullet in the first place? My plan is that if you can create a bullet which starts moving after a predetermined time period, then this can be combined with another bulletTransform to have the bullet move for a predetermined time, then transform itself into a Delay bullet, thereby creating a stop & go bullet. I could jump right into coding a stop & go bulletType, but I figure this will give me more flexibility when I add-on more functionality in future.


Getting back to the business at hand, in principle, what I need to do is to set the velocity of the bullet to zero when first fired, and after the prescribed period defined by the bulletTransform property, set the velocity to whatever it should be. Since setting velocity of bullet will now be called from various places, we will factor it into a separate function called setMotion.


setMotion(bullet, bearing, speed, acceleration) {

    // when first fired, set the velocity (vector) of the bullet in accordance with the speed (bulletSpeed) and direction (bulletBearing)
    this.scene.physics.velocityFromRotation(
      bearing,
      speed,
      bullet.body.velocity
    );
    // when first fired, set the acceleration vector of the bullet in accordance with the acceleration passed as parameter and direction (bulletBearing)
    this.scene.physics.velocityFromRotation(
      bearing,
      acceleration,
      bullet.body.acceleration
    );

We put in the following code at the beginning of the bullet.fire method, so that the velocity is set to zero for DELAY bullets.

// unless the bullet is a DELAY type, set the bullet moving
    switch (this.bulletTransformType) {
      case "DELAY":
        this.setVelocity(0);
        this.setAcceleration(0);
        break;
      default:
        this.setMotion(this, this.bulletBearing, this.bulletSpeed, this.bulletAcceleration);
        break;
    }

And in the preUpdate function of the bullet object, we add the following, alongside the EXPLODE type. I have put in a check to this.transformed (which is set to false in the beginning, but set to true as soon as the requisite time has been reached), so that we don't have the DELAY code executed repeatedly after the bullet starts moving. The key code is the line highlighted in blue. This sets the bullet moving, when the time is right.


if (!this.transformed && this.timeSinceFired > this.bulletStage1Time) {
      this.transformed = true;
      
      switch (this.bulletTransformType) {
        case "DELAY":
          this.setTexture(this.newBulletTexture, this.newBulletFrame);
          if (this.newBulletSeek) {
            //this returns the angle between 2 points in radians
            this.bulletBearing = Phaser.Math.Angle.Between(this.x,this.y,this.bulletTarget.x, this.bulletTarget.y);
            this.setRotation(this.bulletBearing);
          }
          this.setMotion(this, this.bulletBearing, this.bulletSpeed, this.bulletAcceleration);
        break;

You will also notice in red, code which checks a new variable called this.newBulletSeek. I have included a game object to be passed as a property of the transformBullet parameter object. If this is set, then the "DELAY" bullet, when it starts moving will set its bearing in the direction of the object. It's not a homing bullet; it simply resets the bearing when it starts moving. To make the distinction with the "seek" feature that resets the direction of the danmaku cannon (which would automatically be reflected in the bullet bearing), I have created a new variable called cannonTarget.

Now onto the important bit: "Stop & Go"!

As mentioned above, the plan is that a "stop & go" bullet would be fired normally, but then after a prescribed period, fire a "delay" bullet, and expire itself. We first add the following to the preUpdate function, along side the "DELAY" and "EXPLODE" case statements.


case "STOP&GO":
          this.stopAndGo(this);
          this.switchOff();
          break;

And create the critical stopAndGo function as follows. Literally, the function creates all the parameters necessary to fire a "DELAY" bullet and then calls the bullet.fire method, with the new parameter set.


stopAndGo(scene) {
   
    this.bulletConfig = {
      x: this.x, // the bullet is fired from the middle of the Danmaku class (ie the spawner)
      y: this.y, // the bullet is fired from the middle of the Danmaku class (ie the spawner)
      bulletType: "NORMAL",
      bulletBearing: this.newBulletBearing, // direction in which the bullet is fired
      bulletBearingVelocity: this.newBulletBearingVelocity,
      bulletAngle: this.newBulletBearing, // make the bullet face the direction in which it is fired
      bulletAngularVelocity: this.newBulletAngularVelocity,
      bulletCycleLength: this.newBulletCycleLength,
      bulletSpeed: this.newBulletSpeed, // the speed of the bullet
      bulletTexture: this.bulletTexture, // exiting texture
      bulletFrame: this.bulletFrame,
      bulletTarget: this.newBulletTarget,
      bulletBounceX: this.newBulletBounceX,
      bulletBounceY: this.newBulletBounceY,
      bulletLife: this.newBulletLife,
      bulletTransform: {
         type: "DELAY",
         stage1Time: this.bulletStage2Time,
         seek: this.newBulletSeek,
         texture: this.newBulletTexture, // the new image is when the bullet starts moving
         frame: this.newBulletFrame, // the new image is when the bullet starts moving
      }
    }
  
    const bullet = enemyBullets.getFirstDead(false);
    if (bullet) {       
       bullet.fire(scene, this.bulletConfig); // end of bullet fire method
     } // end of if statement which checks whether bullet is null
  }

I have made other changes to the code, but the above is the meat of the code to create "stop & go" bullets.


I have tried to write the code so that the "stop & go" feature can "co-exist" with much of the functionalities I have coded to date. For example, the danmaku pictured at the beginning of the previous post (lesson 27 part1) is a rotating 3-way with stop & go bullets that seek the player. It was created with the following parameter set.

danmakuPattern.push({
    name: "LEFT ROTATING 3-WAY WITH STOP&GO BULLETS, SEEK",
    danmakuType: { class: "NWAY" },
    numberOfCannons: 3, // number of (pair in case of bi-directional danmaku) cannons
    cannonAngle: 90, // direction in which the primary cannon faces.
    cannonAngularVelocity: -30,
    cannonAngleRange: {start: 360},
    numberOfShots: -1, // how many times to shoot the weapon in succession
    bulletType: {
      class: "NORMAL",
      speed: 80, fireRate: 500,
      texture: "roundBIG", frame: 1,
    }, 
    bulletTransform: {
      type: "STOP&GO",
      stage1Time: 1000,
      stage2Time: 3500,
      bearing: 90,
   //   bearingVelocity: 0,
      speed: 200,
      seek: true, target: player,
      texture: "knife",
      frame: 0,
      
    },
  });

You can combined "polygon" danmaku with stop & go bullets that seek & bounce to create the danmaku at the top of this post.

danmakuPattern.push({
    name: "RIGHT ROTATING TRIANGLE WITH STOP&GO BOUNCING BULLETS, SEEK",
    danmakuType: { class: "POLYGON", count: 3 },
    numberOfCannons: 12,
    cannonAngle: 90,
    cannonAngularVelocity: -5,
    numberOfShots: -1,
    bulletType: {
      class: "NORMAL" ,
      speed: 80, fireRate: 3500,
      texture: "roundBIG", frame: 6,
     
    },
    bulletTransform: {
      type: "STOP&GO",
      stage1Time: 1500,
      stage2Time: 2500,
      bearing: 90,
      speed: 200,
      seek: true, target: player,// if this is set to true, always need target to be set in bulletType
      bounceY: 1,
      bounceX: 1,
      texture: "roundBIG",
      frame: 0,
      lifeSpan: 10000,
       
    },
  });

And that's it for this post. In the next post I will include a few more example danmaku that I created with this new feature, and publish the CodePen.



記事: Blog2_Post
bottom of page