1

I am trying to create a timer, which, for example, every 3 seconds during eg 15 seconds will perform an action.

I tried to use gameTime.ElapsedGameTime.TotalSeconds and loop, but unfortunately it doesn't work.

I have an Attack () function that reduces player statistics when an enemy attacks it. I would like that in case of one particular enemy, this function for a specified period of time would subtract player's HP, eg for every 3 seconds. I guess it should be done in the Update function to access gameTime, unfortunately, I have no idea how to do it.

public override Stats Attack()
{        
    attack = true;
    return new Stats(0, -stats.Damage, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
public override void Update(GameTime gameTime)
{
    spriteDirection = Vector2.Zero;                                 // reset input
    Move(Direction);                                                // gets the state of my keyborad

    float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; // make movement framerate independant

    spriteDirection *= Speed;                                       // add hero's speed to movement 
    position += (spriteDirection * deltaTime);                      // adding deltaTime to stabilize movement
    totalPosition = new Vector2((int)((BottomBoundingBox.Center.X) / 32.0f), (int)((BottomBoundingBox.Center.Y) / 32.0f));

    base.Update(gameTime);
}
Parrish Husband
  • 3,148
  • 18
  • 40

5 Answers5

2

I will make it simple, so you need to modify my code to achieve your desire result.

My best guess is that you want to have a special effect when your monsters hit your player.

First, you need to check if the monster actually hits the player (if collision is detected):

if (collision)//if it's true
{
// Apply your special effect if it is better than
   // the one currently affecting the target :
   if (player.PoisonModifier <= poisonModifier) {
    player.PoisonModifier = poisonModifier;
    player.ModifierDuration = modifierDuration;
   }

   //player.setColor(Color.Blue);//change color to blue
   player.hitPoints -= Poision.Damage;//or enemy.PoisonDamage or whatever you define here
   hit.Expire();//this can be for the arrow or bullet from your enemy or simply just a normal hit
}

In your Player class, you need:

public float ModifierDuration {
 get {
  return modifierDuration;
 }
 set {
  modifierDuration = value;
  modiferCurrentTime = 0;
 }
}

Then in Update method of Player class:

// 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
}

Hope this helps!

Silver
  • 482
  • 2
  • 6
0

You need to store the start time, or the last time that the action was carried out. Then during each update compare the elapsed time to the stored time. If 3 seconds have passed then perform the action, store the current time and repeat the process.

T_Bacon
  • 404
  • 6
  • 22
0

I do not know monogame, but if I were doing this in one of my C# applications, I would use a timer, and pass in anything that the timer would need to modify.

There is good info here https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer?view=netframework-4.8 and I stole a bit of code from here and modified it as an example to demonstrate my idea. I extended the System.Timer to allow it to run for a duration and stop itself. You can set the frequency and duration and forget about it. Assuming that you are able to update this information from a 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();
                }
            }
        }
    }
}

You could see examples of how to pass objects into the timer here (@JaredPar's example) How do I pass an object into a timer event?

string theString = ...;
timer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, theString);

static void MyElapsedMethod(object sender, ElapsedEventArgs e, string theString) {
...
}
TychonOmega
  • 116
  • 8
0

One way to do this would be to use coroutines. MonoGame does not have built-in support for them like other game engines, but they are not too complicated to implement yourself. You need some knowledge of the yield keyword and enumerators to understand them, but once abstracted away they make your game code way easier to write and understand.

Here's an example of what your gameplay logic would look using a Coroutine system like the one described below:

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;
    }
}

The code reads very close to the way you described the actual problem you're trying to solve. Now for the coroutine system...

The StartCouroutine method creates a Coroutine class instance and stores it. During the Update step of the game loop you iterate through the coroutines and update them, providing gameTime to calculate when the next step of the method should run. Each step executes the code in the routine until a yield is found OR until the method ends naturally. Once the coroutine is finished you clear them out. This logic looks something like this:

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);
}

A Coroutine class is responsible for tracking the time remaining between steps of the routine, and tracking when the routine is finished. It looks something like this:

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?;
        }
    }
}

This may seem considerably more complex than other solutions provided here, and it may be overkill, but Coroutines allow you to forgo tracking a bunch of state in tracking variables, making complex scenarios easier to follow and cleaner to read. For example, here's a arrow spawning strategy I used Coroutines for in Ludum Dare 37. It spawns 3 arrows 600 milleseconds apart with a 3 second wait between them: https://github.com/srakowski/LD37/blob/477cf515d599eba7c4b55c3f57952865d894f741/src/LD37/GameObjects/BurstArrowSpawnBehavior.cs

If you'd like more social proof of the value of Coroutines take a look at Unity. Unity is one of the more popular game engines, and it has Coroutine support. They describe a scenario where it is useful in their documentation: https://docs.unity3d.com/Manual/Coroutines.html.

Shawn Rakowski
  • 5,644
  • 2
  • 27
  • 29
0

I use this for my game :

Public Async Function DelayTask(Time As Double) As Threading.Tasks.Task
    Await Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time))
End Function

Converted to C# :

public async System.Threading.Tasks.Task DelayTask(double Time)
{
    await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time));
}

You would use it like this in an Async Function :

Await DelayTask(1.5);

The number is in seconds, you can change this by changing the TimeSpan.whateverformat.

Considering that you'll have various things that affect your stats maybe you're better off at having an update subroutine in your Stats class that will check a list of effects that are scheduled to update after one point in time. This would be better for performance than having each effect relying on its own thread.

VampireMonkey
  • 177
  • 2
  • 11