Як я можу збільшити змінну, не перевищуючи максимального значення?


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", повинні змінювати значення і, як правило, нічого не повертати.
— Даррел Хоффман

Відповіді:


225

Я б просто зробив це. В основному потрібно мінімум від 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;
}

Однак, як зауважив bland, іноді при багатопотоковості (декілька блоків коду, що виконуються одночасно) перевищення рівня здоров'я у будь-який момент може становити проблеми, і зміна властивості здоров'я кілька разів також може бути поганою. У такому випадку ви можете зробити це, як зазначено в інших відповідях.

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

9
Якщо ніколи не дозволяти їй переходити, це може спричинити нові проблеми, такі як перегоновий стан, коли стан здоров’я, як вважається, є щонайбільше визначеним максимумом здоров’я (100). Навряд чи для проекту цього рівня я здогадуюсь, але на початку слід застосовувати передові практики.
— м'який

6
@bland Якби хтось використовував цей підхід, способом уникнення такого стану перегони було б використання тимчасової змінної для зберігання нового значення здоров'я, а потім встановлення здоров'я для цього нового значення здоров'я в одному місці, при необхідності синхронізації.
— Боб

13
@bland: Умови перегонів актуальні лише при багатопотоковості. І якщо він робить багатопотоковість (у чому я дуже сумніваюся) , рішенням буде заблокувати всі звернення до healthабо забезпечити healthдоступ лише з одного потоку. Обмеження "Ніколи не дозволяти здоров'ю перевищувати 100" не є реалістичним.
— BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft Я заявив, що для цього проекту це малоймовірно. Як правило, замки не завжди будуть використовуватися, і якщо у вас максимальне значення, тоді цього слід чітко дотримуватися, ігровий чи інший. Приклад: Якщо подія пов’язана зі зміною властивості та використовує відсоток, то тепер ви надзвичайно перекосили цю обробку, а також двічі викликали її. Я намагаюся залишатися загальним тут і забезпечувати, щоб ОП навчався - я вважаю, що ця відповідь, хоча вона працює, є занадто вузькою та конкретною для студента, оскільки змусить його не думати про загальну картину.
— м'який

@ Боб, я думаю, це непотрібно для чогось такого простого.
— Math chiller

45

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

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

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 або вище, незалежно від того, на що ви його встановили.


Ваша getHealed()реалізація може бути такою:

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

Чи є сенс Characterмати getHealed()метод - це вправа, що залишається за читачем :)


5
+1: Це чудовий спосіб зробити це об’єктно-орієнтовано! Єдине, що я можу запропонувати (і це, мабуть, залишилось би читачеві) - це, можливо, два методи ( heal(int hp)і damage(int hp)), кожна з яких називає ваш setHealth(int newValue)метод.
— Chris Forrence

18
Що не так із викликом функцій бібліотеки? Як іменовані функції вони виражають намір чіткіше, ніж група умовної логіки.
— erickson

2
Також багато функцій бібліотеки (і, швидше за все, ці) дійсно вбудовані і взагалі не виконуватимуть жодного виклику.
— kriss

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. Роблячи це таким чином, ви зможете пошкодити та зцілити будь-яку суму за допомогою того ж коду.


1
minHealthможе бути негативним, скажімо, наприклад, у D&D ... :)
— JYelton

1
Безумовно найкраща відповідь тут.
— Glitch Desire

@JYelton, так, ви просто сказали б (здоров'я <= 0) у твердженні if. Ви можете впоратися з цим, як завгодно, якщо ви хочете, щоб у них було життя, ви лише мінус 1 від lifeCount або просто якщо вони втрачають, тоді він може це впоратись. Якби ви хотіли, ви могли б навіть змусити їх розпочати своє нове життя з тієї кількості -HP, яку вони мали. Ви можете вставити цей код майже в будь-яке місце, і він зробить цю роботу просто чудово, в цьому і була суть цієї відповіді.
— 5tar-Kaster,


4

Я б зробив статичний метод у допоміжному класі. Таким чином, замість повторення коду для кожного значення, яке повинно входити в певні межі, ви можете використовувати один універсальний метод. Він приймає два значення, що визначають min і max, і третє значення, яке слід затиснути в цьому діапазоні.

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

3

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

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

і викликати функцію:

getHealed(15);
getHealed(25);

... тощо ...

Крім того, ви можете створити свій максимальний HP, створивши змінну, яка не є локальною для функції. Оскільки я не знаю, якою мовою ви користуєтесь, я не буду показувати приклад, оскільки він може мати неправильний синтаксис.


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.


Чи можете ви розповісти про те, як це працює, щоб вирішити питання запитувача?
— Ро Йо Мі

Якщо розрив у лікуванні становить 15 або менше, здоров’я стане 100, якщо розрив більше 15, це додасть 15 здоров’ю. так, наприклад, стан здоров'я 83 стане 98, але не 100. Якщо є якісь конкретні проблеми, будь ласка, дайте мені знати, дякую за коментар.
— MarmiK

&& health < 100Умова НЕ є необхідним. Якщо воно дорівнює 100, йому буде встановлено значення 100, без змін. Єдина причина, по якій вам це потрібно, - це якби можна було якось отримати> 100, і ми не хочемо, щоб зцілення зменшило вас до 100.
— Даррел Хоффман,

@DarrelHoffman Я думаю, ти маєш рацію, якщо гра не надає додаткового здоров'я 100+, то ти маєш рацію, я виправив свою відповідь :) дякую
— MarmiK

1

Якби я хотів бути потокобезпечним, я б зробив це таким чином, а не використовуючи синхронізований блок.

Atomic compareAndSet досягає того самого результату, що і синхронізований без накладних витрат.

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 здоров'ям.
— Парзіфал

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 параметром :)
— Йорданія

@ Йорданія: Це залежить від контексту, в якому я пишу код. :-)
— поцілуй мене в пахву
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.