¿Cómo puedo incrementar una variable sin exceder un valor máximo?

89

Estoy trabajando en un programa de videojuego simple para la escuela y he creado un método en el que el jugador obtiene 15 puntos de salud si se llama a ese método. Tengo que mantener la salud en un máximo de 100 y con mi capacidad de programación limitada en este momento estoy haciendo algo como esto.

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

Entiendo que mi sintaxis no es perfecta, pero mi pregunta es, cuál puede ser una mejor manera de hacerlo, porque también tengo que hacer algo similar con los puntos de daño y no bajar de 0.

A esto se le llama aritmética de saturación .

Steven Eck
fuente
7
Como nota al margen, elegiría un nombre diferente para este método. Según el estándar de Java (y la mayoría de los otros lenguajes que conozco), un nombre de método que comience con "get" debería devolver un valor y no cambiar nada. Del mismo modo, los nombres de métodos que comienzan con "set" deberían cambiar un valor y, por lo general, no devolver nada.
Darrel Hoffman

Respuestas:

225

Solo haría esto. Básicamente, toma el mínimo entre 100 (la salud máxima) y cuál sería la salud con 15 puntos extra. Asegura que la salud del usuario no supere los 100.

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

Para asegurar que los puntos de golpe no caen por debajo de cero, puede utilizar una función similar: Math.max.

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

solo agregue 15 a la salud, entonces:

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

Sin embargo, como ha señalado Bland, a veces con el subproceso múltiple (varios bloques de código ejecutándose a la vez) tener la salud sobre 100 en cualquier punto puede causar problemas, y cambiar la propiedad de salud varias veces también puede ser malo. En ese caso, puede hacer esto, como se menciona en otras respuestas.

if(health + 15 > 100) {
    health = 100;
} else {
    health += 15;
}
Jordán
fuente
9
Nunca debería permitir que se supere, esto podría introducir nuevos problemas, como una condición de carrera en la que se supone que la salud del personaje es como máximo la vida máxima definida (100). Es poco probable para este proyecto de nivel, supongo, pero debería aplicar buenas prácticas desde el principio.
suave
6
@bland Si uno usara este enfoque, una forma de evitar tal condición de carrera sería usar una variable temporal para almacenar el nuevo valor de salud y luego establecer la salud a este nuevo valor de salud en un lugar, con sincronización si es necesario.
Bob
13
@bland: las condiciones de carrera solo son relevantes cuando se realizan múltiples subprocesos. Y si está haciendo múltiples subprocesos (lo cual dudo mucho) , la solución sería bloquear todos los accesos healtho asegurarse de healthque solo se acceda desde un subproceso. La restricción "Nunca debe permitir que la salud supere los 100" no es realista.
BlueRaja - Danny Pflughoeft
@ BlueRaja-DannyPflughoeft Dije que era poco probable para este proyecto. En general, las cerraduras no siempre estarán en uso y si tiene un valor máximo, entonces debe respetarse estrictamente, ya sea para juegos o de otro tipo. Ejemplo: si un evento está vinculado a un cambio de una propiedad y usa un porcentaje, entonces ha sesgado enormemente ese procesamiento, además de haberlo invocado dos veces. Estoy tratando de ser general aquí y asegurarme de que OP aprenda: siento que esta respuesta, aunque funciona, es demasiado estrecha y específica para un estudiante, ya que lo obligará a no pensar en el panorama general.
Suave
@Bob creo que eso es innecesario para algo tan simple.
Enfriador de matemáticas
45

No necesita un caso separado para cada uno de los intanteriores 85. Solo tenga uno else, de modo que si la salud ya es 86o superior, configúrelo directamente en 100.

if(health <= 85)
    health += 15;
else
    health = 100;
rgettman
fuente
25
Un poco demasiados números mágicos para mí (incluso considerando 100 permitido) - al cambiar de 15 a 16, el 85 debería ser ajustado. ¿No sería cambiar 85 a al menos 100 - 15(o 100 -HEALED_HEALTH) una mejora?
Maciej Piechotka
37

Creo que una forma idiomática y orientada a objetos de hacer esto es tener un setHealthen la Characterclase. La implementación de ese método se verá así:

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

Esto evita que la salud vaya por debajo de 0 o por encima de 100, independientemente de lo que establezca.


Tu getHealed()implementación puede ser simplemente esta:

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

Si tiene sentido Charactertener un getHealed()método es un ejercicio que se deja al lector :)

Daniel Kaplan
fuente
5
+1: ¡Esta es una excelente manera de hacer esto de una manera orientada a objetos! Lo único que podría sugerir (y esto probablemente quedaría en manos del lector) es posiblemente tener dos métodos ( heal(int hp)y damage(int hp)) cada uno de los cuales llama a su setHealth(int newValue)método.
Chris Forrence
18
¿Qué hay de malo en llamar a las funciones de la biblioteca? Como funciones con nombre, expresan la intención con más claridad que un montón de lógica condicional.
erickson
2
Además, muchas funciones de la biblioteca (y muy probablemente estas) están realmente integradas y no realizarán ninguna llamada.
kriss
2
@Matteo Respuesta incorrecta: lo más probable es que la función de la biblioteca haga exactamente lo mismo internamente, entonces, ¿por qué repetirse y contaminar su código? NO usar las funciones de la biblioteca no sigue los principios básicos de DRY.
NickG
2
@Matteo esto no es para evitar un if. Esto es para evitar que pueda dispararse en el pie. Si es demasiado detallado, simplemente use importaciones estáticas. Entonces se ve así: health = max(0, min(100, newValue)) Si aún es ilegible para usted, extráigalo a un método llamado clamppara que la línea se vea así:health = clamp(0, 100, newValue)
Daniel Kaplan
14

Solo voy a ofrecer un fragmento de código más reutilizable, no es el más pequeño, pero puede usarlo con cualquier cantidad, por lo que aún vale la pena decirlo.

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

También puede cambiar el 100 a una variable maxHealth si desea agregar estadísticas al juego que está creando, por lo que todo el método podría ser algo como esto

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

EDITAR

Para más información

Podrías hacer lo mismo cuando el jugador se dañe, pero no necesitarías un minHealth porque sería 0 de todos modos. Si lo hace de esta manera, podrá dañar y curar cualquier cantidad con el mismo código.

5tar-Kaster
fuente
1
minHealthpodría ser negativo, por ejemplo, en D&D ... :)
JYelton
1
De lejos, la mejor respuesta aquí.
Glitch Desire
@JYelton, sí, solo diría (salud <= 0) en la declaración if. Podrías manejarlo como quieras, si quieres que tengan vidas que tú solo menos 1 de lifeCount o simplemente si pierden rotundamente, entonces puede manejar eso. Si quisieras, incluso podrías hacerlos comenzar su nueva vida con la cantidad de -HP que tenían. Puede pegar este código en prácticamente cualquier lugar y funcionará bien, ese fue el punto de esta respuesta.
5tar-Kaster
10
health = health < 85 ? health + 15 : 100;
Aroo
fuente
4

Haría un método estático en una clase auxiliar. De esta manera, en lugar de repetir el código para cada valor que debe ajustarse a algunos límites, puede tener un método para todo uso. Aceptaría dos valores que definen el mínimo y el máximo, y un tercer valor que se fijará dentro de ese rango.

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

Para su caso, declararía su salud mínima y máxima en algún lugar.

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

Luego llame a la función pasando su salud mínima, máxima y ajustada.

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

Sé que este es un proyecto de la escuela, pero si quieres expandir tu juego más adelante y poder mejorar tu poder curativo, escribe la función así:

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

y llame a la función:

getHealed(15);
getHealed(25);

... etc ...

Además, puede crear su HP máximo creando una variable que no sea local para la función. Como no sé qué idioma está usando, no mostraré un ejemplo porque podría tener la sintaxis incorrecta.

BlackBeltScripting
fuente
2

¿Tal vez esto?

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

Si quieres ser descarado y ajustar tu código en una línea, puedes usar un operador ternario :

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

Tenga en cuenta que algunas personas desaprobarán esta sintaxis debido a (posiblemente) una mala legibilidad.

Oleksiy
fuente
4
Encuentro health = (health <= 85)?(health+15):100más legible (si realmente desea usar un operador ternario)
Matteo
1

Creo que esto servirá

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

Explicación:

  • Si la brecha para la curación es de 15 o menos, la salud se convertirá en 100.

  • De lo contrario, si la brecha es mayor que 15, agregará 15 a la salud.

Entonces, por ejemplo: si la salud es 83, se convertirá en 98 pero no 100.

MarmiK
fuente
¿Puede explicar cómo funcionará esto para resolver la pregunta del solicitante?
Ro Yo Mi
Si la brecha para la curación es 15 o menos, la salud se convertirá en 100; si la brecha es mayor que 15, agregará 15 a la salud. Entonces, por ejemplo, la salud es 83 se convertirá en 98 pero no 100. Si tiene alguna inquietud específica, hágamelo saber, gracias por el comentario.
MarmiK
La && health < 100condición es innecesaria. Si es 100, se establecerá en 100, sin cambios. La única razón por la que lo necesitaría es si fuera posible obtener> 100 de alguna manera y no queremos que la curación lo reduzca a 100.
Darrel Hoffman
@DarrelHoffman Creo que tienes razón si el juego no proporciona salud extra 100+, entonces tienes razón, he corregido mi respuesta :) gracias
MarmiK
1

Si quisiera ser seguro para subprocesos, lo haría de esta manera en lugar de usar un bloque sincronizado.

El compareAndSet atómico logra el mismo resultado que sincronizado sin la sobrecarga.

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));
}
Robert Sutton
fuente
1

La forma más sencilla utilizando el operador de módulo.

salud = (salud + 50)% 100;

la salud nunca será igual o superior a 100.

enlace
fuente
Pero si haces esa operación cuando healthtienes 100, terminarías con 50 de salud.
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;
    }
besa mi axila
fuente
si va a hacer funciones, al menos debe hacer que el 15 sea un parámetro :)
Jordan
@Jordan: Depende del contexto en el que esté escribiendo el código. :-)
Bésame la axila