J'essaie de créer une minuterie qui, par exemple, toutes les 3 secondes pendant 15 secondes par exemple, exécutera une action.
J'ai essayé d'utiliser gameTime.ElapsedGameTime.TotalSeconds et une boucle, mais malheureusement cela ne fonctionne pas .
J'ai une fonction Attack () qui réduit les statistiques du joueur lorsqu'un ennemi l'attaque. Je voudrais que dans le cas d'un ennemi particulier, cette fonction pour une période de temps spécifiée soustrait les PV du joueur, par exemple toutes les 3 secondes. Je suppose que cela devrait être fait dans la fonction Update pour accéder à gameTime, malheureusement, je ne sais pas comment le faire.
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); }
public override Stats Attack() { attack = true; return new Stats(0, -stats.Damage, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); }
5 Réponses :
Vous devez enregistrer l'heure de début ou la dernière fois que l'action a été effectuée. Ensuite, lors de chaque mise à jour, comparez le temps écoulé à l'heure enregistrée. Si 3 secondes se sont écoulées, effectuez l'action, enregistrez l'heure actuelle et répétez le processus.
Je vais simplifier les choses, vous devez donc modifier mon code pour obtenir le résultat souhaité.
Ma meilleure hypothèse est que vous voulez avoir un effet spécial lorsque vos monstres frappent votre joueur.
Tout d'abord, vous devez vérifier si le monstre touche réellement le joueur (si une collision est détectée):
// 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 }
Dans votre classe Player
, vous avez besoin :
public float ModifierDuration { get { return modifierDuration; } set { modifierDuration = value; modiferCurrentTime = 0; } }
Puis dans la méthode Update
de la classe Player
:
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 }
J'espère que cela vous aidera!
Je ne connais pas le monogame, mais si je faisais cela dans l'une de mes applications C #, j'utiliserais une minuterie et je transmettrais tout ce que la minuterie aurait besoin de modifier.
Il y a de bonnes informations ici https://docs.microsoft.com/ fr-fr / dotnet / api / system.timers.timer? view = netframework-4.8 et j'ai volé un peu de code d'ici et je l'ai modifié comme exemple pour démontrer mon idée. J'ai étendu le System.Timer pour lui permettre de fonctionner pendant un certain temps et de s'arrêter. Vous pouvez définir la fréquence et la durée et l'oublier. En supposant que vous puissiez mettre à jour ces informations à partir d'un minuteur.
string theString = ...; timer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, theString); static void MyElapsedMethod(object sender, ElapsedEventArgs e, string theString) { ... }
Vous pouvez voir des exemples de comment passer des objets dans le minuteur ici (exemple de @ JaredPar) Comment passer un objet dans un événement de minuterie?
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(); } } } } }
Une façon de faire serait d'utiliser des coroutines. MonoGame n'a pas de support intégré pour eux comme les autres moteurs de jeu, mais ils ne sont pas trop compliqués à implémenter vous-même. Vous avez besoin de connaissances sur le mot-clé yield
et les énumérateurs pour les comprendre, mais une fois résumés, ils facilitent l'écriture et la compréhension du code de votre jeu.
Voici un exemple de ce à quoi ressemblerait votre logique de jeu. en utilisant un système Coroutine comme celui décrit ci-dessous:
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?; } } }
Le code se lit très près de la façon dont vous avez décrit le problème réel que vous essayez de résoudre. Maintenant pour le système coroutine ...
La méthode StartCouroutine crée une instance de classe Coroutine et la stocke. Au cours de l'étape de mise à jour de la boucle de jeu, vous parcourez les coroutines et les mettez à jour, en fournissant à gameTime pour calculer quand l'étape suivante de la méthode doit s'exécuter. Chaque étape exécute le code dans la routine jusqu'à ce qu'un rendement soit trouvé OU jusqu'à ce que la méthode se termine naturellement. Une fois la coroutine terminée, vous les effacez. Cette logique ressemble à ceci:
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); }
Une classe Coroutine est responsable du suivi du temps restant entre les étapes de la routine, et du suivi de la fin de la routine. Cela ressemble à quelque chose comme ceci:
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; } }
Cela peut sembler beaucoup plus complexe que les autres solutions proposées ici, et cela peut être excessif, mais Coroutines vous permet de renoncer à suivre un tas d'états dans le suivi des variables, ce qui rend les scénarios complexes plus faciles à suivre et plus propres à lire. Par exemple, voici une stratégie d'apparition de flèches pour laquelle j'ai utilisé Coroutines dans Ludum Dare 37. Elle engendre 3 flèches à 600 millièmes de seconde d'intervalle avec 3 secondes d'attente entre elles: https://github.com/srakowski/LD37/blob/src/LD37/GameObjects/BurstArrowSpawnBehavior.cs" rel = "nofollow noreferrer"> https://github.com/srakowski/LD37/blob/477cfawn4srakowski/LD37/blob/477cfawn4srakowski/LD37/blob/477cfawn4srakowski/LD37/37/blob/477cfawn4srakowski/LD37/37/blob/477cfawn4srakowski/LD37/37/blob/477bspawn4s155b7959b7b8 / p> Si vous souhaitez plus de preuves sociales de la valeur de Coroutines, jetez un œil à Unity. Unity est l'un des moteurs de jeu les plus populaires et prend en charge Coroutine. Ils décrivent un scénario où il est utile dans leur documentation: https://docs.unity3d.com/ Manuel / Coroutines.html .
J'utilise ceci pour mon jeu:
Await DelayTask(1.5);
Converti en C #:
public async System.Threading.Tasks.Task DelayTask(double Time) { await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time)); }
Vous l'utiliseriez comme ceci dans une fonction asynchrone :
Public Async Function DelayTask(Time As Double) As Threading.Tasks.Task Await Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time)) End Function
Le nombre est en secondes, vous pouvez changer cela en changeant le TimeSpan.wwhatformat.
Considérant que vous aurez diverses choses qui affectent vos statistiques, peut-être que vous feriez mieux d'avoir un sous-programme de mise à jour dans votre classe Stats qui vérifiera une liste d'effets qui sont programmés pour se mettre à jour après un moment donné. Ce serait mieux pour les performances que d'avoir chaque effet reposant sur son propre thread.