Quick answer: Parameters are zero-indexed: Function.Param(0) for the first. Ensure the function signature declares enough parameters and the types match (Number vs String). For object references, pass UID as a number and reacquire via Pick by UID.

Here is how to fix Construct 3 function parameters not passed correctly. You call a function with three parameters: ApplyDamage(enemy.UID, 25, "fire"). Inside the function, Function.Param(1) returns 0 instead of 25. Or Function.Param(2) returns an empty string instead of “fire.” Or the first parameter works but later ones are missing. Construct 3’s Function object is straightforward but strict about indexing and types.

The Symptom

A Function object call passes parameters but the callee sees wrong values:

What Causes This

Zero-based indexing. Function.Param(0) is the first parameter, not the second. Developers used to 1-based UIs sometimes write Function.Param(1) expecting the first param and get the second (or nothing if only one was passed).

Parameter count mismatch. The function’s declaration in On Function event specifies parameter count. If the caller passes fewer parameters, the missing ones default to 0 (for Number) or empty string (for String). If the caller passes more, extras are silently dropped.

Type mismatch. Parameters have declared types (Number or String). If you try to use a String parameter in a math expression, Construct coerces to 0. Same for passing a Number where the function expects String.

SOL (Scope of Logic) differences. Functions start with a fresh SOL — the callee does not inherit the caller’s picked instances. If your caller had Enemy picked to a specific instance and the function expects to operate on that Enemy, the function sees all Enemies, not the picked one. Pass the UID and pick by UID in the function.

Async issues. Async functions run asynchronously via promises. Wait for the result with Wait for Previous Actions to Complete or handle the promise. Premature reads get stale values.

The Fix

Step 1: Audit parameter indexes. For a three-parameter function:

On Function "ApplyDamage":
  Param(0)  -> enemy_uid    // first parameter
  Param(1)  -> damage       // second parameter
  Param(2)  -> element_type // third parameter

Always zero-based. Match your mental model to this convention.

Step 2: Declare parameters in Function signature. In the On Function event’s signature (visible in the Functions sidebar), declare each parameter with name and type. A declared signature:

Named parameters are self-documenting and reduce errors:

On Function "ApplyDamage" (UID Number, Amount Number, Element String):
  Enemy: Pick by UID == Function.UID
  Enemy: Subtract Function.Amount from Health

Step 3: Pass object UIDs, reacquire in callee.

// Caller: pass UID to preserve object reference
Enemy on collision with Bullet:
  Call Function "ApplyDamage" (Enemy.UID, 25, "fire")

// Callee: reacquire using UID
On Function "ApplyDamage":
  Enemy: Pick by UID == Function.UID
  Enemy: Subtract Function.Amount from Health

This pattern keeps the callee scoped to the specific enemy instead of iterating all enemies.

Step 4: Match types deliberately. For Number types, use Function.Param for math. For String, use Function.Param in text operations or comparisons. Mixed usage produces implicit conversion that may not be what you want.

// Correct: Number parameter in arithmetic
Text: Set text to "Damage: " & Function.Param(1)

// Wrong: String parameter in math
// Enemy.Health = Enemy.Health - Function.Param(2) // subtracts 0 if param is string

Debugging Parameter Values

Log parameters at function entry to verify:

On Function "ApplyDamage":
  Browser: Log "ApplyDamage called: uid=" & Function.Param(0)
    & ", dmg=" & Function.Param(1)
    & ", elem=" & Function.Param(2)

Open browser console during preview. The log shows exact values received. Mismatch from what you expected tells you where the bug is — caller or callee.

Script-Based Alternative

For complex parameter handling, JavaScript functions are cleaner:

// In a Script action
function applyDamage(uid, damage, element) {
    const enemy = runtime.getInstanceByUid(uid);
    if (enemy) {
        enemy.instVars.Health -= damage;
        enemy.instVars.LastElement = element;
    }
}

No indexing, no type coercion issues, same performance. For large projects with many parameters, Script is often cleaner.

“Function parameters are zero-indexed, typed, and lose SOL context. Named parameters + UID passing cover the most common bugs.”

Related Issues

For collision issues, see Construct 3 Collision Not Detecting. For performance, Construct 3 Performance Tips covers related patterns.

Named parameters. Pass UID and Pick by UID. Zero-indexed. Three rules cover 90% of bugs.