Come posso incrementare una variabile senza superare un valore massimo?


89

Sto lavorando a un semplice programma di videogiochi per la scuola e ho creato un metodo in cui il giocatore ottiene 15 punti salute se viene chiamato quel metodo. Devo mantenere la salute a un massimo di 100 e con la mia limitata capacità di programmazione a questo punto sto facendo qualcosa del genere.

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

Capisco che la mia sintassi non sia perfetta, ma la mia domanda è: quale potrebbe essere un modo migliore per farlo, perché devo anche fare una cosa simile con i punti di danno e non andare sotto lo 0.

Questa è chiamata aritmetica della saturazione .


7
Come nota a margine, sceglierei un nome diverso per questo metodo. Secondo lo standard Java (e la maggior parte degli altri linguaggi che conosco), un nome di metodo che inizia con "get" dovrebbe restituire un valore e non modificare nulla. Allo stesso modo i nomi dei metodi che iniziano con "set" dovrebbero cambiare un valore e di solito non restituire nulla.
Darrel Hoffman

Risposte:


225

Lo farei solo. Fondamentalmente ci vuole il minimo tra 100 (la salute massima) e quella che sarebbe la salute con 15 punti extra. Assicura che la salute dell'utente non superi 100.

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

Per garantire che punti ferita non scendono sotto lo zero, è possibile utilizzare una funzione simile: Math.max.

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

71

aggiungi solo 15 alla salute, quindi:

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

Tuttavia, come ha notato blando, a volte con il multi-threading (più blocchi di codice eseguiti contemporaneamente) avere l'integrità superiore a 100 in qualsiasi momento può causare problemi e anche la modifica della proprietà dell'integrità più volte può essere dannosa. In tal caso, potresti farlo, come menzionato in altre risposte.

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

9
Non dovrebbe mai permettere che vada oltre, questo potrebbe introdurre nuovi problemi come una condizione di razza in cui si presume che la salute del personaggio sia al massimo la salute massima definita (100). Immagino improbabile per questo progetto di livello, ma dovrebbe applicare le buone pratiche all'inizio.
blando

6
@bland Se si utilizzasse questo approccio, un modo per evitare una tale condizione di competizione sarebbe quello di utilizzare una variabile temporanea per memorizzare il nuovo valore di integrità, quindi impostare l'integrità su questo nuovo valore di integrità in un unico punto, con la sincronizzazione se necessario.
Bob

13
@bland: le condizioni di gara sono rilevanti solo durante il multi-threading. E se sta facendo multi-threading (cosa di cui dubito fortemente) , la soluzione sarebbe bloccare tutti gli accessi a health, o assicurarsi che healthsia accessibile solo da un thread. Il vincolo "Non dovrebbe mai permettere che la salute superi i 100" non è realistico.
BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft Ho affermato che era improbabile per questo progetto. In generale i lucchetti non saranno sempre in uso e se hai un valore massimo, allora dovrebbe essere rigorosamente rispettato, giochi o altro. Esempio: se un evento è legato a una modifica di una proprietà e utilizza la percentuale, ora hai distorto tale elaborazione immensamente, oltre ad averla invocata due volte. Sto cercando di rimanere generale qui e assicurarmi che l'OP impari - sento che questa risposta, mentre funziona, è troppo ristretta e specifica per uno studente in quanto lo costringerà a non pensare al quadro generale.
blando

@ Bob, penso che non sia necessario per qualcosa di così semplice.
Math chiller

45

Non hai bisogno di un caso separato per ciascuno intsopra 85. Basta averne uno else, in modo che se la salute è già 86o superiore, impostala direttamente su 100.

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

25
Un po 'troppi numeri magici per me (anche considerando 100 ammessi) - quando si cambia da 15 a 16, l'85 dovrebbe essere regolato. Cambiare 85 in almeno 100 - 15(o 100 -HEALED_HEALTH) non sarebbe un miglioramento?
Maciej Piechotka

37

Penso che un modo idiomatico e orientato agli oggetti per farlo sia quello di avere un ruolo setHealthnella Characterclasse. L'implementazione di quel metodo sarà simile a questa:

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

Ciò impedisce che la salute scenda al di sotto di 0 o superiore a 100, indipendentemente da ciò su cui è impostata.


La tua getHealed()implementazione può essere proprio questa:

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

Se ha senso per Characteravere un getHealed()metodo è un esercizio lasciato al lettore :)


5
+1: Questo è un ottimo modo per farlo in modo orientato agli oggetti! L'unica cosa che potrei suggerire (e questo probabilmente sarebbe lasciato al lettore) è di avere possibilmente due metodi ( heal(int hp)e damage(int hp)) che chiamano ciascuno il tuo setHealth(int newValue)metodo.
Chris Forrence

18
Cosa c'è di sbagliato nel chiamare le funzioni di libreria? In quanto funzioni denominate, esprimono l'intento in modo più chiaro di un mucchio di logica condizionale.
erickson

2
Anche molte funzioni di libreria (e molto probabilmente queste) sono realmente integrate e non eseguiranno alcuna chiamata.
kriss

2
@Matteo Risposta sbagliata - molto probabilmente la funzione libreria fa esattamente la stessa cosa internamente, quindi perché ripetersi e inquinare il codice? NON usare le funzioni di libreria non segue i principi di base di DRY.
NickG

2
@Matteo questo non è per evitare un if. Questo per evitare che tu possa spararti al piede. Se è troppo dettagliato, usa solo le importazioni statiche. Quindi assomiglia a questo: health = max(0, min(100, newValue)) Se è ancora illeggibile per te, estrailo in un metodo chiamato in clampmodo che la riga abbia questo aspetto:health = clamp(0, 100, newValue)
Daniel Kaplan

14

Sto solo per offrire una porzione di codice più riutilizzabile, non è la più piccola ma puoi usarla con qualsiasi importo quindi è comunque degno di essere detto

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

Puoi anche cambiare il 100 in una variabile maxHealth se vuoi aggiungere statistiche al gioco che stai creando, quindi l'intero metodo potrebbe essere qualcosa del genere

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

MODIFICARE

Per ulteriori informazioni

Potresti fare lo stesso per quando il giocatore viene danneggiato, ma non avresti bisogno di un minHealth perché sarebbe comunque 0. In questo modo saresti in grado di danneggiare e curare qualsiasi importo con lo stesso codice.


1
minHealthpotrebbe essere negativo, ad esempio, in D&D ... :)
JYelton

1
Di gran lunga la migliore risposta qui.
Glitch Desire

@ JYelton, sì, dovresti solo dire (salute <= 0) nell'istruzione if. Puoi gestirlo come preferisci, se vuoi che abbiano vite che hai meno 1 da lifeCount o semplicemente se perdono completamente, allora può gestirlo. Se lo volessi, potresti persino farli iniziare la loro nuova vita con la quantità di -HP che avevano. Puoi incollare questo codice praticamente ovunque e funzionerà perfettamente, questo era il punto di questa risposta.
5tar-Kaster

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

4

Creerei un metodo statico in una classe helper. In questo modo, invece di ripetere il codice per ogni valore che deve rientrare in alcuni limiti, puoi avere un metodo per tutti gli usi. Accetterebbe due valori che definiscono il minimo e il massimo e un terzo valore da bloccare entro tale intervallo.

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

Nel tuo caso, dichiareresti la tua salute minima e massima da qualche parte.

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

Quindi chiama la funzione che passa nella tua salute minima, massima e regolata.

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

3

So che questo è un progetto scolastico, ma se vuoi espandere il tuo gioco in un secondo momento ed essere in grado di aumentare il tuo potere di guarigione, scrivi la funzione in questo modo:

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

e chiama la funzione:

getHealed(15);
getHealed(25);

...eccetera...

Inoltre puoi creare il tuo massimo HP creando una variabile che non è locale alla funzione. Poiché non so quale lingua stai utilizzando, non mostrerò un esempio perché potrebbe avere la sintassi sbagliata.


2

Forse questo?

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

2

Se vuoi essere sfacciato e adattare il tuo codice su una riga, potresti usare un operatore ternario :

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

Nota che alcune persone non vedranno questa sintassi a causa di (probabilmente) cattiva leggibilità!


4
Trovo health = (health <= 85)?(health+15):100più leggibile (se proprio si vuole usare un operatore ternario)
Matteo

1

Credo che andrà bene

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

Spiegazione:

  • Se il divario per la guarigione è 15 o meno, la salute diventerà 100.

  • Altrimenti, se il divario è maggiore di 15, aggiungerà 15 alla salute.

Quindi, ad esempio: se la salute è 83, diventerà 98 ma non 100.


Puoi spiegare come funzionerà per risolvere la domanda del richiedente?
Ro Yo Mi

Se il divario per la guarigione è 15 o meno, la salute diventerà 100, altrimenti se il divario è maggiore di 15 aggiungerà 15 alla salute. quindi per esempio la salute è 83 diventerà 98 ma non 100. Se qualche preoccupazione specifica per favore fatemelo sapere, grazie per il commento.
MarmiK

La && health < 100condizione non è necessaria. Se è 100, verrà impostato su 100, nessuna modifica. L'unico motivo per cui avresti bisogno è se fosse possibile ottenere> 100 in qualche modo e non vogliamo che la guarigione ti riduca a 100.
Darrel Hoffman

@DarrelHoffman Penso che tu abbia ragione se il gioco non fornisce una salute extra 100+, allora hai ragione, ho corretto la mia risposta :) grazie
MarmiK

1

Se volessi essere thread-safe, lo farei in questo modo anziché utilizzare un blocco sincronizzato.

L'atomic compareAndSet ottiene lo stesso risultato della sincronizzazione senza l'overhead.

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

Il modo più semplice utilizzando l'operatore modulo.

salute = (salute + 50)% 100;

la salute non sarà mai uguale o superiore a 100.


Ma se fai quell'operazione quando healthè 100, finiresti con 50 salute.
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;
    }

se hai intenzione di creare funzioni, dovresti almeno rendere il 15 un parametro :)
Jordan

@Jordan: dipende dal contesto in cui sto scrivendo il codice. :-)
baciami l'ascella
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.