Comment incrémenter une variable sans dépasser une valeur maximale?


89

Je travaille sur un programme de jeu vidéo simple pour l'école et j'ai créé une méthode où le joueur obtient 15 points de santé si cette méthode est appelée. Je dois garder la santé à un maximum de 100 et avec ma capacité de programmation limitée à ce stade, je fais quelque chose comme ça.

public void getHealed(){
    if(health <= 85)
        health += 15;
    else if(health == 86)
        health += 14;
    else if(health == 87)
    health += 13; 
}// this would continue so that I would never go over 100

Je comprends que ma syntaxe n'est pas parfaite, mais ma question est de savoir quelle est la meilleure façon de le faire, car je dois également faire une chose similaire avec les points de dommage et ne pas descendre en dessous de 0.

C'est ce qu'on appelle l' arithmétique de saturation .


7
En remarque, je choisirais un nom différent pour cette méthode. Selon la norme Java (et la plupart des autres langages que je connais), un nom de méthode commençant par "get" doit renvoyer une valeur et ne rien changer. De même, les noms de méthodes commençant par "set" devraient changer une valeur et ne renverraient généralement rien.
Darrel Hoffman

Réponses:


225

Je ferais juste ça. Il faut essentiellement le minimum entre 100 (la santé maximale) et ce que serait la santé avec 15 points supplémentaires. Il garantit que la santé de l'utilisateur ne dépasse pas 100.

public void getHealed() {
    health = Math.min(health + 15, 100);
}

Pour vous assurer que hitpoints ne laissez pas tomber en dessous de zéro, vous pouvez utiliser une fonction similaire: Math.max.

public void takeDamage(int damage) {
    if(damage > 0) {
        health = Math.max(health - damage, 0);
    }
}

71

ajoutez simplement 15 à la santé, donc:

health += 15;
if(health > 100){
    health = 100;
}

Cependant, comme fade l'a noté, parfois avec le multi-threading (plusieurs blocs de code s'exécutant à la fois), la santé dépassant 100 à tout moment peut causer des problèmes, et la modification de la propriété de santé plusieurs fois peut également être mauvaise. Dans ce cas, vous pouvez le faire, comme mentionné dans d'autres réponses.

if(health + 15 > 100) {
    health = 100;
} else {
    health += 15;
}

9
Ne devrait jamais lui permettre de dépasser, cela pourrait introduire de nouveaux problèmes tels qu'une condition de concurrence où la santé du personnage est supposée être au plus la santé max définie (100). Peu probable pour ce projet de niveau, je suppose, mais je devrais appliquer les bonnes pratiques dès le début.
bland

6
@bland Si l'on devait utiliser cette approche, un moyen d'éviter une telle condition de concurrence serait d'utiliser une variable temporaire pour stocker la nouvelle valeur de santé, puis de définir la santé sur cette nouvelle valeur de santé en un seul endroit, avec une synchronisation si nécessaire.
Bob le

13
@bland: Les conditions de course ne sont pertinentes que lors du multi-threading. Et s'il fait du multi-threading (ce dont je doute fortement) , la solution serait de verrouiller tous les accès à health, ou de s'assurer qu'il healthn'est accessible qu'à partir d'un seul thread. La contrainte "Ne devrait jamais permettre à la santé de dépasser 100" n'est pas réaliste.
BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft J'ai déclaré que c'était peu probable pour ce projet. En général, les verrous ne seront pas toujours utilisés et si vous avez une valeur maximale, elle doit être strictement respectée, que ce soit pour le jeu ou autre. Exemple: si un événement est lié à un changement de propriété et utilise un pourcentage, vous avez maintenant considérablement faussé ce traitement, ainsi que l'avoir appelé deux fois. J'essaie de rester général ici et de m'assurer qu'OP apprend - je pense que cette réponse, tant qu'elle fonctionne, est trop étroite et spécifique pour un étudiant car elle l'obligera à ne pas penser à la situation dans son ensemble.
fade le

@Bob je pense que c'est inutile pour quelque chose d'aussi simple.
Math chiller

45

Vous n'avez pas besoin d' un cas distinct pour chaque intci - dessus 85. Il suffit d'en avoir un else, de sorte que si la santé est déjà 86ou supérieure, réglez-la directement sur 100.

if(health <= 85)
    health += 15;
else
    health = 100;

25
Un peu trop de nombres magiques pour moi (même en considérant 100 autorisés) - lors du changement de 15 à 16, le 85 devrait être ajusté. Est-ce que changer 85 en au moins 100 - 15(ou 100 -HEALED_HEALTH) ne serait pas une amélioration?
Maciej Piechotka

37

Je pense qu'une manière idiomatique et orientée objet de faire cela est d'avoir un setHealthsur la Characterclasse. L'implémentation de cette méthode ressemblera à ceci:

public void setHealth(int newValue) {
    health = Math.max(0, Math.min(100, newValue))
}

Cela empêche la santé de descendre en dessous de 0 ou au-dessus de 100, quel que soit votre réglage.


Votre getHealed()implémentation peut être la suivante:

public void getHealed() {
    setHealth(getHealth() + 15);
}

Que ce soit logique pour la méthode d' Characteravoir-une getHealed()est un exercice laissé au lecteur :)


5
+1: C'est un excellent moyen de le faire d'une manière orientée objet! La seule chose que je pourrais suggérer (et ce serait probablement laissé au lecteur) est d'avoir éventuellement deux méthodes ( heal(int hp)et damage(int hp)) qui appellent chacune votre setHealth(int newValue)méthode.
Chris Forrence

18
Quel est le problème avec l'appel des fonctions de bibliothèque? En tant que fonctions nommées, elles expriment l'intention plus clairement qu'un tas de logique conditionnelle.
erickson

2
De plus, de nombreuses fonctions de bibliothèque (et très probablement celles-ci) sont vraiment intégrées et n'effectuent aucun appel.
kriss

2
@Matteo Mauvaise réponse - il est fort probable que la fonction de bibliothèque fasse exactement la même chose en interne, alors pourquoi vous répéter et polluer votre code? NE PAS utiliser les fonctions de la bibliothèque ne suit pas les principes de base de DRY.
NickG

2
@Matteo ce n'est pas pour éviter un if. C'est pour vous éviter de pouvoir vous tirer une balle dans le pied. S'il est trop détaillé, utilisez simplement des importations statiques. Ensuite, cela ressemble à ceci: health = max(0, min(100, newValue)) Si c'est toujours illisible pour vous, extrayez-le dans une méthode nommée de clampsorte que la ligne ressemble à ceci:health = clamp(0, 100, newValue)
Daniel Kaplan

14

Je vais juste proposer une tranche de code plus réutilisable, ce n'est pas la plus petite mais vous pouvez l'utiliser avec n'importe quel montant, donc c'est toujours digne d'être dit

health += amountToHeal;
if (health >= 100) 
{ 
    health = 100;
}

Vous pouvez également changer le 100 en une variable maxHealth si vous voulez ajouter des statistiques au jeu que vous créez, donc toute la méthode pourrait être quelque chose comme ça

private int maxHealth = 100;
public void heal(int amountToHeal)
{
    health += amountToHeal;
    if (health >= maxHealth) 
    { 
        health = maxHealth;
    }
}

ÉDITER

Pour plus d'informations

Vous pouvez faire la même chose lorsque le joueur est endommagé, mais vous n'avez pas besoin d'un minHealth car ce serait de toute façon 0. En procédant de cette façon, vous seriez en mesure d'endommager et de guérir n'importe quel montant avec le même code.


1
minHealthpourrait être négatif, par exemple, dans D&D ... :)
JYelton

1
De loin la meilleure réponse ici.
Glitch Desire

@JYelton, ouais, vous diriez simplement (santé <= 0) dans l'instruction if. Vous pouvez le gérer comme vous le souhaitez, si vous voulez qu'ils aient des vies à moins de 1 de lifeCount ou simplement s'ils perdent à fond, il peut gérer cela. Si vous le vouliez, vous pourriez même leur faire commencer leur nouvelle vie avec la quantité de HP qu'ils avaient. Vous pouvez coller ce code à peu près n'importe où et cela fera très bien l'affaire, c'était le but de cette réponse.
5tar-Kaster

10
health = health < 85 ? health + 15 : 100;

4

Je créerais une méthode statique dans une classe d'assistance. De cette façon, plutôt que de répéter le code pour chaque valeur qui doit tenir dans certaines limites, vous pouvez avoir une méthode polyvalente. Il accepterait deux valeurs définissant le min et le max, et une troisième valeur à serrer dans cette plage.

class HelperClass
{
    // Some other methods

    public static int clamp( int min, int max, int value )
    {
        if( value > max )
            return max;
        else if( value < min )
            return min;
        else
            return value;
    }
}

Pour votre cas, vous déclareriez votre santé minimum et maximum quelque part.

final int HealthMin = 0;
final int HealthMax = 100;

Appelez ensuite la fonction en passant votre santé min, max et ajustée.

health = HelperClass.clamp( HealthMin, HealthMax, health + 15 );

3

Je sais que c'est un projet scolaire, mais si vous vouliez étendre votre jeu plus tard et pouvoir améliorer votre pouvoir de guérison, écrivez la fonction comme suit:

public void getHealed(healthPWR) {
    health = Math.min(health + healthPWR, 100);
}

et appelez la fonction:

getHealed(15);
getHealed(25);

...etc...

De plus, vous pouvez créer votre HP max en créant une variable qui n'est pas locale à la fonction. Comme je ne sais pas quelle langue vous utilisez, je ne montrerai pas d'exemple car il pourrait avoir la mauvaise syntaxe.


2

Peut être ça?

public void getHealed()
{
  if (health <= 85)
  {
    health += 15;
  } else
  {
    health = 100;
  }
}

2

Si vous voulez être effronté et adapter votre code sur une seule ligne, vous pouvez utiliser un opérateur ternaire :

health += (health <= 85) ? 15 : (100 - health);

Notez que certaines personnes désapprouveront cette syntaxe en raison (sans doute) d'une mauvaise lisibilité!


4
Je trouve health = (health <= 85)?(health+15):100plus lisible (si vous voulez vraiment utiliser un opérateur ternaire)
Matteo

1

Je crois que ça va faire

if (health >= 85) health = 100;
else health += 15;

Explication:

  • Si l'écart de guérison est de 15 ou moins, la santé deviendra 100.

  • Sinon, si l'écart est supérieur à 15, cela ajoutera 15 à la santé.

Par exemple: si la santé est de 83, elle deviendra 98 mais pas 100.


Pouvez-vous expliquer comment cela fonctionnera pour résoudre la question du demandeur?
Ro Yo Mi

Si l'écart de guérison est de 15 points de vie ou moins, il deviendra 100, sinon si l'écart est supérieur à 15, il ajoutera 15 points de vie. donc par exemple la santé est de 83 deviendra 98 mais pas 100. En cas de problème particulier, veuillez me le faire savoir, merci pour vos commentaires.
MarmiK

La && health < 100condition est inutile. Si c'est 100, il sera mis à 100, aucun changement. La seule raison pour laquelle vous en auriez besoin est s'il était possible d'obtenir> 100 d'une manière ou d'une autre et nous ne voulons pas que la guérison vous ramène à 100.
Darrel Hoffman

@DarrelHoffman Je pense que vous avez raison si le jeu ne fournit pas de santé supplémentaire 100+ alors vous avez raison, j'ai corrigé ma réponse :) merci
MarmiK

1

Si je voulais être thread-safe, je le ferais de cette façon plutôt que d'utiliser un bloc synchronisé.

L'atomic compareAndSet obtient le même résultat que synchronisé sans la surcharge.

AtomicInteger health = new AtomicInteger();

public void addHealth(int value)
{
    int original = 0;
    int newValue = 0;
    do
    {
        original = health.get();
        newValue = Math.min(100, original + value);
    }
    while (!health.compareAndSet(original, newValue));
}

1

Manière la plus simple en utilisant l'opérateur module.

santé = (santé + 50)% 100;

la santé ne sera jamais égale ou supérieure à 100.


Mais si vous faites cette opération à health100, vous finirez avec 50 points de vie.
Parziphal

0
   private int health;
    public void Heal()
    {
        if (health > 85)
            health = 100;
        else
            health += 15;
    }
    public void Damage()
    {
        if (health < 15)
            health = 0;
        else
            health -= 15;
    }

si vous allez créer des fonctions, vous devriez au moins faire du 15 un paramètre :)
Jordanie

@Jordan: Cela dépend du contexte dans lequel j'écris le code. :-)
embrasse mon aisselle
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.