Site Logo


Sparen's Danmakufu ph3 Tutorials Lesson 4 - Danmakufu Syntax and Structures Part 1

The video for this lesson is AJS's entry for the LOCAA 5 contest. It won first place for reasons obvious in the video. In my opinion, this is one of the best dual-boss scripts ever made in Danmakufu ph3, especially considering that although AJS has had a significant amount of experience in Danmakufu, this is his very first ph3 script.

Part 1: What will be Covered in this Lesson?

In this lesson, I will cover the following:

  • Comments
  • Variables and Variable Declaration
  • Constants
  • Mathematical Operations
  • Characters
  • Strings
  • One-Dimensional Arrays

In the next lesson, I will cover the following:

  • Booleans and Boolean Operations
  • Loops
  • Functions
  • If, If-Else, and Alternative-Case Statements
  • Tasks and yield;
  • Usage of the Semicolon

Please note that the contents of this page are partially based off of CK Crash's Danmakufu ph3 tutorial and that it may be a more suitable option for learning about how Danmakufu treats variables and the like.

In addition, I will attempt to explain all concepts in these two lessons in a way that is familiar to both those with no prior programming experience and to those coming from another language.

Part 2: What are Comments and How do I Use Them?

//This is a comment.
//Anything in Danmakufu that is preceded with two slashes (//) is a line comment.
//Therefore, anything past a // on a line is not treated as code by Danmakufu and will be ignored.
/*This is also a comment*/
//Comments inside /* and */ are considered 'block comments'.
/*Block
comments
can
span
multiple
lines*/
//Line comments are very good for providing descriptions of what certain code does.
//Line comments are also very good for commenting out code that you don't want to use right now, such as debug code.
/*Block Comments are useful for cutting out large chunks of code from execution 
in order to test one part of a script.*/
/*Block comments are also used to hold descriptive information about functions and the like*/
/*Block and Line comments work exactly the same as in Java, C, C++, and JavaScript*/

Note that in code within these tutorials, comments will often be used to note certain important concepts or the functions of certain lines of code, as well as a plethora of other things. It is highly suggested that you use comments in your code noting what things do so that when you return to it, you can read the comments to remember what your code does.

Part 3: What is a Variable?

A variable in computer science is not exactly the same as in mathematics. In computer science, it is possible for a variable to change its value. A variable stores information, whether it is a constant, a string, or an ID. There are global variables and there are local variables, both of which will be covered shortly. You can think of a variable as a container for some type of information.

let x; //This code declares a variable called x.
x = 5; //Now the value of x has been set to 5.
x = 3; //The value of x has been overridden and is now 3.
let y = x; //We have created a new variable y, whose current value is that of x, which is 3.
x = 4; //x is currently 4, but y maintains its value as 3.

For future reference, a single instruction, such as let x;, is called a statement, and statements should end with a semicolon.

For those of you with experience in other programming languages, note that Danmakufu is pass-by-value.

Now lets consider the following code.

let x; //This code declares a variable called x.
let x = 5; //Attempt to declare a variable x storing the value 5.

The above code, unfortunately, will bring up an error because a variable x already exists! You cannot declare something that already exists (there are exceptions to this rule, which we will refer to when we talk about local and global variables).

Now lets consider the following code.

x = 5; //This code tells the variable x to store the value 5.
let x = 3; //Declare a variable x storing the value 3.

The above code, unfortunately, also brings up an error. This time, you get an error for trying to use a variable that has not been declared. In this case, the line x = 5; refers to a variable x that, at that point in time, does not exist. You must declare a variable before you can use it, before meaning in code located *above* the code in which the variable is used.

As a final note about variables, you can use real instead of let to declare a variable, although let is much much more commonly used. They are functionally the same.

Part 4: What are Constants?

Constants are identifiers that do not change while a program is running, as opposed to variables. Names for constants are all uppercase by convention. For example, built-in constants in Danmakufu include BLEND_ADD_ARGB and EV_GRAZE, among other things. Most of these are built in or used in shotsheets.

If you decide to use your own constants, make sure that the name is UPPERCASE and that you do not edit the value at all. For example, let CIRNO = 9;

Part 5: What Mathematical Operations Can I Perform in Danmakufu?

let x;
x = 3 + 5; //x now holds the value 8
x=x+3; //x now holds the value 11. Note that white space doesn't matter.
x++; //This is equivalent to x = x + 1. It increments x by 1. Very helpful for counters.
x--; //Like the above. Decrements the value of x by 1.
let y = 6;
y = y * 2; //y is now equal to 12.
y = y / 3; //y is now equal to 4.
y = y ^ 2; //y is raised to the second power and is now 16.
y = y % 2; //Since the remainder/modulus when y / 2 = 0, y is now 0.

Keep in mind that Danmakufu uses the Order of Operations and that the square root is the equivalent of raising to the 1/2 (or 0.5) power. Below are some more examples and notes on the angle system used by Danmakufu, as well as some more shortcuts.

let angleT = 30; //Danmakufu uses degrees
angleT += 360/5; //angleT incremented by 72 and is now 102. 'var += n' is a shortcut for 'var = var + n'
angleT *= -1; //angleT is now -102, using the same process as the line above. 
//Mathematical shortcuts also exist in the form of -=, and /=
let x = (30 + angleT) * 3; //Use parentheses for order of operations. x now equals -216.

However, please pay attention to the following

let y = (55)(33); //This will not work. 
let y = 55 * 33; //This is the correct way to multiply the two numbers. 

Also, note that you cannot perform operations on variables that do not exist.

let y = 30; 
let z = x * y; //Will bring up an error since x has not been declared.

And finally, one of the more obnoxious things that will bring up an error.

let y = 30; 
y += .5;

The error? Well, Danmakufu doesn't understand what the period means. You need to use 0.5 instead of .5 in order for Danmakufu to recognize it and not error at you.

CHECKPOINT: How do you multiply and divide in Touhou Danmakufu?

Part 6: What are Local and Global Variables?

So now, for our discussion on Local and Global Variables (and variable scoping), we will refer back to the code used in the previous lesson.

Click this link to open massive wall of code in a separate window

Global Variables are variables declared outside of tasks, functions, and routines. They are usually declared between the Danmakufu Header and either @Initialize or @Event (whichever comes first). A variable declared here can be referenced and changed from any other part of the script. For example, in the code provided, objEnemy and frame are declared as global variables. In @MainLoop, frame is referenced and changed multiple times. Just remember that changes made to a global variable used in multiple locations will affect all locations it is used, so it is recommended that you only change a global variable in one routine/function/task and nowhere else. For counters, I suggest using @MainLoop.

In @MainLoop, the loop starts with the declaration of ex and ey. These are local variables - they only exist in @MainLoop, and once @MainLoop finishes one round, they are reset. This means that they are updated each frame due to @MainLoop's nature.

Local Variables are also created in ascent and descent loops, and parameters of functions and tasks are also local variables, except that these local variables have a special quality - they will override global variables with the same name when called within the given loop, function, or task. This is known as variable shadowing and will be explained in the sections for the given concepts. These local variables, since they can only be accessed within the block (area inside {}) in which they are declared, have their scope (the area in which they can be referenced) limited to that block, in contrast to global variables whose scope is limited to the entire script (the specific file, not the entire project, which we also tend to refer to as a 'Danmakufu script').

Finally, you can declare local variables in the following manner:

local{
     let x;
}

However, very few scripters use it, and you will most likely never encounter this at all unless you are trying to decode someone else's code from the starting days of ph3.

To give a more concrete example of local and global variables (and variable scoping), observe below. Note that the other structures used here are explained in this lesson and Lesson 5.

let x = 1;

@MainLoop {
    let y = 2 * x;
    ascent(i in 0..y){
        let j = i + x/y;
    }

    //cannot access i or j here
}

//cannot access i, j, or y here

In the above code, x is a global variable that can be accessed anywhere in the script. As a result, within @MainLoop, we can access x. y however is local to @MainLoop and cannot be accessed outside of @MainLoop. However, it can be accessed within the ascent loop as it is global in scope with respect to the ascent loop. As such, i and j, which are local with respect to the ascent loop, can use both x and y, but cannot be accessed outside of the ascent loop.

Quiz: Mathematical Operations

1) What is the value of x after the following code has been executed?

let x = 8;
x += 5;
x = x ^ 2;
x -= 3;
A. 26
B. 23
C. 166

2) What is the value of x after the following code has been executed?

let x = 8;
let y = 6;
x = x * (y - 3);
x += 9;
A. 33
B. 23
C. 54

Quiz: Decimals

Nitori is sharing her love of decimals when she receives the following error:

Error

Why is this error showing up?

A. She did not declare the variable x
B. Decimals in Danmakufu cannot start with a period
C. She forgot a semicolon

Part 7: What are Characters and Can I Change the Type of a Variable?

In Computer Science, a character typicall refers to a single letter, number, or symbol. Characters are denoted using single quotes, like such: let x = 'a';

In Danmakufu, you will most likely not use characters - rather, you will use strings, the topic of the next part. However, now is a good time to showcase one of the more important concepts in Danmakufu (and Computer Science in general) - variable types.

let x = 6; //x is a container holding an integer 6
let y = '$'; //y is a container holding the character $
let z = '3'; //z is a container holding the character 3
x = '3'; //This code will bring up an error

So, why can't you set the variable x's value from 6 to '3'? Well, that's because when you first assign a value to x, it is holding an integer. Later on, you are trying to set its value to a character. This is not allowed, since x has been set to only accept numbers. '3' is not a number in Danmakufu - it is a character. In other languages, characters may have integer equivalents and addition may be possible, but I suggest that you simply don't try to do this as it may have unwanted results.

But what if you want to define x = '33';? Well... that's not going to work, and we'll see that in the next part.

Part 8: What are Strings?

Strings have much more use than characters in Danmakufu. Strings can be used to hold phrases, filepaths, and more, and are denoted using double quotes. Please note that if you use smart quotes (e.g. “Do not program in Microsoft Word please.”), Danmakufu will not understand what they are and may either not find the file they are used in/crash/other unwanted behavior. Below are examples of strings.

let randomstring = "This is a random string.";
let spellname = "Fire and Water Sign \"Geysers of the Deep\"";
let imgRumia = GetCurrentScriptDirectory() ~ "img/imgrumia.png";
let desc = "This is a spellcard made by an unknown scripter.[r]Please play at your own risk.";

Obviously, strings hold much more information than a character and are far more useful. You can include numbers in strings, such as "3 + 6 = Cirno" as well as other things. In the four examples above, we have three special scenarios that deserve extra attention - spellname's escape character, imgRumia's string concatenation, and desc's [r].

Firstly, the escape character \. In most programming/scripting languages, \ is used as the escape character, and if followed by certain symbols, will perform a specific function. For example, in most languages, \n denotes a new line (not in ph3 - we will discuss this later), \t denotes a tab, and, more specific to this scenario, \" denotes a ". Since strings begin and end with double quotes, having double quotes inside a string requires some workarounds, and that workaround is provided by \", which will not be read as closing the string but rather as a symbol that, when the string is printed, will use a double quote instead of \". This is the simplest way to include a double quote within a string. Very helpful for spell names, depending on your format for spellcard names.

Next is string concatenation. For the purposes of this tutorial, be advised that GetCurrentScriptDirectory returns a string.

let imgRumia = GetCurrentScriptDirectory() ~ "img/imgrumia.png";

Here we have a string ~ string. So what does this do? This is known as string concatenation, and merges the two strings. For example:

let string = "String1" ~ "String2"; //string is now "String1String2"

You will almost always use this for paths being assigned to variables. It also comes in handy in a number of other situations.

And finally, [r]. [r] is new in ph3 (0.12m used \n). [r] denotes a new line and functions as a line break. Therefore, let string = "one[r]two";, if printed, would show the following:

one
two

Very helpful in #Text, with dialogue, and numerous other things.

Note that for Japanese text, there is also [ruby], but this is beyond the scope of this tutorial.

Part 9: What are Arrays?

The final thing we will discuss in this lesson are arrays, which are essentially containers that hold multiple things of the same type. For example, a string is an array of characters. In Danmakufu, you declare arrays as the following:

let array = []; //Declare an empty array
let array2 = [3, 4, 6, 8]; //Declare an array of numbers of length 4 containing 3, 4, 6, and 8
let array3 = GetAllEnemyID(); //Declare an array containing the IDs of every enemy object currently existing

Note that once you declare an array, the variable name you use cannot be used for anything other than an array. You cannot do the following:

let array = []; //Declare an empty array
array = 999;

The variable 'array' has been used for an array and cannot be used for anything else.

For those of you coming from other programming languages, note that it is not possible to declare an array of a predefined size in Danmakufu. We recommend concatenation of new elements (see below).

Arrays are very useful, but of course, you will need to add and retrieve data from them. As such, there are ways to add things to arrays, remove things from arrays, and get the values held in arrays.

First we will discuss adding things to arrays. Using the concatenation operator ~, you can do the following:

let array = [3, 6]; //Define an array of integers
array = array ~ [4]; //array is now [3, 6, 4]

Please note that you must include the item you are going to add as an array - in essence, you are combining two arrays. Also, you can use concatenate(array1, array2) in order to concatenate two arrays, although the method with the ~ is much simpler. Here is another example, this time showing how to include the value of a variable in an array:

let array = []; //Declare an empty array
let element = "Test";
array = array ~ [element]; //array is now [element], where the value of element is "Test"

Next we will discuss indexing. You can obtain the length of an array using length(nameofarray). First thing's first though - the first item in an array is indexed 0, not 1. You index an array as follows:

let dnh = ["ExPorygon", "Naut", "Trickysticks", "AJS"]; //Declare an array of length 4 containing the names of Danmakufu Scripters
let lengthdnh = length(dnh); //4
let scripter = dnh[2]; //Find the item with index 2 - "Trickysticks"
let scripter2 = dnh[2..4]; //Obtain an array with the 2 through 3rd indices of dnh - ["Trickysticks", "AJS"]
let scripter3 = dnh[lengthdnh]; //Brings up an error because the fourth index does not exist

Once you get over the fact that it starts from 0, it's not too bad. Also, note the two dots for obtaining a subarray of the initial array. The new array includes the index before the two dots but not the one after. We will be seeing this later with ascent loops, and the function slice() also performs the same function. Now, consider the following array:

let array = [3, 6, 9, 12, 9, 9, 12];

We are going to perform operations on this array above!

array = [3, 6, 9, 12, 9, 9, 12];
array[4] = array[4] - 1;

Now the array looks like this:

[3, 6, 9, 12, 8, 9, 12]

What we have done is the following: We indexed an array, obtaining a reference to the value contained in that index, and then performed an operation (- 1) to it. This is how you perform operations on the contents of arrays.

There are, of course, other operations (many significantly more complicated), but for now, we will close with removal of indices. It is actually very simple.

array = [3, 6, 9, 12, 8, 9, 12];
array = erase(array, 5); //Erase fifth index of array 'array'

After executing the function erase() on the array, we are left with the following array

array = [3, 6, 9, 12, 8, 12];

As a final note, since strings are arrays of characters, you can also index strings the same way you index arrays. CK Crash's tutorial (see Part 1 for link) also includes the boolean trick, but we will discuss booleans in the next lesson. There are also 2D and 3D arrays (etc), but we will not deal with those now - there is limited use for them until you are trying to organize CommonData or spawn bullets in a shape, etc.

Quiz: Strings and Arrays

let spellsigns = ["Fire Sign", "Water Sign", "Moon Sign"];

1) What is the value of spellsigns[1]?

A. "Fire Sign"
B. "Moon Sign"
C. "Water Sign"

Now we do the following:

spellsigns = spellsigns ~ ["Magical Storm"];

2) What is the value of length(spellsigns) after the code has been executed?

A. 4
B. 3
C. "4"

Now we do the following:

erase(spellsigns, 0);

3) What is the value of spellsigns[0] after the code has been executed?

A. "Magical Storm"
B. "Water Sign"
C. "Fire Sign"

We will now let str = spellsigns[2]; //"Magical Storm"

4) Which of the following will result in the value of str being printed as Magical Storm "Flurry of Stars"?

A. str = str + "Flurry of Stars";
B. str = str ~ " \"Flurry of Stars\"";
C. str = str ~ " "Flurry of Stars"";

Sources and External Resources

[ 弾幕風 PH3 Tutorial ] Introduction to Danmakufu (Helepolis)
-->Should give an alternative, visual example to the contents of this lesson

Danmakufu for Easy Modos by CK Crash (CK Crash)
-->Since this tutorial was used as a source for mine, I highly suggest that you read it if you haven't already.