Unity general questions

edited in Questions and Answers
Edit: Converting into a general Unity Q&A thread, cos it is *needed*

Hi guys :)

So I started venturing into Unity and already I've hit a bit of a stumping point...

How do I do 2D sprite animation? I've googled around but nothing seemed... Understandable. I found one piece of code that gave me the ability to run a spritesheet as a material and animate it, but it wasn't positioned correctly on the plane, it was interploated to mushy hell and not clean pixels, and there was no transparency...

I know many people have done this sprite animation thing before me sooooo...

Oh brothers (and sisters) in arms, lend me your pixelly spritey animation sword and please point me to something I can use to do animated sprites...

Thanks :D

Comments

  • Unity doesn't do it out of the box, at least until 4.3 is released.

    Many people seem to recommend 2D Toolkit or SpriteManager.
  • edited
    I can help you out, but my code is at home Will post in 2 hours if you're still struggling by then.
  • @Tuism, 2D sprite animation is tricky to accomplish in Unity. Lately I've been using 2D toolkit which makes it easy to do. There are other ways to do it, but I'm not sure if it's worth it for you to try and figure those out.

    I don't know of free libraries that do it well unfortunately.
  • It took me a while to figure out but once I got it working it really does work rather simply. Going home in an hour and I'll post the code here.
  • edited
    @Tuism on your material, at the top you will find a drop down that says "Shader", change that to something like "transparent/diffuse". that should fix your transparency issue.

    On the sprite sheet itself make sure it is not being compressed, and is set to true colour. should fix your texture looking bad. also have a look at the "Texture type"

    on the material you will find "Tiling" and "Offset", make sure those are set up right for your sprite sheet.

    but ideally you would not change the material to animate...unity has some funny stuff happening, each time you change the material it creates a new one apparently, so after a while that could add up. best to animate by changing the UV coordinates on your plane. @tbulford wrote one for his A-Maze workshop (most of the stuff I said here is stuff he found out). For my D competition entry I also wrote my own if I remember correctly, but I'm sure there must be plenty floating around on the interwebs :) hope this helped

    Edit: well seems I was a bit slow :P
  • Thanks for the prompt input guys! I'm at work so I'm not going to be able to fiddle with anything just yet anyway, so I'll take whatever asset/code/whatever that comes this way when I get home! :D

    At this stage I'm only going to venture into the free stuff for now, $400 for GMS later I'm going to stick to my own advice and not go into paid for stuff until I *need* it XD
  • @Tuism just ping me on FB and I can send you the unity project and the pdf for the workshop. Basically you will end up with a full animating 2D engine. Its certainly not the best but its free and very little code.
  • renderer.material.SetTextureScale("_MainTex", new Vector2(0.125f - (direction * 0.25f), 0.125f));
    renderer.material.SetTextureOffset("_MainTex", new Vector2(((state + direction) * 0.125f), 1 - ((action + 1) * 0.125f)));


    This bit of code will set the UVs of an object correctly assuming the spritesheet contains 8x8 tiles. Action represents the row and state represents the column. direction can be 1 or 0. I initially tried achieving this by setting the UVs manually but that turned out to be all kinds of terrible.
  • @bigbadwofl: Doesn't that leak materials? renderer.material.anything raises alarm bells for me.
  • dont know if anyone noticed but I posted the 4.3 beta link in the Montez thread : http://makegamessa.com/discussion/comment/14833#Comment_14833
  • @dislekcia could you expand on that? I'm very new to unity and although been using that method without issues, I could very well be completely wrong.
  • edited
    @dislekcia - Yes, please expand. I posted a thread about this issue today (I think it is the same issue), so I too would really like some more info. What do you mean by "Leak" materials?
  • edited
    I use the following method:

    var walkLeft:Texture; //create public variables to edit in Unity Inspector
    var walkRight:Texture; //create one for each animation you need, eg, run right, jump up etc. 
    var runRight_1:Texture;
    var runRight_2:Texture; //create one for each frame you would need.
    
    function Update () {
    
    
    counter += Time.deltaTime*frameRate; //keep track of time to animate different frames
    
    if("whatever condition eg, run jump etc" && renderer.material.mainTexture != runRight_1) {
       if(counter > 0 && renderer.material.mainTexture != runRight_1){
    			renderer.material.mainTexture = runRight_1;
    		}
    		if(counter > 1 && renderer.material.mainTexture != runRight_2){
    			renderer.material.mainTexture = runRight_2;
    		}
    		if(counter > 2){ //add more "IFs" for more frames/variables
    		counter = 0.0;
    		}
           }
    
    }


    If you add the script to your gameobject/player you should be able to upload each frame into the Inspector under the script's tabs.

    Just shout if something doesn't make sense. It's not really easy to explain like this :|
  • Before trying to figure this out... I would rather go figure out how the Unity 4.3 Beta works, because once it's out you'll already know how to use it :D It has 2D all figured out in Unity 4.3 >> Thanks @Kobusvdwalt9 for the link!
    dont know if anyone noticed but I posted the 4.3 beta link in the Montez thread : http://makegamessa.com/discussion/comment/14833#Comment_14833
  • Each time you change a material in code it creates a new instance of that material. @dislekcia has had issues with material leak I think he is best to explain that. However there are other reasons to avoid randomly changing texture in code in a non controlled way.

    Batching can only happen if objects share a material. Any change to any material means its a new instance even if its exactly the same as all the other instances. If you change a material u/v for a sprite each sprite will be a separate draw call and potentially ruin your performance. This is ofc hardly a big deal on smaller games esp if you don't care about mobile. Its however good to try keep good practices always.

    @RickyGC I really don't think having a texture per animation frame is a good approach. You loosing so much performance opportunity. The common method is to change the texture coords in the mesh. I just dropped the Unity 2D workshop project in another thread the code to do the UV swap is there. Its how we manage Toxic Bunny HD sprites in Unity and works on many platforms already.
  • @tbulford thanks for the heads up! I'll check it out :)
  • @tbulford Is modifying the offset truly creating a new instance in memory though? It is in essence still the same material? I can agree on the draw calls issue though but in cases where animated (and duplicated) sprites are the minority on the screen (the majority being taken up by terrain tiles, etc...) surely it shouldn't be a problem?

    Has anyone worked with the 4.3 beta yet, specifically working with sprite animation?
  • @bigbadwofl yip sad is it not. Just look at a material funny and Unity will create a new instance. Consider if it did not when you change the UV all objects on the scene would change. That's not typically what people would expect.
  • Hehe. Argh, these hoops we jump though. Makes me just want to get back to writing my own engines.

    But I jest, I can see the use for the nuance. Thanks, it's been enlightening.
  • I really think you own engine would face the same issues. After all the change would have cause a new instance or not. Its really a design philosophy not an error.
  • No definitely. We need to evolve with our technology but human nature makes me nostalgic for simpler times, regardless of whether things were truly better.
  • @dislekcia could you expand on that? I'm very new to unity and although been using that method without issues, I could very well be completely wrong.
    http://www.makegamessa.com/discussion/comment/11000#Comment_11000

    Check your material counts in the profiler and use Resources.UnloadUnusedAssets() to check if you've got resources floating around with no references to them. Chances are you probably do if you're modifying materials via script - every time you call renderer.material, you're creating a clone. If you do it twice, you're leaking the first clone.
  • @dislekcia that's a pretty awful bug. Although when I looked at the Unity forums they not really listing it as bug but rather as something you have to manage. Seams the common expected solution for developers is to Destroy the old material yourself. But obviously on the first clone not the original material. Sounds a bit weak to me. At least I understand the need for your Map of materials better.
  • @tbulford: Yeah, it's pretty much all managed by our framework now. The key thing was giving each object a material and wrapping any and all calls to that material so that it first checks if that material is null, if it is and it wants to do something that will alter that material (so we can't use sharedMaterial because we don't want all the objects that use that changing) it'll first clone a "personal" copy of that material for that object via renderer.material and then never ever call renderer.material again. Everything at that point goes through the material reference. Then when the object gets destroyed, it'll nuke that material if it exists.

    This means that enemies are free to share the same material as long as they don't need anything unique done to them graphically, but as soon as we start fading an individual object (for instance), it'll spawn its own material and then get dumped out of the drawing batch. It sounds complicated, but all I'm doing is letting Unity manage the draw calls for me and buffering material creation/loading as needed.
  • edited
    dont know if anyone noticed but I posted the 4.3 beta link in the Montez thread : http://makegamessa.com/discussion/comment/14833#Comment_14833
    Apparently that's supposed to be a private link or something, because I got shat on on Polycount for posting that there. :/
  • edited
    @dislekcia doesn't sound too complicated, my only concern would have been knowing exactly when Unity clones a material. Sound like its when you request the material from the renderer. Makes since since right off there you have the opportunity to make 3 or 4 changes not a clone per change. Then I assume when you set the material it works fine, but each read will clone another. Is that accurate to the experiences you have had?
  • @Elyaradine Crap, I'm sorry I wasn't aware of this.
  • Check your material counts in the profiler
    How would one do this? Sorry if this is a stupid question, but I don't know how to check if this is the issue I am experiencing. So where in the unity editor would 1 see if materials are being cloned?
  • The profiler is a pro-only feature. Open it with Ctrl+7. I don't have it at home, so I can't tell you where to look in there.
  • dislekcia said:
    @bigbadwofl: Doesn't that leak materials? renderer.material.anything raises alarm bells for me.
    You can use .material just fine (and often you want to). Just keep in mind that as soon as you do it once on a renderer, it will make a duplicate of that material. You can access .material as many times from a single renderer (or whatever parent object) it and only and Unity will only make a single internal duplicate of that material. That duplicate instanced material will also be disposed of properly when you stop running. The major determent in doing this is that it you can't batch across any objects that once shared that material as the new material will now belong to its own batch.

    Using .sharedMaterial can help when it comes to batching, but be super careful with it because it will modify the *actual* material data in your project, meaning that you could end up saving those changes permanently once your app has stopped. Sometimes this doesn't matter (ie. dynamic variables stored in the material for a shader), but it could cause big headaches too.

    We made use of both methods extensively in Bladeslinger, depending on the circumstances.
  • @Tuism - man your thread got hijacked badly, but i'm sure you can also learn from the info too. Anyway, did you find an answer to your orignal question?
  • Oh hells yes I don't mind the jack, it's all relavant info anyway :)

    I haven't had a chance to try the info out (life getting in the way of game making, what IS that?! :P), but really thanks everyone for the input! :D
  • On a related note - why can't I use transform.Translate to move by a number smaller than 1?
    For example transform.Translate(-0.1,0,0) gives me an Error...

    Is there no way of tweaking movement to a slower speed (I'm doing this per step) unless using rigidbody and velocity/addForce? :s
  • Should be transform.Translate(-0.1f, 0, 0)

    The f stands for floating point which is the value type the function is accepting. I believe without the f the number is implicitly cast as a double?
  • Also, multiply the value by Time.DeltaTime (as in transform.Translate(-0.1f * Time.DeltaTime, 0, 0) )

    So that you move in units per second instead of units per frame (assuming this code is in Update() )
  • THANKS! :D That worked brilliantly! Not quite understanding the difference between 1 and 1f - cos they seem to be completely different units (if I move it by 1 it's too big, if I move it by 1f it's not big enough, clearly they're different units...)

    Also then, I've been reading about getComponent but can't get it to bloody work.

    I have a controller with a public variable
    the script is called RunningController and the variable is called RunningSpeed...

    I want to call that variable from another script,
    So this is far as I got, but it doesn't work...

    globalRunningSpeed = GetComponent<RunningController>().runningSpeed;

    This script lets the game start, but it throws errors during runtime...
    NullReferenceException: Object reference not set to an instance of an object
    BuildingBehaviour.Update () (at Assets/BuildingBehaviour.cs:17)

    Help?
  • @Tuism, 1 and 1f isn't different units. What probably is happening is that your code is frame rate dependent. Like @Stray_Train said, you need to multiply the value by Time.DeltaTime in order to make it frame rate independent.

    As far as the getComponent goes, you need to specify the object that you want to get the component from. So it should be specificObject.GetComponent<RunningController>().runningSpeed;

    An easier way to do it (although slightly messy) is to just declare a public reference. So in the script that you need the speed do something like :

    public RunningController myRunningController


    you can then access the speed with :

    myRunningController.runningSpeed


    You can then just drag and drop the game object that has the RunningController script on it into the reference to link it.
  • edited
    [EDIT: GAH! Out-helped by @Rigormortis :)]

    You have to be getting the component from an object :). So you have to tell it where to took before you can tell it what to look for :).

    It's like Gamemaker's syntax kinda: gameobject.variableName

    So in this case nameOfObjectWhereVariableIs.GetComponent<RunningController>().runningSpeed;

    But, since the script is on the same object as the script that's calling get component, just use gameObject (which refers to the game object the script/component is on).

    So the code would be: gameObject.GetComponent<RunningController>().runningSpeed; :)
  • edited
    :O

    I don't get it... I've been trying that and it hasn't been working...

    I have a gameObject. It's named controllerRun. It says so in the hierarchy.
    I have these instantiated prefabs, Building01, Building02, Building03.
    In them are a script component BuildingBehaviour.
    In that script are these lines:

    using UnityEngine;
    using System.Collections;
    
    public class BuildingBehaviour : MonoBehaviour {
    
    float globalRunningSpeed;
    	
    	// Use this for initialization
    	void Start () {
    		// transform.localScale += Vector3(0.1,0,0);
    	}
    	
    	// Update is called once per frame
    	void Update () {
    		// transform.localScale += Vector3(0.1,0,0);
    		
    
    		globalRunningSpeed =  controllerRun.GetComponent<RunningController>().runningSpeed;
    		
    		transform.Translate(-globalRunningSpeed*Time.deltaTime,0,0);
    
    		if (transform.position.x < -40) {
    			Destroy(gameObject);
    		}
    	
    	}
    }


    The last bits regarding globalRunningSpeed are what matters, I think...

    What am I doing wrong here @_@
  • @Tuism, unity doesn't actually know where to find controllerRun. You need to have a reference to the object in the script. Replace the line with this and it should work :

    globalRunningSpeed =  GameObject.Find("controllerRun").GetComponent<RunningController>().runningSpeed;


    an easier way that might make more sense as well would be to do something like this:

    using UnityEngine;
    using System.Collections;
    
    public class BuildingBehaviour : MonoBehaviour {
    
    float globalRunningSpeed;
    public RunningController runController;
    	
    	// Use this for initialization
    	void Start () {
    		// transform.localScale += Vector3(0.1,0,0);
    	}
    	
    	// Update is called once per frame
    	void Update () {
    		// transform.localScale += Vector3(0.1,0,0);
    		
    
    		globalRunningSpeed =  runController.runningSpeed;
    		
    		transform.Translate(-globalRunningSpeed*Time.deltaTime,0,0);
    
    		if (transform.position.x < -40) {
    			Destroy(gameObject);
    		}
    	
    	}
    }


    Then you just have to drag and drop the object with the RunningController script on it into the public variable and it will link it.
    Thanked by 1Tuism
  • That first bit worked - I'll stick with that for now :P

    THAAAAAANKS :D

    Cross-engine syntax and stuff is so hard to wrap head around @_@
  • Just beware of using gameobject.find, or getcomponent, in your update calls (or any way that will call it that often - like property gets being called from update). It's quite expensive. An alternative would be to call them in Start() or to create a property that can you expose via editor to link up there.
    Thanked by 1Tuism
  • edited
    So... You can declare any variable as public, with the script name as type, and then link it up in the unity interface as reference?

    public RunningController runController;

    Can be read as:

    public nameOfExternalObject objectNameInThisScript;

    Is that right?

    Omg unity syntax XD

    Edit: So I'm trying to use @rigormortis's second method, but in the component of my prefab I have this line that I can't drag objects into - neither the script object nor the object in the hierarchy that contains the runningController script... I've tried many combination of the three things, trying different combinations... It doesn't let me put anything into that field. What' the logic of this syntax... I'm not getting it XD
  • @Tuism, I'm not 100% sure, but I think the problem is that if you want to link a reference for a prefab it has to be with another prefab. You can't link a specific object in the scene to a prefab.
  • @tuism: correct.

    Also, to use prefabs they have to be instantiated first. Either in game hierarchy, or as a gameobject property linked via editor. If you need to instantiate prefabs in run time the best method is to have a script that can hold cached references to prefabs in design time, and then just clone them in run time.
  • edited
    Class Global
    {
        public static float runningSpeed = 0.5f;
    }


    //stuuuuuufffff....
    
    //Hey, I want to know what our running speed is right now...
    currentSpeed = Global.runningSpeed;
    
    //Now I know! Also, no slow lookups, yay for static variables - which behave like GM's global variables
    
    //But what if I want to change the value?
    Global.runningSpeed = 0.2f;
    
    //Holy crap, it's public, we can just modify the thing, wow
    Thanked by 2Rigormortis Tuism
  • Holy crap I feel REALLY stupid now.

    @dislekcia your code is theoretically *that* simple. I get that it should be that simple.

    But where the heck do I stick that first bit of Class Global, etc? I tried putting it in something like this:

    using UnityEngine;
    using System.Collections;
    
    public class globalVariables : MonoBehaviour {
    
    class Global
    {
        public static float runningSpeed = 15f;
    }
    	// Use this for initialization
    	void Start () {
    	
    	}
    	
    	// Update is called once per frame
    	void Update () {
    	
    	}
    }


    And stuck that onto my empty gameObject called gameController, which is in the scene, in the hierarchy. I've tried renaming this to "Global". no success.
    And then tried to access Global.runningSpeed from other scripts, and it tells me "Global does not exist in the current context".

    Where do I stick a class?
  • Have you tried declaring the speed variable as public static. Then you can call from any script and change value in inspector
  • so if I declare the variable as public static in any script, I can access it from any other script?
Sign In or Register to comment.