top of page
Search
cedarcantab

Danmaku using Phaser 3 (lesson 24): Revisiting NWAY

NWAY's form the basis of so many of the danmaku patterns. We built on the standard NWAY to create a multiple NWAY in lesson14. However, in the existing version of the code, the "individual" nway's are equally spaced around a complete circle. How about if we can determine the angle range across which the nway's are space, so that for example, 3 nways are pointing all towards the player?


And while we are at it, how about if we could adjust the direction of the individuals arms of an nway?


Those are the objectives of this post.


Multi-Nway KAI!

Let's start with the easier one; the ability to specify how the individual nways are spaced out around the primary cannon angle.


To do this, we create a new variable called NWAYAngleRange. Default is 360 which would result in the same effect as the existing code. Once we know how "wide" to space out the nways, then the rest is relatively straight forward. We adjust the code which calculates the angle of the individual nways for "MULTI_NWAY" danmakucalss, to take into account the NWAYAngleRange, as opposed to the hardcoded 360. It is not quite as straighforward as replacing 360 with NWAYAngleRange, the code is a bit different between 360 degrees and less than 360 degreees. The principle is the same as the code already created to calculate the spacing angles for individual NWAY.


case "MULTI_NWAY":
        switch (this.NWAYAngleRange) {
          case 360: // if the multiple NWAYs are spaced equally around a circle
            for (let j = 0; j < this.numberOfNWAY; j++) {
              let nwaycentre = this.referenceAngle + j * (360 / this.numberOfNWAY);
              for (let i = 0; i < this.numberOfCannons; i++) {
                this.cannonAngles.push(nwaycentre + (i / (this.numberOfCannons - 1) - 0.5) * this.cannonAngleRangeRef);
              }
              speedsArray = speedsArray.concat(this.shotSpeedArray(this.cannonShotType)[0]);
            } // end of numberOfNWAY for-loop
            this.cannonShotSpeeds.push(speedsArray);
            break;

          default:
            // the multiple NWAYs are to be space at less than 360

            for (let j = 0; j < this.numberOfNWAY; j++) {
              let nwaycentre = this.referenceAngle + this.NWAYAngleRange * (0.5 - j / (this.numberOfNWAY - 1));
              for (let i = 0; i < this.numberOfCannons; i++) {
                this.cannonAngles.push(nwaycentre + (i / (this.numberOfCannons - 1) - 0.5) * this.cannonAngleRangeRef);
              }
              speedsArray = speedsArray.concat(this.shotSpeedArray(this.cannonShotType)[0]);
            } // end of numberOfNWAY for-loop

            this.cannonShotSpeeds.push(speedsArray);
        } // end of the switch statement to determine if MULTINWAY is a circle or not

        break;

And that's it. Now onto the much more difficult part, which is to adjust the angle of the "arms" of an NWAY.


Step change in the cannonAngleRange

In the existing code, we can specify how wide the individual arms of an NWAY are spaced (centered around the primary cannon), by using the cannonAngleRange parameter, which is currently expected to remain constant.


We need a way of telling the danmaku object to change this variable, like increment by 20 degrees every so often. We will change the cannonAngleRange parameter into an javascript object to hold this information. To hold the actual cannon angle range (which will change according to cannonAngleRange object) we will create a new variable called cannonAngleRangeRef.


For example, setting the cannonAngleRange parameter to below would instruct the danmaku to start with an Nway spaced out across 150 degrees, and that angle range will shrink by 30 degrees until the range reaches 10 degrees, then return to the beginning.

cannonAngleRange: { start: 150, step: -30, end: 10 }

Then the following new function called cannonAngleRangeStep() is created in order to "step" the cannon angle range (ie cannonAngleRangeRef)


cannonAngleRangeStep() {
    
     this.cannonAngleRangeRef += this.cannonAngleRange.step;
      switch (Math.sign(this.cannonAngleRange.step)) {
        case 1:
          if (
            this.cannonAngleRangeRef > Math.min(360, this.cannonAngleRange.end)
          ) {
            this.cannonAngleRangeRef = this.cannonAngleRange.start;
          }
          break;
        default:
          if (
            this.cannonAngleRangeRef < Math.max(0, this.cannonAngleRange.end)
          ) {
            this.cannonAngleRangeRef = this.cannonAngleRange.start;
          }
      }
  }

And this function is called at the beginning of each repeatShotFire timer event, using the below code:

if (this.cannonAngleRange.step !== 0 && this.repeatShotsTimer.repeatCount === this.numberOfShots)
    {
     this.cannonAngleRangeStep();
    }

The somewhat convoluted part of the cannonAngleRangeStep() function is the switch statement to determine the appropriate end of the angle range step sequence, in combination with situation where the end of the range is not specified. If the step angle is positive (ie the cannon range increases) the end of the sequence would be the lower of 360 and the specified end. Otherwise, it is the higher of 0 and the specified end range, if that makes sense.


Testing the code

The below parameters are used to create the danmaku pictured at the top of this post.

danmakuPattern.push({
    name: "3 x 2-WAY INTERMITTENT",
    danmakuType: "MULTI_NWAY",
    numberOfCannons: 2, // number of (pair in case of bi-directional danmaku) cannons
    cannonAngle: 90, // direction in which the primary cannon faces.
    //    cannonAngularVelocity: 30,
    cannonAngleRange: { start: 15 }, // used to n-way type. angle range across which cannons are positioned.
    numberOfNWAY: 3,
    NWAYAngleRange: 150,
    numberOfShots: 5, // how many times to shoot the weapon in succession
    stopShotsTime: 1000,
    // properties for individual bullets
    bulletType: "NORMAL",
    bulletSpeed: 200, // speed of bullets fired from cannon

    timeBetweenShots: 100, // time between bullets in ms
    bulletTexture: "roundMID", // specify the bullet image to be used,
    bulletFrame: 1
  });

Of course, you can set the angularVelocity to generate rotating versions.


The following parameters are used to create the danmaku pictured below.


danmakuPattern.push({
    name: "INTERMITTENT 2-WAY LINE ANGLE STEP",
    danmakuType: "NWAY",
    numberOfCannons: 2, 
    cannonAngle: 90, // direction in which the primary cannon faces.
    cannonAngleRange: { start: 150, step: -30, end: 10 }, // used to n-way type. angle range across which cannons are positioned.
//    cannonAngularVelocity: 45,
    numberOfShots: 8, // how many times to shoot the weapon in succession
    stopShotsTime: 100,
    // properties for individual bullets
    bulletType: "NORMAL",
    bulletSpeed: 80, // speed of bullets fired from cannon
    timeBetweenShots: 200, // time between bullets in ms
    bulletTexture: "skinny",
    bulletFrame: 0,
    cannonAngleLock: true 
  });

Again, you can set an angularVelocity to make the 2-way rotate, while the angle range shrinks. Or you can set the cannonAngleLock to false, which, in combination with a positive angular velocity would create a strange 2-way rotating spiral, where the arms change direction.



I have included various example configurations in the CodePen, for your perusal and comment.



Comments


記事: Blog2_Post
bottom of page