Game Logic

Game Components

A basic game consists of a few simple components:

Timing & Delta

Time plays a very important role in any game. You can use it for physics calculations or custom timers:

# get current unix time
num nowTime = GAME.TIME.now();

# delta value
public action update(num delta) {
    (num) object.x += ((num) object.speed * delta);
    # using delta ensures consistent speed
    # regardless of framerate
}

# stop default game timer (server-side only):
GAME.TIME.freeze();
GAME.TIME.unfreeze();

Countdown Timer

num countdown = 10000; # 10000 ms = 10 seconds

public action update(num delta) {
    countdown -= delta; # delta is the time since the last frame/tick
    if (countdown <= 0) {
        countdown = 10000; # reset
        # this code will run every 10 seconds
        GAME.log("Custom Loop");
    }
}

Players

KrunkScript allows you to interact with the default player objects:

# find a player by their id
obj player = GAME.PLAYERS.findByID(id);
if (notEmpty player) {
    # do something
};

# access player list
obj[] players = GAME.PLAYERS.list();
for (num i = 0; i < lengthOf players; i++) {
    # do something
}

# get player object of active player (client-side only)
obj you = GAME.PLAYERS.getSelf();

Player Properties

player.position.x = 10;           # set player x pos
(num) player.position.y += 0.1;   # move player y pos
player.position.z = 10;           # move player z pos
player.rotation.x = 0.3;          # set player direction
player.velocity.x = 0.1;          # set player velocity
(num) player.health -= 10;        # change player health
player.score = 5;                 # (server-side) change player score
player.team = 1;                  # (server-side) change player team
player.visible = false;           # hide player object

# read-only properties
(num) player.classIndex;           # returns class ID
(num) player.weaponIndex;          # returns current weapon ID
(num) player.sid;                  # short unique ID
(str) player.id;                   # long unique ID
(str) player.username;             # player display name
(str) player.accountName;          # account name (used for data storage)
(str) player.accountID;            # profile ID (persistent across sessions)
(bool) player.active;              # if player has spawned in
(bool) player.onWall;              # if player is touching a wall
(bool) player.onGround;            # if player is on the ground
(bool) player.isCrouching;         # if player is crouching
(bool) player.onTerrain;           # if player is on terrain
(bool) player.isYou;               # (client-side) if this is the local player
player.assetID = "325253";         # update player model asset

Disabling Default Behaviors

Krunker provides a lot of default behavior out of the box. You can disable specific behaviors to customize your game:

GAME.DEFAULT.disablePrediction();        # disable client prediction (client only)
GAME.DEFAULT.disablePlayerBehaviour();   # disable all default player logic (client & server)
player.defaultMovement = false;          # disable movement, jumping & crouching (client & server)
player.defaultVelocity = false;          # disable default velocity (client & server)
player.defaultRotation = false;          # disable default rotations (client & server)
player.disableShooting = true;           # disable shooting & reloading
player.disableMelee = true;              # disable melee
player.disableDefault("jump");           # disable specific default behavior
GAME.DEFAULT.disable3D();                # disable 3D Scene for 2D-only games (client only)
GAME.DEFAULT.disableServerSync();        # disable server sending player data every tick (server only)
GAME.INPUTS.disableDefault();            # don't send default inputs to server (client only)
GAME.UI.hideDefault();                   # hide most default krunker UI (client only)
GAME.UI.hideCrosshair();                 # hide crosshair (client only)
GAME.PLAYERS.disableMeshes();            # hide default player models

Custom Movement

To override Krunker's default movement:

# disable default movement on spawn
public action onPlayerSpawn(str id) {
    obj plr = GAME.PLAYERS.findByID(id);
    if (!!plr) {
        plr.defaultMovement = false;
        # also available:
        # plr.defaultRotation = false;
        # plr.defaultVelocity = false;
        # these are reset on spawn
    }
}

# built-in player update action
public action onPlayerUpdate(str id, num delta, static obj inputs) {
    obj plr = GAME.PLAYERS.findByID(id);
    if (!!plr) {
        if (inputs.jump) {
            # jump key pressed
            # add identical logic to client & server
        }
    }
}

Input Object Properties

The inputs object in onPlayerUpdate contains:

PropertyTypeDescription
inputs.mouseYnumMouse Y direction
inputs.mouseXnumMouse X direction
inputs.movDirnumWASD inputs converted to direction
inputs.lMouseboolLeft mouse button down
inputs.rMouseboolRight mouse button down
inputs.jumpboolJump key down (space by default)
inputs.reloadboolReload key down
inputs.crouchboolCrouch key down
inputs.scrollboolScroll wheel delta
inputs.swapboolSwap keys
inputs.restKboolReset key (parkour)
inputs.interboolInteract key down

You can disable the default behavior of individual keys:

player.disableDefault("jump");
# ignores all default krunker behavior for the jump key
# this is reset on spawn

If you want to simply change speed, velocity, or jump height, you can use the Class Configuration in the Editor instead.

AIs & NPCs

Krunker offers built-in AI objects. Add them using the Editor or KrunkScript (server-side):

# add AI to world
obj testBot = GAME.AI.spawn(
    "11441g", # str asset id
    "bot 1",  # str AI name
    0, 0, 0,  # num x, y, z position
    {}        # obj additional data
);

# optional data to customize your AI:
obj data = {
    animStart: "Idle",  # str starting animation clip
    animSpeed: 0.5,     # num animation playback speed
    health: 100,        # num ai health
    speed: 1.0,         # num speed multiplier
    turnSpeed: 1.0,     # num turn speed multiplier
    gravity: 1.0,       # num gravity multiplier
    respawnT: 1000,     # num respawn time (ms)
    targPlr: false,     # bool target players
    visionDis: 120,     # num vision distance
    chaseDis: 20,       # num chase distance
    canMelee: false,    # bool can melee hit
    meleeRate: 500,     # num melee rate (ms)
    meleeDmg: 500,      # num melee damage
    canShoot: false,    # bool can shoot
    fireRate: 500,      # num fire rate (ms)
    roamRadius: 0,      # num roam radius
    modelScale: 10,     # num model size
    hitBotW: 1,         # num hitbox width multiplier
    hitBoxH: 1          # num hitbox height multiplier
};

AI Management

# remove AI
GAME.AI.remove(testBot.sid);

# list all AIs
obj[] ais = GAME.AI.list();
for (num i = 0; i < lengthOf ais; i++) {
    # do something
}

# interact with AI properties
(str) testBot.displayName;      # returns name
(num) testBot.health -= 10;     # change health
testBot.position.x = 10;        # set position
testBot.rotation.x = Math.PI;   # set rotation
testBot.behaviour = 1;          # 0 = no default, 1 = default
testBot.pathIndex = 5;           # pathnode index

You are limited to 40 active AIs per game. An AI is considered active if it has an active respawn timer or is currently alive.

Keyboard Inputs

Check for keyboard input (client-side only):

action update(num delta) {
    # uses javascript keycodes
    if (GAME.INPUTS.keyDown(67)) {
        # key with code 67 is pressed
    };
};

Mouse Input

# get mouse position on screen (client-side)
obj posNormal = GAME.INPUTS.mousePos();

# get mouse position on overlay canvas
obj pos = GAME.INPUTS.mousePosOverlay();
GAME.OVERLAY.drawCircle((num) pos.x, (num) pos.y, 10, 10, 0, "#ff0000");

# get mouse movement since last frame
obj mov = GAME.INPUTS.mouseMovement();

# unlock mouse for UI interaction
GAME.INPUTS.unlockMouse();

# permanently unlock mouse
GAME.INPUTS.freeMouse();

Input Listeners

The client script has pre-built hooks for input events:

# mouse click listener
public action onMouseClick(num button, num x, num y) {
    # called every mouse click
}
public action onMouseUp(num button, num x, num y) {
    # called on mouse release
}

# key press listener
public action onKeyPress(str key, num code) {
    if (key == "w") {
        # pressed W
    };
}

# key up listener
public action onKeyUp(str key, num code) {
    if (key == "w") {
        # released W
    };
}

# key held listener
public action onKeyHeld(str key, num code) {
    if (key == "w") {
        # holding W
    };
}

# mouse scroll listener
public action onMouseScroll(num dir) {
    # scroll direction
}

Collisions

Krunker offers built-in collision functionality. The most performant method is between two Axis Aligned Bounding Boxes (AABB). All krunker objects default to this method. You can enable it on most objects in the editor using the Collidable option.

Alternatively, select the Complex Collisions option for rotated objects and assets. It is recommended to use a simple invisible mesh for collision and a detailed mesh for display.

Complex collisions use an object's triangle data. You are limited to 5000 collision triangles per map export.

Path Nodes

Path/coordinate 3D objects that can be added in the editor and accessed via KrunkScript:

# fetch nodes list
obj[] nodes = GAME.OBJECTS.getPathNodes();
for (num i = 0; i < lengthOf nodes; ++i) {
    (num) nodes[i].id; # unique id
    (num) nodes[i].x;  # x coordinate
    (num) nodes[i].y;  # y coordinate
    (num) nodes[i].z;  # z coordinate
}

This is useful for creating basic pathfinding or accessing coordinates.