Как я могу увеличить переменную, не превышая максимального значения?

89

Я работаю над простой программой видеоигр для школы и создал метод, в котором игрок получает 15 очков здоровья, если этот метод вызывается. Я должен поддерживать здоровье на максимум 100, и с моими ограниченными возможностями программирования на данный момент я делаю что-то вроде этого.

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

Я понимаю, что мой синтаксис не идеален, но мой вопрос в том, что может быть лучшим способом сделать это, потому что я также должен проделать то же самое с точками повреждения и не опускаться ниже 0.

Это называется арифметикой насыщения .

Стивен Эк
источник
7
В качестве примечания я бы выбрал другое имя для этого метода. Согласно стандарту Java (и большинству других известных мне языков) имя метода, начинающееся с «get», должно возвращать значение и ничего не менять. Точно так же имена методов, начинающиеся с "set", должны изменять значение и обычно ничего не возвращать.
Даррел Хоффман

Ответы:

226

Я бы просто сделал это. Это в основном занимает минимум от 100 (максимальное здоровье) до того, что было бы с 15 дополнительными очками. Это гарантирует, что здоровье пользователя не превышает 100.

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

Для того, чтобы убедиться , что хитпоинты не опускается ниже нуля, вы можете использовать аналогичную функцию: Math.max.

public void takeDamage(int damage) {
    if(damage > 0) {
        health = Math.max(health - damage, 0);
    }
}
Крис Форренс
источник
71

просто добавьте 15 к здоровью, так что:

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

Однако, как было отмечено мягким, иногда при многопоточности (одновременное выполнение нескольких блоков кода), когда состояние работоспособности превышает 100 в любой момент, могут возникнуть проблемы, а изменение свойства работоспособности несколько раз также может быть плохим. В этом случае вы можете сделать это, как упоминалось в других ответах.

if(health + 15 > 100) {
    health = 100;
} else {
    health += 15;
}
Иордания
источник
9
Это может привести к появлению новых проблем, таких как состояние гонки, когда предполагается, что здоровье персонажа не превышает установленного максимума здоровья (100). Я бы предположил, что это маловероятно для проекта этого уровня, но следует применять передовые методы на раннем этапе.
мягкий
6
@bland Если бы кто-то использовал этот подход, способом избежать такого состояния гонки было бы использование временной переменной для хранения нового значения здоровья, а затем установка этого нового значения здоровья в одном месте с синхронизацией, если это необходимо.
Боб
13
@bland: Условия гонки актуальны только при многопоточности. И если он выполняет многопоточность (в чем я очень сомневаюсь) , решением будет заблокировать все доступы к нему healthили убедиться, что healthдоступ к нему осуществляется только из одного потока. Ограничение «Никогда не должно позволять здоровью превышать 100» нереально.
BlueRaja - Дэнни Пфлугофт
@ BlueRaja-DannyPflughoeft Я заявил, что это маловероятно для этого проекта. В общем, блокировки не всегда будут использоваться, и если у вас есть максимальное значение, его следует строго придерживаться, в играх или в других случаях. Пример: если событие связано с изменением свойства и использует процентное соотношение, то теперь вы сильно исказили эту обработку, а также дважды вызывали ее. Я стараюсь оставаться здесь общим и гарантировать, что OP учится - я считаю, что этот ответ, хотя он работает, слишком узок и конкретен для студента, поскольку он заставит его не думать о большой картине.
мягкий
@ Боб, я думаю, что это не нужно для чего-то такого простого.
Math chiller
45

Вам не нужен отдельный кейс для каждого из intперечисленных выше 85. Просто имейте один else, чтобы, если здоровье уже 86или выше, просто установите его прямо на 100.

if(health <= 85)
    health += 15;
else
    health = 100;
rgettman
источник
25
Для меня слишком много магических чисел (даже учитывая, что разрешено 100) - при изменении 15 на 16 нужно будет настроить 85. Разве изменение 85 по крайней мере на 100 - 15(или 100 -HEALED_HEALTH) не было бы улучшением?
Maciej Piechotka
37

Я думаю , идиоматическое, объектно - ориентированный способ сделать это , чтобы иметь setHealthна Characterклассе. Реализация этого метода будет выглядеть так:

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

Это предотвращает падение здоровья ниже 0 или выше 100, независимо от того, что вы его установили.


Ваша getHealed()реализация может быть такой:

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

Имеет ли смысл использовать метод Characterto have-a getHealed()- задача читателя :)

Даниэль Каплан
источник
5
+1: Это отличный способ сделать это объектно-ориентированным способом! Единственное, что я мог бы предложить (и это, вероятно, оставлено на усмотрение читателя), - это, возможно, иметь два метода ( heal(int hp)и damage(int hp)), каждый из которых вызывает ваш setHealth(int newValue)метод.
Крис Форренс 05
18
Что не так с вызовом библиотечных функций? Как именованные функции, они выражают намерение более четко, чем набор условной логики.
erickson 06
2
Также многие библиотечные функции (и, скорее всего, эти) действительно встроены и вообще не будут выполнять никаких вызовов.
Крисс
2
@Matteo Неверный ответ - скорее всего, библиотечная функция делает то же самое внутри, так зачем повторяться и загрязнять свой код? НЕ использование библиотечных функций не соответствует основным принципам DRY.
NickG
2
@Matteo, это не для того, чтобы избежать if. Это делается для того, чтобы не дать себе прострелить себе ногу. Если он слишком подробный, просто используйте статический импорт. Тогда это выглядит так: health = max(0, min(100, newValue)) Если это все еще нечитаемо для вас, извлеките его в метод с именем, clampчтобы строка выглядела так:health = clamp(0, 100, newValue)
Дэниел Каплан
14

Я просто собираюсь предложить более многоразовый фрагмент кода, он не самый маленький, но вы можете использовать его с любым количеством, так что его все же стоит сказать

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

Вы также можете изменить 100 на переменную maxHealth, если хотите добавить статистику к игре, которую вы создаете, чтобы весь метод мог быть примерно таким

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

РЕДАКТИРОВАТЬ

Для дополнительной информации

Вы можете сделать то же самое, когда игрок получает урон, но вам не понадобится minHealth, потому что в любом случае это будет 0. Поступая таким образом, вы сможете наносить урон и лечить любые суммы одним и тем же кодом.

5tar-Kaster
источник
1
minHealthможет быть отрицательным, например, в D&D ... :)
JYelton 06
1
Безусловно, лучший ответ здесь.
Glitch Desire
@JYelton, да, вы бы просто сказали (health <= 0) в инструкции if. Вы можете справиться с этим, как хотите, если вы хотите, чтобы у них были жизни, вы просто минус 1 от lifeCount, или просто если они проиграют, тогда он справится с этим. Если бы вы хотели, вы могли бы даже заставить их начать новую жизнь с количеством -HP, которое у них было. Вы можете вставить этот код практически куда угодно, и он отлично справится со своей задачей, вот в чем суть этого ответа.
5tar-Kaster
4

Я бы сделал статический метод во вспомогательном классе. Таким образом, вместо того, чтобы повторять код для каждого значения, которое должно соответствовать определенным границам, у вас может быть один универсальный метод. Он будет принимать два значения, определяющие минимальное и максимальное значение, и третье значение, которое будет ограничено в этом диапазоне.

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

Для вашего случая вы бы где-нибудь заявили о своем минимальном и максимальном здоровье.

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

Затем вызовите функцию, передав минимальное, максимальное и скорректированное здоровье.

health = HelperClass.clamp( HealthMin, HealthMax, health + 15 );
MildWolfie
источник
3

Я знаю, что это школьный проект, но если вы позже захотите расширить свою игру и иметь возможность повысить свою целительную силу, напишите функцию так:

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

и вызовите функцию:

getHealed(15);
getHealed(25);

...так далее...

Кроме того, вы можете создать максимальное значение HP, создав переменную, которая не является локальной для функции. Поскольку я не знаю, какой язык вы используете, я не буду показывать пример, потому что он может иметь неправильный синтаксис.

BlackBeltScripting
источник
2

Может быть, это?

public void getHealed()
{
  if (health <= 85)
  {
    health += 15;
  } else
  {
    health = 100;
  }
}
Джуто
источник
2

Если вы хотите проявить дерзость и уместить свой код в одну строку, вы можете использовать тернарный оператор :

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

Обратите внимание, что некоторые люди не одобрят этот синтаксис из-за (возможно) плохой читаемости!

Алексей
источник
4
Я нахожу health = (health <= 85)?(health+15):100более читаемым (если вы действительно хотите использовать тернарный оператор)
Маттео
1

Я верю, что это подойдет

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

Пояснение:

  • Если разрыв для исцеления составляет 15 или меньше, здоровье станет 100.

  • В противном случае, если разрыв больше 15, он добавит 15 к здоровью.

Так, например: если здоровье 83, оно станет 98, а не 100.

MarmiK
источник
Не могли бы вы подробнее рассказать о том, как это будет работать, чтобы разрешить вопрос автора?
Ро Йо Ми
Если разрыв для исцеления составляет 15 или меньше, здоровье станет 100, иначе, если разрыв больше 15, он добавит 15 к здоровью. так, например, здоровье 83 станет 98, а не 100. Если есть какие-либо особые проблемы, пожалуйста, дайте мне знать, спасибо за комментарий.
MarmiK
&& health < 100Условие не является необходимым. Если оно равно 100, будет установлено значение 100, без изменений. Единственная причина, по которой вам это нужно, - это если бы можно было каким-то образом получить> 100, и мы не хотим, чтобы исцеление уменьшило вас до 100.
Даррел Хоффман
@DarrelHoffman Я думаю, что вы правы, если игра не дает дополнительного здоровья 100+, то вы правы, я исправил свой ответ :) спасибо
MarmiK 08
1

Если бы я хотел быть потокобезопасным, я бы сделал это таким образом, а не использовал бы синхронизированный блок.

Атомарный compareAndSet дает тот же результат, что и synchronized, без дополнительных затрат.

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

Самый простой способ использовать оператор модуля.

здоровье = (здоровье + 50)% 100;

здоровье никогда не будет равно 100 и не превысит его.

связь
источник
Но если вы выполните эту операцию, когда healthбудет 100, у вас будет 50 единиц здоровья.
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;
    }
поцелуй мою подмышку
источник
если вы собираетесь создавать функции, вы должны, по крайней мере, сделать 15 параметром :)
Джордан
@Jordan: Это зависит от контекста, в котором я пишу код. :-)
поцелуй мою подмышку