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


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.