Monogame C # Timer (делать что-то 15 секунд каждые 3 секунды) - PullRequest
0 голосов
/ 24 мая 2019

Я пытаюсь создать таймер, который, например, каждые 3 секунды, например, в течение 15 секунд, будет выполнять действие.

Я пытался использовать gameTime.ElapsedGameTime.TotalSeconds и цикл, но, к сожалению, он не работает.

У меня есть функция Attack (), которая уменьшает статистику игрока, когда враг атакует его. Мне бы хотелось, чтобы в случае одного конкретного врага эта функция в течение определенного периода времени вычитала HP игрока, например, каждые 3 секунды. Я предполагаю, что это должно быть сделано в функции Update для доступа к gameTime, к сожалению, я понятия не имею, как это сделать.

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

Ответы [ 5 ]

2 голосов
/ 24 мая 2019

Я сделаю это просто, поэтому вам нужно изменить мой код, чтобы достичь желаемого результата.

Мое лучшее предположение, что вы хотите получить особый эффект, когда ваши монстры поражают вашего игрока.

Во-первых, вам нужно проверить, действительно ли монстр поражает игрока (если обнаружено столкновение):

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
}

В вашем классе Player вам необходимо:

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

Тогда в 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
}

Надеюсь, это поможет!

0 голосов
/ 03 июня 2019

Я использую это для своей игры:

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

Конвертировано в C #:

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

Вы можете использовать это в асинхронной функции:

Await DelayTask(1.5);

Число указывается в секундах, вы можете изменить его, изменив TimeSpan.whwhatformat.

Учитывая, что у вас будут различные вещи, которые влияют на вашу статистику, возможно, вам лучше иметь подпрограмму обновления в вашемКласс Stats, который будет проверять список эффектов, которые планируется обновить через один момент времени.Это было бы лучше для производительности, чем если бы каждый эффект опирался на свой собственный поток.

0 голосов
/ 01 июня 2019

Один из способов сделать это - использовать сопрограммы.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;
    }
}

Код выглядит очень близко к тому, как вы описали реальную проблему, которую пытаетесь решить.Теперь для системы сопрограмм ...

Метод StartCouroutine создает экземпляр класса Coroutine и сохраняет его.На этапе обновления игрового цикла вы перебираете сопрограммы и обновляете их, предоставляя gameTime вычислить время запуска следующего шага метода.Каждый шаг выполняет код в подпрограмме, пока не будет найден выход ИЛИ, пока метод не завершится естественным образом.Как только сопрограмма закончена, вы очищаете их.Эта логика выглядит примерно так:

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

Класс Coroutine отвечает за отслеживание оставшегося времени между шагами процедуры и отслеживание ее завершения.Это выглядит примерно так:

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

Это может показаться значительно более сложным, чем другие решения, представленные здесь, и это может быть излишним, но сопрограммы позволяют вам отказаться от отслеживания группы состояний в отслеживании переменных, что усложняет задачу.Сценарии легче следовать и чище читать.Например, вот стратегия порождения стрел, для которой я использовал Coroutines в Ludum Dare 37. Она порождает 3 стрелы с интервалом в 600 милсекунд с интервалом в 3 секунды между ними: https://github.com/srakowski/LD37/blob/477cf515d599eba7c4b55c3f57952865d894f741/src/LD37/GameObjects/BurstArrowSpawnBehavior.cs

Если вы хотите больше социального доказательстваценность сопрограмм взглянуть на Unity.Unity - один из самых популярных игровых движков, и он имеет поддержку Coroutine.Они описывают сценарий, где это полезно в их документации: https://docs.unity3d.com/Manual/Coroutines.html.

0 голосов
/ 24 мая 2019

Я не знаю monogame, но если бы я делал это в одном из моих приложений на C #, я бы использовал таймер и передавал все, что нужно было изменить таймеру.

Здесь есть хорошая информация https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer?view=netframework-4.8, и я украл здесь немного кода и изменил его в качестве примера, чтобы продемонстрировать свою идею. Я расширил 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) Как передать объект в событие таймера?

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

static void MyElapsedMethod(object sender, ElapsedEventArgs e, string theString) {
...
}
0 голосов
/ 24 мая 2019

Вам необходимо сохранить время начала или время последнего выполнения действия. Затем во время каждого обновления сравнивайте истекшее время с сохраненным временем. Если прошло 3 секунды, выполните действие, сохраните текущее время и повторите процесс.

...