Como posso incrementar uma variável sem exceder um valor máximo?

89

Estou trabalhando em um programa de videogame simples para a escola e criei um método em que o jogador ganha 15 pontos de saúde se esse método for chamado. Eu tenho que manter a saúde em um máximo de 100 e com minha capacidade de programação limitada neste ponto, estou fazendo algo assim.

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

Eu entendo que minha sintaxe sobre não é perfeita, mas minha dúvida é, qual pode ser a melhor maneira de fazer isso, porque eu também tenho que fazer algo semelhante com os pontos de dano e não ficar abaixo de 0.

Isso é chamado de aritmética de saturação .

Steven Eck
fonte
7
Como observação, eu escolheria um nome diferente para esse método. De acordo com o padrão Java (e a maioria das outras linguagens que conheço), um nome de método começando com "get" deve retornar um valor e não alterar nada. Da mesma forma, os nomes de métodos que começam com "set" devem alterar um valor e geralmente não retornam nada.
Darrel Hoffman

Respostas:

225

Eu simplesmente faria isso. Basicamente, leva o mínimo entre 100 (a saúde máxima) e o que seria a saúde com 15 pontos extras. Isso garante que a saúde do usuário não exceda 100.

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

Para garantir que hitpoints não cair abaixo de zero, você pode usar uma função semelhante: Math.max.

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

basta adicionar 15 à saúde, então:

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

No entanto, como observou sem graça, às vezes com multi-threading (vários blocos de código em execução ao mesmo tempo), ter a integridade acima de 100 em qualquer ponto pode causar problemas, e alterar a propriedade de integridade várias vezes também pode ser ruim. Nesse caso, você poderia fazer isso, conforme mencionado em outras respostas.

if(health + 15 > 100) {
    health = 100;
} else {
    health += 15;
}
Jordânia
fonte
9
Nunca deve permitir que passe, isso pode introduzir novos problemas, como uma condição de corrida em que a saúde do personagem é assumida como no máximo a saúde máxima definida (100). Improvável para este projeto de nível, eu acho, mas deveria aplicar boas práticas desde o início.
branda
6
@bland Se alguém fosse usar essa abordagem, uma maneira de evitar tal condição de corrida seria usar uma variável temporária para armazenar o novo valor de saúde e, em seguida, definir saúde para esse novo valor de saúde em um lugar, com sincronização se necessário.
Bob
13
@bland: As condições da corrida são relevantes apenas quando multi-threading. E se ele estiver fazendo multi-threading (o que eu duvido muito) , a solução seria bloquear todos os acessos healthou ter certeza de que healthé acessado apenas de um thread. A restrição "Nunca deve permitir que a saúde ultrapasse 100" não é realista.
BlueRaja - Danny Pflughoeft
@ BlueRaja-DannyPflughoeft Eu afirmei que era improvável para este projeto. Em geral, os bloqueios não estarão sempre em uso e se você tiver um valor máximo, ele deve ser estritamente respeitado, seja para jogos ou não. Exemplo: se um evento está vinculado a uma alteração de uma propriedade e usa porcentagem, então você distorceu imensamente esse processamento, além de tê-lo invocado duas vezes. Estou tentando ser geral aqui e garantir que o OP aprenda - acho que esta resposta, embora funcione, é muito estreita e específica para um aluno, pois o obrigará a não pensar no quadro geral.
branda
@Bob eu acho que isso é desnecessário para algo tão simples.
Resfriador matemático de
45

Você não precisa de um caso separado para cada intacima 85. Basta ter um else, de modo que se a saúde já for 86ou superior, defina-o diretamente para 100.

if(health <= 85)
    health += 15;
else
    health = 100;
rgettman
fonte
25
Números mágicos um pouco demais para mim (mesmo considerando 100 permitidos) - ao alterar 15 para 16, os 85 precisariam ser ajustados. Mudar 85 para pelo menos 100 - 15(ou 100 -HEALED_HEALTH) não seria uma melhoria?
Maciej Piechotka
37

Acho que uma forma idiomática e orientada a objetos de fazer isso é ter um setHealthna Characterclasse. A implementação desse método será semelhante a esta:

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

Isso evita que a saúde fique abaixo de 0 ou acima de 100, independentemente de como você definiu.


Sua getHealed()implementação pode ser apenas esta:

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

Se faz sentido para o método Characterter-um getHealed()é um exercício deixado para o leitor :)

Daniel Kaplan
fonte
5
+1: Esta é uma ótima maneira de fazer isso de maneira orientada a objetos! A única coisa que eu poderia sugerir (e isso provavelmente seria deixado para o leitor) é possivelmente ter dois métodos ( heal(int hp)e damage(int hp)), cada um deles chamando seu setHealth(int newValue)método.
Chris Forrence
18
O que há de errado em chamar funções de biblioteca? Como funções nomeadas, elas expressam a intenção de forma mais clara do que um monte de lógica condicional.
Erickson
2
Além disso, muitas funções de biblioteca (e muito provavelmente essas) são realmente integradas e não executam nenhuma chamada.
Kriss
2
@Matteo Resposta errada - provavelmente a função de biblioteca faz exatamente a mesma coisa internamente, então por que se repetir e poluir seu código? NÃO usar funções de biblioteca não segue os princípios básicos de DRY.
NickG
2
@Matteo isso não é para evitar um if. Isso evita que você seja capaz de atirar no próprio pé. Se for muito detalhado, apenas use importações estáticas. Em seguida, health = max(0, min(100, newValue)) terá a seguinte aparência: Se ainda estiver ilegível para você, extraia-o para um método chamado de clampforma que a linha fique assim:health = clamp(0, 100, newValue)
Daniel Kaplan
14

Vou apenas oferecer uma parte de código mais reutilizável, não é a menor, mas você pode usá-la com qualquer quantidade, então ainda vale a pena ser dito

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

Você também pode alterar o 100 para uma variável maxHealth se quiser adicionar estatísticas ao jogo que você está criando, então todo o método pode ser algo assim

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

EDITAR

Para informações extras

Você poderia fazer o mesmo quando o player for danificado, mas não precisaria de um minHealth porque seria 0 de qualquer maneira. Fazendo desta forma, você seria capaz de danificar e curar qualquer quantia com o mesmo código.

5tar-Kaster
fonte
1
minHealthpode ser negativo, digamos, por exemplo, em D&D ... :)
JYelton
1
De longe a melhor resposta aqui.
Glitch Desire
@JYelton, sim, você apenas diria (saúde <= 0) na instrução if. Você pode lidar com isso da maneira que quiser, se quiser que eles tenham vidas, você apenas menos 1 do lifeCount ou apenas se eles perderem, então ele pode lidar com isso. Se você quisesse, poderia até fazê-los começar uma nova vida com a quantidade de -HP que tinham. Você pode colar esse código em praticamente qualquer lugar e ele fará o trabalho perfeitamente, esse foi o ponto desta resposta.
5tar-Kaster de
10
health = health < 85 ? health + 15 : 100;
Aroo
fonte
4

Eu faria um método estático em uma classe auxiliar. Dessa forma, em vez de repetir o código para cada valor que precisa se ajustar a alguns limites, você pode ter um método para todos os fins. Aceitaria dois valores que definem o mínimo e o máximo, e um terceiro valor a ser fixado nessa faixa.

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 o seu caso, você declararia sua saúde mínima e máxima em algum lugar.

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

Em seguida, chame a função transmitindo sua saúde mínima, máxima e ajustada.

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

Eu sei que este é um projeto escolar, mas se você quiser expandir seu jogo mais tarde e ser capaz de atualizar seu poder de cura, escreva a função assim:

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

e chamar a função:

getHealed(15);
getHealed(25);

... etc ...

Além disso, você pode criar seu HP máximo criando uma variável que não é local para a função. Como não sei qual linguagem você está usando, não vou mostrar um exemplo porque pode ter a sintaxe errada.

BlackBeltScripting
fonte
2

Talvez isto?

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

Se você quiser ser atrevido e colocar seu código em uma linha, pode usar um operador ternário :

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

Observe que algumas pessoas irão desaprovar essa sintaxe devido à (possivelmente) má legibilidade!

Oleksiy
fonte
4
Acho health = (health <= 85)?(health+15):100mais legível (se você realmente quiser usar um operador ternário)
Matteo
1

Eu acredito que isso vai servir

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

Explicação:

  • Se a lacuna de cura for 15 ou menos, a saúde passará a 100.

  • Caso contrário, se a lacuna for maior que 15, isso adicionará 15 à saúde.

Por exemplo: se a saúde for 83, ela se tornará 98, mas não 100.

MarmiK
fonte
Você pode expandir como isso funcionará para resolver a dúvida do solicitante?
Ro Yo Mi
Se a lacuna para a cura for 15 ou menos, a saúde se tornará 100, caso contrário, se a lacuna for maior que 15, isso adicionará 15 à saúde. então, por exemplo, a saúde é 83 se tornará 98, mas não 100. Se houver alguma preocupação específica, por favor me avise, obrigado por comentar.
Marmik
A && health < 100condição é desnecessária. Se for 100, será definido como 100, sem alteração. A única razão pela qual você precisaria disso é se fosse possível obter> 100 de alguma forma e não quisermos que a cura o reduza de volta a 100.
Darrel Hoffman
@DarrelHoffman Eu acho que você está certo se o jogo não fornecer saúde extra 100+ então você está certo, eu corrigi minha resposta :) obrigado
MarmiK
1

Se eu quisesse ter um thread safe, faria dessa forma, em vez de usar um bloco sincronizado.

O compareAndSet atômico atinge o mesmo resultado que sincronizado sem a 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
fonte
1

Maneira mais simples usando o operador de módulo.

saúde = (saúde + 50)% 100;

a saúde nunca será igual ou superior a 100.

vinculo
fonte
Mas se você fizer essa operação quando health for 100, você terminará com 50 de saúde.
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;
    }
beije minha axila
fonte
se você vai fazer funções, você deve pelo menos fazer o 15 ser um parâmetro :)
Jordan
@ Jordan: Depende do contexto em que estou escrevendo o código. :-)
beije minha axila