Until now, bullets were always shot from the centre of the danmaku. What if we could specify where the bullets were fired from? That was the main trigger for the topic of this post - group of bullets that move in parallel.
Creating an array of points to shoot from
In the existing code, we have an array to hold the direction in which the bullets should be fired (this.cannonAngles), and we also have an array to hold the speed with which the bullets should fly (usually the same, but different in some cases such as SPREAD shot), which is held in this.cannonShotSpeeds. Now we need an array to hold the position from where to shoot bullets. this.cannonPositions.
For all the danmaku types to date, the bullets always got fired from the centre of the danmaku (ie this.danmakuPosition). However, we now need to calculate a "line" of positions to either side of the danmaku object. That would be fairly straightforward if, say the "line" was always horizontal or vertical. However, we need to take account of the cannon angle.
First the easy bit. Within the switch statement of setUpCannons() function, we include the following.
case "PARALLEL":
this.parallelCannons(
this.danmakuSize,
this.referenceAngle,
this.danmakuPosition,
this.danmakuCount,
this.danmakuRandom,
this.cannonAngles,
this.cannonPositions
);
this.cannonShotSpeeds = this.shotSpeedArray(this.cannonShotType);
break;
this.paralleCannons function creates the array of "points" from which the bullets should be fired. We use danmakuType.size as the breadth of the parallel line of cannons. I have also added a paramter called this.danmakuRandom, to give the bullets a bit of randomness, if it is so wished.
Creating a line of points at a particular angle, crossing a particular point would normally involve a fair bit of trigonometry, but Phaser 3 makes this relatively painless.
parallelCannons(length, angle, offset, numberOfPoints, randomFactor, anglesArray, pointsArray) {
const aa = new Phaser.Math.Vector2().setToPolar(angle + Math.PI/2, length/2).add(offset);
const bb = new Phaser.Math.Vector2().setToPolar(angle - Math.PI/2, length /2).add(offset);
for (let i = 0; i < numberOfPoints; i++) {
const random = Phaser.Math.Between(-randomFactor/2, randomFactor/2) / length;
const point = new Phaser.Math.Vector2().copy(aa)
pointsArray.push(point.lerp(bb, (i+random)/(numberOfPoints - 1)));
anglesArray.push(angle);
}
} // end of parallelCannons function
First I create the "end points" of the line as Phaser 2D vector objects (blue lines). Then I cycle through required number of times, "lerp"ing from one end (points aa) of the line to the other end (point bb), to create equally spaced points along the line (red line - ignore the random code).
Using Geom.Line object
I can actually achieve the same effect by utilising Phaser's Geom.Line object and Phaser's getPoints method.
const aa = new Phaser.Math.Vector2().setToPolar(angle + Math.PI/2, length/2).add(offset);
const bb = new Phaser.Math.Vector2().setToPolar(angle - Math.PI/2, length /2).add(offset);
const line = new Phaser.Geom.Line(aa.x,aa.y,bb.x,bb.y);
for (i=0; i < numberOfPoints; i++) {
pointsArray.push(line.getPoint(i/(numberOfPoints - 1)));
anglesArray.push(angle);
}
The logic is the same - effectively, getPoint is lerping across the line from one end to the other end.
There is a getPoints method, which creates n points along a line in one go - however, this method misses one end of the line, so is not quite suitable for our purposes.
Using Curves.Line object
And I can also achieve the same result by utilising Phaser's Curves.Line object, as below.
const aa = new Phaser.Math.Vector2().setToPolar(angle + Math.PI/2, length/2).add(offset);
const bb = new Phaser.Math.Vector2().setToPolar(angle - Math.PI/2, length /2).add(offset);
const line = new Phaser.Curves.Line(aa,bb);
for (i=0; i < numberOfPoints; i++) {
pointsArray.push(line.getPoint(i/(numberOfPoints - 1)));
anglesArray.push(angle);
}
Anyway, that's the meat of the code.
We can combine parallel danmakuType with differing bullet types. For example, the following parameters creates a intermittent parallel firing danmaku with waving bullets that seek.
danmakuPattern.push({
name: "RIGHT ROTATING INTERMITTENT PARALLEL WAVING BULLETS",
danmakuType: {
class: "PARALLEL", size: 50
},
numberOfCannons: 3,
cannonAngle: 90,
cannonTarget: player,
cannonAngleLock: true,
numberOfShots: 5,
stopShotsTime: 1000,
bulletType: {
class: "WAVING", cycleLength: 600,
speed: 100, fireRate: 250,
texture: "ribbonMID", frame: 0
},
});
As always, here's the CodePen for your perusal and comment.
Comments