Designing a better modular code.

Started by Tarnos12, Thu 26/05/2022 15:54:34

Previous topic - Next topic

Tarnos12

Hello,

I came to the forums to seek some help regarding code design in ags.

My goal is to take advantage of built in events to setup my own event system in order to create modules.
The idea of a module as far as I know is to make it easy to copy paste to a new project since it should work on it's own(unless it has a dependency)
The module itself should not be dependent on the "user script".

For example if my module has a property
Code: ags
.isReady = true;
user script should NEVER set the value directly like
Code: ags
.isReady = false;

This destroys the purpose of a module.
The module itself should set the property to true/false after some conditions are met(usually 1 frame passes, so on next update)
At least in case we want to make custom event system for the module.
There are cases where we want user to change the state of a module with functions etc.(but this make it so module can be used in 1 place only usually)
The event system allows 10 different scripts to use
Code: ags
.isReady
and on the next frame it will automatically turn itself off(set to false) and no other script needs to worry about it.

Here is my example(I cut extra code) for input and movement scripts:

Code: ags

// Input script

function repeatedly_execute()
{
  if(mouse.IsButtonDown(eMouseLeft))// mouse is down
  {
     input.wasMouseDown= true;
  }
  else if(input.wasMouseDown)// mouse is not longer held down, but it was down last time(so a click happened)
  {
     input.wasMouseDown = false;
     input.isClickReady = true;
  }
}

function repeatedly_execute_always()
{
  if(input.isClickReady) inputManager.isClickReady = false;
}


Code: ags

// Movement script

function repeatedly_execute()
{
  if(input.isClickReady) HandleMovement();//prevents movement if GUI is visible.(Important)
}


Code: ags

/// global script

function gOverlay_OnClick(GUI *theGui, MouseButton button)
{
	gOverlay.Visibility = false;
}



Order of execution in ags is as follows(doesn't include everything, only the relevant bits)
rep_exec_always > late_rep_exec_always > rep_exec > gui events
This means my code executes in this order:(on mouse click, those event fire regardless, but the results will be different if I dont click the mouse.(like gui event wont fire, since its an onclick event)


  • input rep_exec_always(nothing happens)
  • movement rep_exec_always(nothing happens)
  • input.wasMouseDown = true(rep_exec)
  • gui click
  • input rep_exec_always(nothing happens)
  • movement rep_exec_always(nothing happens)
  • input.isClickReady = true(rep_exec)
  • HandleMovement(rep_exec)
  • input.isClickReady = false;(rep_exec_always)
  • movement rep_exec_always(nothing happens)

Once again to clarify where the issue is and how I fixed it(dirty fix imo, I'd rather not rely on it)
The order of execution is as follows = input rep_exec(1st phase) > movement script rep_exec(nothing happens yet) > gui click(gui becomes invisible) > input rep_exec_always(nothing happens) > input rep_exec(2nd phase, click happens) > movement script rep_exec(click happens, gui is not visible so we are are "ignoring" the gui click and the code assumes that there is no GUI in front of the mouse which allows character to move towards clicked location(not what I want)

The idea is that `input.isClickReady` is set to `true` for 1 frame, then on next game loop it will be set to `false` so other scripts won't execute twice for 1 mouse click.

My current "fix" is to call
Code: ags
input.Clear();
on GUI Click which basically sets all properties to false meaning no input/movement/interactions happen.
This is needed for this specific gui click, it wouldn't be a problem if the click happened before rep_exec this way my movement script would be aware that the GUI is under the mouse.


I am looking for ideas, how to improve this and overall how to write better code and modules.
What kind of solutions do you use in your code?

eri0o

I am not sure when you say modules if you are thinking about code organization in your game, or for script modules that can be exported and used regardless of the game.

But if you have some worries about when things happen, you can just have an Update () method and then the user calls this whenever they fell like, be rep_exec_always, rep_exec, an event or whatever.

Tarnos12

Quote from: eri0o on Thu 26/05/2022 21:00:31
I am not sure when you say modules if you are thinking about code organization in your game, or for script modules that can be exported and used regardless of the game.

But if you have some worries about when things happen, you can just have an Update () method and then the user calls this whenever they fell like, be rep_exec_always, rep_exec, an event or whatever.

Yeah it's a code organization.
And yeah, this is actually a great idea.

I will use Update method and update all modules I care about in 1 specific script to have full control.
Didn't think about that, but it makes more sense.

The only issue is that when I move those modules to a new game I have to setup Update calls again.
Which is fine I suppose(or I could have a boolean that decides if it needs to be updated manually or not)

SMF spam blocked by CleanTalk