1. Hey! Guest! The 33rd GMC Jam will take place between May 23rd, 12:00 UTC (Friday noon) and May 27th, 12:00 UTC (Monday noon). Why not join in! Click here to find out more!
    Dismiss Notice

GML Inheritance vs data structure

Discussion in 'Programming' started by ElegantMistake, Mar 14, 2019.

  1. ElegantMistake

    ElegantMistake Member

    Joined:
    Mar 5, 2019
    Posts:
    10
    Imagine having the ability to pick up different kinds of weapons but you can only wield 2 guns at the same time (primary and secondary gun). Each gun has a different type of projectile (laser gun -> laser, shotgun -> bullet, ...) and each gun has different properties (time to reload, recoil, ..)

    Would you create a parent object o_weapon with all its properties and create sub-objects (e.g o_laser_gun) inheriting from o_weapon or would you create a data structure storing all gun properties in the structure, and why?

    Code:
    weapons[0] = ds_map_create();
    ds_map_add(weapons[0],"sprite",-1); 
    ds_map_add(weapons[0],"damage",0); 
    ds_map_add(weapons[0],"projectile",-1); 
    ds_map_add(weapons[0],"time_to_reload",0);
    ds_map_add(weapons[0],"bullet_speed",0); 
    
     
  2. immortalx

    immortalx Member

    Joined:
    Sep 6, 2018
    Posts:
    293
    I would use enums to reference and access things. Something like this for example (not tested)

    Code:
    enum GUN_PROPERTIES
    {
        SPRITE,
        DAMAGE,
        PROJECTILE,
        RELOAD_TIME,
        BULLET_SPEED
    }
    
    enum PROJECTILES
    {
        BULLET,
        BEAM,
        PLASMA
    }
    
    enum GUN_LIST
    {
        PISTOL,
        LASER,
        SHOTGUN,
        RIFFLE
    }
    
    gun_arsenal = ds_list_create();
    
    var gun_pistol = 0;
    gun_pistol[GUN_PROPERTIES.SPRITE] = spr_pistol;
    gun_pistol[GUN_PROPERTIES.DAMAGE] = 20;
    gun_pistol[GUN_PROPERTIES.PROJECTILE] = PROJECTILES.BULLET;
    gun_pistol[GUN_PROPERTIES.RELOAD_TIME] = 3;
    gun_pistol[GUN_PROPERTIES.BULLET_SPEED] = 120;
    
    ds_list_insert(gun_arsenal, GUN_LIST.PISTOL, gun_pistol);
    // add all other guns
    Then to set it to a slot and access it's various properties:
    Code:
    slot_primary = ds_list_find_value(gun_arsenal, GUN_LIST.PISTOL)
    
    var bullet_spd = slot_primary[@ GUN_PROPERTIES.BULLET_SPEED];
    EDIT: I believe it's better to replace:
    Code:
    ds_list_insert(gun_arsenal, GUN_LIST.PISTOL, gun_pistol);
    with:
    Code:
    gun_arsenal[| GUN_LIST.PISTOL] = gun_pistol;
    That will ensure that you'll always be referencing the correct index in the list, even if you at a later time add more guns.
     
    Last edited: Mar 14, 2019
    ElegantMistake likes this.
  3. ElegantMistake

    ElegantMistake Member

    Joined:
    Mar 5, 2019
    Posts:
    10
    Why would you use this rather than using inheritance and overriding the different properties from the parent class?
    If you have over 100 guns (theoretically), you'd have a very long code block (or in scripts) and I think it would be harder to maintain then having different objects through abstraction.

    If I understand correctly, you'd only have one weapon object in the room which changes its properties dynamically based on the input list.
     
    immortalx likes this.
  4. immortalx

    immortalx Member

    Joined:
    Sep 6, 2018
    Posts:
    293
    Well, in my opinion it depends on if you are indeed going to have 100 guns, or a dozen. If it's the latter, it's easy setting a couple of enums, do some copy-pasting, and setting up some values. These will then be pre-computed and added to the list, which makes it easy and efficient. All you have to do then is access its values.
    In the other case, yes it would be harder if you have tons of weapons and you'd like to tweak many many values.
     
  5. samspade

    samspade Member

    Joined:
    Feb 26, 2017
    Posts:
    1,822
    For me, I would say whether the differences are purely data or whether they are a mixture of data and code. If pure data, then I would use a data structure, if there's code, then probably an object. But it isn't as clear cut as that. As minor difference in code might be within the same gun.

    For example lets say you have one gun type. The elements of this gun are how fire_speed, ammo_capacity, and reload time. If I wanted to make another gun that fired faster, had more ammo capacity, but less load time, I would probably create a data structure for these guns. But now lets say I wanted to add a fourth variable, burst_fire. Here, there are two options. I could add a burst_fire variable to the current gun data structure (and to all previous guns), add burst fire code to the gun object and use the variable to switch between them. Or I could have a separate gun that does burst fire.

    For burst_fire it would probably depend on whether I wanted guns to be able to switch. So a better example might be laser. As a laser gun wouldn't have entirely different variables and very different code. So I would almost certainly have a new object for the laser gun, with different types of laser guns being contained within a data structure that you could set the main object too.

    Also, since you (probably) wouldn't need any of the extra functionality of a list or map for this, I would probably be using arrays of arrays which makes it very easy to change a lot of code at once in the editor.
     
    ElegantMistake likes this.
  6. TheouAegis

    TheouAegis Member

    Joined:
    Jul 3, 2016
    Posts:
    6,514
    If I'm going to have a hundred guns, I wouldn't want a hundred objects in my resource tree. For me I would write all the data to a buffer at the start of the game and then simply read that buffer when I need the data. Is it hard to keep track of, that's what comments and enumerators are for.
     
    ElegantMistake likes this.
  7. FrostyCat

    FrostyCat Member

    Joined:
    Jun 26, 2016
    Posts:
    3,969
    If you're taking the map or array route, why should you inherit by copy-and-paste when you have ds_map_copy() or array_copy()?
     
  8. ElegantMistake

    ElegantMistake Member

    Joined:
    Mar 5, 2019
    Posts:
    10
    This is what I am doing at the moment. I created a 2d array lookup table for my weapons and projectiles.

    Code:
    global_weapons = [];
    
    //Machine Gun
    global_weapons[Weapon.MACHINE_GUN, WeaponProperty.SPRITE] = s_laser_gun;
    global_weapons[Weapon.MACHINE_GUN, WeaponProperty.TYPE] = o_machine_gun;
    global_weapons[Weapon.MACHINE_GUN, WeaponProperty.PROJECTILE_TYPE] = Projectile.BULLET;
    global_weapons[Weapon.MACHINE_GUN, WeaponProperty.PROJECTILE_OBJECT] = o_bullet;
    global_weapons[Weapon.MACHINE_GUN, WeaponProperty.RELOAD_TIME] = .25;
    
    //Laser Gun
    global_weapons[Weapon.LASER, WeaponProperty.SPRITE] = s_laser_gun;
    global_weapons[Weapon.LASER, WeaponProperty.TYPE] = o_laser_gun;
    global_weapons[Weapon.LASER, WeaponProperty.PROJECTILE_TYPE] = Projectile.LASER;
    global_weapons[Weapon.LASER, WeaponProperty.PROJECTILE_OBJECT] = o_laser_projectile;
    global_weapons[Weapon.LASER, WeaponProperty.RELOAD_TIME] = .25;
    
    Each weapon has a different projectile type and every projectile has a certain effect (disarm enemy, ...) hence I'm also creating different weapon objects (o_gun_laser, ...) and projectiles (o_projectile_laser, o_projectile_bullet, ...). All weapons inherit from o_weapon so my fire_weapon script can check on this.

    Code:
    
    if (object_get_parent(weapon.object_index) != o_weapon) {
        show_error(string(weapon) + " is not a weapon.", true);
    }
    
    switch (weapon.object_index) {
        case o_machine_gun:
            shoot_machine_gun();
        break;
     
        //Generic weapons
        default:
            shoot_projectile(x, y, direction_facing, get_projectile(current_weapon.type));
        break;
    
    }
    
    Instances can be created by the data structure and the correct projectile can be instantiated for the correct weapon. When creating the weapon and projectile, in the create event, I read in the properties from the data structure.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice