单游戏C#计时器(每3秒做15秒)

单游戏C#计时器(每3秒做15秒),c#,monogame,C#,Monogame,我正在尝试创建一个计时器,例如,在15秒内每隔3秒将执行一个操作 我尝试使用gameTime.ElapsedGameTime.TotalSeconds和循环,但不幸的是它不起作用 我有一个Attack()函数,当敌人攻击它时,它可以减少玩家的统计数据。我希望在一个特定的敌人的情况下,这个函数在指定的时间内会减去玩家的生命值,例如每3秒。我想应该在更新功能中访问gameTime,不幸的是,我不知道怎么做 public override Stats Attack() { at

我正在尝试创建一个计时器,例如,在15秒内每隔3秒将执行一个操作

我尝试使用gameTime.ElapsedGameTime.TotalSeconds和循环,但不幸的是它不起作用

我有一个Attack()函数,当敌人攻击它时,它可以减少玩家的统计数据。我希望在一个特定的敌人的情况下,这个函数在指定的时间内会减去玩家的生命值,例如每3秒。我想应该在更新功能中访问gameTime,不幸的是,我不知道怎么做

public override Stats Attack()
{        
    attack = true;
    return new Stats(0, -stats.Damage, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}

您需要存储开始时间或上次执行操作的时间。然后在每次更新期间,将经过的时间与存储的时间进行比较。如果已过3秒,则执行该操作,存储当前时间并重复该过程。

我将使其变得简单,因此您需要修改我的代码以获得所需的结果

我最好的猜测是,当你的怪物击中你的玩家时,你想要有一个特殊的效果

首先,您需要检查怪物是否真的击中了玩家(如果检测到碰撞):

然后在
Update
方法的
Player
类中:

// If the modifier has finished,
if (modiferCurrentTime > modifierDuration) {
 // reset the modifier.  
 //stop losing HP code is here   
 modiferCurrentTime = 0;//set the time to zero
 setColor(Color.White);//set back the color of your player
}

count += gameTime.ElapsedGameTime.TotalSeconds;//timer for actions every 3s

if (posionModifier != 0 && modiferCurrentTime <= modifierDuration) {
 // Modify the hp of the enemy.
 player.setHP(player.getCurrentHP() - posionDamage);
 //Or change it to every 3s
 //if (count > 3) {
 //  count = 0;
 //DoSubtractHP(player);
 //}
 // Update the modifier timer.
 modiferCurrentTime += (float) gameTime.ElapsedGameTime.TotalSeconds;
 setColor(Color.Blue);//change the color to match the special effect
}
//如果修改器已完成,
如果(修改当前时间>修改持续时间){
//重置修改器。
//停止丢失HP代码在这里
ModifierCurrentTime=0;//将时间设置为零
设置颜色(Color.White);//设置播放器的颜色
}
计数+=gameTime.ElapsedGameTime.TotalSeconds//每3s进行一次操作的计时器
if(posionModifier!=0&&ModifierCurrentTime 3){
//计数=0;
//DoSubtractHP(播放器);
//}
//更新修改器计时器。
ModifierCurrentTime+=(浮点)gameTime.ElapsedGameTime.TotalSeconds;
setColor(Color.Blue)//更改颜色以匹配特殊效果
}

希望这有帮助

我不知道monogame,但如果我在我的一个C#应用程序中这样做,我会使用计时器,并传入计时器需要修改的任何内容

这里有很好的信息,我从这里偷了一些代码,并修改了它作为一个例子来证明我的想法。我扩展了System.Timer,允许它运行一段时间并自行停止。您可以设置频率和持续时间,然后忘记它。假设您能够从计时器更新此信息

class Program
{
    private static FixedDurationTimer aTimer;
    static void Main(string[] args)
    {
        // Create a timer and set a two second interval.
        aTimer = new FixedDurationTimer();
        aTimer.Interval = 2000;

        // Hook up the Elapsed event for the timer. 
        aTimer.Elapsed += OnTimedEvent;

        // Start the timer
        aTimer.StartWithDuration(TimeSpan.FromSeconds(15));

        Console.WriteLine("Press the Enter key to exit the program at any time... ");
        Console.ReadLine();
    }

    private static void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
    {
        FixedDurationTimer timer = source as FixedDurationTimer;

        if (timer.Enabled)
        {
            Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
        }
    }

    public class FixedDurationTimer : System.Timers.Timer
    {
        public TimeSpan Duration { get; set; }
        private Stopwatch _stopwatch;


        public void StartWithDuration(TimeSpan duration)
        {
            Duration = duration;
            _stopwatch = new Stopwatch();
            Start();
            _stopwatch.Start();
        }

        public FixedDurationTimer()
        {
            Elapsed += StopWhenDurationIsReached;
        }

        private void StopWhenDurationIsReached(object sender, ElapsedEventArgs e)
        {
            if (_stopwatch != null && Duration != null)
            {
                if (_stopwatch.Elapsed > Duration)
                {
                    Console.WriteLine("Duration has been met, stopping");
                    Stop();
                }
            }
        }
    }
}
您可以在这里看到如何将对象传递到计时器的示例(@JaredPar的示例)


一种方法是使用协同程序。MonoGame不像其他游戏引擎那样内置支持它们,但它们并不太复杂,无法自己实现。你需要一些关于
yield
关键字和枚举数的知识来理解它们,但一旦抽象出来,它们会使你的游戏代码更容易编写和理解

下面是一个使用如下所述的协同程序系统的游戏逻辑的示例:

public void Attack(Enemy enemyAttacking)
{
    if (enemyAttacking.Type == "OneParticularEnemy")
    {
        StartCoroutine(RunDamageOverTimeAttack());
    }
}

// This coroutine starts a second coroutine that applies damage over time, it
// then waits 15 seconds before terminating the second coroutine.
public IEnumerator RunDamageOverTimeAttack()
{
    var cr = StartCoroutine(ApplyDamageOverTime());
    yield return 15000; // in milleseconds (ms), i.e. 15000 ms is 15 seconds
    cr.IsFinished = true;
}

// This coroutine applies the damage every 3 seconds until the coroutine is finished
public IEnumerator ApplyDamageOverTime()
{
    while (true)
    {
        ApplyDamageToPlayer();
        yield return 3000;
    }
}
代码的读取方式与您描述要解决的实际问题的方式非常接近。现在是协同程序系统

StartCustomine方法创建一个协同例程类实例并存储它。在游戏循环的更新步骤中,您迭代了协同程序并对其进行更新,从而提供了计算该方法下一步何时运行的游戏时间。每个步骤都执行例程中的代码,直到找到屈服点或方法自然结束。一旦合作项目完成,你就把它们清除掉。这一逻辑如下所示:

private List<Coroutine> coroutines = new List<Coroutine>();

public Coroutine StartCoroutine(IEnumerator routine)
{
    var cr = new Coroutine(routine);
    couroutines.Add(cr);
    return cr;
}

public void UpdateCoroutines(GameTime gameTime)
{
    // copied in case list is modified during coroutine updates
    var coroutinesToUpdate = coroutines.ToArray();

    foreach (coroutine in coroutinesToUpdate)
        coroutine.Update(gameTime);

    coroutines.RemoveAll(c => c.IsFinished);
}

public void Update(GameTime gameTime)
{
    // normal update logic that would invoke Attack(), then...
    UpdateCoroutines(gameTime);
}
public class Coroutine
{
    private IEnumerator routine;
    private double? wait;

    public Coroutine(IEnumerator routine)
    {
        this.routine = routine;
    }

    public bool IsFinished { get; set; }

    public void Update(GameTime gameTime)
    {
        if (IsFinished) return;

        if (wait.HasValue)
        {
            var timeRemaining = wait.Value - gameTime.ElapsedGameTime.TotalMilliseconds;
            wait = timeRemaining < 0 ? null : timeRemaining;

            // If wait has a value we still have time to burn before the
            // the next increment, so we return here.
            if (wait.HasValue) return;
        }

        if (!routine.MoveNext())
        {
            IsFinished= true;
        }
        else
        {
            wait = routine.Current as double?;
        }
    }
}
private List coroutines=new List();
公共协同程序启动例程(IEnumerator例程)
{
var cr=新的协同程序(例行程序);
添加(cr);
返回cr;
}
公共无效更新例程(游戏时间游戏时间)
{
//案例列表中复制的内容在协同程序更新期间被修改
var coroutinesToUpdate=coroutines.ToArray();
foreach(coroutinesToUpdate中的coroutine)
更新(游戏时间);
coroutines.RemoveAll(c=>c.IsFinished);
}
公开作废更新(游戏时间游戏时间)
{
//调用攻击()的正常更新逻辑,然后。。。
更新常规(游戏时间);
}
协同程序类负责跟踪例程各步骤之间的剩余时间,并跟踪例程何时完成。它看起来像这样:

private List<Coroutine> coroutines = new List<Coroutine>();

public Coroutine StartCoroutine(IEnumerator routine)
{
    var cr = new Coroutine(routine);
    couroutines.Add(cr);
    return cr;
}

public void UpdateCoroutines(GameTime gameTime)
{
    // copied in case list is modified during coroutine updates
    var coroutinesToUpdate = coroutines.ToArray();

    foreach (coroutine in coroutinesToUpdate)
        coroutine.Update(gameTime);

    coroutines.RemoveAll(c => c.IsFinished);
}

public void Update(GameTime gameTime)
{
    // normal update logic that would invoke Attack(), then...
    UpdateCoroutines(gameTime);
}
public class Coroutine
{
    private IEnumerator routine;
    private double? wait;

    public Coroutine(IEnumerator routine)
    {
        this.routine = routine;
    }

    public bool IsFinished { get; set; }

    public void Update(GameTime gameTime)
    {
        if (IsFinished) return;

        if (wait.HasValue)
        {
            var timeRemaining = wait.Value - gameTime.ElapsedGameTime.TotalMilliseconds;
            wait = timeRemaining < 0 ? null : timeRemaining;

            // If wait has a value we still have time to burn before the
            // the next increment, so we return here.
            if (wait.HasValue) return;
        }

        if (!routine.MoveNext())
        {
            IsFinished= true;
        }
        else
        {
            wait = routine.Current as double?;
        }
    }
}
公共类协同程序
{
私有IEnumerator例程;
私人双人房?等等;
公共协同程序(IEnumerator例程)
{
这个。例行程序=例行程序;
}
公共bool已完成{get;set;}
公开作废更新(游戏时间游戏时间)
{
如果(完成)返回;
if(wait.HasValue)
{
var timeRemaining=wait.Value-gameTime.ElapsedGameTime.totalmillizes;
等待=剩余时间<0?空:剩余时间;
//如果wait有一个值,那么我们仍然有时间在
//下一个增量,我们返回这里。
if(wait.HasValue)返回;
}
如果(!routine.MoveNext())
{
IsFinished=true;
}
其他的
{
等待=例行程序。当前为双精度?;
}
}
}
这似乎比这里提供的其他解决方案要复杂得多,而且可能有些过分,但协同程序允许您放弃跟踪变量中的一系列状态,从而使复杂场景更容易理解和更清晰地阅读。例如,这里有一个我在Ludum Dare 37中使用协同程序的箭产卵策略。它产生3个箭头,间隔600千秒,其间有3秒的等待时间:


如果你想更多的社会证据来证明合作项目的价值,可以看看Unity。Unity是比较流行的游戏引擎之一,它支持协同程序。他们在文档中描述了一个有用的场景:

我在游戏中使用这个:

Public Async Function DelayTask(Time As Double) As Threading.Tasks.Task
    Await Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time))
End Function
转换为C#:

您可以在异步函数中这样使用它:

Await DelayTask(1.5);
数字以秒为单位,您可以通过更改
Public Async Function DelayTask(Time As Double) As Threading.Tasks.Task
    Await Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time))
End Function
public async System.Threading.Tasks.Task DelayTask(double Time)
{
    await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time));
}
Await DelayTask(1.5);