In the previous 2 posts, we went as far as the code to accept arrays for bullet texture/frame, number of cannons and cannonAngleRange parameters. In this instalment of this series, I add the same option to the bulletSpeed parameter.
Introducing another counter variable
In the previous instalment of this series of posts, I talked at length about how the counter used to cycle through array items this.repeatShotsCount actually counts downwards, hence required the elements in arrays to be ordered "backwards". I have thought about it a bit more, and decided it makes like simpler if I introduced a new variable called this.danmakuCounter which counts upwards.
I have retained this.repeatShotsCount some of the danmaku such as "PAINT" and shot types such as "OVERTAKE" are closely tied to this variable and aligning it to this.danmakuCounter would involve a fairly major re-write.
Changed readParam into a function from method
In order to make the readParam method accessible from outside the danmaku object, I have spun it out to a separate function. As such, it now requires the counter to be specifically passed to the function (in the previous version, if unassigned, assumed to be this.repeatShotsCount, which was available within the danmaku object).
function readParam(param, count) {
if (Array.isArray(param)) {
return param[count % param.length]
} else {
return param
}
} // end of readParam function
With this amendment, the rest of the changes are relatively straightforward. Rather than pass the variable this.bulletSpeed to the setUpCannons function, it is passed via function readParam.
default:
// based on the "spokes", set up the relevant arrays for "NORMAL", "OVERTAKE", "SPREAD" shots
setUpCannons({
shotType: this.shotType,
spokesArray: cannonIndex,
originsArray: cannonOrigin,
centre: this.danmakuPosition,
shotsCount: this.shotsCount,
spreadCount: this.spreadCount, // this is to define the number of shots in each "SPREAD" shot
bulletSpeed: readParam(this.bulletSpeed,this.danmakuCounter),
...
And that is essentially it. It should be noted though that such change has not been made to the other cannon classes. In other words, passing arrays in place of bulletSpeed only works for standard Nway (ie does not work with cannon classes DRAW or "PAINT"). The code will break if you pass bulletSpeed arrays as parameter to "DRAW" or "PAINT".
Furthermore, the change made to the setUpCannons function is as follows.
...
switch (shotType) {
case "OVERTAKE":
speedArray.push(new Array(anglesArray.length).fill(bulletSpeed + (shotsCount - counter) * shotAcceleration));
break;
case "SPREAD":
speedArray.push(...new Array(spreadCount).fill(bulletSpeed).map((item,i) => new Array(anglesArray.length).fill(item + shotAcceleration * i)))
break;
default:
// "NORMAL"
speedArray.push(new Array(anglesArray.length).fill(1).map((x,i)=>readParam(bulletSpeed,i)));
}
} // end of setUpCannons
Change has only been made to the "default" bulletClass, namely the "NORMAL" bullet class.
Within the setUpCannons function, the bulletSpeed parameter is only read via the "NORMAL" bullet. ie the code will break if you pass nested bulletSpeed parameter to other bullet classes, such as "OVERTAKE" or "SPREAD".
Replicating the "OVERTAKE" shot
With the above changes made, it is easy to replicate the "OVERTAKE" shot with parameter set such as below.
danmakuPattern.push({
name: "REPLICATE 'OVERTAKE' SHOT WITH SPEEDS ARRAY",
danmakuConfig: {
countA: 5,
angle: 90,
},
cannonConfig: {
numberOfShots: 5, // how many times to shoot the weapon in succession
stopShotsTime: 1000,
fireRate: 100,
},
bulletConfig: {
speed: [80,110,140,170,200],
texture: "roundMID", frame: [1,2,3,4,5]
},
});
The above parameter set achieves exactly the same effect as the following parameter set.
danmakuPattern.push({
name: "5-WAY WITH 'OVERTAKE' SHOTS",
danmakuConfig: {
countA: 5,
},
cannonConfig: {
shotType: "OVERTAKE",
fireRate: 100, // time between bullets in ms
numberOfShots: 5, // how many times to shoot the weapon in succession
stopShotsTime: 1000,
acceleration: 30,
},
bulletConfig: {
speed: 80,
texture: "roundMID", frame: [1,2,3,4,5]
},
Replicating the "SPREAD" shot
The "SPREAD" shot can also be replicated, for example with the below parameter set.
danmakuPattern.push({
name: "REPLICATE 5-WAY 'SPREAD' SHOT WITH SPEEDS ARRAY",
danmakuConfig: {
countA: 5,
angle: 90,
},
cannonConfig: {
numberOfShots: 5, // how many times to shoot the weapon in succession
stopShotsTime: 2000,
fireRate: 0,
},
bulletConfig: {
speed: [200,230,260,290,320],
texture: "roundMID", frame: [1,2,3,4,5]
},
});
The above parameter set achieves the same effect as below, except with the added feature of changing bullet textures, which cannot be achieved with the standard "SPREAD" shot.
danmakuPattern.push({
name: "5-WAY WITH 'SPREAD' SHOT",
danmakuConfig: {
class: "NWAY",
countA: 5, // number of (pair in case of bi-directional danmaku) cannons
angle: 90, // direction in which the primary cannon faces.
// angleRange: 25, // used to n-way type. angle range across which cannons are positioned.
},
cannonConfig: {
shotType: "SPREAD",
spreadCount: 5, acceleration: 30,
numberOfShots: -1, // how many times to shoot the weapon in succession
fireRate: 2000, // time between bullets in ms
},
bulletConfig: {
class: "NORMAL",
speed: 200, // speed of bullets fired from cannon
texture: "roundMID"
}
});
Simulating popular Toho bullet hell patterns
With the above changes, I have tried to simulate one of the most popular bullet hell patterns from the Toho series. In this post, with the amendments made, I have attempted to (partially) mimic the first pattern from EXTRA STATE of 東方風神録 ~ Mountain of Faith, called 神符「水眼の如き美しき源泉」(for information, there are many sites which catalogue the various patterns of the toho game series, including one here). The parameter set used to generate the pattern posted at the top of this post is as follows.
danmakuPattern.push({
name: "21-WAY WITH SPEED VARIETY",
danmakuConfig: {
countA: 21, // number of (pair in case of bi-directional danmaku) cannons
angleLock: true,
target: player
},
cannonConfig: {
numberOfShots: 12, // how many times to shoot the weapon in succession
stopShotsTime: 500,
fireRate: 150, // time between bullets in ms
},
bulletConfig: {
speed: [[200,150,100]],
texture: "oblongMID" , frame: 5
}
});
I use the word "simulate" rather broadly, as my current code is inherently limited in its functionality (in particularly, it can only generate 1 danmaku pattern - whereas pretty much all toho game bullet hell patterns involve running two patterns at the same time and bullet patterns being fired from multiple moving enemies).
And that's it for this post. Here is the CodePen for your perusal and comment.
Comments