当前位置: 首页 > 知识库问答 >
问题:

在Godot中使用C#异步延迟

汝昀
2023-03-14

我目前正在用Godot C#做一个基本的射手,为了提高枪的射速,我一直在用不同的延迟系统做实验。虽然我试图使脚本通用化,但是节点计时器仍然工作,计时器调用似乎只调用父脚本中的函数。

我现在正在看C#的任务。延迟方法似乎也有效,因为它是一个异步动作,看起来不会受到帧速率的影响,也不会降低游戏速度。

我的问题是,在游戏应用程序中使用 Task.Delay 是否存在任何已知问题:例如它是否不可靠,或者如果调用该方法的太多实例,它会崩溃吗?

这是下面的代码,尽管我认为这并不重要:

 private void shoot() {
  //if "canShoot" spawn bullet
  ShootCooledDown();
}

private async void ShootCooledDown() {
  TimeSpan span = TimeSpan.FromSeconds((double)(new decimal(shotDelay)));
  canShoot = false;
  await Task.Delay(span);
  canShoot = true;
}  

共有3个答案

何浩荡
2023-03-14

在Godot或任何其他游戏引擎中开发游戏时,不应使用基于计算机时钟的任何计时器,如秒表Task.delay。相反,您必须使用前一帧的增量时间来管理自己所用的时间,该时间在_Process(float delta)_PhysicsProcess(float delta)方法中接收。原因是:

  • 在帧速率下降的情况下,时间将更准确
  • 如果你暂停游戏,计时器也会暂停

这就是Godot为您提供Timer 组件的主要原因,您必须将其附加到当前场景才能使用它。如果你不想向场景添加任何东西,这是完全合理的,你必须获取增量,将经过的时间存储在一个变量中,并检查这个变量是否达到某个限制。

在我的游戏中,我在这个非常简单的类中使用我自己的计时器:

    public class Timer {
        public float Elapsed { get; private set; } = 0;
        public bool Stopped { get; private set; } = true;
        public float Alarm { get; private set; } = float.MaxValue;

        public Timer Start() {
            Stopped = false;
            return this;
        }

        public Timer Stop() {
            Stopped = true;
            return this;
        }

        public Timer Reset() {
            Elapsed = 0;
            return this;
        }

        public Timer ClearAlarm() {
            Alarm = float.MaxValue;
            return this;
        }

        public Timer SetAlarm(float finish) {
            Alarm = finish;
            return this;
        }

        public bool IsAlarm() => Elapsed > Alarm;

        public Timer Update(float delta) {
            if (!Stopped) {
                Elapsed += delta;
            }
            return this;
        }

    }
```

You have to Update the timer in every frame

凤柏
2023-03-14

我的问题是,在游戏应用程序中使用 Task.Delay 是否存在任何已知问题:例如它是否不可靠,或者如果调用该方法的太多实例,它会崩溃吗?

本质上不是。< code >任务没有什么特别的问题。延迟在游戏中,也没有太多的实例。

然而,在<代码>任务之后你在做什么。延迟可能是一个问题。如果执行< code>await任务。延迟(跨度);,后面的代码可能会在不同的线程中运行,因此可能会导致争用情况。这是因为< code>await,而不是因为< code>Task。延迟。

例如,如果在等待 Task.Delay(span) 之后,您将向场景树添加一个节点(例如项目符号),这将干扰使用场景树的任何其他线程。戈多将在每一帧都使用场景树。快速浏览线程安全 API 会告诉您场景树不是线程安全的。顺便说一下,几乎任何小部件API都会发生同样的情况。

解决方案是使用 call_deferred(C# 中的 CallDeferred)与场景树进行交互。而且,是的,这可能会抵消下一帧发生的那一刻。

我会给你一个非线程的选择来做到这一点。

操作系统类上有方法get_ticks_msecget_ticks_usec(C#中的GetTicksMsec和GetTicksUsec),它们为您提供了可用于时间比较的单调时间。

所以,如果你用它应该射击的时间来创建一个队列(通过获取当前时间加上你需要的任何间隔来计算)。然后在你的进程或物理进程回调中,你可以检查队列。退出所有逾期的时间,并创建这些项目符号。

如果您不想使用Godo API解决此问题,请在游戏开始时启动Stopwatch,并使用其经过的时间。

但也许这不是你想要的机制。如果你想要一个好的旧冷却,你可以在需要冷却时启动Stopwatch,然后将经过的时间与你想知道是否结束的冷却持续时间进行比较。

谭宏盛
2023-03-14

我对戈多没有任何经验..但是我的想法是....

您可以将上次拍摄时间存储在变量/字段中,而不是使用计时器。如果您尝试在 lastTimeShot coolDown 内拍摄,只需忽略拍摄命令即可。

例如:

private DateTime _lastShot = DateTime.MinValue;

private void shoot() 
{
    TimeSpan span = TimeSpan.FromSeconds((double)(new decimal(shotDelay)));
    
    // if the time when the last shot has fire with the cooldown time
    // is greater than the current time. You are still in the cooldown time.
    if(_lastShot.Add(span) > DateTime.UtcNow)
        return; // within cooldown, do nothing
        
    //if "canShoot" spawn bullet
    ShootCooledDown();
    _lastShot = DateTime.UtcNow;
}

由于西奥多的一个有效评论,关于更改系统时间将导致容易出错的游戏。

我写了第二个版本。

private Stopwatch _shootingCooldownStopwatch = default;
    
private void shoot()
{
    var shotDelayMs = shotDelay * 1000;

    // if the _shootingCooldownStopwatch is ever started
    // and the ElapsedMilliseconds are in the showDelay
    // we're not allowed to fire again. So exit the method.
    if (_shootingCooldownStopwatch?.ElapsedMilliseconds < shotDelayMs)
        return;

    _shootingCooldownStopwatch = Stopwatch.StartNew();

    //if "canShoot" spawn bullet
    ShootCooledDown();
}

我认为这将是一个更好的解决方案。

 类似资料:
  • 我有cxfweb服务,我使用异步方法调用。我使用回调方法(使用javax.xml.ws.asynchHandler-http://cxf.apache.org/docs/developing-a-consumer.html)以获得如下响应: 这个方法很好,我得到了正确的响应。 我的问题是性能。我测量了刚好在上面行所需的时间,我的应用程序显示大约2000ms的上线(我用jmeter做了测试)。我的应

  • 我偶然发现了下面这段代码: 我理解

  • Godot是一个全新开发的游戏引擎,其功能集类似知名的跨平台游戏引擎Unity,可用于开发PC、主机、移动和Web游戏。开发者声称引 擎的2D和动画支持要强于Unity,表示在功能和特性上没有其它开源游戏引擎能相媲美。Godot引擎内置了类似Unity的编辑器,GUI工具 包,2D/3D物理支持,支持OpenGL ES 2.0 功能集的3D渲染器,易于学习的语言和API,支持用ASM.js或Goo

  • 我有下面的科特林协程代码。< code>doWorkAsync是正常(非挂起)函数,它返回< code>Deferred 我不知道如何在< code>doWorkAsync函数中使用< code>delay。 我正在使用kotlin协程版本。

  • 问题内容: 我有2个异步运行的函数。我想使用瀑布模型来编写它们。问题是,我不知道如何。 这是我的代码: 知道如何使用Waterfall编写它,以便我可以控制它吗?请给我写一些例子,因为我是node.js的新手 问题答案: 首先确定步骤并将其编写为异步函数(带有回调参数) 读取文件 } 处理文件(在示例中,我删除了大部分console.log) } 请注意,我在这里没有进行特定的错误处理,我将利用a

  • 我想进行一个基于当前状态的API调用,但不能使setState函数作为异步函数工作。 给我错误: 类型为“”的参数(状态:只读)= 如果我在setState方法之外获取数据,它会起作用,但我害怕对过时的页码进行API调用: