top of page
Search
  • cedarcantab

Danmaku in Phaser 3 (step 7): Bi-Directional Spiral

Updated: Oct 8, 2021

In lesson 2 we created a sprial danmaku which spit out a single stream of bullets which rotated. You could change the direction of rotation by changing the angular velocity parameter of the danmaku. In lesson 6 we created a version which spit out multiple stream of bullets which rotated. All the stream of bullets rotated in the same direction, at the same speed. What if we had clockwise rotating stream of bullets and anti-clock wise rotating bullets at the same time? It is difficult to imagine what this will look like, it is easier to write the code and see how it looks. I have built on the multiple-spiral danmaku code from lesson 6.


Bi-Directional Spiral

In essence, all I have done is to include a second bullet firing code with the bullet angle in the opposite direction to the first bullet, simply by inverting the sign of the direction of the first bullet. It is easier to look at the code, than to explain.


As you can see, all I have done is repeated the bullet fire code except for the formula for calculating the bullet angle. It's crude and I am sure there are much more elegant ways of achieving the same effect. However it gets the job done.

fireweapon() {
    for (let i = 0; i < this.numberOfCannons; i++) {
      const bullet1 = bullets.getFirstDead(false);

      if (bullet1) {
        const direction1 = i * (360 / this.numberOfCannons) + this.angle ;
        bullet1.fire({
          x: this.danmakuPosX, // the bullet is fired from the middle of the Danmaku class (ie the spawner)
          y: this.danmakuPosY, // the bullet is fired from the middle of the Danmaku class (ie the spawner)

          shootAngle: direction1, // direction in which the bullet is fired
          bulletSpeed: this.bulletSpeed, // the speed of the bullet
          bulletAcceleration: this.bulletAcceleration,
          bulletImageAngle: direction1, // make the bullet face the direction in which it is fired
          bulletImageAngleVelocity: this.bulletImageAngleVelocity,
          bulletType: this.bulletType
        }); // end of bullet fire method
      } // end of if statement which checks whether bullet is null

      const bullet2 = bullets.getFirstDead(false);

      if (bullet2) {
        const direction2 = - i * (360 / this.numberOfCannons) - this.angle;
        bullet2.fire({
          x: this.danmakuPosX, // the bullet is fired from the middle of the Danmaku class (ie the spawner)
          y: this.danmakuPosY, // the bullet is fired from the middle of the Danmaku class (ie the spawner)

          shootAngle: direction2, // direction in which the bullet is fired
          bulletSpeed: this.bulletSpeed, // the speed of the bullet
          bulletAcceleration: this.bulletAcceleration,
          bulletImageAngle: direction2, // make the bullet face the direction in which it is fired
          bulletImageAngleVelocity: this.bulletAngleVelocity,
          bulletType: this.bulletType
        }); // end of bullet fire method
      } // end of if statement which checks whether bullet is null
     
    } // end of for loop
  } // end of fireweapon method

Apart from the usual changing of bullet image, that's pretty much all the important changes made to the code.


Cosmetically, I have changed the names of some of the variables (again) so it is easier for myself to understand what I am doing; as a general trend the names are getting longer and longer, but hopefully that is for the better.


I did also move the declaration of all the danmaku parameters to the BasicDanmaku class to the extended class. This means that, depending on the type of danmaku, the Basic Danmaku class will end up declaring a lot of redundant variables but it means less typing of the same variable names in the extended DanmakuWrapper class, and hence less risk of error.


There is now quite a lot of parameters to play with. The screenshot danmaku pattern was generated using the below configuration.

danmaku = new DanmakuWrapper(this, {
    x: enemy.x, // origin of the danmaku spawn
    y: enemy.y, // origin of the danmaku spawn
    shootAngle: -90, // direction in which the cannon faces (in the beginning), ie direction of (primary) bullet.
    angleVelocity: 90, // angular velocity of the 'cannon' that fires the bullets. how many degrees cannon turns in 1 second
    numberOfCannons: 4, // number of (pair in case of bi-directional danmaku) cannons
    bulletSpeed: 200, // speed of bullets fired from cannon
    bulletAcceleration: 0, // acceleration of bullets fired from cannon
    timeBetweenBullets: 100, // time between bullets in ms
    bulletType: "bullet6" // specify the bullet image to be used
  });

Changing just one of the variables can have quite dramatic effect on the generated pattern. For example, if you change the bulletAcceleration to -60 (i.e. the bullets decelerate after getting fired, then reverse direction near the edge of the screen), the effect is quite hypnotic!


Thoughts and reminders to myself for the future

In the code above, the "pair" of bullet streams rotate at the same speed (since they are both based on the same 'angle' property of the danmaku object); hence the pattern of bullets is quite predictable. The pattern becomes considerably more complex if the rotation speeds of the each stream are different. This could potentially be achieved by using 2 "tweens" to set the direction of the individual streams separately. For example, the following 2 tweens would automatically "change" variables this.angleTween1.getValue() and this.angleTween2.getValue().

this.angleTween1 = this.scene.tweens.addCounter({
      from: 0,
      to: 360,
      duration: 1000,
      repeat: -1,
    });
    
this.angleTween2 = this.scene.tweens.addCounter({
      from: 360,
      to: 0,
      duration: 500,
      repeat: -1,
    });

But actually implementing this will be for a future project.


For now, we are already standing at the edge of bullet hell heaven!


As usual, here is the Codepen Pen for your perusal.



記事: Blog2_Post
bottom of page