General Category > ASL Scripts

Animated Textures Using Controller Scripts

(1/4) > >>

Raxx:
Animated Textures Using Controller Scripts
The not as hard way of doing it the hard way
Hello folks, here's a controller script for animating textures within the scene editor. Please read the information below on how to use it, download the .7z file containing the examples, and/or watch the video tutorial. (Note: the images in the .7z file had to be reduced in quality in order to upload correctly).

What it does
This controller script takes an Anim8or texture and swaps the file name on command, using a main controller script that reads the data of an animation controller for a sprite object. This script will animate any texture on any material that is applied to any mesh.

Applications

* Particle effects
* Animating facial expressions when the face or parts of the face uses textures instead of geometry (like for example, Mii characters)
* Videos in the scene, like television screens, computer monitors, or sci-fi projections
* Entirely 2D animated character sprites
* Etc.
How to use the controller script
Download the .zip file attached to this forum post and extract it. The contents of the zip file should be:

* sprite_controller.txt - This is the controller script with the default configuration. Use copies of this, change the parameters inside it, and apply the changed copies to your animated textures in Anim8or following the instructions further down.
* Example 1 - Video and Fire.an8 - This is the same file that was made in the first part of the video tutorial. It has animated fire and an animated computer screen.
* Example 2 - Character Sprite.an8 - This is the same file that was made in the second part of the video tutorial. It has an animated character sprite that runs and jumps.
* Sprites folder - Contains the sprite image sequences used in the two example .an8 files
Step 1: Setting up the sprite object


A unique object, material, and texture for each unique sprite you wish to animate
The sprite object, in the object editor, needs to contain a mesh with a material applied to it. This material needs to have a texture loaded into it. The important thing is the name of this texture object (circled in red in the image above). The controller script will be swapping the filename of this texture, and to do so it needs this name.

If you want to have multiple unique sprites in the scene that use the same sequence of images (just animated differently), you need to create a new object, a new material, and a new texture for each sprite instance you want to use. You don't have to make these copies if you only want to use one instance of the sprite in multiple scenes, or if you want to use duplicates of the sprite that all animate identical to each other in the same scene.

Step 2: Setting up the scene
The first thing you need to do is create a "sprites_enabled" boolean controller in the scene, applied to the "world" element. This is an on/off switch which enables or disables all of the animated textures in the scene. This is handy because sometimes Anim8or will randomly crash if you click on the sprite element while the controller script is active (the setFileName() in the script function seems to cause this random-seeming crash).



Add a "sprites_enabled" boolean controller
Do this by going to Edit->Controllers..., select the world element in the top drop down menu, and then clicking on the "New" button. A boolean controller can only have a value of 0 or 1. In the timeline on the first frame, set its value to 1 to enable it ahead of time.



Add and name your sprite object to the scene
Next, add your sprite object to the scene and give it a suitable name. For example in the image above, I named it "fire1_sprite". The reason for the "1" in the name is in case I wanted multiple unique instances of the sprite in that scene. This name is important since the controller script will be referencing it.



Mouthe needs both an anim_controller and a swap_controller
Now create two float-type controllers for that sprite element. The first one will be the animation controller, so name it something sensible like "anim_controller". The second one will be the swap controller (I usually name it "swap_controller", and is optional and only required if you want to animate two different sets of sprites on the same sprite element (like in the second .an8 file where the mouthe sprite has a run and jump animation that it swaps between).

What you just created were the controllers that tell the controller script which frames to animate for what set of textures. Next you'll have to create the element that holds the controller script itself. There is some flexibility in how you do this, but you can follow the next bit of instructions for an easy, organized way to manage your controller scripts.



Creating the containers for the controller scripts
First, add a target and name it "sprites_scripts" (or something similar). This will be the "container" that holds all of the controller scripts.

Next, add another target and name it based on the sprite you wish to control. For example in the image above, I named it "fire1_script". This target is what will hold the controller script. Then parent the "sprites_scripts" target to this target you just created. In the timeline, it should look something like in the image above.

Step 3: Configuring the controller script
In a text editor, open up the "sprite_controller.txt" file. There is a set of parameters that you need to configure in order for the script to know what textures to work with. Unless you are intentionally modifying the controller script to change its core programming, only modify the parameters that are between "/* FILL OUT ANIMATION DATA HERE */" and "/* EVERYTHING PAST HERE, LEAVE ALONE */"

Below is a description of each parameter. Please change any or all that are required for your specific sprites.

function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Enable/Disable$enabled = 1;
Required. Enables or disables the animation of that sprite. Handy if you have large amounts of sprites and just want to have a single one active while you work with it.


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Texture Name$texName = "";
Required. This is the name of the texture that you made (circled in red in the image in Step 1).


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Animation Controller Details$controllerAnimFloat = GetAttributeFloat("Sprite_Element_Name", "Anim_Controller_Name");
Required. Change "Sprite_Element_Name" to the name of the sprite element it's controlling. For example, it was "fire1_sprite" in Step 2. Change "Anim_Controller_Name" to the name of the animation controller you created for that sprite element. This was named "anim_controller" in the Step 2 example.


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Animation Controller Type$controllerAnimType = "multiplier";
Required. There are two options to specify for how the controller script handles the values passed in the animation controller you specified above. The default option, "multiplier", interprets the values as the percentage of the animation cycle you want to play. For example, a value of 0 means it's the first frame of the animation cycle. A value of 1 means it's the last frame of the animation cycle. A value of 0.5 means it's the middle frame. A value of 2.5 means it's the middle frame, except in the second animation loop.

The second option is "frame". This allows you to specify the exact frame of the animation cycle (starts at 0). So a value of 0 means it's the first frame. A value of 1 means it's the second frame. A value of 51 means it's the 50th frame. If the animation cycle has only 25 images in its sequence, then a value of 51 would mean that it's at the start of the second loop.


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Swap Controller Details$controllerSwapFloat = GetAttributeFloat("", "");
Optional. Leave blank if you don't need to swap the animation cycle with a different one for that particular sprite object. Fill the first string with the name of the sprite element it's controlling. For example, it was "fire1_sprite" in Step 2. Fill the second string with the name of the swap controller you created for that sprite element. This was named "swap_controller" in the Step 2 example.


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Texture File Name - Prefix$texPrefix.push("");
Required. Fill in the quotation marks with the prefix of the texture file the animation cycle is using. For example, if a texture file is named "file_0001.png", then "file_" is the prefix.

If you are using a swap controller, then for each set of textures you need to animate, you have to add another '$texPrefix.push()' line. This is also required for all of the .push() parameters. The first .push() parameter corresponds to the value 0 in the swap controller. The second .push() parameter corresponds to value 1 in the swap controller. Etc, etc. Don't mix up the order of the .push() parameters.


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Texture File Name - Number of digits$texDigits.push();
Required. The texture files in the animation sequence must be numbered sequentially with the same number of digits. "file_1.png" will not be considered in the same sequence as "file_0002.png". It must be named "file_0001.png". The parameter above specifies this number of digits. For example, "file_0001.png" has 4 digits, so you would place the number 4 inside the parenthesis with no quotation marks (it would look like '$texDigits.push(4);')


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Texture File Name - Type$texType.push("");
Required. Set the file type. It'll be either png, bmp, gif, or jpg. For example, if the sequence of files were png files, it would look like '$texType.push("png");'


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Texture File Name - Path$texPath.push("");
Required. Set the path to the sequence of files for that sprite. If you want to set a path relative to the .an8 file, and the files are in subfolders of that .an8 file, you would use something like '$texPath.push("./Path/To/Files/");'. Otherwise if you want to specify the absolute path to it, set it like '$texPath.push("C:/Path/To/Files/");' Make sure a forward slash is at the end for whichever of these options you choose.

If you placed all of the textures in the same path as what you specified for the texture directory in File->Configure, you can leave it as just '$texPath.push("");'. Note however that the script does not and can not look through subfolders.


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Texture File Name - Starting Number$texStartingFrame.push();
Required. Use the same number as the first file in the sequence. For example, if the first file was named "file_0001.png", the parameter would look like '$texStartingFrame.push(1);'. If the first file was named "file_0024.png", it would look like '$texStartingFrame.push(24);'.


function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Texture File Name - Ending Number$texEndingFrame.push();
Required. Use the same number as the last file in the sequence. For example, if the last file was named "file_0036.png", the parameter would look like '$texEndingFrame.push(36);'. If the last file was named "file_0125.png", it would look like '$texEndingFrame.push(125);'.

-----

If you want to make it easier to read which set of .push() parameters applies to which set of file sequences, you can re-order it to look like this:

function selectText(containerid){if(document.selection){var range = document.body.createTextRange();range.moveToElementText(document.getElementById(containerid));range.select();}else if (window.getSelection){var range = document.createRange();range.selectNode(document.getElementById(containerid));window.getSelection().removeAllRanges();window.getSelection().addRange(range);}}Alternative way to set multiple swap parameters// Swap value 0$texPrefix.push("");$texDigits.push();$texType.push("");$texPath.push("");$texStartingFrame.push();$texEndingFrame.push(); // Swap value 1$texPrefix.push("");$texDigits.push();$texType.push("");$texPath.push("");$texStartingFrame.push();$texEndingFrame.push(); // Swap value 2$texPrefix.push("");$texDigits.push();$texType.push("");$texPath.push("");$texStartingFrame.push();$texEndingFrame.push();


Step 4: Applying the controller script
Assuming you completed Step 3, copy the entire controller script to your clipboard (Ctrl+C). Next, double-click on the controller script target you had made in Step 2. Click on the '...' button next to the Location details, set the Expressions to 'On', and click the 'Edit' button. Select everything in the text field that pops up and press Ctrl+V to replace it with the controller script.


Copy the controller script into the script target's position controller
The reason we're using a controller script target and applying the controller script to its 'position' controller is so that we can bypass a bug in Anim8or, in which after setting a controller as an Expression, you can no longer access it via the timeline (or any other means aside from editing the .an8 file in a text editor). The only way you can access a controller is through the element's dialog box and clicking on the buttons corresponding to its preset controllers. Until this bug is fixed, you'll have to do it this way. Otherwise you don't have to create controller script 'targets' and instead just add controllers directly to the controller script container target you had first created. Then you'd apply the controller scripts to these controllers.

Step 5: Animating
If you want to animate the textures, you have to keyframe the animation controller and (optionally) the swap controller assigned to that sprite element. For example, in Step 2, this was "anim_controller" and "swap_controller".

Animation Controller
This is the main meat of the animation and tells the controller script which frame of the animation cycle corresponds to which frame in the timeline. Keyframe this on the timeline to any values you need, based on the animation controller type parameter you specified in Step 3. Anim8or will interpolate between these values on the timeline. Anim8or will interpolate between these values on the timeline and the controller script will round this value to the nearest integer, thus picking which frame of the animation cycle to use.

Anim8or currently doesn't have a means to create linear keyframes (that I know of, anyway). Meaning if you look in the graph editor, the paths connecting the keyframes will all look like curves until you manually change the path nodes. If you want to slow down or speed up the animations then having curves is fine. Otherwise adjust the node handles so that these paths are as linear (straight) as possible. You may have to set the keyframe type to "corner" or "step" in order to accomplish this.

Swap Controller
This controller swaps between the sets of animation sequences you had specified in Step 3 (using the .push() parameters). The first set corresponds to the value 0. The second set corresponds to the value 1, etc etc. Keyframe this on the timeline to any values you need. Anim8or will interpolate between these values on the timeline and the controller script will round this value to the nearest integer, thus picking which set to use. I recommend using "step" type keyframes for the swap controller.


Important things to know or remember

* Always create and use the "sprites_enabled" boolean controller in each scene that you use this controller script. A value of 1 is enabled, 0 is disabled.
* Crashing happens much more often thanks to this script. Save often.
* If crashing occurs as soon as you open the Anim8or project and click on one of the sprites or controllers, set "sprites_enabled" to 0 first, then click on them. It *shouldn't* crash after you enable it again during the same session, though sometimes it will anyway. Again, save often.
* If you have a non-syntax-related error in your controller script parameters, you'll have to manually troubleshoot the problem and figure out what parameters are missing or incorrect. So double-check that each parameter has the right value.
* Sometimes if you make major changes to the scripts or add new controller scripts, the textures will no longer swap in the workspace. This usually happens if it looks for a file that's not there (usually caused by an incorrect parameter), and almost always during the first time you set up the scene for controller scripts. To avoid frustration, once you're sure that the parameters are correct and you've set up the scene as described in the steps above, save the project and restart Anim8or. Then try moving through the frames to see if it works.
* If you can't get it to work no matter what, look through the two provided .an8 examples for reference and/or post in this topic and attach the .an8 file and associated textures (put it all in one .zip file). If you don't want to have your project available to the public, PM me and we can work it out
* Only tested to work with Anim8or development release 1210. I recommend using this version or higher
* Report bugs, questions, or comments by posting in this topic

Video Tutorial

Part 1 | Part 2

Hypure:
Your the man, guy! It looks tricky... But it's worth it.  Thanks Raxx!   8)

Steve:
Wow, nice job, Raxx!

neirao:
Very cool Raxx! a little complex but easy to implements! :)
for a help in "initial square' i go post in "Script page" my "square standing" script(tree script modified) ...

neirao:
here the script(i posted time ago...) 
http://www.anim8or.com/smf/index.php/topic,1705.msg37891.html#msg37891

:)

Navigation

[0] Message Index

[#] Next page

Go to full version