Custom Object tutorial/File structure
|Custom Object tutorial||[view]|
Now we have learned what SOCs consist of, but we haven't yet learned how they are constructed. To do that, we will take a look at a typical SOC file.
Creating an example
Let's look at an example of a state and of an Object type in a SOC file. Create a new SOC file, open it in Notepad and paste the following text into it:
State S_POSS_RUN1 SpriteName = POSS SpriteFrame = A Duration = 3 Next = S_POSS_RUN2 Action = A_Chase Var1 = 0 Var2 = 0 Object MT_BLUECRAWLA MapThingNum = 100 SpawnState = S_POSS_STND SpawnHealth = 1 SeeState = S_POSS_RUN1 SeeSound = sfx_None ReactionTime = 32 AttackSound = sfx_None PainState = S_NULL PainChance = 200 PainSound = sfx_None MeleeState = S_NULL MissileState = S_NULL DeathState = S_XPLD1 XDeathState = S_NULL DeathSound = sfx_pop Speed = 3 Radius = 24*FRACUNIT Height = 32*FRACUNIT DispOffset = 0 Mass = 100 Damage = 0 ActiveSound = sfx_None Flags = MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE RaiseState = S_NULL
What you see here is a part of the SOC language. When the SOC file is loaded into the game, it will read from top to bottom and left to right through this text and apply the given parameters.
State S_POSS_RUN1: This is the header for the state block.
S_POSS_RUN1is the name of the state that this block will modify. In our case, this is the first state of the blue Crawla's running animation. Note that this is a state that already exists in SRB2. Had we used a name that does not correspond to a state in SRB2, we would have needed to declare it in the freeslot block first, to allow SRB2 to associate it with a state slot. Since we chose to modify an existing Object, all parameters that we set in this block will overwrite SRB2's default values when we load the SOC, and we would see a difference in the blue Crawla's behavior. States always begin with a
SpriteName: This is the name of the sprite set the Object displays as long as this state lasts. In our case this is
POSS, the sprite set for the blue Crawla. The names for sprite sets always have four letters. Each sprite set consists of multiple frames. The Sonic sprite set for example has one frame for its standing animation and a set of frames for its walking animation.
SpriteFrame: This defines which frame of the sprite set should be shown. In our case this is frame A, the first frame of the set.
Duration: This controls how long the state should last. The time unit is "tics". A second consists of 35 tics, so one tic is 1/35th of a second. In our case, this is set to 3, so the state will last about a tenth of a second.
Next: This indicates which state will follow after the
Durationhas passed. In our case, this is
S_POSS_RUN2. If you set it to
S_NULL, nothing will happen after this state is complete. You can also set states to loop back to themselves.
Action: This is where things become interesting. As mentioned in the last chapter, actions are functions that control what the Object does during the state, so whenever you want your Object to do something, no matter what, you'll need to use these. Furthermore, an action is executed only once per state and at the start of the state (during its first tic). There are over 100 different actions programmed into SRB2. Except for "None" (meaning no action at all should be performed), they all start out with the prefix "A_", followed by the action's name. In our case, this is
A_Chase. This is a very basic enemy thinker that (each time it is called) forces the Object to make one step into its target's direction. The target of the Object was set by another action in a state that was called before this one, and is the player in this case. Now you know why the
Durationvalue was so short. Since all the Object does is make a step, the state doesn't need to be long. To look up how many actions there are, what they do and how they work, see the Actions article.
Var2: These are variables that are used by some actions for additional parameters. They have a different meaning depending on which action you call.
A_Chasedoes not need any additional parameters. This is why both values are set to 0 here (changing them has no effect).
This is what state blocks look like. But what about Objects? Let's move on to the next text block.
Object MT_BLUECRAWLA: This is the header for the Object type block.
MT_BLUECRAWLAis the name of the Object type that this block will modify. In our case, this is – as the name tells us – the blue Crawla. Object type names always begin with a
MapThingNum: This number is used by map editors to refer to this Object type. This sets the Thing type that this Object type can be placed with on a map. It can be set to any random number as long as it's not already reserved. Later on, when you create a level with your first own enemy, you'll need to place a Thing on the map that uses this number as its type ID. If you don't want your Object to be placed as a Thing, set this value to "-1" or omit it. Maximum is 4095.
SpawnState: This is the state that will be called at the time when the Object is spawned. Most enemies start off with a state (
S_POSS_STNDin our example) that loops back to itself and constantly executes
A_Look. This action tells the Object to sit and wait until it sees a player. Then it will change its target to this particular player and call the
SeeState. Note that the first time the
SpawnStateis called, its action is usually not executed. However, since in our case the state loops back to itself, it will start being executed shortly afterwards when the state is called for the second time.
SpawnHealth: These represent the Object's health points. If an Object gets attacked, its life points will decrease by one for each tic the harming contact lasts. Crawlas have this set to 1, since they will be destroyed by one hit.
SeeState: This is the state the Object will call if it detects a player through
A_Look. In our example, this is
S_POSS_RUN1, the same state that our first text block represents. Now you should get a grasp of how the Crawla behaves: Upon spawning, it looks out for a player until it finds one, and then chases this player.
SeeSound: This is the sound that will be played if an Object sees a player through
A_Look. Since the blue Crawla plays no sound upon detecting a player, this is set to
sfx_None. The playback of this sound is optional, and can be turned on or off by A_Look's Var2. Similar to other sounds, this may also be called by other actions, but it mainly executes when the
ReactionTime: This is relevant for various purposes and specific to an Object's thinker/actions. Usually, you can ignore it.
AttackSound: This is the sound that is played when certain actions are used. Usually, set it to
sfx_Noneand ignore it.
PainState: This state will be called every time the Object is attacked, except if its
SpawnHealthreaches 0. Since the blue Crawla only has one health point, this state is unused and therefore set to
PainChance: This is relevant for various purposes and specific to an Object's thinker/actions. Usually, you can ignore it.
PainSound: This is the sound that will be played when an Object uses
MissileState: These states are used as attack patterns by some of the more complex enemies, as well as bosses. Usually, you can ignore them.
DeathState: This state will be called if the Object's
SpawnHealthreaches 0, which means the Object dies. In the blue Crawla's case, this is
S_XPLD1, a generic state that is used by all dying enemies.
XDeathState: This State is used for various purposes, most notably for bosses. Usually, you can ignore it.
DeathSound: This is the sound that will be played when an Object uses
DeathStateis called. In our case, it is the popping sound,
Speed: This defines the movement speed of the Object. In our case, 3 as a value means that the Object will cover a distance of 3 fracunits with each step it makes.
Height: Keeping in mind that an Object's animation consists of 2D sprites, its actual 3D size obviously has to be defined by something else. This is done with these two values. Each Object in SRB2 has a cuboid shape whose distance from its center to the center of each of its sides is
Radius, and whose height is
Height. You will notice that in our example, both values are multiplied by
FRACUNITis a special constant that represents the number 65536. This is necessary because internally, the game uses a very precise length measuring that allows for very small adjustments. One fracunit equals 65536 of these very small units, so to give to Object a size that makes sense, you have to convert the value to fracunits. This is what the
DispOffset: This value is used to draw certain sprites in front of others when they are in the same location. Usually, you can ignore it.
Mass: This is used internally by the game for various purposes. It has no relation whatsoever with the Object's heaviness. Usually, you can ignore it.
Damage: This is relevant for various purposes and specific to an Object's thinker/actions. Usually, you can ignore it.
ActiveSound: This is a sound that is played when certain actions are used. Usually, you can ignore it.
Flagsare a series of variables that define the general properties of the Object. In our case, the Object has three flags:
MF_ENEMYmeans that the Object is an enemy.
MF_SHOOTABLEmeans that it can be hurt by attacks.
MF_SPECIALmeans that the player can interact with the Object by touching it. There are numerous other flags that an Object can have; look at the Object flags article for a complete lists. You will see that the flag names are separated by the "|" character. This is necessary because internally, the game stores the flags as a single 32-bit number, where every bit stands for one flag: If the bit is 0, the flag is disabled, if it is 1, the flag is enabled. Just as with Object/state/sprite/sound names, the game recognizes the flag names and automatically converts them to the corresponding flag values. The "|" character tells the game to calculate the bitwise OR function, which is needed to add the flags together.
RaiseState: This state is used for various purposes, most notably for dropping Objects. Usually, you can ignore it.
|Custom Object tutorial||[view]|