How to start an animation every time a gui.button is clicked?

Hello forum. I need some quidance on how to make a texture animation on a GameObject to run, every time a gui.button is pressed. Let me explain what I have done so far. I have the “Texture Swap Animator.js” script from WiKi, attached to a GameObject, which I call to run through another js script, that contains a GUI.Button.

The Texture Swap Animator script is modified so, it only runs once, without looping. I want to make the script run from start everytime I hit the GUI.Button. How can this be done? Need your help to make it work as it should.

Here is the codding of the “Texture Swap Animator.js”:

#pragma strict

    var frames : Texture2D[];
    var framesPerSecond = 10.0;
     
    function Update () 
 {
        var index : int = Time.time * framesPerSecond;
        if (index < frames.Length)
        {
            renderer.material.mainTexture = frames[index];
        }
}

Here is the coding of the script with the GUI.Button that is calling the above script to function:

#pragma strict
    var native_width :  float = 480;
    var native_height : float = 320;
    var btnTexture1 : Texture;
    var Cam1 : Camera;
    var Cam1On : boolean = false;
 
    function Start() 
    {
    Cam1.enabled = false;
    }   
    
    function Update() 
    {
    if (!Cam1On) 
    {
    Cam1.enabled = false;
    } 
    else 
    {
       Cam1.enabled = true;
    }
    }

function OnGUI ()
    {
        //set up scaling
        var rx : float = Screen.width / native_width;
        var ry : float = Screen.height / native_height;
     
        GUI.matrix = Matrix4x4.TRS (Vector3(0, 0, 0), Quaternion.identity, Vector3 (rx, ry, 1));
     
        //now create your GUI normally, as if you were in your native resolution
        //The GUI.matrix will scale everything automatically.

 if (!btnTexture1) 
               {
            Debug.LogError("Please assign a texture on the inspector");
            return;
            }
            
            if(GUI.Button(Rect(230, 120, 40, 25), btnTexture1))    
                {
                Cam1On = !Cam1On;
                }

I need to mention that the GameObject with the attached “Texture Swap Animator.js” script is visible only when the “Cam1On=true”, by clicking that GUI.Button.

Could someone be kind enough to point me to the right direction on how to implement this functionality to my Unity project?

Thank you all in advance for your time reading this post. Waiting for your answers.

It looks to me like what you’re wanting to do could be done with a public boolean variable on the main script. In the following I put the main script on a gameobject named MyGameobject and I named the main script TestCam. The texture script is on a separate object. The bulk of the texture script doesn’t run unless the gui button created in the main script is pressed and the runMe variable gets temporarily set to true.

#pragma strict

var frames : Texture2D[];
var framesPerSecond = 10.0;
var MyGameobject : GameObject;
private var runMe = false;

function Update () {

	runMe = MyGameobject.GetComponent(TestCam).runMe;

	if (runMe) {
	
		MyGameobject.GetComponent(TestCam).runMe = false;
	
	    var index : int = Time.time * framesPerSecond;
	    
	    if (index < frames.Length) {
	        renderer.material.mainTexture = frames[index];
	    }
	
    }

}
#pragma strict
var native_width :  float = 480;
var native_height : float = 320;
var btnTexture1 : Texture;
var Cam1 : Camera;
var Cam1On : boolean = false;
public var runMe = false;

function Start() {

	Cam1.enabled = false;

}  

function Update() {

	if (!Cam1On) {
		Cam1.enabled = false;
	}
	
	else {
	   Cam1.enabled = true;
	}

}

function OnGUI () {

    //set up scaling
    var rx : float = Screen.width / native_width;
    var ry : float = Screen.height / native_height;
 
    GUI.matrix = Matrix4x4.TRS (Vector3(0, 0, 0), Quaternion.identity, Vector3 (rx, ry, 1));
 
    //now create your GUI normally, as if you were in your native resolution
    //The GUI.matrix will scale everything automatically.

	if (!btnTexture1) {
    	Debug.LogError("Please assign a texture on the inspector");
        return;
    }
       
    if(GUI.Button(Rect(230, 120, 40, 25), btnTexture1)) {
    	Cam1On = !Cam1On;
    	runMe = true;
    }

}

Thanks @QuinnWinters for your time, modifing the code I’ve posted. I’ ll give it a try and I’ ll report back about the outcome.

Well @QuinnWinters, I have tried to run the code you posted, but I think I don’t quite following.

In my scene there is a gameobject named “Cam1” with a boolean “Cam1On” that if set to true, the camera is enabled and sees the other gameobject named “Animator” that the “TextureSwapAnimator” script is attached to.

I understand that, there should be a public variable, you named it “runMe = false” at the “TextureSwapAnimator” script, which should be changed to “true”, through the main script with the GUI.button, that you named it “TestCam”, by accessing it as a component, right?

So for now we have 2 gameobjects, the one in my scene named “Animator”, that is the “TextureSwapAnimator” script attached, and the other gameobject, named “Cam1”. Should there be another gameobject to function properly? I’m quite puzzled.

Finally after some trial and error attempts, managed to make the code run, as expected, following @QuinnWinters suggested code snippet.

But there is a catch. Although the “TextureSwapAnimator” script is running on gui.button click, it is only run for one time that button is pressed.

What modifications should be done to the code, to make it run from start, every time the user clicks that gui.button?

Sould a counter be attached to the script, and if so, how this can be coded?

Anyone having a suggestion, please respont to a noob’s request. Thank you all.

Well it seems that I was mistaken by the behaviour of @QuinnWinter’s suggested code snippet. Although the logic of variable acting as a trigger to make the texture animation started, is valid, the implementation does not functions as it should.

Let me explain. The variable “runMe” that is set at start as “false” should be declares “true”, on the click of the gui.button, and only by this action of the user. Well the script at Unity play, does not behaves like that, instead either the variable “runMe” cannot be chanced, or either the player starts with the variable declared as “true”, even without the user interaction!

Any clues why this is happening? Maybe there is an alternative approch to start the texure animation only on a gui.button click and every time this click taken place.

So if anyone of you have an idea, please respont to a new comer in Unity and programming. Thank you all in advance for your answers.

My apologies, I took it that you wanted the texture animation code to run only once. It’s easily fixed to make it run until the gui button is clicked again.

Remove the following line from the texture animation script:

MyGameobject.GetComponent(TestCam).runMe = false;

And in the main script in the gui button click section replace runMe = true; with:

runMe = !runMe

Hi again @QuinnWinters and thanks for your time to review the code snippet. If you read through my post (06:44 pm), the script does not functions as it should be. I have ommited the lines you suggested, and now and before that, the textures advance without user interaction on button click, as if the variable “runMe” is not be taken into account by the compiler.

Any thougths or a work arround on this issue?

I don’t see a reason why the runMe variable should get set to true without user interaction, but you will need to alter the code to change the texture so that it no longer animates when runMe gets set back to false. As the code currently is the texture gets told to start animating but is not told to stop when runMe goes back to false. Adding an else after the if in the texture script and setting the texture back to its normal mode without the frames array should accomplish this.

Sorry for being that noob, to ask again but how should the texture script be modified to its normal state, without the frames array?
I don’t quite following you @QuinnWinters. The frames should be there to animate, don’t they?

I tried to modified as you suggested @QuinnWinters, so the texture scripts is now as following:

#pragma strict

    var frames : Texture2D[];
    var framesPerSecond = 10.0;
    var myGameobject : GameObject;
    private var runMe = false;
     
    function Update () 
    {
     
        runMe = myGameobject.GetComponent(CameraTrigger).runMe;
     
        if (runMe) 
        {
       
            //myGameobject.GetComponent(CameraTrigger).runMe = false;
       
            var index : int = Time.time * framesPerSecond;
           
            if (index < frames.Length) 
            {
                renderer.material.mainTexture = frames[index];
                 Debug.Log (Time.time + ", " + framesPerSecond + ", " + frames.Length + ", " + index);
            }
            else
            {
            index = index % frames.Length;
            renderer.material.mainTexture = frames[index];
            }
        }
     
}

But I can get the funtionality I want. The texture on users click has already started, as I can see the 2nd to 3rd frame of the animation, as the camera is on. Furthermore, on the last frame it loops from start.

This is not good. It should only start on the gui.button clicked, and everytime the user clicks this button, should start from the beginning. What are we measing here?

After the if statement in the texture script put the following to stop the texture from animating.

else {

	renderer.material.mainTexture = renderer.material.mainTexture;

}

Well, @QuinnWinters, I have added the “else statement” as you suggested and I get no compilation errors but I get this message in console:

“(63,63): Warning BCW0020: WARNING: Assignment made to same expression. Did you mean to assign to something else? (BCW0020) (Assembly-UnityScript-firstpass)”

The code now should be function as it should, right? Well It does not, cause on gui.button click, still I miss the first 2 frames of the animation, as the camera switches on (Cam1On = !Cam1On).

Also the texture script advances to the end and stop, which is good to do so.

If I click again the gui.button I see the last frame of the texture script. It does not rewind so to start from frame 1.

Below is a screenshot to show what is the functionality so far:

Why this is happening as, I have reseted the frames as you suggested? Is there something else to think off, to make this script work o.k.?
Meaning, startting the camera to see from the 1st frame of animation, and on everytime the button is clicked to start from the very first frame.

My apologies, I’m not sure what I was thinking with that last reply, that does indeed give a warning. Up until now I’ve just been looking at the code and not trying to run it, but now that I’ve tried to run it I can’t get the texture to actually animate. I tinkered with it for a bit with no luck, so I went an alternate route to achieve the same goal. Instead of using the texture animation script you were using I used a different one. After some tweaking I made it reset to frame 1 when the gui button stops it, and start from frame 1 when the gui button starts it again. Perhaps this will work for you.

#pragma strict

var MyGameobject : GameObject;
private var runMe = false;
var uvAnimationTileX = 10; //Here you can place the number of columns of your texture sheet.  I used a texture with 10 columns.
var uvAnimationTileY = 1; //Here you can place the number of rows of your texture sheet.
var framesPerSecond = 2.0;
var myStart : float;

function Update () {

	runMe = MyGameobject.GetComponent(TestCam).runMe;

	if (runMe) {
	
		if (myStart == 0) {  //If myStart is 0
			myStart = Time.time;  //Set the time to be the current time
		}
	
		//Calculate index
		var index : int = (Time.time-myStart) * framesPerSecond;
		//Repeat when exhausting all frames
		index = index % (uvAnimationTileX * uvAnimationTileY);
	 
		//Size of every tile
		var size = Vector2 (1.0 / uvAnimationTileX, 1.0 / uvAnimationTileY);
	 
		//Split into horizontal and vertical index
		var uIndex = index % uvAnimationTileX;
		var vIndex = index / uvAnimationTileX;
	 
		//Build offset
		//V coordinate is the bottom of the image in opengl so we need to invert.
		var offset = Vector2 (uIndex * size.x, 1.0 - size.y - vIndex * size.y);
	 
		renderer.material.SetTextureOffset ("_MainTex", offset);
		renderer.material.SetTextureScale ("_MainTex", size);
	
	}
	
	else {
    
    	renderer.material.SetTextureOffset ("_MainTex", Vector2(0,0));  //Reset the texture to frame 1
    	myStart = 0;  //Set the time to 0 so it can start over when the button is pressed again
    
    }

}

Hello @QuinnWinters. Nice to have you back on this thread.

Well this code you posted, certainly looks much better than the other script. I have tried it, by placing a strip of 10 frames. The script runs every time user clicks the gui.button, plays from start to finish, and runs again from start (looping), so this function is o.k., as expected.

Well, it is not as much important to have a run ones script for what I want to achieve in my app, but for learning purposes only, how should I stop the looping? I have tried to commented out, lines 23 and 44, but the script keeps looping. I have tried anything else I can thing off, but no good.

Please respond, so we can close this thread successfully. Thank you for your time and effort.

You can add in another boolean to check if it’s played all the frames in the animation, and if it has have it stop playing the animation on the last frame. I cleaned up the code and fully commented it so you can understand exactly what’s going on in each line.

#pragma strict

var myGameObject : GameObject;														//The gameobject with the TestCam script on it.  Drag and drop the object into this slot in the inspector
var uvAnimationTileX = 10;															//The number of columns of your sheet.  I used a texture with 10 columns
var uvAnimationTileY = 1;															//The number of rows of your sheet
var framesPerSecond = 2.0;															//Frames of animation to run each second
private var myStart : float;														//For temporarily holding the time
private var runMe = false;															//Should the texture animate or not
private var totalFrames : float;													//How many frames the animation has
private var animationDone = false;													//Has the animation finished played

function Update () {

	runMe = myGameObject.GetComponent(TestCam).runMe;								//Get the runMe variable from the TestCam script
	
	if (runMe) {																	//If runMe is true
	
		if (myStart == 0) {															//If myStart is 0
			myStart = Time.time;													//Set the time to be the current time
			animationDone = false;													//The animation has not finished playing
			totalFrames = uvAnimationTileX * uvAnimationTileY - 1;					//Get the total number of frames in the animation (and subtract one to keep the animation from going back to frame 1 when it finishes)
		}
		
		if (!animationDone) {														//If the animation is not finished playing
		
			var index : int = (Time.time - myStart) * framesPerSecond;				//Calculate index.  Index is the current time minus the time the animation started times the frames per second
			index = index % (uvAnimationTileX * uvAnimationTileY);					//Multiply the number of columns by the number of rows, divide them by index and set index to equal the fraction that's left over
		 	
			var size = Vector2 (1.0 / uvAnimationTileX, 1.0 / uvAnimationTileY);	//Size of every tile.  Because there are 10 columns and 1 row this equals out to 0.1, 1 (size.x and size.y)
		 	
			//Split into horizontal and vertical index
			var uIndex = index % uvAnimationTileX;									//Horizontal index, aka the U of UV, is the remaining fraction of the number of columns divided by index
			var vIndex = index / uvAnimationTileX;									//Vertical index, aka the V of UV, is the number of columns divided by index
		 	
			var offset = Vector2 (uIndex * size.x, 1.0 - size.y - vIndex * size.y);	//Build offset.  V coordinate is the bottom of the image in opengl so we need to invert
		 	
			renderer.material.SetTextureOffset ("_MainTex", offset);				//Change the texture offset
			renderer.material.SetTextureScale ("_MainTex", size);					//Change the texture size (tiling)
			
			if (Time.time - myStart >= totalFrames / framesPerSecond) {				//If the elapsed time is greater than or equal to the amount of time it takes the animation to play
				animationDone = true;												//The animation has finished playing
			}
		
		}
	
	}
	
	else {																			//If runMe is false
    
    	renderer.material.SetTextureOffset ("_MainTex", Vector2(0,0));				//Set the texture offset back to U = 0, V = 0
    	myStart = 0;																//Reset the time to 0
    
    }

}

Hello @QuinnWinters. I’d like to thank you for your time and effort to post the code snippet for animation on texture, with a start/stop functionality.
The code works o.k. and the comments, were useful to a new comer in Unity3d like me, to learn from.

You are certainly a skillful coder. Would it be to much, if I may ask you, to take a look at the following posts of mine, regarding some problems that I have with some coding?

A Compilation error when calling a C# from a Unityscript (js) in Unity3d:
http://forum.unity3d.com/threads/243928-Compilation-error-when-calling-a-C-from-a-Unityscript-(js)-in-Unity3d?p=1613611#post1613611

Texture on a GUI.Button disappears when scene is reloaded:
http://forum.unity3d.com/threads/244463-Texture-on-a-GUI-Button-disappears-when-scene-is-reloaded?p=1616987#post1616987

Your opinions and suggestions will be more than welcome.