top of page
Search
  • cedarcantab

Danmaku using Phaser 3 (lesson 40 Part 2): Number of Cannons and Angle Range as Arrays

Updated: Dec 10, 2021


In the previous post, we went as far as the code to accept arrays for bullet texture/frame. In this post, I continue my exploration into passing arrays as parameters to the danmaku object, specifically the number of cannons and the cannonAngleRange.



Ability to specify number of cannons

Ability to accept array to set the number of cannons is easy, code-wise.

danmakuSpokes({
      danmakuType: this.danmakuType,
      heading: this.referenceAngle,
      vOffset: this.danmakuVOffset,
      hOffset: this.danmakuHOffset,
      cannonsInNway: this.readParam(this.danmakuCountA),
      nwayRange: this.cannonAngleRangeRef,
      numberOfPoints: this.danmakuCountB, // number of cannons in PARALLEL      
      nways: this.danmakuMultiple, totalRange: this.danmakuAngleRange,
      offset: this.danmakuAOffset,
      width: this.danmakuWidth,
      spokesArray: cannonIndex,
      originsArray: cannonOrigin
    });

This functionality on its own is not particularly interesting though. It becomes more interesting when combined with ability to accept arrays for cannonConfig.angleRange. However, this is a little bit more complex since it is already coded to accept numbers as well as objects.


Firstly, the code to "analyse" the parameter that's been passed to the danmaku.setProperties method has been amended as follows. The key change is the addition of the blue highlighted code which, if the parameter is an array, it steps through all the elements converting them to radians. That's all.


    // cannonConfig angleRange parameter
    if (Array.isArray(cannonConfig.angleRange)) {
      this.cannonAngleRangeStart = cannonConfig.angleRange.map(x => Phaser.Math.DegToRad(x));
      this.cannonAngleRangeRef = this.cannonAngleRangeStart[0];
    } else {
      switch (typeof(cannonConfig.angleRange)) {
        case "number":
          this.cannonAngleRangeStart = Phaser.Math.DegToRad(cannonConfig.angleRange);
          this.cannonAngleRangeStep = 0;
          break;
        case "object":
          this.cannonAngleRangeStart = Phaser.Math.DegToRad(cannonConfig.angleRange.start);
          this.cannonAngleRangeStep = Phaser.Math.DegToRad(cannonConfig.angleRange.step);
          this.cannonAngleRangeEnd = Phaser.Math.DegToRad(cannonConfig.angleRange.end);
          this.cannonAngleRangeLock = cannonConfig.angleRange.lock;
          this.cannonAngleRangeYoYo = cannonConfig.angleRange.yoyo;
        break;
        case "undefined": // if undefined -> set to 360 degrees
          this.cannonAngleRangeStart = Math.PI*2;
          this.cannonAngleRangeStep = 0;
          break;
      }  
      this.cannonAngleRangeRef = this.cannonAngleRangeStart;
      if (!this.cannonAngleRangeEnd) {
        this.cannonAngleRangeEnd = this.cannonAngleRangeStep > 0 ? 2 * Math.PI : 0;
      }
    }  

And the code to actually define the cannon angle range within the fireShot function is as follows. The key change is the addition of the red highlighted code, which basically steps through the array depending on this.repeatShotsCount.


  // set up the various arrays to hold the cannon related configurations
  // this switch determines how frequently the angle of the primary cannon is "adjusted", for "LINE" type
  switch (this.danmakuAngleLock) {
    case true:
      if (this.repeatShotsCount === this.shotsCount) {
        this.referenceAngle = this.rotation;
      }
      break;
    default:
      this.referenceAngle = this.rotation;
  }

  if (Array.isArray(this.cannonAngleRangeStart)) {
    this.cannonAngleRangeRef = this.cannonAngleRangeStart[this.repeatShotsCount % this.cannonAngleRangeStart.length]
  } else {
    // this switch determines how frequently the cannons angle range is "adjusted"
    switch (this.cannonAngleRangeLock) {
      case false:
        this.cannonStepThruAngleRange();
        break;
    default:
        if (this.cannonAngleRangeStep !== 0 && this.repeatShotsCount === this.shotsCount) {
          this.cannonStepThruAngleRange();
        };        
    }
  }

I cannot help feeling that there is something enormously inefficient in this code, but for now this will have to do.


With the ability to change both the number of cannons and cannonConfig.angleRange, you can create simple patterns rather like the "PAINT" shotType.


  danmakuPattern.push({
    name: "BLOB",
    danmakuConfig: {
      countA: [2,3,2,1],
      multiple: 10,
    },
    cannonConfig: {
      angleRange: [5,10,5,5],
      numberOfShots: 4,
      stopShotsTime: 1000,
      fireRate: 100,    
    },
    bulletConfig: {
      speed: 80, 
      texture: "roundSML", frame: 5,  
    },
  });    


Be careful about order of the array elements

As you will note from the above parameter set, the elements of the array is in "reverse" order. This is because the counter that the array element selection is based on is the variable this.repeatShotsCount, which in turn is based on the Phaser timer event this.repeatShotsTimer, created in setProperties method, as below.


if (this.cannonClass === "PAINT") {
      this.numberOfShots = this.danmakuPicture.length;
      this.shotsCount = this.numberOfShots - 1;
}
this.repeatShotsCount; // declare variable to count the repeat events timer loop
    
this.repeatShotsConfig = {
      delay: this.timeBetweenShots,
      callback: this.fireShot,
      callbackScope: this,
      repeat: this.shotsCount
};
    
if (this.numberOfShots !== 1) {
  this.repeatShotsTimer = new Phaser.Time.TimerEvent(this.repeatShotsConfig);
}
    

Then this.repeatShotsCount is assigned a repeatCount property of the repeatShotsTimer, in the first line of fireShot method.

  fireShot(scene) {

    if (this.repeatShotsTimer) {
      this.repeatShotsCount = this.repeatShotsTimer.repeatCount 
    }

According to the Phaser documentation, it says that repeatCount is "If repeating this contains the current repeat count", which is not very clear but the method to get this property is described as "Returns the number of times this Timer Event will repeat before finishing", hence it is counter that counts downwards to zero. In the case of inifinite loops, it seems to count down from 999,999,999,999.


Anyway, by combing all of the above, you can get quite striking patterns, such as one pictured at the top of this post, which was created with the parameter set below.

  danmakuPattern.push({
    name: "6 & 5-WAY",
    danmakuConfig: {
      countA: [6,5],
    },
    cannonConfig: {
      angleRange: [40,32], 
      fireRate: 150,    
    },
    bulletConfig: {
      speed: 80, 
      texture: "roundMID",
      frame: [0,1],  
    },
  });
    

This series of posts is not quite finished yet, but here is the CodePen with modifications explained to date.




記事: Blog2_Post
bottom of page