Legacy GM [Good Coding Practices] Am I making too many scripts?

A

Albumm

Guest
I'm trying to develop a grid-based roguelike engine and the step code for obj_player looks like this so far:

Code:
p_get_controls(); //saves keyboard_check -> key_x

if isSnappedToGrid()  //return place_snapped(global.gridSize,global.gridSize);
    stop_moving();   //motion_set(0,0);
 
if (isPlayersTurn() && isSnappedToGrid())
{
    p_survey_move();
}

Script: p_survey_move:
Code:
if ((key_up) && (key_right))
    p_attempt_move(DIR_UPRIGHT, spr_player_upright); //DIR_UPRIGHT = 45
else if ((key_up) && (key_left))
    p_attempt_move(DIR_UPLEFT, spr_player_upleft); //DIR_UPLEFT = 135
//...etc

Script: p_attempt_move:
Code:
grid_direction = argument0;
new_sprite = argument1;
pointingTowards_x = 0;
pointingTowards_y = 0;

switch(grid_direction)
{
    case DIR_UPRIGHT:
        pointingTowards_x = x+global.gridSize;
        pointingTowards_y = y-global.gridSize;
        break;
    case DIR_UPLEFT:
        pointingTowards_x = x-global.gridSize;
        pointingTowards_y = y-global.gridSize;
        break;
    //...etc
}
if (current_sprite != new_sprite)
    set_sprite(new_sprite);
if isUnblocked(pointingTowards_x,pointingTowards_y)
    move_towards(grid_direction);
Scripts so far:
Code:
/player/
   p_get_controls
   p_survey_move
   p_attempt_move
/global_get/
   getDiagonalSpeed
   isSnappedToGrid
   isPlayersTurn
   isMoving
   isUnblocked
/global_set/
   set_sprite
   stop_moving
   move_towards

Am I adding too many function scripts? Will this lead to problems later on in development? My first attempt at this engine became unreadable spaghetti code after a few weeks, so I'm trying to structure it better.
 

Slyddar

Member
If you are trying to improve on spaghetti code, learn how to implement states. Then within the states, utilise scripts there and it will all be much more readable and manageable. Scripts are good for readability, but are great for code that you want to reuse elsewhere, which is just what states require.
 

samspade

Member
I'm trying to develop a grid-based roguelike engine and the step code for obj_player looks like this so far:

Code:
p_get_controls(); //saves keyboard_check -> key_x

if isSnappedToGrid()  //return place_snapped(global.gridSize,global.gridSize);
    stop_moving();   //motion_set(0,0);
 
if (isPlayersTurn() && isSnappedToGrid())
{
    p_survey_move();
}

Script: p_survey_move:
Code:
if ((key_up) && (key_right))
    p_attempt_move(DIR_UPRIGHT, spr_player_upright); //DIR_UPRIGHT = 45
else if ((key_up) && (key_left))
    p_attempt_move(DIR_UPLEFT, spr_player_upleft); //DIR_UPLEFT = 135
//...etc

Script: p_attempt_move:
Code:
grid_direction = argument0;
new_sprite = argument1;
pointingTowards_x = 0;
pointingTowards_y = 0;

switch(grid_direction)
{
    case DIR_UPRIGHT:
        pointingTowards_x = x+global.gridSize;
        pointingTowards_y = y-global.gridSize;
        break;
    case DIR_UPLEFT:
        pointingTowards_x = x-global.gridSize;
        pointingTowards_y = y-global.gridSize;
        break;
    //...etc
}
if (current_sprite != new_sprite)
    set_sprite(new_sprite);
if isUnblocked(pointingTowards_x,pointingTowards_y)
    move_towards(grid_direction);
Scripts so far:
Code:
/player/
   p_get_controls
   p_survey_move
   p_attempt_move
/global_get/
   getDiagonalSpeed
   isSnappedToGrid
   isPlayersTurn
   isMoving
   isUnblocked
/global_set/
   set_sprite
   stop_moving
   move_towards

Am I adding too many function scripts? Will this lead to problems later on in development? My first attempt at this engine became unreadable spaghetti code after a few weeks, so I'm trying to structure it better.
I have upwards of 100 scripts in some of my larger projects. So that definitely isn't a lot yet. Use good (consistent) naming conventions and organize your scripts into folders. A personal rule I have for scripts is this: If the code only appears once, do not put it in a script. If the code appears more than once, put it in a script (or parent object depending upon the scenario).

I say this is a personal rule because I know not everyone follows it. And as a downside it can lead to some very large events of hundreds of lines. GMS 2 has code folding though which helps with readability. Personally, even without code folding, I'd still follow this rule as I find it easier to read and understand a sequential wall of code than to track the line of thought through scripts embedded ten deep. However, the reason duplicated code goes in a script (or parent object) is so that you can reuse code and so that if you decide to change something, you only have to change it in one place.

In your examples, the player specific scripts I might do in the step event (or whatever is relevant) rather than as a script. As it seems like they are only called for the player. However, all of the other ones seem to be global and so I would leave as scripts. Also, it's possible that elements of the players scripts might be more global in nature. For example, p_attempt_move doesn't really seem specific to the player and p_survey_move only seems specific to the player because it takes key inputs. It seems like p_survey_move could be more global just by giving it directions rather than inputs. Then you could use the same set of scripts for nonplayer objects that need to move on the grid.
 

Cameron

Member
Looks good to me. I prefer readability over having everything in one spot. There is a book called "Clean Code" that steers towards this style. I would much rather work in a project like yours than one that has a bunch of code I need to sift through.
 

TheouAegis

Member
Calling a script is slower for the computer than just reading the code, but you shouldn't notice any effect until you have thousands of instances trying to run scripts. The execute_script function is even slower and should be used sparingly, though.
 
T

Taddio

Guest
Calling a script is slower for the computer than just reading the code, but you shouldn't notice any effect until you have thousands of instances trying to run scripts. The execute_script function is even slower and should be used sparingly, though.
Are you saying that there's actually a difference between calling
scr_script();
And
execute_script(scr_script);
?
If so, I'm really surprised (and curious why!)!!
 

rytan451

Member
Likely because the former can be optimized by the compiler. It's static: no matter what, it's always scr_script being called. However, the latter is dynamic. At compile-time, it isn't constant, so it can be slower. Depending on how things work (and I don't know how things work), it is entirely likely that the script_execute is as slow or slower than calling the script directly.
 
T

Taddio

Guest
Likely because the former can be optimized by the compiler. It's static: no matter what, it's always scr_script being called. However, the latter is dynamic. At compile-time, it isn't constant, so it can be slower. Depending on how things work (and I don't know how things work), it is entirely likely that the script_execute is as slow or slower than calling the script directly.
Never tought of it that way, but it makes sense. Guess I'll just add that function on the "Never Use Again" list and accept the fact that I will never truly deeply understand computers o_O
 

TheouAegis

Member
One uses a look up, the other
Never tought of it that way, but it makes sense. Guess I'll just add that function on the "Never Use Again" list and accept the fact that I will never truly deeply understand computers o_O
It's not a bad function. Not at all, it has many uses. But if you can just as easily call the script directly using your own code, then there is no reason to use it. the point of the function is for you to be able to call scripts from variables. the normal method of calling a script requires you to know immediately what script you are going to call. That can make code very messy. If you can store the script ID in a variable and reduce your code by 20 or 30 or even a hundred lines, then by all means go right ahead and do that. Anybody reading your code will love you for it.
 
T

Taddio

Guest
One uses a look up, the other


It's not a bad function. Not at all, it has many uses. But if you can just as easily call the script directly using your own code, then there is no reason to use it. the point of the function is for you to be able to call scripts from variables. the normal method of calling a script requires you to know immediately what script you are going to call. That can make code very messy. If you can store the script ID in a variable and reduce your code by 20 or 30 or even a hundred lines, then by all means go right ahead and do that. Anybody reading your code will love you for it.
Super interesting, I'll have to meditate on that one for a while before it sinks in to a usable state, but I think I'm getting the principle of it.
Thanks for the explanation :)
 
Every function you have access to kind of works in the same way. For instance, repeats work faster than for loops. Does that mean you should never use for loops? Not at all, they are very useful within certain contexts and the speed difference between the two is minor. With efficiency gains like this, it's usually FAR better to ignore the efficiency loss in a specific function until you actually run into a speed problem. A lot of people spend waaay more time than they should trying to make things as efficient and fast as possible. But this has many drawbacks, with loss of 'progress time' (i.e. time actually adding things to your project) being the foremost, alongside 'readability' of your code (making things more efficient often obscures the thing that the code is actually doing to someone reading it).

Basically, ignore efficiency of code until it becomes a real problem that adversely impacts the real life performance of your project. Focus on making clean, readable code first. Then when you find performance becomes a problem, start restructuring the things that hamper speed the most. For instance, if you are drawing an entire GUI with draw_ calls, focus on changing the way you are handling that BEFORE you start rewriting all your for loops as repeat loops (because draw_ calls negatively impact performance much more than basically any loop structure, within reason).

Remember, computers are really fast nowadays. They can handle a lot of sub-optimal code before the ship starts sinking.

"Premature optimization is the root of all evil" - Knuth
 
Top