This is might be a much broader answer than you wanted, but I would separate the things that I do into two categories.
The first category is using the tools that GM provides:
Scripts: Almost any code that is repeated and needs to take arguments goes into a script. There are lots of resources about using scripts out there.
User Events: Almost any code that will be repeated and doesn't need arguments goes into user events. User events have several disadvantages over scripts. They can't be as effectively named and they can't take arguments. However, they stick with the object that needs them which is helpful for organization purposes. And you can call a non-existing one without crashing:
Code:
with (all) {
event_user(0);
}
won't cause any problems even when an object doesn't have a user event 0.
Inheritance: This one is tricky but nothing feels better than when you've got it right and you can change up entire things game wide in a large project with just a few minutes of code. For example, switch how every button in the game from tapping to holding by changing the parent.
Componentizing: You have to be careful with this one, especially since GM doesn't have lightweight objects yet, but it is important to remember that a 'thing' doesn't have to be all one object. For example, in a small project I'm working on I have space ships. The space ships are made of several different components. For example, the hp for each ship is a separate object (instance of an object) that has its id saved in the ship. The script that deals damage to a space ship looks like this:
Code:
with (argument0) {
with (my_hp) {
event_user(0);
}
}
What this means is that any ship can be given any type of hp system, one that includes shields, or is immune to certain types of damage, etc, at any type by simply changing the hp object or even not giving them one, and there will be no crashes or errors. The cost is that I've doubled the amount of objects.
Regions: They are great and you should use them. While I'm all for creating scripts for things that get re-used, the idea of breaking apart all my code into scripts just for normal code in order to cut down on clutter would drive me nuts (personally) as all scripts get stored in the same resource tab. Until GM gets methods, you basically have to choose between having 100s, if not 1000s of script in a large project or having a lot of code in one place. I view regions as giving you the ability to make one time 'methods' allowing you to have a lot more code in one event without it actually being that cluttered or hard to manage. Even though all lot of code is in one place, it can read as if it is all in scripts. There are a number of keyboard short cuts that make them even easier to use (control + m/u opens and closes all regions, control + enter toggles the currently selected region open or close, control + up/down move between regions, F4 then r create a region).
The second (and honestly more important) category is using basic programming patterns and principals. I'm not really qualified to teach these, and they are too big to teach in a single post anyways, but my suggestion would be do a google or youtube search on the following terms and watch some videos on them. My caveat is that I don't put any of these terms out as best or mandatory just as things you should eventually become aware of and understand.
- Don't Repeat Yourself (DRY)
- SOLID design principals
- Programming Patterns
- Clean Code
- Coding Conventions
I would say that the more specific question you asked isn't really even about clutter. Where to put a collision check is more about a specific implementation of organization which could change a lot depending upon what you want. In other words, I would answer the above differently depending upon what you wanted contact to do. If it was a spike that would harm the player, I would put that in the spike. As I think that makes more sense. If it was collision code, I would put it in the player.