PWLib Character Creation Tutorial

<< Back to Tutorial Page

 

Tutorial: Creating a Custom Character on PWLib

 

Creating a character in PWLib takes 2 things: the sheet file and the code file. The former, preferably in .PNG format, and the latter, in .HLSL format.

 

STEP 1: Setting up the sheets

 

First of all, we need to have all the sheet files set up. PWLib’s default folder for sheet files is PW/character/(character name). We will create a folder, and name it as the character we are willing to create. For example, we’ll create animations for Young Valant Gramarye’s sprites. Therefore, our folder will be PW/character/Valant Young. Here’s where we will put all our sheet files for Young Valant.

 

After the sheets are accordingly organized, we will proceed to write a code that will create PWLib-compatible animations for Young Valant’s sprites. The default folder for this code is pwlib/data. Our file will follow the “pw_(char name)” layout, so this file will be called “pw_valantyoung”, followed by the .HLSL extension.

 

STEP 2: Loading the sheet files

 

The first thing our code should contain is the “Create” function. The layout for this function is “pw_create(character name)”. So, in this case, our Create function will be “pw_createvalantyoung”. We will start our script with this function. The Create function will only contain 1 command: running the Load function. The Load function will load all necessary textures, and it will be called “pw_load(char name)files”. Our Load function will be called “pw_loadvalantyoungfiles” (so the command under the Create function will be “runscript(“pw_loadvalantyoungfiles”);”), and will load all sheet files for Young Valant as there are.

 

[pw_createvalantyoung]

runscript("pw_loadvalantyoungfiles");

 

For this, we will use either the loadtexture or replacetexture command. For example, we will load the sheet file “ValantYoung01.png” with the command “loadtexure(3103,”PW/character/Valant Young/ValantYoung01.png”);”. Here, we are assigning a texture ID to the sheet; in this case, it is 3103, but you can use whichever number you wish. If your texture collides with another one (that is, another texture uses the same ID), simply change the ID, or use replacetexture instead of loadtexture.

 

We will repeat this process for all the sheets we need:

 

[pw_loadvalantyoungfiles]

loadtexture(3103,"PW\character\Valant Young\ValantYoung01.png");

loadtexture(3104,"PW\character\Valant Young\ValantYoung02.png");

loadtexture(3105,"PW\character\Valant Young\ValantYoung03.png");

loadtexture(3107,"PW\character\Valant Young\ValantYoung04.png");

 

Once all textures have being loaded under the Load function, we can start scripting our animation poses.

 

STEP 3: Scripting a pose

 

Animations in PWLib don’t need their own function. To use them, we just need to create them, assign frames to it, and assign a texture to those frames.

 

First of all, we will code the basic, always-present Normal animation. This is the default, non-expressing pose that every character has.

The first thing we do when we will create an animation is, of course, tell the engine that we are creating it. For that, we use the Create animation command:

 

createanimation(“Valant_Young_Normal”,0);

 

This will name our animation “Valant_Young_Normal” (ignore the 0).

Next, we will insert our first frame, with the Insert animation frame command:

 

insertanimationframe(“Valant_Young_Normal”,01,3103,40);

 

This will insert frame 01 to the “Valant_Young_Normal” animation. The texture for this frame will be taken from texture ID 3103. And, lastly, this frame will last 40 milliseconds on screen.

After the frame is inserted, we will tell the engine where to take the texture from, exactly. Here, we’ll use the Set animation frame texture coordinates command:

 

setanimationframetexturecoordinates(“Valant_Young_Normal”,01,-01,-01,176,192);

 

This command will tell the engine to extract the texture for Valant_Young_Normal’s frame 01 from the sheet files -01,-01 X,Y coordinates. These are not 00,00 due to a graphical glitch of PWLib’s. Then, we indicated the frame’s texture’s width and length. Its width is 176 pixels, and its length, 192 pixels.

 

After this, the frame’s done! We will now repeat this process for the rest of the animation’s frames:

 

insertanimationframe("Valant_Young_Normal",02,3103,6);

setanimationframetexturecoordinates("Valant_Young_Normal",02,-01,191,176,192);

insertanimationframe("Valant_Young_Normal",03,3103,6);

setanimationframetexturecoordinates("Valant_Young_Normal",03,-01,383,176,192);

insertanimationframe("Valant_Young_Normal",04,3103,6);

setanimationframetexturecoordinates("Valant_Young_Normal",04,-01,191,176,192);

insertanimationframe("Valant_Young_Normal",05,3103,80);

setanimationframetexturecoordinates("Valant_Young_Normal",05,-01,-01,176,192);

 

Now, the pose seems to be ready, but it isn’t. Every pose needs a silent and a talking animation (we will talk about the ones that don’t have talking ones later). We will do exactly the same we did for the silent animation, adding a “_Talking” after “Valant_Young_Normal”. This will end up like this:

 

createanimation("Valant_Young_Normal_Talking",0);

insertanimationframe("Valant_Young_Normal_Talking",01,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",01,175,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",02,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",02,351,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",03,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",03,175,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",04,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",04,-01,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",05,3103,6);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",05,175,191,176,192);

insertanimationframe("Valant_Young_Normal_Talking",06,3103,6);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",06,175,383,176,192);

insertanimationframe("Valant_Young_Normal_Talking",07,3103,6);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",07,351,191,176,192);

insertanimationframe("Valant_Young_Normal_Talking",08,3103,6);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",08,351,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",09,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",09,175,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",10,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",10,-01,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",11,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",11,175,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",12,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",12,351,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",13,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",13,175,-01,176,192);

insertanimationframe("Valant_Young_Normal_Talking",14,3103,12);

setanimationframetexturecoordinates("Valant_Young_Normal_Talking",14,-01,-01,176,192);

 

Now, yes, we are done with the pose. Next up is creating the Enable function, which will allow us to call the character’s pose while scripting a game.

 

STEP 4: Creating the pose’s Enable function

 

The Enable function follows the “pw_enable(char name)(char pose)” layout. For example, for Young Valant’s Normal pose, the function will be called “pw_enablevalantyoungnormal”. The Enable function will be as such:

 

[pw_enablevalantyoungnormal]

recfunctionname([pw_charfunc]);

placecharacter("pw_char",37,0);

setcharactersize("pw_char",176,192);

setcharacterlipsyncsilent("pw_char","Valant_Young_Normal");

setcharacterlipsynctalking("pw_char","Valant_Young_Normal_Talking");

 

The contents of the Enable function are:

+recfunctionname: Must always be “([pw_charfunc])”. Tells the engine that this function is a character function.

+placecharacter: Places the character on the desired location. In this case, pixel 37,0 of the screen.

+setcharactersize: Sets the character object’s size. In this case, as it was indicated while scripting the animations, the size is 176,192.

+setcharacterlipsyncsilent: Sets the pose’s silent animation. In this case, it is “Valant_Young_Normal”.

+setcharacterlipsynctalking: Sets the pose’s talking animation. In this case, it is “Valant_Young_Normal_Talking”.

 

This is it for a regular pose, which has a silent and a talking animations. But what if the pose doesn’t have a talking animation, or if it contains movement (that is, shows 2 animations in 1)? We will use Young Valant’s “Twirl” pose to explain it:

 

First, we will script the required animations normally. Then, when it comes to expressions, it will be as such:

 

[pw_enablevalantyoungtwirl]

recfunctionname([pw_charfunc]);

placecharacter("pw_char",6,0);

setcharactersize("pw_char",240,192);

setcharacterlipsyncsilent("pw_char","Valant_Young_Twirl");

wait(81);

runscript("pw_enablevalantyoungnormal");

 

As you see, its layout is as in a regular one, but without the setcharacterlipsynctalking command, and a couple more commands under it. The wait will start at the same time the animation starts. It will tell the engine that after the Twirl animation runs, it must run the Normal animation (that’s why, below the wait, we run “pw_enablevalantyoungnormal”).

If the pose didn’t require jumping to another pose, but didn’t have a talking animation, we just omit the setcharacterlipsynctalking, without the wait nor anything below it.

 

That is all you need to create the character’s usable pose, also called “expression”. This should be done for every pose you need: first script the animations, then the expressions. But, for every character to be officially finished, there must be a function to change the name box accordingly to the character.

 

STEP 5: Set the name box

 

The command for setting the name box’s layout is “pw_setnameboxto(char name)”. In this case, it will be “pw_setnameboxtovalant” (not Valant Young, because his name won’t change because of his age). The function is as follows:

 

[pw_setnameboxtovalant]

recfunctionname([pw_nameboxfunc]);

setmsgboxtickersound("sfx-blipmale.wav");

setgenobjgraphic("pwnamebox",112);

setgenobjtexturecoordinates("pwnamebox",150,406,50,14);

showgenobj("pwnamebox");

 

+recfunctioname does the same than what it does with expressions, but in this case, it must always be “([pw_nameboxfunc])”, showing that it is a name box function.

+setmsgboxtickersound sets the sound played while the character speaks. In this case, it is “sfx-blipmale.wav”, because Valant is male. If the character were female, it would be “sfx-blipfemale.wav”.

+setgenobjgraphic must always be “(“pwnamebox”,112)”, unless the file from where the name box image is loaded weren’t the default one (PW/PWNameBoxes.png). If it weren’t, we must keep “pwnamebox”, but load, in “pw_loadvalantyoungfiles”, the nex name box texture. Say, the image where the character’s name box is is in PW, and it’s called “PWNameBoxes2.png”. Then, under “pw_loadvalantyoungfiles”, you would use replacetexture(112,”PW/PWNameBoxes2.png”); (since 112 is the texture ID of the name box image).

+setgenobjtexturecoordinates tells the engine where in the name box image this character’s name box is. The size will always be 50,14; the position will vary.

+showgenobj this must always be “pwnamebox”. This shows the name box on screen.

 

 

This is all you need for a character to be usable in PWLib.