Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

c# - need some help wtih classes game object intancing

Discussion in 'Scripting' started by Zocky, Dec 26, 2013.

  1. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Hey folks!

    I'm still somewhat new to unity, and starting to work on a little game (mostly to practise), and i'm bit stuck at one point, and not sure what would be the best way to proceed.

    So, for starters, i have IEnemyBase interface:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4.  
    5. public interface IEnemyBase
    6. {
    7.     string enemyName {get; set;}                //name of the enemy
    8.     float health {get; set;}            //How much health the enemy has
    9.     float shield {get; set;}            //How much shield enemy has
    10.     float enemySpeed{get;set;}          //how fast does enemy move
    11.     GameObject enemyPrefab{get;set;}    //prefab that enemy will use (model, particles, collision....
    12.  
    13.     void move();
    14. }
    And class that implements this interface.
    Code (csharp):
    1.  
    2. public class EnemyBase : IEnemyBase
    3. {
    4.     private string _enemyName = "DefaultProjectileName";   
    5.     private float _health = 100.0f;
    6.     private float _shield = 100.0f;
    7.     private float _enemySpeed = 1.0f;
    8.     private GameObject _enemyPrefab = null;
    9.  
    10.     public void move ()
    11.     {
    12.  
    13.     }
    14.  
    15.     public string enemyName
    16.     {
    17.         get
    18.         {
    19.             return _enemyName;
    20.         }
    21.         set
    22.         {
    23.             _enemyName = value;
    24.         }
    25.     }
    26.  
    27.     public float health
    28.     {
    29.         get
    30.         {
    31.             return _health;
    32.         }
    33.         set
    34.         {
    35.             _health = value;
    36.         }
    37.     }
    38.  
    39.     public float shield
    40.     {
    41.         get
    42.         {
    43.             return _shield;
    44.         }
    45.         set
    46.         {
    47.             _shield = value;
    48.         }
    49.     }
    50.  
    51.     public float enemySpeed
    52.     {
    53.         get
    54.         {
    55.             return _enemySpeed;
    56.         }
    57.         set
    58.         {
    59.             _enemySpeed = value;
    60.         }
    61.     }
    62.  
    63.     public GameObject enemyPrefab
    64.     {
    65.         get
    66.         {
    67.             return _enemyPrefab;
    68.         }
    69.         set
    70.         {
    71.             _enemyPrefab = value;
    72.         }
    73.     }
    74. }
    Now, lets say i want to create enemy "Generic_Enemy_1" based on this class. Lets say i have "Generic_Enemy_1_prefab", meaning, model, particle system, collision mesh, etc.

    I assume i should create new script, "generic_enemy_1" class, and assign this class to this prefab, and it extends MonoBehaviour class.

    But this is where i'm getting a little confused.
    So, idea was to have EnemySpawner in the scene, and it would, say, generate 20 enemies, on random positions, and each enemy would have it's own class instance (object) of the EnemyBase class. This way, i could keep track health status and such things for each of the enemy, but i'm not really sure how to approach this.

    Also, since gameObject(enemy prefab) must have a script with class that extends monobehaviour, i'm not really sure how should i connect enemy prefab (+ assinged Script) with the enemyBase class (or it's object). And above all, how to then keep track on health status of each enemy within the main script of the game that will handle these things?

    Anyhow, i hope someone can give me some idea how to approach this particular problem.

    Tnx!
     
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    Why do you have an interface? Does multiple classes implement it?

    Why do you have a class that derive from nothing and only implement an interface? Shouldn't you do something like

    In other word, in this context, what is the purpose of this interface?
     
  3. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Well, i think i screwed up a bit.

    But, idea was, to have interface, like, generic enemy interface, and then you'd have various enemy classes implementing this interface. Just to make sure they all share all those similar properties and methods.

    So, i guess what i'm trying to do is, each enemy type(prefab) would have one of the class assigned to it, that implements this interface.

    Then idea was to use some script to to "spawn" various enemies of these types.
    Does that sound ok? Or am i thinking wrong here?

    If ok, i think i can make this work, but, since i'm going to instance multiple enemies of some enemies types , i guess i should find a way to store all these instance, either array or list or something, so i can access them and set health and such as needed. Would you suggest using arrays or?

    Also, tnx for your help!
     
  4. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    Since they would all need to derive from a common trunk, I don't see the use of an interface in this specific case.

    What I would do;

    Code (csharp):
    1.  
    2. public abstract class BaseEnemy : MonoBehaviour { }
    3.  
    4. public class MyEnemyA : BaseEnemy { }
    5.  
    6. public class MyEnemyB : BaseEnemy { }
    7.  
    An interface would be used, as an example, if you want multiple none-related trunk to share a specific method signature. Let's say you want every character to be able to receive damage... but you also want the player, door, crate, or environment object to receive damage too.

    Code (csharp):
    1.  
    2. public interface IDamageable
    3. {
    4.     void ReceiveDamage(object source, Vector3 impact, Vector3 direction, DamageType type, float damage);
    5. }
    6.  
    7. public abstract class BaseEnemy : MonoBehaviour, IDamageable { }
    8.  
    9. public class Player : MonoBehaviour, IDamageable { }
    10.  
    11. public class Door : MonoBehaviour, IDamageable { }
    12.  
    13. public class Crate : MonoBehaviour, IDamageable { }
    14.  
    So everything that can be damaged can be cast to a IDamageable and can receive damage. Do you understand where I'm going with this?
     
  5. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Hm, so, if you have similar elements, like various enemies, it's better to just create base class, and inherite from them?

    But, like you mentioned, i have both enemy AND player, and maybe something else as well, and i want them all to recieve damage.

    In this case....for enemies, i'd use base class (enemyBase lets say), and for each enemy type, i'd have class that extends it. Is that correct?
    But since it also wants to use damage/health status, would it be correct to say, that i'd have this interface with this damage property (since i want to use this same property, or rather, method, in player class aswell)?
     
  6. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    - When different classes share enough elements and are similar enough in roles, yes, they should derived from a similar base class.

    You can have unlimited number of hierarchical classes.
    Ex.: MonoBehaviour -> Character -> Enemy -> FlyingEnemy -> Crow
    MonoBehaviour -> Character -> Enemy -> Turret -> FlameThrowerTurret
    MonoBehaviour -> Character -> Player
    MonoBehaviour -> Character -> NonPlayingCharacter -> QuestGiver

    You have to find what elements are shared between different types and build a hierarchical tree that is simple and useful.

    - When different classes have no shared elements or logical relationship, but they offer a similar behaviour, they should implement an interface.

    Ex.: Crates and Player share nothing, but they both can perform an action when receiving damage.
    Crates, Switches, Doors share nothing, but they all can be interacted with.

    So, Crates and Player implement IDamageable.
    Crates, Switches and Doors implement IInteractive.

    Be careful in your interface definition. Why would you enforce "Health" property for a crate? Wouldn't that crate just blow up when it takes a threshold of damage? Why another object would need to know the health of a crate? The purpose of an interface should be as explicit as possible. IDamageable define something that can receive damage in the most simple way. IInteractive is an object that can be interacted with. You should leave the proper implementation to the class itself.
     
  7. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Hm, so let me see if i get this right.

    So, when you have bunch of things that have a lot of things in common, and logically feels they should be together, you shoud use class inheritance. Like the EneyFlying, EnemyWalking, EnemySwimming, all deriving from baseEnemy class. In this case, they all have bunch of stuff in common, and the difference is put in the subclass (like EnemyFlying in this case).

    But, if you have bunch of things that don't seems like they should be having much in common, but share some similarities, it's best to put these "similarities" in interface. Like in your case, both crate and player (and enemy) all react to "receiveDamage", so instead of having "health", you rather use something more broader in interface, and let the class that implements it, choose just what exactly should this "receiveDamage" method do.

    So, you can have classes:
    enemy->flyingEnemy ->Crow
    sceneryObject->crates->metalCrate

    both extend from their own base classes (flying enemy crate), but they can also implement same interface, becasue when you, say, shoot at both, they both perform SOMETHING, but what this something is, is left to these subclasses to decide(crow metalCrate).

    Did i get that atleast somewhat right? (sorry, before starting this thread, i didn't have clear idea when does one use them).

    Well, tnx a lot, you are helping me a great deal here!
     
  8. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    I think you got it.

    It's also good to know that you can implement an interface at any point in the inheritance.

    For example;
    Code (csharp):
    1.  
    2.              IDamageable
    3.                    |
    4.                    \/
    5. SceneryObject -> Crate -> MetalCrate
    6.       /\
    7.       |
    8.   IInteractive
    9.  
    Assuming all SceneryObject can be Interacted with, but not all of them can be Damaged.
    Assuming all Crates can be Interacted with and be Damaged.
    MetalCrate having specific behaviour compared to, let's say WoodenCrate in that it can be used to press a floor switch.

    In this case, the MetalCrate can be cast to Crate, SceneryObject, IDamageable and IInteractive.

    If I shot a rocket to a MetalCrate, the rocket would get a IDamageable that it can apply it's explosive damage to.
    If I press interact near the crate, the player would only see a IInteractive I'll be able to open or pick it up.
    If I destroy the MetalCrate, the Crate class can count the number destroyed and trigger an achievement.
     
  9. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Hm yeah i think i understand it now.

    Maybe just one last questions. If i create my enemySpawner, and make several instances of the enemyPrefab, what is the best way to store those? Thing is, I'm guessing during the level, i'll probably be adding and removing (as you kill them) enemies from this list of prefabs.

    Is it better to use list, or rather just arrays?
     
  10. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    Frankly, the overhead of using a list is negligible, versus the code you would have to add to handles adding and removing elements of an array, and in the end, it would give a fairly similar performance result. Also, List pre-cache some memory space in case you add a bunch of elements.

    Overall, I would say go for the List. It's just simpler.
     
  11. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    OK, got it!

    Tnx again for all your help man, really appreciate it!
     
  12. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Hm ok, so i'm not really sure how would i approach this one.

    So,lets say,i have a space ship. I can use my arrows (left-right) to controll the rotation of model at any time. Now, once you press and hold foward key, your ship should read the direction or rather, rotation of itself, and start translating (moving) into that direction. Even if you rotate ship in the mean time, ship is still suppose to run into direction set when you were holding foward key.

    I would simply use local space translation, but the problem i get is, when i hold foward and and release it, ship is still moving for short period on time (slowing down and then stops), but if i also rotate the ship in the mean time, before it stops, it actually changes the direction where it moves, depanding on it's rotation (again, because it's local space translation).

    So, i guess what i would have to do, is somehow convert rotation of the spaceship, at the time i press foward, to the (unit) vector, so that i can use THAT vector, when translating in world, not local space.

    Or am i getting this completly wrong and there's better way to do this? Anyhow, i hope someone can point me in right direction with this, i'm having a feeling this is something i will come across a lot in future.

    Tnx!
     
  13. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,848
    OK. I assume you're not using the physics engine at this point, but instead are just translating the transform directly. And for the desired effect, I'm picturing something like the classic Asteroids game.

    So, yeah, what you need to do is keep your own velocity vector, and keep this in world coordinates. So in your Update method you have something like:

    Code (csharp):
    1.   transform.position += velocity * Time.deltaTime;
    Then, when the forward (thrust) button is pressed, you need to figure out which way "forward" is in world coordinates. The Transform class has handy properties and methods for this sort of thing; in this case you want Transform.forward. So it'd be something like:

    Code (csharp):
    1.   velocity += transform.forward * thrust * Time.deltaTime;
    where 'thrust' is a property on your class that controls how fast the ship accelerates.

    Finally, of course, you need to reduce velocity when the engines are not in use, so the ship will coast to a stop. The easiest way to do this would be to use Vector3.MoveTowards, moving your velocity vector towards zero, e.g.:

    Code (csharp):
    1.   velocity = Vector3.MoveTowards(velocity, Vector3.zero, brakeSpeed * Time.deltaTime);
    where brakeSpeed is another property on your class that controls how quickly your ship can decelerate.

    Have fun,
    - Joe

    P.S. Next time, it'd probably be better to start a new thread for a new question, even if the old thread you're hijacking is your own. :)
     
  14. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Well yeah, it's not something like asteroids, it IS asteroids. Hey, you gotta start somewhere. :p

    Also, yeah, i'm not using any physics at this point, doing everything through the c ode.

    So, hm, i was actually using transform.translate to move my ship (with local space coord. system), but i guess i was a bit wrong to use that.

    So, if i get this, first, when game runs, ship is positioned and 0,0,0, and velocity vector is 0 too, meaning, it's not moving (unless it's later in the game, where ship has some velocity from before). Is that ok?

    And then, when player presses foward key, i basically use transform. foward to basically convert angle at which ship is rotated at given time, to world space coordinates. I then add this vector to the the velocity, and with that, ship gets new velocity.

    Is this all correct, or am i missing something?

    Also, tnx a lot for your help and yeah, i didn't want to spam too many new threads, so i posted here, but i'll open new thread next time!
     
  15. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,848
    Yes, it sounds like you've got the right idea!

    Good luck, and have fun!
    - Joe
     
  16. Zocky

    Zocky

    Joined:
    Dec 26, 2013
    Posts:
    22
    Oh wow, just managed to make it the way you suggested, worked like a charm! Tnx a lot!