Create your own
Lesson illustration

Implementing Gameplay Rules with C# Methods and Conditionals

Good to see you again. In the last lesson, you made gameplay values such as health, damage, and movement speed easy to tune through serialized fields. Values alone do not make a game behave, though. This lesson turns them into rules: code that receives an input, calculates a result, and decides what should happen next.

You will write a small health-and-damage rule for your arena game. Along the way, you will learn how to define and call methods, pass values through parameters, return calculated values, and use if and else to make decisions.


Methods: giving a gameplay action a name

A method is a named block of code that performs one focused job. C# also calls methods functions in many learning materials; in Unity scripts, “method” is the usual term.

Imagine the eventual shooting mechanic. When a projectile hits a player, several things may need to happen: subtract health, prevent health becoming negative, check for elimination, and show a message or effect. Rather than placing all that code wherever a hit occurs, you can place it in a method such as ApplyDamage.

Then other code only needs to say:

ApplyDamage(25);

That is easier to read, reuse, and change later.

A C# method declaration labeled with its access modifier, return type, method name, parameter list, and body. The example returns the larger of two integer parameters; your game methods use the same structure for rules such as damage and elimination.

A basic method declaration has this shape:

accessModifier returnType MethodName(parameters)
{
    // Instructions that run when the method is called.
}

For example:

private void PlayHitSound()
{
    Debug.Log("Hit sound would play here.");
}

Read it from left to right:

  • private means this method is intended for use inside this script.
  • void means the method performs an action but does not send a value back.
  • PlayHitSound is the method name. C# convention uses PascalCase for methods: each word begins with a capital letter.
  • () is the parameter list. It is empty because this method needs no input.
  • { } contains the method body.

Defining a method does not run it. You must call it:

private void Start()
{
    PlayHitSound();
}

When Unity starts this GameObject in Play mode, it calls Start. In turn, Start calls PlayHitSound, so the Debug.Log line runs.

6.Unity C# Tutorial - All About Functions & Parameters

Watch “Unity C# Tutorial - All About Functions & Parameters” from Charger Games for a visual introduction to why methods prevent repeated code, how a method is declared, and how calls, parameters, and return values fit together.

Watch why methods help for the shooting example and the idea of bundling a repeated game action. Then watch declaring and calling to distinguish a method definition from a call in Start. Continue with parameters and return values; focus on where the input arrives and where the result is stored by the caller.

One small syntax rule is worth fixing in memory now: a method call ends with a semicolon, but a method declaration does not.

ApplyDamage(25); // A call: semicolon required.

private void ApplyDamage(int damageAmount) // A declaration: body follows.
{
}

Parameters and return values: inputs and outputs

A method can accept information through parameters. A parameter is a temporary, named variable in the method declaration. The actual value supplied when calling the method is an argument.

private void ApplyDamage(int damageAmount)
{
    Debug.Log($"Damage received: {damageAmount}");
}

When you call it:

ApplyDamage(25);

25 is the argument. Inside this particular call, damageAmount holds 25.

This is useful because the same rule works for different attacks:

ApplyDamage(10);  // light projectile
ApplyDamage(35);  // melee attack
ApplyDamage(80);  // powerful special attack

The method contains the rule once; the caller supplies the appropriate damage value. The parameter exists only while that call runs. It is different from a field such as damagePerHit, which belongs to the component and remains stored between method calls.

Methods can also return an output. Replace void with the type of value to be returned, and use the return keyword.

private int CalculateRemainingHealth(int health, int damageAmount)
{
    return health - damageAmount;
}

This method takes two int inputs and returns one int result. A caller can store that returned value:

int healthAfterHit = CalculateRemainingHealth(100, 25);

Debug.Log(healthAfterHit); // 75

The method call is evaluated first. It calculates 75, returns that number, and the assignment stores it in healthAfterHit.

A returned value is not automatically saved anywhere. This does not alter a field by itself:

CalculateRemainingHealth(currentHealth, damagePerHit);

To keep the result, assign it:

currentHealth = CalculateRemainingHealth(currentHealth, damagePerHit);

A method with a non-void return type must return a value compatible with that type on every possible route through its code. For example, an int method must return an int; a bool method must return either true or false.

Overview of methods - C# | Microsoft Learn

Read Microsoft Learn’s Overview of methods to consolidate the vocabulary and syntax you will use in the damage rule: access level, return type, name, parameters, arguments, and returned results.

In the Method signatures section, read the complete explanation and list of declaration parts. Pay particular attention to the parameter-list explanation: multiple parameters are separated with commas, and each has both a type and a name. Then find the Return values section. Read from its opening paragraph through the two SimpleMath examples and the examples that store or directly use a result; stop before the discussion of returning multiple values with tuples. Focus on using returned values, especially the difference between assigning a result and using one method call inside another expression.

For your game, these return types will be especially common:

Return typeMeaningExample purpose
voidPerforms an action, returns no valueApply damage and update game state
intReturns a whole numberCalculate remaining health or score
floatReturns a fractional numberCalculate projectile travel time
boolReturns true or falseDecide whether a target is eliminated

A helpful naming convention follows the returned type. A bool method often sounds like a yes-or-no question:

private bool IsEliminated(int health)
{
    return health <= 0;
}

The expression health <= 0 already produces a boolean result, so the method can return it directly.


Conditionals: making the rule respond to state

A calculation tells you what a value is. A conditional tells your game what to do based on that value.

The fundamental C# conditional is if:

if (currentHealth <= 0)
{
    Debug.Log("Target eliminated.");
}

The expression inside parentheses must evaluate to true or false.

For health, <= 0 is usually safer than == 0. A hit might reduce 20 health by 25; the result is -5, not exactly 0. The condition currentHealth <= 0 correctly treats both zero and negative values as an elimination state.

Use else when the game should take an alternative action:

if (currentHealth <= 0)
{
    Debug.Log("Target eliminated.");
}
else
{
    Debug.Log("Target is still fighting.");
}

Exactly one of these blocks runs each time this if/else statement is reached.

8.Unity C# Scripting Tutorial- If Else Statements

Watch “Unity C# Scripting Tutorial - If Else Statements” from Charger Games for the core decision structure behind health, elimination, ammo checks, cooldowns, and match rules.

Watch the health motivation to see why a game needs a rule that treats positive and depleted health differently. Then watch basic if and else. Focus on the condition in parentheses, the braces belonging to each branch, and the fact that only the matching branch executes.

A useful variation is an early return. In a void method, return; immediately stops that method:

if (isEliminated)
{
    return;
}

This protects a rule from continuing after it has detected a situation that should stop processing. For example, an already eliminated player should not be eliminated a second time by another delayed hit.

When conditions overlap, order matters. Consider a future health display:

if (currentHealth <= 0)
{
    Debug.Log("Eliminated");
}
else if (currentHealth <= 25)
{
    Debug.Log("Critical health");
}
else
{
    Debug.Log("Healthy enough");
}

The most urgent condition comes first. If currentHealth is 0, the elimination branch runs, and the later branches are skipped.


Build a small damage rule

Create an empty GameObject called ArenaRuleDemo, then create and attach a C# script with the same name: ArenaRuleDemo. This component is a safe, Console-based prototype of a rule you will reuse later when your players can shoot and attack each other.

Replace the generated script with this code:

using UnityEngine;

public class ArenaRuleDemo : MonoBehaviour
{
    [SerializeField] private int currentHealth = 100;
    [SerializeField] private int damagePerHit = 25;

    private bool isEliminated = false;

    private void Start()
    {
        ApplyDamage(damagePerHit);
    }

    private void ApplyDamage(int damageAmount)
    {
        if (isEliminated)
        {
            Debug.Log("Hit ignored: target is already eliminated.");
            return;
        }

        currentHealth = CalculateRemainingHealth(currentHealth, damageAmount);

        if (IsEliminated(currentHealth))
        {
            isEliminated = true;
            Debug.Log("Target eliminated.");
        }
        else
        {
            Debug.Log($"Target has {currentHealth} health remaining.");
        }
    }

    private int CalculateRemainingHealth(int health, int damageAmount)
    {
        if (damageAmount < 0)
        {
            return health;
        }

        int remainingHealth = health - damageAmount;

        if (remainingHealth < 0)
        {
            return 0;
        }

        return remainingHealth;
    }

    private bool IsEliminated(int health)
    {
        return health <= 0;
    }
}

This component separates the rule into three methods with distinct responsibilities.

ApplyDamage: perform the state-changing action

private void ApplyDamage(int damageAmount)

This is a void method because its job is to change this component’s state and report what happened. It does not need to return a separate result to its caller.

Its first conditional prevents duplicate handling:

if (isEliminated)
{
    Debug.Log("Hit ignored: target is already eliminated.");
    return;
}

If the target has already been eliminated, the method logs the reason and stops. The lines below it therefore cannot run.

CalculateRemainingHealth: calculate and return an integer

private int CalculateRemainingHealth(int health, int damageAmount)

This method does not edit currentHealth directly. Instead, it receives numbers, calculates a safe result, and returns that result. Keeping the calculation separate makes it easier to test and reuse.

It also implements two useful safeguards:

  1. Negative damage does not heal the target accidentally.
  2. Health cannot fall below zero.

For an ordinary hit:

A target with health hit for damage would mathematically have health. The conditional changes the gameplay result to 0, which is clearer for later health bars and elimination logic.

IsEliminated: answer one question with a boolean

private bool IsEliminated(int health)
{
    return health <= 0;
}

This method has a narrow job: it answers whether a health value counts as eliminated. Its returned bool feeds directly into the conditional in ApplyDamage.

The line below first calls IsEliminated(currentHealth). If it returns true, the code enters the first branch; otherwise it enters else.

if (IsEliminated(currentHealth))

That separation is valuable. Later, if you change the rule—for example, a player is eliminated at -10 rather than 0—you can change the condition in one focused place.

Test the rule from the Inspector

Save the script, select ArenaRuleDemo, and inspect its two fields. Make lasting changes outside Play mode, as you did in the previous lesson.

Run these configurations one at a time and inspect the Console:

Current HealthDamage Per HitExpected result
10025Target has 75 health remaining.
2025Target eliminated.
2525Target eliminated.
100-10Target has 100 health remaining.

The last case is not meant to be a real attack; it tests the defensive conditional in CalculateRemainingHealth.

For a quick test of the duplicate-elimination guard, temporarily call the method twice in Start:

private void Start()
{
    ApplyDamage(damagePerHit);
    ApplyDamage(damagePerHit);
}

Set currentHealth to 20 and damagePerHit to 25. The first call eliminates the target. The second reaches the isEliminated check and exits early. Afterwards, restore Start to one call.

At this stage, ApplyDamage is private because only this script calls it. Later, when a projectile or melee system needs to call a player’s damage method, you will deliberately choose an appropriate way for components to communicate. The important rule for now is simple: keep a method private unless another script genuinely needs access to it.


Key takeaways

A method packages a focused gameplay action behind a meaningful name. Its declaration states who can call it, what it returns, its name, and any parameters it needs.

  • Parameters accept input values; the values supplied at a call are arguments.
  • A void method performs actions without producing a separate result.
  • A method declared as int, float, or bool returns a compatible value using return.
  • if and else select behavior from a true/false condition.
  • return; can stop a void method early, which is useful for preventing duplicate actions.
  • Separating calculation (CalculateRemainingHealth), decision (IsEliminated), and state change (ApplyDamage) makes a gameplay rule easier to read and extend.

Next, you will use Unity event methods such as Start, Update, and Awake, along with the Console, to recognize and fix common syntax and null-reference errors.

Can't find a good explanation? Sign up and we'll make it for you

Sign up