Site Logo


Sparen's Danmakufu ph3 Tutorials Lesson 11 - Using Trigonometry and Parametrics

The video for this lesson is Ultima's Sanae. Sanae Stars have a variety of spawn methods, but all require knowledge of Danmakufu's coordinate system and how to manipulate it.

Part 1: What will be Covered in this Lesson?

In this lesson, I will be explaining Danmakufu's coordinate system in greater detail, and will explain how you can use trigonometry and parametric equations (based off of the angle theta, primarily) to create bullets at points relative to an object such as a boss or relative to coordinates. This lesson will supplement existing tutorials, such as my Spawning Danmaku in Circles and Ellipses tutorial.

The main focus of this lesson will be on utilizing trigonometry in Danmakufu and using it to create simple shapes such as circles and ellipses. There is a secondary focus on the general coordinate system, and I will also be covering basic Rotation Matrices (in the future) and Parametric Equations. As much as I would love to cover Polar Equations, it's not meant for this tutorial and if you would like to implement those, you can find information on Polar Equations in Danmakufu in my old tutorials for 0.12m.

Part 2: How Does Touhou Danmakufu ph3's Coordinate System Work?

As stated back in Lesson 6, Danmakufu's coordinate system is not the same as taught in most mathematics courses. Since the origin (for render priorities between 0.20 and 0.80) is in the top left corner, the x direction increases as you move to the right but the y direction increases as you move downwards. Using the same system, angles increase from 0 to 360 starting directly right and increasing in a clockwise direction so that 90 degrees is directly down, 180 degrees is directly left, and 270 degrees is straight up.

For this lesson, we will need to take all three factors into consideration when we deal with angles and coordinate systems.

As shown in previous lessons, we can create a bullet 10 pixels to the left of the boss and 5 pixels above the boss by setting the x parameter of CreateShot to ObjMove_GetX(objBoss)-10 and by setting the y parameter to ObjMove_GetY(objBoss)-5. We will use this in the next part.

Part 3: How Can I Use Trigonometry to Create a Ring of Bullets?

Now we will aim to write a script that will spawn bullets in a circle of radius 90 around the boss, and another circle of radius 60 around the player. We saw this earlier in Lesson 9, when I described a method of creating Non-Directional Lasers.

To begin, we will need to understand the Unit Circle, as well as sine and cosine.

Unit Circle

Above is an image of the unit circle - a circle of radius (L in diagram) 1. All of the angles are the angles used in Danmakufu.

To understand how we will be using the unit circle in Danmakufu, we will look at the angle theta (A in diagram), X, and Y, where X and Y are the X and Y components of radius L.

In trigonometry involving right angles, sine refers to the opposite side of the angle divided by the hypotenuse, while cosine refers to the adjacent side of the angle divided by the hypotenuse. Therefore, X = L*cos(A) and Y = L*sin(A) as shown in the diagram. These are the X and Y components of the radius 1 for a given angle A.

Our first goal is to create bullets in a ring of radius 90 around the boss. To accomplish this, we will let our radius be 90, relative to the Boss's current location.

So let's say we want our ring to have 30 bullets in it, since it's going to be pretty far from the boss. Now, we know that we will use a loop like the following:

    let angleT = 0;
    loop(30){
        //Create bullet
        angleT += 360/30;
    }

The question now is how we will spawn the bullets. We want the bullets to be spawned exactly in a circle, and we can't manually set the x and y coordinates to do that. We will therefore use sine and cosine.

Danmakufu's coordinate systems basically use parametric equations, where the x, y, and z (if using render objects) coordinates are defined separately. As we loop through the code above, we want x and y to be written in terms of angleT, with is our theta (A in the diagram). Let's take the rightmost point, spawned when angleT = 0. We want it to be located at (90, 0) with respect to the boss.

Sine and Cosine Waves

Above I have the graphs of sine and cosine. Our angle is 0, which is smack in the middle of the diagram. At this point, cos(x) is 1 and sin(x) is 0. So how can we use this?

Well, we want our bullet at (90, 0), right? And the y coordinate is 0, just as sin(0) = 0. And our x coordinate is 90, when cos(0) is 1. Therefore, our code becomes the following:

    let angleT = 0;
    loop(30){
        CreateShotA1(ObjMove_GetX(objBoss) + 90*cos(angleT), ObjMove_GetY(objBoss) + 90*sin(angleT), 2, angleT, 1, 0);
        angleT += 360/30;
    }

This will work correctly for all points in the circle. What the 90* does is basically scale the graph. Therefore, we essentially scale the unit circle so that instead of having a radius of 1, we have a radius of 90. And in the above graph, we scale the vertical axis to go from 90 to -90.

The end result is a ring of bullets of radius 90 centered around the boss.

How about the player? Well, the only real difference is that we will multiply by 60 instead of 90, and will change the base coordinate (currently (ObjMove_GetX(objBoss), ObjMove_GetY(objBoss)) to (GetPlayerX(), GetPlayerY())

And with that, we have made what we planned to make! To see a gif representation of what we are doing, click here.

CHECKPOINT: How do sine and cosine change as theta goes from 0 to 360? Using the unit circle, how does this translate into the circle of bullets?
EXERCISE: Experiment with the provided code. Create rings of bullets, or even rings of rings of bullets. You can also do rings of rings of rings of bullets - if you can pull this off, you are all set!

Part 4: How Can I Use Trigonometry to Create an Ellipse of Bullets?

An ellipse, as static as generically as possible, is a circle stretched in a direction. Technically, it is a conic where the sum of the distances from two foci on the major axis is always equal, but that gets lost when you make ellipses using parametric equations because you don't need to know about the foci to create the ellipse. If you really want to know, here's a link to a good gif.

Now that that's out of the way, we can move on to what we actually want to do!

Let's say that we want to have an ellipse where half the length of the major axis (we will use the x axis) is 75, and half the length of the minor axis (we will use the y axis) is 30. We will begin by examining our code from earlier, where we created a circle.

    let angleT = 0;
    loop(30){
        CreateShotA1(ObjMove_GetX(objBoss) + 90*cos(angleT), ObjMove_GetY(objBoss) + 90*sin(angleT), 2, angleT, 1, 0);
        angleT += 360/30;
    }

We can think of this as follows: In the X direction, the maximum distance you can go is 90, and the same goes for the Y axis. Now we want 75 and 30 instead of 90 and 90.

And yes, it's not that complicated.

    let angleT = 0;
    loop(30){
        CreateShotA1(ObjMove_GetX(objBoss) + 75*cos(angleT), ObjMove_GetY(objBoss) + 30*sin(angleT), 2, angleT, 1, 0);
        angleT += 360/30;
    }

Hooray! We now have an ellipse in Danmakufu! It works the same way as with the circle - it's just that the bullets will be more bunched up towards the sides and more spread out at the top. You can bend and contort this ellipse as much as you want, and you can do lots of stretching in the x and y directions. However, if you want to rotate it... well... you're getting into some pretty ugly territory now - specifically, you will need to use rotation matrices. Since I am not yet able to use them effectively myself and therefore cannot write a tutorial for them, I encourage you to study them if you ever decide that you want to use rotated ellipses or other rotated graphs.

Quiz: Circles and Ellipses in Danmakufu ph3

1) As part of her daily routine, Suwako is retaliating against Cirno using Mishaguji-sama. To do this, she must employ circles. Which of the following will spawn a circle?

A. ascent(i in 0..36){CreateShotA1(ObjMove_GetX(objBoss), ObjMove_GetY(objBoss), 1, i * 10, 5, 5);}
B. ascent(i in 0..36){CreateShotA1(ObjMove_GetX(objBoss) + 30*cos(i * 10), ObjMove_GetY(objBoss) + 30*sin(i * 10), 1, i * 10, 5, 5);}
C. ascent(i in 0..36){CreateShotA1(ObjMove_GetX(objBoss) + 30*cos(i * 10), ObjMove_GetY(objBoss) + 30*cos(i * 10), 1, i * 10, 5, 5);}
D. ascent(i in 0..36){CreateShotA1(ObjMove_GetX(objBoss) + 30*cos(i * 10), ObjMove_GetY(objBoss) + 45*sin(i * 10), 1, i * 10, 5, 5);}

2) We create an ellipse with the following code. What is the farthest distance from the boss that bullets will spawn?

    ascent(i in 0..30){
	CreateShotA1(ObjMove_GetX(objBoss) + 30*cos(i * 12), ObjMove_GetY(objBoss) + 75*sin(i * 12), 1, i * 10, 5, 5);
    }
A. 75 pixels
B. 30 pixels
C. 80.8 pixels

Part 5: What are Parametric Equations?

Sometimes you can't describe things in math with simple coordinates. And so, you express them in terms of other systems, such as polar coordinates. These are parameterizations, and although the usage of parameterized functions, curves, and surfaces have a significant application in mathematics, we will stick with simple 2D curves. Specifically, we will talk about parameterizing in terms of a single variable t (or the angle theta, for basically everything).

Let's take Danmakufu's coordinate system for Move Objects. You can set the x and y coordinates of an object like so:

    ObjMove_SetPosition(obj, 384 / 2, 448 / 2); //place object at center of screen

So, what does this have to do with parametric equations? Aren't we going to spawn ridiculous shapes with lots of curves and stuff?

Well, simply put, we can take the function's second and third (x and y) arguments and display them as two functions:

\(x = 384/2 + 0t\)

\(y = 448/2 + 0t\)

If you believe the +0t is redundant, that is because it is - we can write the equations without them, but I am putting them here to show that this is actually a parametric equation. In fact, since Danmakufu separates X, Y, and Z coordinates, you can basically plug in any parametric equation and it will work. Let's start by examining the following:

    let angleT = 0;
    loop(30){
        CreateShotA1(ObjMove_GetX(objBoss) + 75*cos(angleT), ObjMove_GetY(objBoss) + 30*sin(angleT), 2, angleT, 1, 0);
        angleT += 360/30;
    }

This is the ellipse we spawned at the end of Part 4 earlier in this lesson. Now, if we treat the x and y parameters to the function as parametric equations... We get this:

\(x = 75\times cos(t)\)

\(y = 30\times sin(t)\)

Note that I am treating the boss's position as the origin for the purposes of these equations. Above is the parametric equation for an ellipse.

So how exactly does it work? Well, we have our angleT variable, which we initialized to 0 at the start of the code. We then looped through, changing its value. As we changed the parameter angleT, the values of x and y in both equations changed, resulting in bullet placement that formed an ellipse. This is actually quite powerful, because you can use virtually any 2D parametric equation in Danmakufu by simply plugging the equations into a function.

Part 6: How Can I Use Parametric Equations in Danmakufu?

Let's take some ridiculous equation and spawn it in Danmakufu.

    task ParametricRing(){
        let angleT = rand(0, 360);
        loop(120){
            loop(3){
                let obj = CreateShotA2(GetEnemyX(objBoss) + 120*cos(angleT * 3), GetEnemyY(objBoss) + 90*sin(angleT * 5), 0, angleT * 3, 0.001, 2, 25, 10);
                angleT += 360/3;
            }
            angleT += 7;
            yield;
        }
    }

This is code from my Veemon midboss in Digital Earth: Conquest, where he spawns a wall of danmaku. For the sake of this tutorial, I have stripped all of the non-essential code from the above task.

If we look at this code, we first declare a variable angleT and assign a random angle to it. Then we loop 120 times. Inside the outer loop, we have an inner loop and some bullet spawn code that creates what would normally be a ring of three bullets if we were creating a circle. But instead of a circle, we have some strange equation with angleT*5 and the like. What could this be? Well, there's a name for it but we won't get into that. Below are the parametric equations that create this thing.

\(x = 120\times cos(3\times t)\)

\(y = 90\times sin(5\times t)\)

As you can see, we can take a complicated curve and make Danmakufu act like a really inefficient graphing calculator.

Danmakanvas is a Javascript Danmaku simulation made by Sparen. It does NOT work the same way as Danmakufu. Please be advised that the speed at which the simulation runs is therefore not equivalent to the speed that the code would run in Danmakufu.


Anyways, what's the point of this lesson? What kind of parametric curves would I ever want to use in Danmakufu?

Well, polar equations aside, there are a few that you may want...

Generally speaking, people don't appreciate math in Danmakufu, and math for math's sake is generally not fun if you're not into that kind of thing. But you can do some interesting things. We will use the Astroid as our focus for the remainder of the lesson.

So, an Astroid is the following:

Astroid

This curve could result in some pretty nice Danmaku if used correctly, don't you think? Well, let's first begin with its parametric equation!

\(x(t) = a\times cos^{3}(t)\)

\(y(t) = a\times sin^{3}(t)\)

It's not too bad. Let's spawn bullets in an astroid!

    let angleT = 0;
    loop(60){
        CreateShotA1(ObjMove_GetX(objBoss) + 120*cos(angleT) ^ 3, ObjMove_GetY(objBoss) + 120*sin(angleT) ^ 3, 2, angleT, 1, 0);
        angleT += 360/60;
    }

To see a simulation of what the astroid might look like if the boss were at the center and the bullets did not move, see below:

Danmakanvas is a Javascript Danmaku simulation made by Sparen. It does NOT work the same way as Danmakufu. Please be advised that the speed at which the simulation runs is therefore not equivalent to the speed that the code would run in Danmakufu.


Below is a sample of what the astroid might look like if the boss were at the center and the bullets moved as shown in the code above.

Danmakanvas is a Javascript Danmaku simulation made by Sparen. It does NOT work the same way as Danmakufu. Please be advised that the speed at which the simulation runs is therefore not equivalent to the speed that the code would run in Danmakufu.


And that's that. We have an astroid. Of course, with these types of curves, you don't usually want the bullets to just go outwards with the same speed, because it will just look like a square, then a circle as it gets farther from its spawn point. You could do some complex speed alteration, or you could just change the angle and do interesting things with the angle. It's up to you.

That concludes the lesson!

EXERCISE: Try to recreate some parametric equations in Danmakufu. Try incrementing the angle at which the bullets are fired as you go around, or experiment with different speeds.

Summary

  • Angles in danmakufu increase in clockwise order, and the origin is the top left of the playing field for render priorities between 0.20 and 0.80
  • Using sine and cosine, it is possible to create circles and ellipses using danmakufu
  • You can use Parametric Equations in Danmakufu for advanced mathematical patterns

Sources and External Resources

[1] Touhou Danmakufu ph3 Tutorial - Spawning Danmaku in Circles and Ellipses (Sparen)
-->This tutorial is a video supplement to this lesson - it doesn't cover the same things but both lessons depends on each other.