In this lesson, we will discuss the creation of shotsheets, including a discussion on the available fields and manipulations as well as notes on the graphics themselves. This is the logical sequel to Lesson 8 and it is assumed that you have read this beforehand.
In this guide, we will utilize the Default_ShotData.txt located in the default_system of your copy of ph3.
Shotsheets are one of the key components of a Danmaku game, as they define what bullets can be created with the built-in functions as well as their properties. Hitboxes, delay clouds, colors, and more are all defined here. Due to their special role, they have a different syntax from regular Danmakufu scripts - one without semicolons and with a more object-like form.
All Shotsheets begin with #UserShotData
. This, like the Danmakufu header, functions as an identifier that lets the Danmakufu engine know what it is loading.
All shotsheets then need to know which image they are referencing. This is done via shot_image
, which is set to a string with a relative path from the shotsheet to the image containing the actual shot graphics.
In addition, shotsheets allow you to specify the rect on the shot image which will be used as the delay cloud for bullets that are created. This is set by the delay_rect
property, which takes a tuple consisting of the left, top, right, and bottom pixels of the image to use for the delay cloud.
As an example of these, see the start of the Default_ShotData.txt:
#UserShotData
shot_image = "./img/Default_Shot.png"
delay_rect = (209, 474, 240, 505)
It may be worth thinking about a Shotsheet as a form of object where you can set specific values or define specific subcomponents. In this case, the subcomponents are the shot data.
Each Shot Data is noted within a ShotData{}
. Each must have a unique numerical ID and rect in LTRB (left top right bottom) format. Other fields will be discussed in Part 4.
Every shot to fire is defined using this ShotData
format, and the IDs defined here can be used in the CreateShot function calls.
Within the ShotData, note how there are no commas between the definitions for each field. In fact, line breaks aren't necessary - only white space is required. As an example, see the following excerpt from the SeitenTouji shotsheet:
ShotData{ id=1251 rect=(0,15,15,30) render=ALPHA delay_color= (255,64,64) }
ShotData{ id=1252 rect=(15,15,30,30) render=ALPHA delay_color= (255,96,64) }
ShotData{ id=1253 rect=(30,15,45,30) render=ALPHA delay_color= (255,128,64) }
//...
ShotData{ id=1501 render=ADD_ARGB angular_velocity = 2 delay_color= (59,248,80)
AnimationData{
animation_data=(4,0,150,15,165)
animation_data=(4,15,150,30,165)
animation_data=(4,30,150,45,165)
animation_data=(4,45,150,60,165)
}
collision = 4;
}
ShotData{ id=1502 render=ADD_ARGB angular_velocity = -2 delay_color= (59,248,80)
AnimationData{
animation_data=(4,0,165,15,180)
animation_data=(4,15,165,30,180)
animation_data=(4,30,165,45,180)
animation_data=(4,45,165,60,180)
}
collision = 4;
}
Comments are still supported using the //
syntax.
In this section, we will briefly describe creation of the shotsheet image, or rather, the pitfalls one might hit.
The first thing to note is that of rotation. Danmakufu ph3 requires an odd number of pixels in each rect in order to rotate about a point. Having an even number of pixels will cause rotating bullets to wobble. For this reason, we recommend, for all bullets that you want to enable rotation for, to create them with odd dimensions. For example, 15x15 or 17x17. For consistency's sake, we actually recommend doing this for pretty much everything besides lasers and special bullets that will never rotate.
The second thing to note is that of black backgrounds. In the old days of 0.12m and early version of ph3, ADD_ARGB was not an available blend type in shotsheets. This meant that if you wanted bullets that could be nicely ADD rendered, you needed bullets in the image with a black background. This caused two problems - one, if the specified rect aligned along the edge of the black background (IE where black meets transparent), ADD (RGB) blend types would result in ugly rendering artifacts. This required extra black buffer space around your rects. The second problem was that you often needed duplicate bullets if you wanted the same base graphic to be used for both ALPHA and ADD rendering types. The standard today is to use the SAME graphics and rects for ALPHA and ADD_ARGB blend types, with a transparent non-black background and just different bullet IDs between the blend types.
The third thing to note is that standardizing your shotsheet structure with padding and consistent bullet dimensions will make the creation of a shotsheet that much easier. If you have many bullets with the same dimensions, align them in your shotsheet so that they form a nice grid. In addition, always have a pixel of padding around each bullet graphic. This will prevent unfortunate accidents such as the ones noted above where the black background was not big enough.
Finally, a note on default hitboxes. The bullet rect defines the default hitbox size for a bullet. The intersection radius is set to max(min(width, height) / 3 - 3, 3)
by default. This means that all bullets defined in a shotsheet will have a hitbox unless forcefully stated otherwise. In addition, even small bullets will have a hitbox size of 3. Be careful when making very small bullets.
Now we will go over the fields that can be put into a ShotData definition, as well as their syntax. Note that these can be put in any order.
We've already mentioned id and rect earlier. Each ShotData definition must have a unique ID. Note that you can skip numbers - just be aware that if you try and spawn a bullet with that unmapped ID as the graphic, a bullet with no graphic and no hitbox will be spawned (it will still have angle, speed, etc). As for rect, it's a tuple containing the left, top, right, and bottom coordinates on the image file (in pixels) to use for the bullet.
Besides these, the most common field is delay_color
, which takes a tuple consisting of three numbers - red, green, and blue components on a 0-255 scale.
Now, we will cover the truly optional fields. You can use some of these or none of them - it's up to you. The most relevant up front is render
. By default, all bullets are rendered ALPHA. You can set render
to ADD or ADD_ARGB. The former defaults to RGB mode. As mentioned before, there is no longer any reason to use this, and ADD_ARGB should be used instead. As an aside, you can set render
to ALPHA but this does absolutely nothing since it's already ALPHA by default.
Next we have angular_velocity
. This is the default angle rotation per frame of the bullet in degrees. This value is entirely independent from the movement angular velocity. This means you can have bullets that normally rotate in one direction, but moving with a rotation in the other. By default, angular velocity is set to 0.
Next is fixed_angle
. This takes a true/false value. Fixed angle bullets never rotate, even when the bullet goes in a different angle. There are performance benefits when using this, but it is really only applicable to point symmetric bullets such as circles as well as uniquely shaped bullets that require an orientation.
Finally, collision
. This specifies the hitbox radius and overrides the Danmakufu default of max(min(width, height) / 3 - 3, 3)
. There are two ways to use this - if assigned a single number, it uses that number as the radius from the center of the rect. However, if assigned a tuple of (r, x, y)
, it will use the x and y to center the hitbox - useful for bullets like arrows where the hitbox isn't at the center of the bullet.
We left out one field earlier - the AnimationData
component. This is another definition within a ShotData and consists of multiple animation_data
fields. Each of these is a tuple of five values. These are, in order, the number of frames, and then the left, top, right, and bottom of the rect. Each animation_data
effectively states a part of the image to use and the number of frames to use before moving on to the next frame. By extension, there is no reason to use rect
for animated bullets. See an example below (same as in Lesson 8):
ShotData{ id=335 render=ALPHA delay_color= (64,255,255)
AnimationData{
animation_data=(4,0,448,32,488)
animation_data=(4,32,448,64,488)
animation_data=(4,64,448,96,488)
animation_data=(4,96,448,128,488)
}
collision = 5;
}
N/A