• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GML Inheritance vs data structure

E

ElegantMistake

Guest
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);
 
I

immortalx

Guest
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 by a moderator:
E

ElegantMistake

Guest
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];
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.
 
I

immortalx

Guest
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.
 

samspade

Member
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);
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.
 

TheouAegis

Member
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.
 

FrostyCat

Redemption Seeker
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()?
 
E

ElegantMistake

Guest
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.
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.
 
Top