Unity general questions

Comments

  • edited
    @Tuism, yes.

    //how to declare it
    using UnityEngine;
    
    public class GlobalVariables : MonoBehaviour
    {
        public static float runningSpeed = 15;
    }


    //how to access it
    public class Character : MonoBehaviour
    {
       void Update()
       {
           transform.Translate(GlobalVariables.runningSpeed*Time.deltaTime, 0,0);
       }
    }


    You can test if this is correct, but I'm pretty sure you don't even need to attach the global variables to an object even. It should work by just having the script in the project hierarchy.
  • edited
    @Tuism: You're declaring a class inside a class there, so it's only going to be accessible inside that globalVariables class "space". Dunno why you're doing that, not all classes need to inherit from MonoBehavior, so that's why the "Class Global" doesn't have any inheritance stuff going on. It's essentially just a storage class for static variables that exists in the C# "space".

    Also, it's good practice to define a class type with leading capital letters - that way it's easy to tell if you're trying to access a class or a variable. Programming best practices, etc ;)

    @Rigormortis: 15f. Otherwise you're forcing a conversion from an int just to initialise a variable...
  • OOOOKKKKAAYYY!!! I think I finally get it...

    First of all I have/had no idea what a "class" is. Now I think I have an inkling.

    That Global isn't a keyword like I thought it was - it's just a name - I could name it ANYTHING and it would work (as long as it doesn't dupe some other keyword) - right?

    Then, as long as I declare a variable static - it'll be accessible "like" global variables, by referring to the cClass first, right?

    Classes are always accessible from all scripts <--- is that right or is there some kind of restriction?

    IF classes are always accessible from all scripts, why is static needed to make that variable accessible via its parenting class?

    Sorry, this may all sound stupendously noob but I seriously have problems understanding all this Class stuff. Never really heard of it before.
  • edited
    You could have an "Enemy" class. Many different GameObjects could have the Enemy class attached to it, and they would all behave the way you've scripted them to in your Enemy class. But if you've got a bunch of variables in the class (e.g. current and max hit points, move speed) each of the GameObjects could have different values for those variables. You could have puny, fast-moving little enemies, or giant tanky ones, but they could all be using the same Enemy class (or at least be derived from it).

    When you access a public variable on a GameObject (e.g. myGameObject.GetComponent<Enemy>().maxHP), you're getting the value of the variable on that instance of the Enemy class.

    But when you mark something as "static", it becomes something that can only ever have one value. It isn't tied to any instance of the class in your scene. As far as I know (which I don't really; I don't work with this kind of scripting much to be honest), you access these directly, e.g. Enemy.DifficultyScale, rather than accessing them via an instance of them (like GetComponent).

    My I'm-not-really-a-programmer explanation of it, anyway. :P
  • @Tuism: @Elyaradine is pretty much on the money with all of that. Basically, to put it all in terms of Game Maker, a class in C# is an object in GM. They're definitions for how things are going to behave, but you need to create instances of them first. In GM you create instances in the room view or via instance_create() in code. In Unity you create instances in the editor and adding scripts to them (except those scripts are "behaviors" of a larger object, but it's pretty much the same thing) or by adding scripts to a GameComponent via code.

    The major difference between the two is that the class representation of objects is much more flexible - you can define any variables you want (much like in GM, except variables have much more specific scopes in C# than in GM) but the big thing is you can define any methods you want too. In GM all you have are the existing "actions" (methods) that GM has set up already, stuff like Step is pretty much the same as the Update method in Unity (because Unity calls that for you on MonoBehaviors assigned to GameComponents) but you can create other methods as well, stuff like FindNewTarget() or whatever.

    You can define a method as static, so you could have something like Global.FindNearestEnemy(Vector3 location) that would return an Enemy object, if you defined that way in the Global class.
  • edited
    OK, I think I got the idea of classes and objects. The biggest challenge I have right now frankly seems to be:

    Where to put scripts... what is accessible from where - in GM and flash it's quite simple - there's a root/stage/whatever, and everything under it is nested like a bunch of folders... Like root.object.gun.x - I can dig that.

    But somehow Unity's object space seems... I dunno! It doesn't make sense to me!
    I was reading this documentation:
    Physics.IgnoreLayerCollision
    static void IgnoreLayerCollision(int layer1, int layer2);
    static void IgnoreLayerCollision(int layer1, int layer2, bool ignore);
    Description
    Makes the collision detection system ignore all collisions between any collider in layer1 and any collider in layer2.

    You can set the default values for your project for any layer combinations in the Physics inspector. See Also:
    It LOOKS simple enough, but where do you call this Physics.IgnoreLayerCollision(layer8, layer9) from!? I've put it into a gameController to call at startup(), nope.

    As far as I can tell EVERY bit of script MUST be on a game object, and every game object is in the scene, and I read someone said IgnoreLayerCollision isn't being called specifically from an object to ignore another layer (which is what I thought it was), but instead acts like a global options setting, that tells all of layer8 to ignore all of layer9...

    But where the heck do you declare that??!?

    Thanks everyone four your patience with these Unity questions. This is really much more of a deep end than GMS and Flash for me...

    Edit: Thanks to @Gazza_N I found the collisions matrix and used that - though I'm still massively frustrated with understanding the syntax and conventions of Unity...
  • To simplify and shortify:

    Where do I call IgnoreLayerCollision? Can I please have a usage example?

    Is it in a gameObject in the scene in a StartUp()?
    Is it anywhere outside StartUp() or Update() call?

    Thanks guys for your patience @_@
  • Should work fine from
    void Start () {
    //here
    }
  • @Tuism - depends on when you want it to happen. From what I understand, if it is in Start() it will be called when the scene loads. If it is in update() it will be called once every frame and if it is outside of those it needs to be in a method that gets called somewhere or in a coroutine that gets called somewhere. Please note that I am also a beginner so I hope one of the more senior devs also gives you an answer.
  • Thanks guys :D

    Then I just realised that conceptually I didn't understand it right: Is it a single on/off switch that happens once and the ignoring happens the whole time, or is it called every frame on Update to maintain the ignore?
  • No You should not need to call it every frame
  • @Tuism - if it is in update it will be checked every frame, but not changed every frame. so if it has been set to ignore, it will be seen as ignore every frame until you change it. But as Pixel_Reaper said you should not need to call it every frame, it should be fine in start().
  • @Tuism here's a little gem of a page from the Unity docs describing the event functions and when you can expect them to be called:

    http://docs.unity3d.com/Documentation/Manual/ExecutionOrder.html
  • @mattbenic - thanks for sharing a link to that documentation. A few concepts now make much more sense to me too.
  • @mattbenic oooh thats awesome :D

    @Tuism If it was me, I would only ever change it when i need to, so if i killed a mob and I now want him to ignore collisions between other mobs, I would call it in a Die method or something. otherwise go from the start.

    Oh and you could also have a look at the layer collision matrix. edit>project settings>physics. then you don't need to script it ;)
  • @mattbenic Oh thanks! That helps a lot! Is there something similar for order of scripts that fire off throughout the entire game/scene? What I'm struggling the most with is stuff like where to call stuff and what to aim them at. Dunno if I'm making sense to those who already understand C#...

    @Pixel_Reaper thanks for that, I did find the matrix already and it does help, but I wanted to know how the script worked or else I'll be forever stumped by its brothers :P

    @FanieG @Pixel_Reaper I think I am calling IgnoreLayerCollision like that, I'll check tonight when I'm home again and see what error it throws...

    Thanks all :)
  • @Tuism the general rule is that each of those event functions are called at the appropriate time (as in that doc page), in the order that objects were created in your scene. So if you have a manager object that dynamically spawns objects, then in a given frame Update will be called first on your manager, and then on each of the objects it spawned in the order they were spawned.
    It is possible to bypass this and specify that particular order scripts must be called in (by class, not by object), but that's a bit of a nasty rabbit hole to wander down unless you absolutely need to.
    Also note that you could have multiple components/monobehaviors on a single GameObject, and their events will also be called in creation order.

    I'm not familiar with GameMaker and how it's script execution is managed, but in Unity's case it sounds like it will help you a lot to start thinking in terms of GameObjects and Components and their event functions, rather than in terms of scripts.
  • @mattbenic Is it really true that the execution order follows creation order? I always thought it was unspecified (and hence unpredictable and unreliable).
  • edited
    Edit: Whoa whoa whoa I think I got it working :D

    Simple question:

    I used to fire projectiles with:

    Rigidbody InstantiatedProjectile = Instantiate(projectile,
    						transform.position,
    						transform.rotation)
    						as Rigidbody;


    Then the good Ruan suggested that I don't use rigidbodies for projectiles, rather use colliders and raycasts, which makes perfect sense to me...

    But now I'm not understanding how to replace these things... I know I need to replace:

    1. public Rigidbody projectile ---> public GameObject projectile
    2. Those two Rigidbodys in the code about to GameObjects
    3. Do I have to change velocity? Only Rigidbody class has velocity right? I'll change it to translate calls to move stuff around, that's no problem

    But now:

    In the inspector, projectile is STILL defined as a Rigidbody and I can't put my bullet object in if I remove their Rigidbody components.

    So... What am I doing wrong?? @_@
  • edited
    Edit: Solved! Answer in next post!

    OK now I'm stuck again (sorry for being a frequent flyer here...)

    I have a bullet object with this script on, which raycasts, and if it hits something on the enemies layer, it destroys self and instantiates an explosion. So far so good.

    But now I want to refer to the enemy being hit and deduct life from it, I could do that with void OnCollisionEnter(Collision other) and manipulating the value via other.enemyLifeCurrent -= 1... But I can't manage that with raycast...

    I got as far as to figure out returning a raycastHit, but that doesn't seem to be able to do what I want it to do...

    if (Physics.Raycast(transform.position, fwd, out rayHit, 0.5f, mask)){
    	GameObject InstantiatedExplosion = Instantiate(explosion,
    							transform.position,
    							transform.rotation)
    							as GameObject;
    	Destroy (gameObject);
    	rayHit.collider.gameObject.enemyLifeCurrent -= 1;


    it's giving me the error:
    Assets/bulletBehaviour.cs(29,52): error CS1061: Type `UnityEngine.GameObject' does not contain a definition for `enemyLifeCurrent' and no extension method `enemyLifeCurrent' of type `UnityEngine.GameObject' could be found (are you missing a using directive or an assembly reference?)
    Halp?
  • OK! Thanks to @rigormortis this one's solved! :D I just needed to refer to the script component - so EVERY level is needed in Unity/C# - scripts in GMS used to just add variables to the stuff, in Unity you need to level through the script itself too. OK! Lesson learnt!

    This is the line that worked:

    rayHit.collider.gameObject.GetComponent<enemyBehaviour>().enemyLifeCurrent -=1;
  • @mattbenic Is it really true that the execution order follows creation order? I always thought it was unspecified (and hence unpredictable and unreliable).
    I read that somewhere and as far as I remember it's proved accurate in what I've seen. You're right though, it's far better to not depend on this order, as it may just be a result of the current implementation that could be changed at any time.
    Thanked by 1hermantulleken
  • edited
    EDIT: Deleted comment. I was really late to the party.
  • @Tuism - just out of curiosity what type of game is your first Unity project? Any builds available yet?
  • Is there an equivalent in Raycast to using the collider to get a collision and then using it to manipulate the object being hit?

    OnCollisionEnter(Collision other)
    other.enemyLifeCurrent -= 1

    That works, the raycast version (described in my previous post) works but uses GetComponent which seems unnecessarily complex, is there something simpler?
  • @FanieG I'll put a build up tonight if I have a chance, it's a runner, and it's about hijacking EVERYTHING from flying trucks to attack choppers to mechs to EVERYTHING :D
  • Tuism said:
    Is there an equivalent in Raycast to using the collider to get a collision and then using it to manipulate the object being hit?

    OnCollisionEnter(Collision other)
    other.enemyLifeCurrent -= 1

    That works, the raycast version (described in my previous post) works but uses GetComponent which seems unnecessarily complex, is there something simpler?
    Afaik, you pretty much have to use GetComponent. But you only have to use it once.

    enemyBehaviour enemy = other.gameObject.GetComponent<enemyBehaviour>();
    enemy.TakeDamage(1.0f); // On the enemy behaviour do the checking of what anim to play, whether to self-destroy, etc.
    enemy.Stun(1.0f); // Say, disable the enemy for 1 second
    // and whatever else you want to do with it
  • edited
    Speaking of GetComponent,

    I found this last night on Reddit, I think it's quite a good read for all of us Unity novices:

    http://www.reddit.com/r/Unity3D/comments/1d3ma9/what_can_i_do_to_make_my_game_slow_and_inefficient/
  • @Elyaradine - so once you have cached a reference to a variable using the GetComponent you can manipulate that variable any way you want?
  • @Tuism something like this maybe? (written from memory, so it's untested)

    RaycastHit hit;
    if (Physics.Raycast(gun.position, gun.rotation, out hit, gunRange)) {
     if (hit.tag == "Enemy") { //can also use hit.name
      hit.collider.SendMessage("Damage"); // "Damage" would be a mothod on your enemy
     }
    }
  • @FanieG what @Elyaradine did there was to get a reference to the class called enemyBehaviour, and then to call public methods in that class. if you have variables in that class that are marked as public you will be able to change them directly.
  • @Pixel_Reaper - Okay. Now it makes sense. Thanks.
  • @Elyaradine Where is the collision detection bit in your script?

    In my examples there were two methods of collision detect - one with rigidbody and OnCollisionEnter, one with Raycast and getting a raycastHit back which needs the GetComponent... the first method doesn't need GetComponent, but it's using rigidbody which I don't wanna use (to dodge the complication of physics and collision ignoring stuff).

    So in your code, you're referencing 'other' - that means it's based on method one, rigidbody, collision detect? I got that working without the GetComponent at all?
  • @Pixel_Reaper I'll try that out when I get home, just a few questions:

    1. can I use layer instead of tag? How would that code look? I've not been able to figure out the layer variable yet. It's an int but wouldn't work when I put in just a number... I had to use a public variable and set it on the inspector for it to work...

    2. is a Method on the enemy the same as a function declared, for example, "void Damage"?

    Thanks! :D
  • edited
    @Tuism: Whoops. I got mixed up between the two. :P The point was just that you're likely going to end up using GetComponent() a lot in any case, and that if you're just storing a reference for it first (assuming you're not doing that already), it's neater and runs faster. If it's something you're going to be using a lot (but which doesn't change a lot) (e.g. if an enemy is doing a lot of calculations based on where the player is), then make the reference in Start(), so you're only calling GetComponent() once instead of every frame.

    1. I stand to be corrected here, but I imagine that using .layer is faster. It uses some bitwise comparison or something, but you're also limited to using only 31 of them, afaik. When you check for .tag, you're doing a string comparison, which is slower, but you can use an unlimited number of tags. I imagine that the difference is negligible until you're really heavy into optimization though. <_<

    2. Yeah, to my knowledge a "method" is just C# speak for a "function". If you want to access it from another component though, you need to make it public, i.e. "public void Damage()".
  • To be honest I'm just happy that I could read your code and understand it enough to say "hey that's not gonna work for me"

    :D :D :D
  • @Tuism

    1. yes, you can set your raycast up to cast on a certain layer if you wish. I believe you do it like this:

    int layer = 1; //the layer you want as an integer
    RaycastHit hit;
    if (Physics.Raycast(gun.position, gun.rotation, out hit, gunRange, layer)) {
     if (hit.tag == "Enemy") { //can also use hit.name
      hit.collider.SendMessage("Damage"); // "Damage" would be a mothod on your enemy
     }
    }


    2. yes, sorry my terminology isn't great :P

    void Damage (float dmg) {
     health -= dmg;
     if (health < 1) {
      Die();
     }
    }


    your SendMessage will then look like this:

    hit.collider.SendMessage("Damage", 10.5f);
    // you could also add SendMessageOptions.DontRequireReceiver then you wont get accidental errors
  • edited
    @mattbenic It's a shame it's not official :--/

    @Tuism - just a general reminder - there is a lot of talk about writing fast code. If you render something on screen, almost all code is fast in comparison. Just concentrate on what is easy and clear.
  • @Pixel_Reaper thanks for the code I'll look into it! :D
    @hermantulleken I know you're right, I'm really more looking for a simpler syntax that I could remember/make sense of instead of square brackets and funny things :P But yeah, for now getting it to run comes first, everything else is for later!

    Speaking of getting it to run, I've posted the thing I've been working on :D

    JACK KING!
    http://makegamessa.com/discussion/1359/prototype-jack-king#Item_1
  • Question: How to make a whole prefab have an alpha of 0.5f?

    I can't do it in the Inspector, so I have to do it in code.

    So how to I refer to elements inside of a prefab?

    I've already instantiated the object, that works:

    GameObject InstantiatedTargeter = Instantiate(targeter,
    									transform.position,
    									transform.rotation)
    									as GameObject;
    InstantiatedTargeter.transform.Translate(0,0,1.5f);
    InstantiatedTargeter.transform.parent = transform;


    The object looks like this in the hierarchy:

    image

    But this code doesn't work:

    InstantiatedTargeter.group_0.renderer.material.color.a = 0.5f;

    with the error:

    Assets/enemyBehaviour.cs(31,46): error CS1061: Type `UnityEngine.GameObject' does not contain a definition for `group_0' and no extension method `group_0' of type `UnityEngine.GameObject' could be found (are you missing a using directive or an assembly reference?)

    How do I refer to parts of objects in a prefab, and are there special things I need to do to make the parts accessible to code?
  • @Tuism
    you cant access a child object like that to my knowledge.

    you can get a reference to materials by using GetComponentsInChildren();

    or to the child itself by using GameObject.find or GameObject.findWithTag or better yet in your case transform.find("group_0");
  • For the most part, I use a public variable. This shows up in the inspector, and you drag the child objects into there.

    If you're doing it to all materials, you can use GetComponentInChildren<Renderer>() to get an array of all renderer components.
  • So... There's no easy way to make an entire prefab/gameobject/whatever transparent by, say, a factor of 0.5? You have to manually go and find reference to all the objects and change their renderer.material.color.a on each's own? @_@
  • edited
    @Tuism - can't you create the transparent material. declare it as a public variable. drag the created material into the variable slot in the inspector and then change the object's material via script? Or am I misunderstanding your question completely.

    something like this

    public Material activatedMaterial;
    private Material deactivatedMaterial;
    
    void Start()
    {
    	deactivatedMaterial = renderer.material;
    }
    
    void activatePower()
    {
    	renderer.material = activatedMaterial;
    }
    
    void deactivatePower()
    {
            renderer.material = deactivatedMaterial;
    }


    where activatedMaterial is your new transparent material
  • @FanieG Hmmmm I'm not sure I entirely understand the solution you're proposing here, I have a prefab that I instantiate dynamically, and I want that prefab (is a collection objects, of two colours/materials) to be transparent at 0.5 alpha.

    I can't create the prefab to have its things already transparent (right?), so I have to do it at runtime when I instantiate.

    Is that the thing you're talking about?
  • edited
    Can't you create a transparent material, that you can apply to your prefab? So that it is already transparent when you instantiate
  • I don't see any option in materials in the inspector that lets you change alpha, and I haven't seen anything from Google... Will dig deeper :)
  • edited
    I know it is possible, because that is how I created my transparent walls. If you select the colour for your material in the inspector, there should be sliders for RGBA. I think you need to set A to around 90 for transparency and then choose a transparent shader for it. I will double check for you tonight at home how I did it and let you know.

    Maybe have a look at this:
    http://docs.unity3d.com/Documentation/Components/shader-TransparentFamily.html

    and:
    http://docs.unity3d.ru/Components/shader-TransDiffuse.html
  • I found the transparency thing! :D It's in the color editor of the inspector when you're on the renderer - WOO :D
Sign In or Register to comment.