Help I don't know how to OO

Discussion in 'TZT GameDev' started by AgelessDrifter, Mar 15, 2017.

  1. AgelessDrifter

    AgelessDrifter TZT Neckbeard Lord

    Post Count:
    45,688
    for all the time I've spent coding random projects I've never sat down and taken the time to really understand classes.

    I mean I kinda get what they are and what the point of them is, and I'm figuring out how to use them by trial and error.

    But my current question is: what if I have a class--say it's Enemy--and I want some trigger to generate between 1 and 100 instances of Enemy depending on some raw input or previously defined parameter. How do I write that code? How do I dictate the behaviors of the code-generated instances?

    To be clear (because I think the reason I wasn't able to look this up is because I dunno the jargon very well, so maybe my question above doesn't make sense as stated)

    What I know how to do is this:

    class Enemy(self, hp):
    def __init__(self,hp):
    self.hp=hp
    def attack(self):
    self.hp-=1
    e=Enemy(5)
    e.attack()

    What I don't know how to do is this:

    n=int(raw_input())
    //create n enemies//
    //attack the first enemy once//
    //attack the second enemy twice//



    if that makes any sense. Seems like this must be a commonly used thing in game programming
     
  2. Sear

    Sear TZT Neckbeard Lord

    Post Count:
    34,655
    in Unity (C#) I would do this with:

    Code:
    int r = Random.Range(1,100);
    
    for(int i=0; i < r; i++)
    {
    Enemy newEnemy = (Enemy)Instantiate(enemyPrefab, transform.position, transform.rotation);
    }
    and I'd probably be calling this from an EnemySpawner class or something like that

    This is assuming your Enemy class is attached to an existing object which will be used in some way (i.e., an actual enemy in the game). If you just want a new instance of the class, you don't need to instantiate anything.

    I'd have each Enemy initialize some variables (e.g., stats like HP, movement, etc) from within its own class. In Unity, I'd use the Awake() function for this. If you used a constructor when creating these instances, that essentially accomplishes the same thing.
     
  3. Sear

    Sear TZT Neckbeard Lord

    Post Count:
    34,655
    for that "attack the first enemy once" bit, you'd prob want a TakeDamage method inside your Enemy class for that and you'd want to pass the damage taken as a parameter

    Code:
    public void TakeDamage(float dmg)
    {
    currentHP -= dmg; 
    // whatever else happens when taking damage
    } 
    
    and I'm not sure where/why you'd be doing this, but if you were to call that from your Spawner class you'd want to store a reference to the first enemy and then call the TakeDamage method on it

    Code:
    
    private Enemy firstEnemy;
    
    void WeirdSpawnMethod()
    {
    int r = Random.Range(1,100);
    // spawn a random number of enemies
    for(int i=0; i < r; i++)
    {
    Enemy newEnemy = (Enemy)Instantiate(enemyPrefab, transform.position, transform.rotation);
    if(i == 0) { firstEnemy = newEnemy } 
    }
    
    // attack the first enemy for a lot of damage
    firstEnemy.TakeDamage(94320432f); 
    }
    can't tab format the code on this so pretend it looks nice
     
  4. Sear

    Sear TZT Neckbeard Lord

    Post Count:
    34,655
    also for whatever its worth, I only ever call a TakeDamage method from a trigger/collision event in my game

    the way that works:

    // Enemy calls its own Attack method (based on its conditions & timers)
    // Attack method spawns some object that has a collider/trigger on it
    // Whenever that object collides with something (i.e., a hitbox) it calls the TakeDamage method on whatever it's hitting
     
  5. Yogr

    Yogr Meatball Sub Pro

    Post Count:
    1,214
    Hey pal.

    You've kind of hit the nail on the head with the difference between OO and traditional programming with your little code block there.

    class Enemy(self <---- this little "self" is the whole point of Object Orientation.

    Object Orientation builds that in for you, it's called the "this" pointer.

    From a traditional programming point of view, a class is just a struct (or custom data type) with a set of supporting functions built to modify that struct.

    Here's the translation of your code (this code should work in C# or Java)
    Code:
    class Enemy
    {
        // Define data members of this class
        private String mEnemyName;
        private int mHitpoints;
        
        // Constructor
        public Enemy(String name, int hp)
        {
            mEnemyName = name;
            mHitpoints = hp;
            // Can also do this.mHitpoints = hp, as "this" actually exists silently in every method and points to the 
            // object reference in question, but it also silently adds it before member variables, so no need to add it 
            // manually. Only add it manually if the 2 var names are the same.
        }
    
        public void GetHit(int damage)
        {
            this.mHitpoints -= damage;
        }
    
    }
    
    // Now, in some OTHER class, you do create your Enemy objects. The above is just a definition of an Enemy.
    
    public CombatManager
    {
        private List<Enemy> mEnemies;
    
        public void StartBattle(int numEnemies)
        {
            mEnemies = new ArrayList<Enemy>();
    
            for(int i = 0; i < numEnemies; i++)
           {
                Enemy e = RandomEnemyFactory.getEnemy();
                mEnemies.add(e);
           }
    
           Random r = new Random();
           foreach(Enemy e in mEnemies)
           {
                 int numAttacks = r.nextInt(3);
                 for(int i = 0; i < numAttacks; i++)
                 {
                     e.GetHit(1);
                 }
           }
        }
    }
    
    class RandomEnemyFactory
    {
        private const int ENEMY_MAXHP = 50;    
    
        public static Enemy getEnemy()
        {
             Random r = new Random();
             // TODO: Do some work with random numbers and pull from some enemyDB table or a pre-cached enemy 
             // data object
             Enemy e = new Enemy( "Goblin", r.nextInt(ENEMY_MAXHP) );
        }
    }
    
    So. As you can see, typically you would have some sort of wrapper class, like a Manager or DataProvider or something to store your object instances.

    Each object instance uses the functions defined in the Enemy class, and calling a method like e.GetHit(1) is behind-the-scenes doing this: Enemy.GetHit(e, 1); as if it were a traditional static method with an object reference.
    public void GetHit(int dmg) really is this -> public static void GetHit(Enemy e, int dmg). But it coding it the 2nd way would break Object Oriented principles.


    I wrote this before Sear's posts, but im gonna post anyway, sorry if it adds confusion. lol.
     
  6. Yogr

    Yogr Meatball Sub Pro

    Post Count:
    1,214
    Just read Sear's posts.

    If you are building a game in C# w/ Unity, he outlined how to do what you want there.
     
  7. AgelessDrifter

    AgelessDrifter TZT Neckbeard Lord

    Post Count:
    45,688
    Wow guys, thanks for the thorough responses.

    The "//attack first enemy//" bit wasn't because that specifically is something I'd want to do--it was just a for-instance (no pun intended); since I know how to "attack" an Enemy I hard-coded (since I can assign it a variable name) but didn't know how to call methods from a particular Enemy (or, generically, a particular instance of a class) when I haven't named it myself.

    It's been a while since I've looked at any coding language besides python, so I need to scrutinize what you guys wrote up a bit more once I have some time in order to make complete sense of it (am at work presently), but what I gather from a quick peek is that the enemy should be instantiated in an array inside some larger class that is used to keep track of all the different things going on during battle? And then so I can call methods from particular ones by using their index within that array (if, say, I wanted to run a loop that affected every mth monster's hp because of some item or other spell)?
     
  8. Sear

    Sear TZT Neckbeard Lord

    Post Count:
    34,655
    Yeah.

    Instantiate and Random.Range are Unity functions, so ignore that unless you are using it.

    In Yogr's example he creates a new instance using the class constructor, which is standard.
     
  9. Yogr

    Yogr Meatball Sub Pro

    Post Count:
    1,214
    You could create a BattleController that upkeeps and monitors all GameObjects (enemies, players, other elements, etc) and relays info between the objects.

    Sometimes its better to put the code on the GameObject's custom controller classes as well. i.e. your "Enemy" class will most likely look like this in Unity or other game engine:
    Code:
    public class EnemyController : MonoBehaviour
    
    The : in above example means "extends from" in Object Oriented terms. Meaning, it gains all the data variables and methods of a MonoBehaviour class, but you are also going to add to it.
    MonoBehaviour is the building block for Unity scripts, and allows you to do a lot of things inside a controller script.

    For instance, a PlayerController class can check for User Input in it's Update loop, and then call a method to handle things like walking, attacking, casting spells, etc.
    All of that *could* just exist inside of the PlayerController, and not a separate InputHandler class.

    In a Card game I had worked on, I did create a BattleController script that owned references to all of the GameObjects. Cards, monsters on battlefield, even the HPs and other HUD elements. The BattleController kept track of whose turn it was and also handled input, and told everybody what to do.

    It's really all about how you want to design it. Some patterns are more efficient than others, some just make more sense for specific game, and some just make it easy to code.

    For your question of accessing a specific monster based on their index, that usually isn't the case.
    If you had some global spell that damaged all monsters, you could iterate through and hit them 1 by 1. But most common case is some attack or spell that has to "hit" the monster based on a collision trigger.
    Unity makes it fairly easy to find the specific enemies whose Collider was triggered.

    If you are planning on making a game, I would recommend reading up more on OO stuff and C# and then jump into Unity and read the fuck out of some tutorials. Lots of help out there.
     
  10. AgelessDrifter

    AgelessDrifter TZT Neckbeard Lord

    Post Count:
    45,688
    I'm actually just dicking around in python trying to recreate some experiments with emergent order in complex systems that are talked about in Origins of Order (book Agrul plugs a lot). I'm not sure OO really makes a lot of sense for what I was trying to use it for (and maybe that's why the question seems odd), but since I have a tendency to avoid classes like the plague and end up with really inefficient (both in terms of time taken to write it and run time) spaghetti code I figured I'd try it out.

    [if you're curious, the program is basically:
    -An nxn grid of "lightbulbs"
    -each lightbulb has:
    ---a position in the grid
    ---a "state" (on/off)
    ---two (or variably many, but I'm working on this) "buddy" lightbulbs elsewhere on the grid
    ---a type (the type of logic gate that determines the bulb's next state based on the states of its buddies)
    And then once I get the "variably many" part worked out I'm gonna try to randomly generate logic gates with weighted outcomes (percentage of input combos that result in zero vs those which result in 1 as an output).
    Then stats stuff and animation stuff to examine qualitatively and quantitatively what I get out of all this.]

    I was thinking it might make sense to have the bulbs as classes, but after thinking about it I'm not sure. I can't have the bulbs dynamically changing themselves because if I use a loop to do that then the bulbs at the end of the loop will be getting input from the bulbs at the beginning of the loop that are already at their t+1 state, but which got there by taking in the states of the bulbs at the end of the loop in their t states.

    Anyway I'm just doing it to fuck around--the idea of eventually taking a crack at cobbling together a game has been lurking in the periphery for me for a long time (since I tried my hand at Actionscript 2 all those years ago and made that shitty spamalot "game"), but I'm realizing there's probably a lot of pretty fundamental stuff I'd need to learn in terms of best practices to trial-and-error my way from this kind of tinker-toy code to making even a shitty game.
     
  11. Yogr

    Yogr Meatball Sub Pro

    Post Count:
    1,214
    Not knowing the fundamental stuff can be frustrating at times when you know what you want it to do but can't get it to work 100% of the time.

    But, do what you can, and Google what you can't.
     
  12. Chemosh

    Chemosh TZT Addict

    Post Count:
    4,349
    Also python is a bitch for oop. Start with a easier language line java or c#, it'll help you grasp things a bit easier.

    Sent from my SM-G920V using Tapatalk
     
  13. AgelessDrifter

    AgelessDrifter TZT Neckbeard Lord

    Post Count:
    45,688
    For real? I feel like python is the easiest language by far for basic functionality--I'm surprised to hear there's something (besides low runtimes) that it's outstandingly bad/not simple for

    I'm totally spoiled by the lack of need for curly brackets
     
  14. AgelessDrifter

    AgelessDrifter TZT Neckbeard Lord

    Post Count:
    45,688
    I am creating complexity

    [​IMG]

    Next I will weight the probability of bulb linkages so that bulbs are more likely to be buddies with neighboring bulbs

    I think that should make patterns of behavior more visible
     
  15. Agrul

    Agrul TZT Neckbeard Lord

    Post Count:
    48,117
    i dont agree w/ chemosh i think OOP's simple in python

    what did u do 2 resolve ur t / t+1 updating problem? i probably would have given the bulb classes 'current' and 'next' time attributes to distinguish which attribute was bein updated
     
  16. Chemosh

    Chemosh TZT Addict

    Post Count:
    4,349
    I'm just saying from what most other languages use for OOP, python is just a bit 'odd'. Using the self in the method names, and classes just seems odd. Where as most other languages use this or some form of implements or inherits from xyz. It's just hard to find a lot of good doc on python OOP where as other OOP based languages (Java/C#) really excel at this.

    Plus while I love python, I will always hate it for the indention model they use instead of semi-colon.
     
  17. Agrul

    Agrul TZT Neckbeard Lord

    Post Count:
    48,117
    semicolons arent even full colons

    i cant respect them
     
  18. Sear

    Sear TZT Neckbeard Lord

    Post Count:
    34,655
    I've never used Python, but the class/method syntax does seem weird to me on first glance.

    Writing lambda expressions (or LINQ statements) in C# seems cleaner to me than the Python equivalent. I like that it uses indentation over curly braces for readability.
     
  19. Sear

    Sear TZT Neckbeard Lord

    Post Count:
    34,655
    seriously tho

    lambda x: x + y

    vs

    x => x+y


    WHERE IS UR GOD NOW, PERFECT SIMPLER FART-SNIFFING UNCLUTTERED LANGUAGE?
     
  20. Agrul

    Agrul TZT Neckbeard Lord

    Post Count:
    48,117
    i do really dislike python's syntax for lambda expressions

    but i only very rarely have to use them so it dont bother me v much

    u guys r wrong about everything else AS USUAL