Jump to content


Photo

Design Patterns and Principles in GameMaker (or: "Organising Code")


  • This topic is locked This topic is locked
12 replies to this topic

#1 Stratadox

Stratadox

    GMC Member

  • GMC Member
  • 927 posts
  • Version:Unknown

Posted 30 November 2015 - 10:20 PM

Hello :)
 
It's relatively easy to write GML code.
It's also very easy to write Bad GML code.
 
This topic is meant to discuss how best to organise and write Good GML code.
 
What do I mean by Bad GML code?

As a professional software developer, I shall not allow any of such monstrosities in my code base, not even for a hobby project!

When I add new functionality to any project, I do so in the following way:

How do all of you keep your projects from becoming overly complex and unmanageable?


  • 4
greetings[0] = 'hello';
greetings[1] = 'world';
repeat for_all(greetings) {
  show_message(this_one());
}
Get repeat for_all here

#2 slayer 64

slayer 64

    Slayer of gingers

  • GMC Member
  • 4301 posts
  • Version:GM:Studio

Posted 30 November 2015 - 11:11 PM

i like to start new projects and copy over code, organizing it in the process.
  • 0

5y5rs3d.pngfg0UQNL.png


#3 Erik Leppen

Erik Leppen

    GMC Member

  • GMC Member
  • 2751 posts
  • Version:GM:Studio

Posted 01 December 2015 - 02:24 PM

How do all of you keep your projects from becoming overly complex and unmanageable?

I wish I knew.
 

As a professional software developer, I shall not allow any of such monstrosities in my code base, not even for a hobby project!

Yes, that's the ideal world. However, as I have learned, many (if not most) professional code environments do not work that way. Far from that. I know a place that violates almost every rule, and no, it's not a GameMaker project. Really, compared to that place, the Turmoil codebase is a neatly organised, shining transparent diamond of code that reads like a novel. However, standing by itself, I feel that the best way to dscribe how Turmoil is organised, is: "well, it works".

Anyhow, things I do to try to organize code... The post below is a bit of a random collection of ideas, and it by itself already not really organized...

Visual documentation.
My documentation looks like this:
0svjvrn.png
This scheme shows all rooms (and all possible room transitions), all objects and where they are created, all global variables, all instance variables, all constants, all events, all actions, and the logical if-then structure of some routines. Many scripts and resources are not shown.

As you can see, it's... not... really... structured...

Thing is, I don't see how it can be structured. After all, there are variables that many parts of the game need. Almost every ingame object needs to know which upgrades the player has researched. There has to be a way to get that information from the campaign towards the ingame objects, no matter whether directly or via intermediate stops in other instances. While playing, a copy of this data is held inside the game object, so that I can use the game object without needing the campaign (for the single levels).
I created a "gameinput" object which works as a varibale container for the game. Something in the menu or town room creates the gameinput, puts the required data in it, and calls a room_goto. Then, in the game room, the game object pulls the data from the gameinput and destroys it. At the end of the round, the game creates a "gameoutput" before ending the room, so the menu can get the data from there.

This way, I don't have to make the game object persistent and can still carry data beyond the borders of a room :)

Creative use of English to name things
The word "level" is a dangerous term, because it can mean anything. And you're in for trouble if you use the same word for two different things. So I try to avoid the word, and use words like "plot", "world", "estate" or "storey" for the level you're in (depending on the project), "tier" for the upgrade level of a structure, "occasion" for the current level number in the campaign, etc. When I made a prototype for a game about trains, I deliberately used the word "quay" for the platforms, because in gaming, "platform" unfortunately already has two meanings. This way, I minimize the risk of confusion. I try to use metaphors for things, and I often use Google Translate to find a good word for something. I may get an unusual word, like "wharf" for "load or unload" but at least loading wagons will not be mistaken for loading files.

One object, one responsibility
This is hard. Turmoil has an invisible object "bill" that does everything related to money. It determines the starting money, it can take loans, and the stats object pulls the financial data from it. I also have a persistent object "pricing" that does nothing but hold all variables related to pricing. (If GML had array constants, I would have used those instead.) This keeps thing separate, as much as possible.

What I keep finding difficult is thinking about the responsibility of objects over other objects. That's probably why my scheme is such a mess, but which object is responsible for the creation and destruction of a certain object? Ideally, one should define this for every object.

Prefixing.
Everything related to the bill starts with bill. bill_borrow, bill_can_repay, bill_repay, bill_landlease_value_remaining, those kinds of things.

I usually don't do extensions, except for things I can use in many games, but usually I just have a set of scripts. For example, localization, where everything starts with lz_.

Typing.
All my variables have, implicitly, a type. I don't mean, real or string, I mean, this variable holds a province, that variable holds a wheel, this variable is an orientation, that vairable is a hud item, etc. Except for the value undefined, variables won't change type. I use constants for the type's values, although enums would have been better (were it not for syntax coloring). I have constants
  • province_desert
  • province_forest
  • province_island
  • province_none
  • province_snow
  • province_temperate
and every variable that holds a province, is not allowed to hold anything else than one of these constants. There's almost always a "none" value. Also, there's many scripts to convert one data type to another, or to make smiple computations.
  • province_before(province), province_after(province), province_is_earlier(province1, province2)
  • province_name(province), province_named(name)
  • province_tooltip_colour(province)
  • province_spillage_fine_factor(province)
  • province_occasion_first(province), province_occasion_last(province)
Constants, constants, constants. (Well, macros.)
At the time of writing, Turmoil has 409 constants, many of which are merely indices, or "type values" such as the provinces above. But many constants are there to avoid magic numbers, e.g.
bill_level1_winthreshold
campaign_file
estate_oilsupply_average
game_halfwidth
gui_title_height

Also, I use many instances as variable containers. For example, Turmoil's 14 characters each have a walking sprite, an array of talking sprites, a portrait, a label, a colour. At game start, I create 14 persistent invisible instances of the "guy" object (because "character" already has another meaning in code) and put their ids in global variables (e.g. global.guy_anthony). I made 14 constants whose values are the global variables. I can then use _g_anthony.talk[0], where _g_anthony will be syntax-coloured and is in autocomplete whenever I type _g.

Dummy sprites for all objects
All objects get a 16x16 dummy sprite for easy recognition in the resource tree, and persistent objects usually get a spearate sprite. Next to persistent objects, I also usually have a separate category of omnipresent objects, meaning instances of those are created at game start and never destroyed or created anywhere else during play. Except things like "settings", these are usually merely "data holders". I prefer instances and constants to data structures for structuring my data, because of syntax coloring and autocomplete.

Not sure this is the kind of organisation you mean, but all of this helps me a lot. It gets me beyond 20,000 lines of code and I can still find things. I do notice though that development is slower than in the beginning, but that can also just be because many small things do need to be fixed and this is simply more work than adding new stuff.
  • 2

promo_briquidmini_500x150.png


#4 iam1me

iam1me

    GMC Member

  • GMC Member
  • 521 posts
  • Version:GM:Studio

Posted 04 December 2015 - 02:55 AM

Design Patterns and Principles are important, and I certainly have some I've developed when working with GM, though I haven't written them down as explicit rules or anything. One of the things that I've started to do is to avoid Macros/Constants as such. I've always found the interface to them lacking - and it is hard to keep things organized with it. Also, I dislike the terminology - when I think of a Macro I think of a C/C++ preprocessor Macro - which would be amazing to have in GML!!! Unfortunately Macro in GML just equates to simple string replacement. Even this doesn't work well. If you try to use a defined Macro for a function call, the compiler will yell at you :/

In their place I recommend enums. They are still constants, and you can make them reals/strings. Most importantly: you can define them in separate files within your directory structure, allowing you to easily organize them. They are handy both as a means of defining the structure of your custom data structures and as a means of providing settings.

I often use them to control my debug. Validation and verbose trace can really slow down your program, so I have developed the habit of creating the following enum for any data structure I'm working on:

enum XXX_DEBUG_SETTINGS

{

   ENABLE_VERBOSE_TRACE = true,

   ENABLE_VALIDATION = true

}

replace XXX with the name of the data strcuture/object. Then, whenever I feel like putting in debug, check first if I'm in debug_mode and then if the trace/validation settings are turned on respectively. When I'm finished debugging it - I set those settings to false.


Edited by iam1me, 04 December 2015 - 03:03 AM.

  • 0

#5 stainedofmind

stainedofmind

    GMC Member

  • GMC Member
  • 1775 posts
  • Version:GM:Studio

Posted 04 December 2015 - 02:39 PM

 

In their place I recommend enums. They are still constants, and you can make them reals/strings.

 

They can only be real values, FYI.  I actually made a suggestion/request to have them handle string values, as I also really don't like how constants/macros work, but Mike denied it.


  • 0

#6 iam1me

iam1me

    GMC Member

  • GMC Member
  • 521 posts
  • Version:GM:Studio

Posted 04 December 2015 - 07:39 PM

Ack- you're right. I hadn't tried it yet (obv) but thought I had seen it supported
In the doc. Just as well- makes them function closer to other programming languages. That said - they need to support constant strings defined in a similar manner as enums. AND they need to let you declare/initialize global a in the same way, without explicitly calling a script to do it
  • 0

#7 Author

Author

    GMC Member

  • GMC Member
  • 126 posts
  • Version:GM:Studio

Posted 11 December 2015 - 12:32 PM

Not sure how one would go about unit testing in game maker. Is this possible? Right now i'm using various debug commands, cheats n such to make it easier to test the systems, but this is not ideal.

For one of the points, specifically "Assume nothing about the executing instance, pass instances through arguments.", I would mostly agree, though I have encountered a situation, mainly in my dialogue system, where it would help to assume somethings about the instance, in order to achieve cleaner code in the controller. This is also for scripts that is meant to be used only here, so it makes sense.

This would also be true if scripts you are making, is supposed to represent methods on an instance. Just make sure you structure it correctly, which brings me to folder structure. Many interesting ways this could be handled, and I'm not sure how other people have done it, but i'll give my two cents on how I handle this. I'd love to hear how other people handle this.

I have 6 main folders I use for objects. These are as follows:
  • Core:
  • Controllers:
  • Interface:
  • Gameplay:
  • Systems:
  • Engine:
All these folders can contain various nested subfolders whenever it makes sense.

The Core folder contains all main core objects of the game. These are useally controllers such as ctr_globals, that instantiate all global variables on game start, or ctr_savadata, that resets various globals on different saveslots n such.

Then I have the Controllers folder. This folder contains all specific ingame controllers, that handle action throughout the game. Again I'll just use my dialogue system as an example, this could be the dialogue controllers for various npc's and dialogues such as, ctr_teresa_dialogue_recruit, ctr_teresa_dialogue_gossip. This would of course be nested in their correct subfolders, such as Dialogue -> Characters -> Teresa. If I then find myself reusing lots of controller functionality, I'd divide furhter into subfolders and have a parent controller for each of these.

In the Interface folder, I have all UI interface elements the player can interact with. This is mostly for the main menu with buttons and sliders and such, but also for pause menus, various shops and vendors, any interactable interface really.

In the Gameplay folder, I have various gameplay objects. My game would be a bad example of this, but in say a platformer, this is where you would have your solids, platform types, doors, enemies, coins, or any such gameplay object. If vendor example again, the vendor NPC would be here, but not the vendor interface, which would be in interface. This makes logical sense, since lots of interface elements is generic and can be reused.

The Systems folder is for all my various systems, specific to the current game. This would be the dialogue system, which have some model objects that handles all the control here, or a pause system, that handles pausing various alarms, a combat system that handles all combat controls n such. All the modelclasses, and maybe a few generic parent controllers and such would be located here, but the specfic controllers for the specfic situations would be in the controllers folder.

Last folder, the Engine folder, I use for all my various extremly generic and reusable engine objects. These are mainy tempoary, and includes things such as objects that handle a fade transition, background change, storing data n such. Auctelly working on an extension for these, as well as their companion scripts,
as these could be useful in any project.

The scripts folder itself is similar to the objects folder, but won't get into it here.
  • 1
If it's worth making, make it worthy.

- Branching Dialogue System
- Ocean Storm Alpha

#8 Stratadox

Stratadox

    GMC Member

  • GMC Member
  • 927 posts
  • Version:Unknown

Posted 13 December 2015 - 01:37 PM

Nice to see such animo for this subject :)

Well structured codebases are infinitely better maintainable.
They remain manageable after many additions and modifications.

When all parts are separate:
- Something that was used for one thing can be reused for something else
- Each part can be independently tested and easily debugged
- It becomes easier to focus on a particular task


Warning: This is going to be a huge post.
But if you're interested in organising code in GML, it might be worth the reading effort.

 

i like to start new projects and copy over code, organizing it in the process.


I guess that could work. But it also brings problems, unless you:
- Work on one project at a time
- Consider all previous projects as complete and never to be worked on again

Otherwise, you'd have to:
- Copy-and-paste the changes you made to the copied code from the latest project
- Re-adjust the whole older project to the new organization

Or work in an old version of your code, risking:
- Having to make thing that were already made in the new version
- Creating a separate branch (in case of cool new stuff)
- Creating "merge conflicts" for the next new project

Also when you fix a bug, you need to fix it many times for all projects where you used that code.

To prevent such issues, I always put all application logic in separated extension modules.

If I work on an older project, I can easlily import all new functionality I need.
When I start a new project, I can easily import the required functionality I made before.


 

Design Patterns and Principles are important, and I certainly have some I've developed when working with GM, though I haven't written them down as explicit rules or anything. One of the things that I've started to do is to avoid Macros/Constants as such. I've always found the interface to them lacking - and it is hard to keep things organized with it. (...) In their place I recommend enums.


I've always used constants until I recently found out about and adopted these enums.
Their naming is quite odd: an enum is usually something entirely different.
I sometimes use true enums, but implement them as array of string values.

A GML "enum" is more like a struct with constant numerical values.
That's really nice to have; they are like constants, but with a namespace :D


 

Not sure this is the kind of organisation you mean, but all of this helps me a lot. It gets me beyond 20,000 lines of code and I can still find things. I do notice though that development is slower than in the beginning, but that can also just be because many small things do need to be fixed and this is simply more work than adding new stuff.


Those are definitely the organisational topics I was talking about.
You've got some great discussion-worthy subjects in there.


 

This scheme shows all rooms (and all possible room transitions), all objects and where they are created, all global variables, all instance variables, all constants, all events, all actions, and the logical if-then structure of some routines. Many scripts and resources are not shown.


It looks like.. a level!

But in all seriousness, the diagram looks pretty good.
Not many projects have such a diagram available...
If they had, I think most would be much more tangled up than this one.


 

As you can see, it's... not... really... structured...

Thing is, I don't see how it can be structured. After all, there are variables that many parts of the game need. Almost every ingame object needs to know which upgrades the player has researched.

I always try to keep objects as self-contained as possible.
A practical way to do this is to think about how it goes in reality.

For example:
In the real world, a house does not know anything about the renovations it has had.
It "knows" only its current state.
The state of the house is influenced by the renovator, but does not depend on it.
When the renovation business disappears, the house does not return to its old state.

The same principle could go with ingame objects.
If the ingame objects would not depend on, but receive from the upgrades, they become more self-sufficient and versatile.


 

The word "level" is a dangerous term, because it can mean anything. And you're in for trouble if you use the same word for two different things. So I try to avoid the word, and use words like "plot", "world", "estate" or "storey" for the level you're in (depending on the project), "tier" for the upgrade level of a structure, "occasion" for the current level number in the campaign, etc.

This, I cannot quite comprehend.
Naming things is already one of the hard parts of software development: Why make it even harder?

I use plenty of words that have several slightly different meanings.

For example, the word index.
Whether it be a sprite index, a channel-, buffer- or font index, they're all indices.
They mean something slightly different, but in the end they all indicate the location of a resource.

Depending on the prefix, they mean different things:
In case of a sprite index, it indicates the location of a sprite.
In case of a buffer index, it indicates the location of a buffer.

It can be the same with level; it's a progress indicator.
For a campaign level, its an indicator for the progress of the player through the campaign.
For an upgrade level, its an indicator for the progress of the upgrades for that entity.

Sure, their meaning slightly changes based on the context.
But GML, like most other languages, is quite capable of dealing with that.


 

What I keep finding difficult is thinking about the responsibility of objects over other objects. That's probably why my scheme is such a mess, but which object is responsible for the creation and destruction of a certain object? Ideally, one should define this for every object.


There's a whole range of design patterns for the creation of objects; you could look into those.
If you want one object to be responsible for the creation of one (or several) other object(s), you might want to consider that object a Factory.
I think I'd implement Factories as scripts, for objects in gamemaker cannot have methods other than their events, but I guess that's an implementation detail.

Alternatively, there's repositories.
They are mostly used in webdevelopment, to provide methods to find, load and persist objects in a database.

I'm not sure yet as to how exactly, but I think the cancept can be used in GameMaker.
When properly implemented, repositories can bring great benefits.

They provide a single point of access to your objects.
This makes it easy to use one object instead of another, without the need to make changes all over the place.
One use case for that is to substitute a dummy object to a unit test.
Another is when you decide to rename an object, or use an entirely new one in its place:
Instead of needing to change every instance_create call, you change only one repository.

Even more important, it helps separating the business logic layer from the application layer.

What do I mean by "business logic", while most of us aren't running businesses?
Business logic is what I'd call each part of your code that determines what when to use.
The application layer is the code that defines how that is done.
There's also the presentation layer, defining how these things are shown.

These concepts are common in Multilayered architectures.
Multilayered or multitiered systems completely seperate the "what to do" from the "how to do it" and the "how to present it".

GameMaker already separates the presentation layer from the others.
This separation is encouraged by the draw events.
It could be separated even better, of course, by using objects that do rendering only.

I think GameMaker projects have a lot to gain by separating the "what" from the "how" as well.
When application logic is properly separated from business logic:
- All application logic can be easily reused
- The business logic is greatly reduced

When the application logic is properly encapsulated in .gex extension files, the amount of code in the .gmx of the game is decreased enormously.


 

One object, one responsibility
This is hard. Turmoil has an invisible object "bill" that does everything related to money. It determines the starting money, it can take loans, and the stats object pulls the financial data from it.


Sounds more like a bank :P
 

Prefixing.
Everything related to the bill starts with bill. bill_borrow, bill_can_repay, bill_repay, bill_landlease_value_remaining, those kinds of things.


In the case you describe, I would probably have made an object type Bank.
A Bank instance would provide access to the following features:
- Manage debit accounts of its customers (the players)
- Make payments from one account to another
- Provide loans and charge interest


The Bank would provide access to these services.
But the Bank is only a storefront.
Business layer logic.

Each seperate feature would be in its own module.
You'd have a Bank Account extension module, responsible for:
- Defining what a bank account is
- Creating bank accounts
- Retrieving the account balance
- Updating the account balance
- Destroying bank accounts
Those are just the basic CRUD operations.

Then there's a Bank Payments extension module, responsible for:
- Making a payment from one Bank Account to another
- Providing a check to see if a transaction can be made
- Charging transaction fees
- Keeping a record of previous transactions
The Bank Payments extension module would depend on the Bank Account extension module.

And of course, the Bank Loan extension module, responsible for:
- Providing loans, possibly with collateral
- Providing a mechanism to determine if the maximum loan
- Accepting loan repayments and charging interest
- Retrieving information about the loan
- Checking if loan repayments are behind
- Calling a business-layer script if a loan is unpaid
The Bank Loan extension module would depend on the Bank Payments extension module.

The Bank object now accesses the different bank_ functions in three separate extension modules.
These would now have a double prefix, for example:
- bank_account_create(bank, player)
- bank_account_view(account)
- bank_payment_make(account_from, account_to, amount)
- bank_loan_create(bank, player, amount, collatteral)
- bank_loan_repay(loan, account, amount)

Now lets say for a next game, I'd need only the money accounts and a payment mechanism, but no loans.
I'd simply include the bank accounts and the bank payments, but not the bank loans.
If for this game, I'd need savings accounts, I'd make an additional Bank Savings extension module.
In case I decide to also use bank savings in the previous game, I include the savings module.

Easily extensible and cleanly separated.

 

Not sure how one would go about unit testing in game maker. Is this possible? Right now i'm using various debug commands, cheats n such to make it easier to test the systems, but this is not ideal.


There's limitations, but unit testing is possible, sure.
Unit testing means that each separate part of the code is tested separately.

What I'm doing right now, is writing a script for each test case.
It returns an array of length 3, with:
- The actual result of the testing operation
- The expected result of the test case
- A description of what should have happened
In a testing environment (the .gmx I use for a module) I then:
- Run all the scripts
- Compare the expected value with the actual result
- Display the descriptions where the comparison failed

An example of such test cases is for_all_tests.gml, which I wrote for my for_all extension.

I'm currently working on a unit testing module to facilitate and improve such tests.
 

For one of the points, specifically "Assume nothing about the executing instance, pass instances through arguments.", I would mostly agree, though I have encountered a situation, mainly in my dialogue system, where it would help to assume somethings about the instance


It's tempting, I know :P
But assumptions, more often than not, lead to errors.

Sometimes you still want to use variables of an instance from a scripts.
The usual way to handle this, is to use interfaces.
Interfaces are like contracts for objects.
They make sure that, for example, an object has some required properties.
Usually, they also define a set of methods which a class must define.
Since gamemaker does not support class methods, the latter would not make sense.

Interfaces might be aproximated in GML, though only for properties (variables).
In a script, you'd limit your input to instances that implement an interface.
That way, you can use instance variables which are defined in the interface without assuming anything.


 

which brings me to folder structure. Many interesting ways this could be handled, and I'm not sure how other people have done it, but i'll give my two cents on how I handle this. I'd love to hear how other people handle this.


Since all my application layer logic is properly tucked away in extension modules, they don't show up in the folder structure.
If they would show up - they might in the future - I'd put them in a root folder "Application logic" under scripts.

Under `scripts` I only have those scripts that are Business logic.
These are the scripts that define what should happen on what moment.

They are often the scripts that are called by various extension functions.
Those are also named "callbacks"

Lets take the Bank Loan example from before.
The Bank Loan mechanism is properly encapsulated in an extension module.
This mechanism is not, however, responsible for enforcing whatever happens if the borrower does not pay up.
Such consequences are typical business layer logic, and should not be defined in the extension module.

I'd therefore make the Bank Loan mechanism call a script when such a thing happens.
That script would show up in the .gmx file of the game.
It might transfer ownership of the collateral from the borrower to the lender.
In another game, the script called on that occasion would tell the bank to raise an army to punish the borrower.
Whatever the decision, it's a business layer decision, for it is entirely game-specific.

In a .gmx file that is for a game (as opposed to one used for an extension), I use a structure like this for scripts:
- Callbacks (with a subfolder for each module that uses callbacks)
- Events (with a subfolder for each object type that uses such scripts in their events)

My objects folder has a different structure for every game type, but often still very small for the size of the game.
I regard them as classes, and often only make objects for what some people would consider parents.
Their names do not start with an (ugly) "obj_" prefix, but with a Capital.

For example, in an RPG game with NPC's, I might have:
GameEntities/NPCs/Npc
To act as parent for:
GameEntities/NPCs/FriendlyNpc
GameEntities/NPCs/NeutralNpc
GameEntities/NPCs/HostileNpc

But I would not have a class for each NPC.
Instead, I'd load their data from a file, or have their properties generated.

In the RTS I'm building, there will be hundreds of different units, buildings and other interactive instances.
Around a dozen objects will be sufficient all of them.

This is because of yet another separation I make: The separation between code and content.
The name of a character is content, not code.
If an NPC has a specific dialoge, that's content.

So instead of creating a class for every character, I make a general class `Character` and provide it with content.

Through all these separations, I end up with a very small amount of scripts and objects in my game file.
Any organisation through folder structure is pure bonus at that point.



TL;DR
- Separate the "what" from the "how"
- Separate the code from the content
- Re-use as much as possible
- End up with less code

Edited by Stratadox, 13 December 2015 - 03:31 PM.

  • 4
greetings[0] = 'hello';
greetings[1] = 'world';
repeat for_all(greetings) {
  show_message(this_one());
}
Get repeat for_all here

#9 djk164

djk164

    GMC Member

  • GMC Member
  • 301 posts
  • Version:GM:Studio

Posted 14 December 2015 - 03:29 AM

-snip-

 

Well worth the read!

This gives me so much more to think about while organizing stuff. I am going to have to start making a lot of extensions  XD


  • 0

Bye old GMC!


#10 Dragonite

Dragonite

    GMC Member

  • GMC Member
  • 69 posts
  • Version:GM:Studio

Posted 15 December 2015 - 07:32 AM

Oh, man, I have to come back here and read this thread cover-to-cover a few more times . . .

 

The honest answer is I don't, my code style is terrible, but I do try and follow a few rules so that I can still read it after I get back from the bathroom:

 

 - If a verb exists in the English dictionary it probably has a script. walk(object, direction, speed), walkToLocation(object, x, y, speed), walkToObject(object, target, speed), giveItem(object, item, quantity), all that fun stuff.

 

 - More or less like Erik, everything as a type, especially things that GM indexes like ds_lists and stuff. And also things like Items Locations and WeatherEffects and stuff. Would be nice if GM supported some kind of struct-like thing, like an object that just held data and didn't bother with events or anything, though (if there is such a thing let me know). ds_maps or lists could technically work for that, I suppose, like doorKey[? "name"]="a key", doorKey[? "essential"]=true, doorKey[? "sprite"]=sprite ?

 

 - Enums are amazing. They're even more amazing now that they're actually supported by GM and you don't have to spoof them with the Macros/Constants window.

 

 - Most tangible things in the game are all the same. You can give a Potion to a rock if you really wanted to, though that's kind of pointless; more importantly, rocks and NPCs can be moved, scaled, made solid/not solid, turned into each other, whatever, with the same code. Originally did that so that you could change control between characters without having to shuffle a lot of code around and eventually just threw my hands in the air and did it for everything. It'll probably turn from a help to a hindrance at some point, but so far it's worked pretty well.

 

 - Polymorphism through executing script indices. Of all things, this is probably why I'm still using GM and not goofing off with Python or something else right now. Brain kind of got glued to polymorphism in high school and never let go.

 

Kind of related, but not really:

 

 - Most local variables I use are preceded with an underscore, except for names like for (var i=0; i<whatever and var f=file_bin_open(stuff) which are only ever used for that purpose. I used to spend days trying to debug problems that were caused by the overlapping of a local and instance variable. (If I ever use "i" for something besides a loop counter, I probably deserve to waste that much of my own time.)

 

This all usually works decently well until I take like a day off and come back and try to change one thing and it all blows sky-high and I spend the next week fixing it. I guess my next step should be learning how to write design documents that I actually intend to follow?

 

Also, am I the only one who uses GM whoCamelCasesEverything instead_of_using_underscores? Yes? I'll be off now.


  • 0

zIatugK.png


#11 xDGameStudios

xDGameStudios

    GMC Member

  • GMC Member
  • 71 posts

Posted 08 March 2016 - 12:03 PM

After reading this and trying to understand better how I should program in a efficient way... let's take the example of a window (RPG type) system.
you have a window object that has a variables width, height, x, y, columns, scroll_y, scroll_x...
for giving a in-depth explaining the window has multiple items and the user can select one of them...
Having a function like this is bad?!?!

///window_selectable_top_row()

return floor(scroll_y / item_height);

code wise it can be good because if I need to know the top_row ar a given time it's better to have a function for it then writing the same code over and over... but it breaks the rule "scripts should not be aware of object variables"....

the script is only to be used inside the window_object... and all the variables exist... and so on... I wanted to know if I'm "doing it wrong" :(
 another example:
 

///window_selectable_max_page_rows()

var page_height = height - 2*v_padding;
return floor(page_height/item_height); 

what do you think, 

Stratadox
  • 0

#12 lolslayer

lolslayer

    GMC Member

  • GMC Member
  • 242 posts
  • Version:GM:Studio

Posted 10 March 2016 - 12:39 PM

Don't tell me what to do and what not!


  • 0

#13 stainedofmind

stainedofmind

    GMC Member

  • GMC Member
  • 1775 posts
  • Version:GM:Studio

Posted 10 March 2016 - 04:57 PM


Having a function like this is bad?!?!

 

///window_selectable_top_row()

return floor(scroll_y / item_height);
code wise it can be good because if I need to know the top_row ar a given time it's better to have a function for it then writing the same code over and over... but it breaks the rule "scripts should not be aware of object variables"....

 

See, now to me, this kind of code is closer to an OOP style class method then a function.  So really, because of the idiosyncrasies of GM, it's not bad, more of a unique method (pun intended) of programming we use with this language.


  • 0