Suggestion Make Game Maker aspect-oriented instead of object-oriented with these easy steps!

Yal

🐧 *penguin noises*
GMC Elder
So, clickbaity title aside, I think I figured out a way that could make GM aspect-oriented with just a relatively small change to the codebase. I'm making this conclusions based off of assumptions that might or might not be correct, and I might be overlooking some obvious flaws in my idea, so this might just be stillborn wishful thinking, of course. Opinions are welcome, logical arguments moreso.

First off, what is aspect-oriented programming? It's a different way to handle inheritance. Traditional inheritance is purely hierarchical in nature: your ghost enemy inherits its behavior from parentFlyingEnemy, which inherits some stuff from parentEnemy, which in turn inherits from parentContactDamage since the player is hurt by just touching it. This works all fine and dandy until you start wanting certain aspects to be inheritable, but with no real logical hierarchical structure. For instance, let's say you also want your ghost to glow in the dark. You have parentLightsource since before, but how would you make a light-emitting enemy using that? If you change the ghost's parent into parentLightsource, it loses all its enemy code and won't be treated as a damage object anymore. You could duplicate the parentFlyingEnemy into a new parentFlyingEnemyLightsource, but now you have code duplication.

The least messy solution is manually executing all the parentLightsource's events using event_perform_object, but it means you need to keep track of every "bastard child" object yourself and add new event_perform_object calls if you ever were to add new events to the parent. And the bastard children won't be covered by with(parentObject) loops, cause collision events with the parent, and so on, which might be desired for a whole lot of inheritances (say, parentCanSetWoodenThingsOnFire).

Aspect oriented programming essentially lets you have multiple unrelated inheritances, picking and mixing the functionality you need without having your objects be a tree of hierarchical definitions.

So what's my proposed solution for this?
Make GM object parents an array.

The idea is as follows:
  • Objects have multiple parents (if so desired), and they can be ordered manually. (This is important, since it makes the order conflicting events would resolve well-defined).
  • When an event is empty (and the parent event is run instead), parent events of all applicable objects (i.e., most immediate ancestor with a non-empty event) are run, in the order that parents are listed.
  • event_inherited() has the same change: all first-nonempty-ancestor events are run, and in the order the parents were listed.
  • object_is_ancestor() checks all ancestry branches to see if there's a match on any of them.
  • with(parentObj), collision events, and all other loops that implicitly include children will count an object as eligible if any of its parents/ancestors matches the looped-over object.
  • The user is responsible to make sure potential ancestry conflicts (e.g. the "diamond problem") doesn't happen in their game. Since GM allows you to create new variables anywhere, and generally is "it happens when the line is executed" instead of "everything exists at compile time", conflicts generally shouldn't be more serious than "two different pieces of code overwrites the same variable with different values"... and that should be taken care of by the priority system.
  • The user is also responsible to make shared grandparent code idempotent (i.e., executing it multiple times doesn't cause resource leaks, errors, repeated side effects, or invalid code) for diamond-shaped ancestry where a shared ancestor's code might be executed multiple times thanks to multiple parents having event_inherited()s for the same object's event.
  • If the user only uses 1 parent for each object, the system is 100% compatible with the old way of handling parenting. Additionally, converting legacy projects from the old system to the new is trivial.
I'm imagining most of these steps would be implementable in GM-behind-the-scenes territory by just taking existing parent checks and wrapping them in a for loop going through the entire list of parents; this is the big shaky assumption most of my argument is based around. The big effort hogs would be changes to the object file format to turn parents into an array, and the new GUI to allow for adding multiple parents and sorting them.


Apart from being easier to work with in large projects (where you end up with a lot of different systems and no really extensible way to tie them together), this functionality could also help promote code reuse and properly organized projects for new users (and D&D users!) who might not be comfortable setting up the script framework necessary to simulate parenting on their own, and asset pack creators could make systems that are much more easily to slot into existing projects (by adding a new parent to affected objects).
 
Last edited:

Xer0botXer0

Senpai
Can't you just put that code into scripts and call the scripts instead of using inheritance at all, because from what I remember you're completely right about the parent-child relation
In that it's annoying and linear, I haven't gone beyond one parent and a grandparent before stopping cause it's not the kind of thing I'd want to admit to using.

In my case I use scripts, and just call the scripts in the create events and where relevant under boolean conditions to give an object it's structure.

Say I want to have ten types of insects, instead of coding each insects movement individually, and their abilities individually, and what not..
I'd call a script in the create event of the object which all insects will call, then I'd modify the variables that need to change for that insect after the script
Then I'd perform a switch statement based on one of those variables that would tell me the insect type, if it's a dungbeetle then scr_flight(1) because it can fly.

I can't see why I'd want to create an object that won't even be in the game and call it obj_flight just to create a chain of inheritance for my types of insects.

But maybe I'm viewing this in the wrong manner. So keen on seeing where the thread goes.
 

Yal

🐧 *penguin noises*
GMC Elder
Can't you just put that code into scripts and call the scripts
You can, but:
  • There's no easy way to iterate over objects using the system.
  • If you add a new event that the system needs to handle, you need to go through every object using the script collection and manually add it, hoping you don't forget any object.
There are plenty of cases where you simply want inheritance, and the built-in loops (collision events, with(parentObj), etc) is a really big chunk of those cases.
 

GMWolf

aka fel666
A composition model might be a nicer approach.
The industry as a whole is moving towards schemes like ECS that achieve aspects through pure composition.

In a way, tags kinda allow this already: you could tag an object with a specific aspect.
Then a system can loop over all instances with said tag, and apply a transformation...

Actually.... I like that... I'm gonna need to investigate.
 

Yal

🐧 *penguin noises*
GMC Elder
You could still achieve the tag scheme using multi-parenting: simply give the parents no events of their own, never use grandparents, and just have a control object use with loops over each parent where appropriate.
 

GMWolf

aka fel666
You could still achieve the tag scheme using multi-parenting: simply give the parents no events of their own, never use grandparents, and just have a control object use with loops over each parent where appropriate.
I'm referring to GMS 2.3 tags.
 

Yal

🐧 *penguin noises*
GMC Elder
I'm referring to GMS 2.3 tags.
*does research*

Tags give you a list of asset names, so you need to asset_get_index them and you also can't use a single loop to go through all of them (you need a nested loop over the processed asset indices). And of course, nothing stops you from tagging non-object assets, adding another layer of stuff you need to check. Sure, you could do it, but it's definitely not going to be the optimal solution what with all the extra bookkeeping you need to do it, and it's definitely not a newbie-friendly way to organize your code.
 
Top