Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add async behavior to functions of Godot classes in C# #3527

Closed
LordBrkica opened this issue Nov 10, 2021 · 3 comments
Closed

Add async behavior to functions of Godot classes in C# #3527

LordBrkica opened this issue Nov 10, 2021 · 3 comments

Comments

@LordBrkica
Copy link

LordBrkica commented Nov 10, 2021

Describe the project you are working on

Strategy game

Describe the problem or limitation you are having in your project

I have a an Area2D acting as one unit.

Unit moves towards targetPosition like so (this is from godot best practices):

    public override void _Process(float delta)
    {
            if (Position.DistanceTo(targetPos) > 5)
            {
                Position = Position.MoveToward(targetPos, delta * speed);
            }
    }  

I have a Control as my UI control. There, in public override void _GuiInput(InputEvent @event) a mouse click can detect where do we want to move the unit to.

I made a simple async Task so I can call Unit.MoveTo(targetPos);

So in my UI Control I basically have:

public override void _GuiInput(InputEvent @event)
{
      if (@event is InputEventMouseButton mouseButton)
      {
              Task unitMovementTaskResult = unitToMove.MoveUnitToPosition(mouseButton.Position);
              Debug.WriteLine($"IsCompleted: {unitMovementTaskResult.IsCompleted}");  //here we will immediately get false
      }
}

The problem is, right now we CANNOT await until an async Task is complete. This is because **_GuiInput** is NOT an async function. So the task execution WILL happen but the code cannot await for it.

I could write:

Task unitMovementTaskResult = unitToMove.MoveUnitToPosition(mouseButton.Position);
unitMovementTaskResult.Wait()
Debug.WriteLine($"IsCompleted: {unitMovementTaskResult.IsCompleted}"); 

but this hangs and waits indefinately.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

It would be great if godot functions could be marked/changed to async.

So instead of
public override void _GuiInput(InputEvent @event)

we would have:
public override async void _GuiInput(InputEvent @event)

Async helps integrate async C# Tasks and it would be usefull for tens od thousands of C# programmers using them.

And people who want to continue to use the functions in an (existing) sync dont have to do anything and can continue to use it like so.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

It would be great if godot functions could be marked/changed to async.

So instead of
public override void _GuiInput(InputEvent @event)

public override void _Input(InputEvent @event)

we would have:
public override async void _GuiInput(InputEvent @event)
public override async void _Input(InputEvent @event)

If this enhancement will not be used often, can it be worked around with a few lines of script?

The nearest work around right now is to use signals

So right now I am using the following.

public override void _GuiInput(InputEvent @event)
{
      if (@event is InputEventMouseButton mouseButton)
      {
              unitToMove.MoveUnitToPosition(mouseButton.Position);
              await ToSignal(unitToMove, "MovementStopedSignal");  //Here an await of some sort happens ! Hurray!
              Debug.WriteLine($"Movement completed!"); 
      }
}

Disadvantage of this approach is that I am forced to work with Signals and the same thing could be done much more elegantly using C# tasks.

So this would be ideal instead:

public override void _GuiInput(InputEvent @event)
{
      if (@event is InputEventMouseButton mouseButton)
      {
              Task unitMovementTaskResult = unitToMove.MoveUnitToPosition(mouseButton.Position);
              await unitMovementTaskResult;
      }
}

Is there a reason why this should be core and not an add-on in the asset library?

This is cannot be an addon since it changes the core godot functions.

@Calinou Calinou changed the title C# add async behaviour to functions of Godot classes Add async behavior to functions of Godot classes in C# Nov 10, 2021
@raulsntos
Copy link
Member

raulsntos commented Nov 10, 2021

TL;DR: You can already add the async keyword to any method.


This code is not valid, it does not compile because you are using await inside a method that is not async so I don't know how you are using this right now, I'm assuming you added the async keyword but forgot to write it in the proposal.

public override void _GuiInput(InputEvent @event)
{
      if (@event is InputEventMouseButton mouseButton)
      {
              unitToMove.MoveUnitToPosition(mouseButton.Position);
              await ToSignal(unitToMove, "MovementStopedSignal");  //Here an await of some sort happens ! Hurray!
              Debug.WriteLine($"Movement completed!"); 
      }
}

You can already add the async keyword to any method, it's not part of the method signature. So you can already do this:

public override async void _GuiInput(InputEvent @event)
{
      if (@event is InputEventMouseButton mouseButton)
      {
              Task unitMovementTaskResult = unitToMove.MoveUnitToPosition(mouseButton.Position);
              await unitMovementTaskResult;
      }
}

This means the method will be awaited before the next line of code is executed. But Godot won't await the _GuiInput method since it doesn't return a Task (and can't since the signature of the method that is being overridden returns void). That's by design since otherwise Godot will have to freeze in that line of code (and then the method that is being awaited will probably never return either) as you already discovered by using Wait().

I'd like to know what is it that you really want to do, if your intention is to ignore all input while the units are moving you can just do something like this:

bool inputDisabled = false;

public override async void _GuiInput(InputEvent @event)
{
      if (inputDisabled) return;

      if (@event is InputEventMouseButton mouseButton)
      {
              inputDisabled = true;

              Task unitMovementTaskResult = unitToMove.MoveUnitToPosition(mouseButton.Position);
              await unitMovementTaskResult;

              inputDisabled = false;
      }
}

@LordBrkica
Copy link
Author

TL;DR: You can already add the async keyword to any method.
You are right ! I now simply added async after the override and it worked !

I thought the _GuiInput() may only be overriden by a sync function, I dont know why I had this thought in my head (well I am a noob thats why...).

This code is not valid, it does not compile because you are using await inside a method that is not async so I don't know how you are using this right now, I'm assuming you added the async keyword but forgot to write it in the proposal.

You are right again, I wrote this out of my memory and of course thats invalid code.

In reality I dont wait in_GuiInput(InputEvent @event) but rather have some event system and a global boolean lock, so when I call Unit.MoveTo() I change the bool and when the unit arrives at end point I change the bool again.

Thanks a lot for help again, I owe you a beer !

@Calinou
Copy link
Member

Calinou commented Nov 10, 2021

Closing, as this is already feasible as demonstrated above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants