Examples
Complete working examples demonstrating KrunkScript features.
Table of Contents
Editor Templates
These examples are from the in-game editor templates.
- Simple .io Game
- Moving Objects
- Bouncing Ball
- Popup Window
- Flashlight
- Snake Demo
- Basic Networking
- Networking Objects
- Converting 3D to 2D
- Jetpacks
- Sprinting
- Triggers & Scripts
- Game of Life
- Custom Geometry
- Attaching
Editor Templates
These examples come from the built-in editor templates and demonstrate various KrunkScript features.
Simple .io Game
Client Script
# INIT:
obj window = {};
obj ball = {
x: 0,
y: 0,
scale: 30,
speed: 0.4
};
# PEBBLES
obj[] pebbles = obj[];
num pebbleCount = 70; # number of pebbles
# SPIKES:
obj[] spikes = obj[];
num spikePowerDivisor = 2;
num spikeCount = 5; # number of spikes
# WORLD:
num worldScale = 2000;
num gridSize = 60;
# IDEAS:
# Randomize Pebble Colors
# Add Multiplayer using NETWORK object
# Add Spike Objects
# PEBBLE:
obj action genPebble() {
num hue = UTILS.randInt(0, 360);
num scale = UTILS.randFloat(17, 20);
return {
x: UTILS.randInt(-worldScale, worldScale),
y: UTILS.randInt(-worldScale, worldScale),
scale: scale,
power: 3,
innerCol: UTILS.hexFromHue(hue),
outerCol: UTILS.hexFromHue(Math.max(0, hue - 10))
};
}
# SPIKE:
obj action genSpike() {
num scale = UTILS.randFloat(150, 200);
return {
x: UTILS.randInt(-worldScale, worldScale),
y: UTILS.randInt(-worldScale, worldScale),
scale: scale,
power: scale / spikePowerDivisor,
color: '#90ff21'
};
}
# PLAYER:
action resetBall() {
ball.x = UTILS.randInt(-worldScale, worldScale);
ball.y = UTILS.randInt(-worldScale, worldScale);
ball.scale = 30;
ball.speed = 0.2;
}
# GAME:
public action start() {
# SETUP
window = GAME.OVERLAY.getSize();
GAME.INPUTS.freeMouse();
GAME.UI.hideDefault();
GAME.DEFAULT.disable3D();
GAME.DEFAULT.disablePlayerBehaviour();
GAME.DEFAULT.disableServerSync();
# INIT PEBBLES
for (num i = 0; i < pebbleCount; i++) {
addTo pebbles genPebble();
}
# SPIKES:
for (num i = 0; i < spikeCount; i++) {
addTo spikes genSpike();
}
}
# GAME:
public action update(num delta) {
# GET POSITION:
obj mousePos = GAME.INPUTS.mousePosOverlay();
# DIRECTION:
num movDir = UTILS.getDir2D(mousePos.x, mousePos.y, ((num) window.width/2), ((num) window.height/2));
# BALL:
(num) ball.x += ((num) ball.speed * delta) * Math.cos(movDir);
(num) ball.y += ((num) ball.speed * delta) * Math.sin(movDir);
# PEBBLE CHECKS:
for (num i = 0; i < lengthOf pebbles; i++) {
obj tmpObj = pebbles[i];
num distance = UTILS.getDist2D(ball.x, ball.y, tmpObj.x, tmpObj.y);
if (distance <= ((num) tmpObj.scale + (num) ball.scale)) {
(num) ball.scale += (num) tmpObj.power;
pebbles[i] = genPebble();
}
}
# SPIKE CHECKS:
for (num i = 0; i < lengthOf spikes; i++) {
obj tmpObj = spikes[i];
num distance = UTILS.getDist2D(ball.x, ball.y, tmpObj.x, tmpObj.y) * 2;
if (distance <= ((num) tmpObj.scale + (num) ball.scale)) {
if ((num) ball.scale >= (num) tmpObj.scale) {
(num) ball.scale += (num) tmpObj.power;
spikes[i] = genSpike();
} else {
resetBall();
}
}
}
# BORDER CHECKS:
if (((num) ball.x - (num) ball.scale) <= -worldScale) {
ball.x = (num) ball.scale + -worldScale;
}
if (((num) ball.x + (num) ball.scale) >= worldScale) {
ball.x = worldScale - (num) ball.scale;
}
if (((num) ball.y - (num) ball.scale) <= -worldScale) {
ball.y = (num) ball.scale + -worldScale;
}
if (((num) ball.y + (num) ball.scale) >= worldScale) {
ball.y = worldScale - (num) ball.scale;
}
}
# SPIKES:
action drawSpike(num x, num y, num scale, str color) {
for (num i = 0; i < 7; i++) {
GAME.OVERLAY.drawRect(x, y, scale, scale, i * 15, color, 1, true);
}
}
# RENDER:
public action render(num delta) {
# SETUP:
GAME.OVERLAY.clear();
window = GAME.OVERLAY.getSize();
# BACKGROUND:
GAME.OVERLAY.drawRect(0, 0, window.width, window.height, 0, '#fafafa', 1);
# GRID:
num xPos = (-((num) ball.x) % gridSize) - gridSize;
while (xPos < (num) window.width) {
xPos = xPos + gridSize;
if (xPos > 0) {
GAME.OVERLAY.drawLine(xPos, 0, xPos, window.height, 3, '#000000', 0.1);
}
}
num yPos = (-((num) ball.y) % gridSize) - gridSize;
while (yPos < (num) window.height) {
yPos = yPos + gridSize;
if (yPos > 0) {
GAME.OVERLAY.drawLine(0, yPos, window.width, yPos, 3, '#000000', 0.1);
}
}
# CAMERA:
GAME.OVERLAY.offset(-((num) ball.x) + ((num) window.width/2), -((num) ball.y) + ((num) window.height/2));
# PEBBLES:
for (num i = 0; i < lengthOf pebbles; i++) {
obj tmpObj = pebbles[i];
GAME.OVERLAY.drawCircle(tmpObj.x, tmpObj.y, tmpObj.scale,
tmpObj.scale, 0, tmpObj.outerCol, 1);
GAME.OVERLAY.drawCircle(tmpObj.x, tmpObj.y, (num) tmpObj.scale * 0.7,
(num) tmpObj.scale * 0.7, 0, tmpObj.innerCol, 1);
}
# SPIKES:
for (num i = 0; i < lengthOf spikes; i++) {
obj tmpObj = spikes[i];
drawSpike((num) tmpObj.x, (num) tmpObj.y, (num) tmpObj.scale, (str) tmpObj.color);
}
# BALL:
GAME.OVERLAY.drawCircle((num) ball.x, (num) ball.y, (num) ball.scale + 10,
(num) ball.scale + 10, 0, '#db3532', 1);
GAME.OVERLAY.drawCircle((num) ball.x, (num) ball.y, (num) ball.scale,
(num) ball.scale, 0, '#ff3b38', 1);
}
Moving Objects
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
# Light Object
obj redLamp = {};
obj blueLamp = {};
# Position
num distance = 18;
num yPos = 12;
# Angle
num angle = 0;
num angularVelocity = 0.006;
# Create a new lamp
obj action createLamp(str color) {
obj light = GAME.SCENE.addPointLight(color, distance, yPos, 0, 100, 1);
obj ball = GAME.SCENE.addSphere('', color, distance, yPos, 0, 5, 5, 5);
ball.emissive = color;
return {
light: light,
ball: ball
};
}
# Runs when the game starts
public action start() {
# force lighting
GAME.SETTINGS.set('lighting', 2);
# create lamps
redLamp = createLamp('#ff0000');
blueLamp = createLamp('#0000ff');
}
# Rotate angle
action rotateAngle(num delta) {
angle = angle + (angularVelocity * delta);
angle = angle % Math.PI2;
}
# Update lamp position
action updateLamp(obj lamp, num offset) {
lamp.light.position.x = distance * Math.cos(angle + offset);
lamp.light.position.z = distance * Math.sin(angle + offset);
lamp.ball.position.x = lamp.light.position.x;
lamp.ball.position.z = lamp.light.position.z;
}
# Runs every game tick
public action update(num delta) {
# rotate angle
rotateAngle(delta);
# update lamp positions
updateLamp(blueLamp, Math.PI);
updateLamp(redLamp, 0);
}
Server Script
# Server Script runs only on Hosted server & not in test mode
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
}
# Runs every game tick
public action update(num delta) {
}
# Player spawns in
public action onPlayerSpawn(str id) {
}
# Player update
public action onPlayerUpdate(str id, num delta, static obj inputs) {
}
# Called from Custom Trigger Action
public action onCustomTrigger(str playerID, str customParam) {
}
# Server receives network message
public action onNetworkMessage(str id, obj data, str playerID) {
}
# When a player leaves the server
public action onPlayerLeave(str playerID) {
}
Bouncing Ball
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
obj ball = {};
num ballScale = 5; # play around with this
num ballVelocity = 0;
num gravity = 0.01; # play around with this
num dropHeight = 100; # play around with this
num squishiness = 0.5; # play around with this
# Runs when the game starts
public action start() {
# detach camera
GAME.CAMERA.detach();
# hide player
GAME.PLAYERS.disableMeshes();
GAME.DEFAULT.disablePlayerBehaviour();
# hide ui
GAME.UI.hideDefault();
GAME.UI.hideCrosshair();
# add ball to scene
ball = GAME.SCENE.addSphere('', '#ff0000', 0, dropHeight, 0,
ballScale, ballScale, ballScale);
# add custom UI
str divID = GAME.UI.addDIV('infoText', true,
'color:#f1f1f1;position:absolute;top:90%;left:50%;' +
'transform: translate(-50%,-50%);font-size: 24px;' +
'border:solid 2px #fff;border-radius:4px;' +
'padding:10px 30px 10px 30px;background-color: rgba(0,0,0,0.2);'
);
# add text to div
GAME.UI.updateDIVText(divID, 'Press R to Reset Ball');
}
# custom reset ball action
action resetBall() {
ball.position.y = dropHeight;
ballVelocity = 0;
ball.scale.y = ballScale;
}
# Runs every game tick
public action update(num delta) {
# Update Gravity
ballVelocity = ballVelocity - (gravity * delta);
# Move Ball
(num) ball.position.y += ballVelocity;
# Check if on Floor
if ((num) ball.position.y - ballScale <= 0) {
ball.position.y = ballScale;
ballVelocity = ballVelocity * -1;
(num) ball.scale.y -= ballVelocity * squishiness;
}
# Squish Animation
if ((num) ball.scale.y <= ballScale) {
(num) ball.scale.y += 0.01 * delta;
if ((num) ball.scale.y > ballScale) {
ball.scale.y = ballScale;
}
num scaleDiff = (ballScale - (num) ball.scale.y);
ball.scale.x = (ballScale + scaleDiff);
ball.scale.z = (ballScale + scaleDiff);
}
# update camera
GAME.CAMERA.move(0, 40, 90);
GAME.CAMERA.rotate(0, 0, 0);
}
# User pressed a key
public action onKeyPress(str key, num code) {
GAME.log(key, toStr code);
if (key == 'r') {
resetBall();
}
}
Popup Window
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
# create popup div
str popupDiv = GAME.UI.addDIV(
'popupTemp',
false,
'width:400px;height:225px;position:absolute;' +
'top:40%;left:50%;transform: translate(-50%, -50%);' +
'background-color:grey;cursor:pointer;border-radius:4px;' +
'border:solid 2px black;'
);
# add text to popup div
GAME.UI.updateDIVText(popupDiv, 'Hello World');
# create close button & add to popup
str closeDiv = GAME.UI.addDIV(
'popupTempClose',
true,
'width:30px;height:30px;position:absolute;' +
'top:10px;left:10px;background-color:white;' +
'cursor:pointer;border-radius:4px;',
popupDiv
);
# add text to close div
GAME.UI.updateDIVText(closeDiv, 'x');
# add custom UI
str divID = GAME.UI.addDIV(
'infoText',
true,
'color:#f1f1f1;position:absolute;top:90%;left:50%;' +
'transform: translate(-50%,-50%);font-size: 24px;' +
'border:solid 2px #fff;border-radius:4px;' +
'padding:10px 30px 10px 30px;background-color: rgba(0,0,0,0.2);'
);
# add text to div
GAME.UI.updateDIVText(divID, 'Press G to Open Popup');
}
# User pressed a key
public action onKeyPress(str key, num code) {
# check if pressed correct key
if (key == 'g') {
# show popup
GAME.UI.updateDIV('popupTemp', 'display', 'block');
# unlock mouse so users can click popup
GAME.INPUTS.unlockMouse();
}
}
# User clicked a DIV (ID)
public action onDIVClicked(str id) {
# check if clicked on close div
if (id == 'popupTempClose') {
# hide popup
GAME.UI.updateDIV('popupTemp', 'display', 'none');
# lock mouse so users can continue;
GAME.INPUTS.lockMouse();
}
}
Flashlight
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
obj torch = {};
# Runs when the game starts
public action start() {
# Hide defaults:
GAME.UI.hideCrosshair();
GAME.UI.hideDefault();
GAME.PLAYERS.disableMeshes();
# Add light object:
torch = GAME.SCENE.addSpotLight("#fff", 0, 0, 0, 0, 0, 0, 60, 1, 1, 60, 0.5);
}
# Runs every game tick
public action update(num delta) {
obj plr = GAME.PLAYERS.getSelf();
if (notEmpty plr) {
num yPos = ((num) plr.position.y + 10);
torch.move(
plr.position.x,
yPos,
plr.position.z
);
torch.lookAt(
((num) plr.position.x + (1000 * Math.sin((num) plr.rotation.x + Math.PI) * Math.cos((num) plr.rotation.y))),
(yPos + (1000 * Math.sin((num) plr.rotation.y))),
((num) plr.position.z + (1000 * Math.cos((num) plr.rotation.x + Math.PI) * Math.cos((num) plr.rotation.y)))
);
}
}
# Add rendering logic in here
public action render(num delta) {
}
# User pressed a key
public action onKeyPress(str key, num code) {
}
# User released a key
public action onKeyUp(str key, num code) {
}
# User held a key
public action onKeyHeld(str key, num code) {
}
# User clicked on screen
public action onMouseClick(num button, num x, num y) {
}
# User scrolled on screen
public action onMouseScroll(num dir) {
}
# User clicked a DIV (ID)
public action onDIVClicked(str id) {
}
# Client receives network message
public action onNetworkMessage(str id, obj data) {
}
Snake Demo
Client Script
# SCREEN
obj bounds = GAME.OVERLAY.getSize();
num width = (num) bounds.width;
num height = (num) bounds.height;
# TIMER
num tick = 0;
num tickRate = 50;
num clock = 0;
# PLAYER
obj player = {
size: 20,
color: 0,
pos: num[] [num [(width - 40) / 2, (height - 40) / 2]],
tail: 1,
horz: 0,
vert: -1
};
public action start() {
# SETTINGS
GAME.SETTINGS.set('scaleUI', 1);
GAME.SETTINGS.set('showUI', false);
# DEFAULT
GAME.DEFAULT.disablePlayerBehaviour();
GAME.DEFAULT.disable3D();
GAME.DEFAULT.disableServerSync();
GAME.DEFAULT.disablePrediction();
}
# DRAW BACKGROUND
action drawBackground() {
GAME.OVERLAY.drawRect(
0,
0,
width,
height,
0, '#000000', 1);
}
# DRAW PLAYER
action drawPlayer() {
for (num i = 0; i < lengthOf (num[][]) player.pos; i++) {
GAME.OVERLAY.drawRect(
(num) player.pos[i][0],
(num) player.pos[i][1],
(num) player.size,
(num) player.size,
0, GAME.UTILS.hexFromHue((num) player.color), 1);
}
}
public action render(num delta) {
drawBackground();
drawPlayer();
}
# MOVE PLAYER
action movePlayer() {
num[] old = (num[]) player.pos[(lengthOf (num[][]) player.pos) - 1];
num[] new = num [old[0] + (num) player.horz * (num) player.size, old[1] + (num) player.vert * (num) player.size];
new[0] = (new[0] + width) % width;
new[1] = (new[1] + height) % height;
addTo (num[][]) player.pos new;
if (lengthOf (num[][]) player.pos > (num) player.tail) {
remove player.pos[0];
}
}
public action update(num delta) {
if (tick > tickRate) {
tick = 0;
clock++;
if (clock % 1 == 0) {
movePlayer();
}
if (clock % 10 == 0) {
(num) player.tail++;
}
}
(num) player.color += 0.1 * delta;
tick += delta;
}
public action onKeyPress(str key, num code) {
if (key == 'w') {
player.vert = -1;
player.horz = 0;
}
if (key == 's') {
player.vert = 1;
player.horz = 0;
}
if (key == 'a') {
player.vert = 0;
player.horz = -1;
}
if (key == 'd') {
player.vert = 0;
player.horz = 1;
}
}
Basic Networking
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
str resDivId = "";
# Runs when the game starts
public action start() {
# add custom UI
resDivId = GAME.UI.addDIV(
'resText',
false,
'color:#f1f1f1;position:absolute;top:50%;left:50%;' +
'transform: translate(-50%,-50%);font-size: 24px;' +
'border:solid 2px #fff;border-radius:4px;' +
'padding:10px 30px 10px 30px;background-color: rgba(0,0,0,0.2);'
);
# add custom UI
str divID = GAME.UI.addDIV(
'infoText',
true,
'color:#f1f1f1;position:absolute;top:90%;left:50%;' +
'transform: translate(-50%,-50%);font-size: 24px;' +
'border:solid 2px #fff;border-radius:4px;' +
'padding:10px 30px 10px 30px;background-color: rgba(0,0,0,0.2);'
);
# add text to div
GAME.UI.updateDIVText(divID, 'Press G to Validate Age');
}
# User pressed a key
public action onKeyPress(str key, num code) {
# check if pressed correct key
if (key == 'g') {
GAME.NETWORK.send("check", {
name: "Test",
age: GAME.UTILS.randInt(0, 21)
});
}
}
# Client receives network message
public action onNetworkMessage(str id, obj data) {
if (id == "check") {
str status = (str) data.status;
GAME.UI.updateDIVText(resDivId, 'You ' + status + ' Age Validation');
GAME.UI.updateDIV(resDivId, 'display', 'block');
}
}
Server Script
# Server Script runs only on Hosted server & not in test mode
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
}
# Runs every game tick
public action update(num delta) {
}
# Server receives network message
public action onNetworkMessage(str id, obj data, str playerID) {
if (id == "check") {
num age = (num) data.age;
bool valid = age >= 13;
GAME.NETWORK.send("check", {
status: valid ? "Passed" : "Failed"
}, playerID);
}
}
Networking Objects
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
}
# Runs every game tick
public action update(num delta) {
}
# Add rendering logic in here
public action render(num delta) {
}
# Player spawns in
public action onPlayerSpawn(str id) {
}
# Player update
public action onPlayerUpdate(str id, num delta, obj inputs) {
}
# User pressed a key
public action onKeyPress(str key, num code) {
}
# User released a key
public action onKeyUp(str key, num code) {
}
# User held a key
public action onKeyHeld(str key, num code) {
}
# User clicked on screen
public action onMouseClick(num button, num x, num y) {
}
# User released clicked on screen
public action onMouseUp(num button, num x, num y) {
}
# User scrolled on screen
public action onMouseScroll(num dir) {
}
# User clicked a DIV (ID)
public action onDIVClicked(str id) {
}
# Client receives network message
public action onNetworkMessage(str id, obj data) {
}
Server Script
# Server Script runs only on Hosted server & not in test mode
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
}
# Runs every game tick
public action update(num delta) {
}
# Player spawns in
public action onPlayerSpawn(str id) {
}
# Player update
public action onPlayerUpdate(str id, num delta, obj inputs) {
}
# Called from Custom Trigger Action
public action onCustomTrigger(str playerID, str customParam, num value) {
# check parameter
if (customParam == "1") {
GAME.log("First Trigger");
}
# check parameter
if (customParam == "2") {
GAME.log("Second Trigger");
}
}
# Server receives network message
public action onNetworkMessage(str id, obj data, str playerID) {
}
# When a player leaves the server
public action onPlayerLeave(str playerID) {
}
Converting 3D to 2D
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
# Add rendering logic in here
public action render(num delta) {
obj coords = GAME.SCENE.posToScreen(0, 10, 0);
if ((bool) coords.onScreen) {
GAME.OVERLAY.drawCircle(coords.x, coords.y, 80, 80, 0, "#ff0000", 1);
GAME.OVERLAY.drawText("1", coords.x, (num) coords.y+40, 0, 70, "center", "#fff", 1);
}
}
Jetpacks
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
# Player update
public action onPlayerUpdate(str id, num delta, obj inputs) {
obj tPlr = GAME.PLAYERS.findByID(id);
if (!!tPlr && (num) tPlr.classIndex == 1) { # Hunter Only
tPlr.disableDefault("jump");
if ((bool) inputs.jump) {
(num) tPlr.velocity.y += 0.0003 * delta;
if ((num) tPlr.velocity.y > 0.03) {
tPlr.velocity.y = 0.03;
}
}
}
}
Server Script
# Server Script runs only on Hosted server & not in test mode
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
# Player update
public action onPlayerUpdate(str id, num delta, static obj inputs) {
obj tPlr = GAME.PLAYERS.findByID(id);
if (!!tPlr && (num) tPlr.classIndex == 1) { # Hunter Only
tPlr.disableDefault("jump");
if (inputs.jump) {
(num) tPlr.velocity.y += 0.0003 * delta;
if ((num) tPlr.velocity.y > 0.03) {
tPlr.velocity.y = 0.03;
}
}
}
}
Sprinting
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
# Player update
public action onPlayerUpdate(str id, num delta, obj inputs) {
obj tPlr = GAME.PLAYERS.findByID(id);
if (!!tPlr) {
# Disable Default Movement:
tPlr.defaultMovement = false;
tPlr.disableDefault("crouch");
# Move Direction:
if ((num) inputs.movDir >= -Math.PI && (num) inputs.movDir <= Math.PI) {
num movDir = 0;
if (!!(num) inputs.movDir) {
movDir = (num) inputs.movDir;
}
movDir -= ((num) inputs.mouseX);
num movSpd = 0.0003 * delta;
if ((bool) inputs.crouch) { # Sprint
movSpd *= 1.6;
}
(num) tPlr.velocity.x += (movSpd * Math.cos(movDir));
(num) tPlr.velocity.z += (movSpd * Math.sin(movDir));
}
}
}
Server Script
# Server Script runs only on Hosted server & not in test mode
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
# Player update
public action onPlayerUpdate(str id, num delta, obj inputs) {
obj tPlr = GAME.PLAYERS.findByID(id);
if (!!tPlr) {
# Disable Default Movement:
tPlr.defaultMovement = false;
tPlr.disableDefault("crouch");
# Move Direction:
if ((num) inputs.movDir >= -Math.PI && (num) inputs.movDir <= Math.PI) {
num movDir = 0;
if (!!inputs.movDir) {
movDir = (num) inputs.movDir;
}
movDir -= ((num) inputs.mouseX);
num movSpd = 0.0003 * delta;
if ((bool) inputs.crouch) { # Sprint
movSpd *= 1.6;
}
(num) tPlr.velocity.x += (movSpd * Math.cos(movDir));
(num) tPlr.velocity.z += (movSpd * Math.sin(movDir));
}
}
}
Triggers & Scripts
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
}
# Runs every game tick
public action update(num delta) {
}
# Add rendering logic in here
public action render(num delta) {
}
# Player spawns in
public action onPlayerSpawn(str id) {
}
# Player update
public action onPlayerUpdate(str id, num delta, static obj inputs) {
}
# User pressed a key
public action onKeyPress(str key, num code) {
}
# User released a key
public action onKeyUp(str key, num code) {
}
# User held a key
public action onKeyHeld(str key, num code) {
}
# User clicked on screen
public action onMouseClick(num button, num x, num y) {
}
# User released clicked on screen
public action onMouseUp(num button, num x, num y) {
}
# User scrolled on screen
public action onMouseScroll(num dir) {
}
# User clicked a DIV (ID)
public action onDIVClicked(str id) {
}
# Client receives network message
public action onNetworkMessage(str id, obj data) {
}
Server Script
# Server Script runs only on Hosted server & not in test mode
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
}
# Runs every game tick
public action update(num delta) {
}
# Player spawns in
public action onPlayerSpawn(str id) {
}
# Player update
public action onPlayerUpdate(str id, num delta, static obj inputs) {
}
# Called from Custom Trigger Action
public action onCustomTrigger(str playerID, str customParam) {
# check parameter
if (customParam == "1") {
GAME.log("First Trigger");
}
# check parameter
if (customParam == "2") {
GAME.log("Second Trigger");
}
}
# Server receives network message
public action onNetworkMessage(str id, obj data, str playerID) {
}
# When a player leaves the server
public action onPlayerLeave(str playerID) {
}
Game of Life
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
obj window = GAME.OVERLAY.getSize();
obj oldWindow = window;
obj pos = GAME.INPUTS.mousePosOverlay();
bool mouseReleased = true;
bool initialized = false;
bool first = false;
bool pauseState = true;
num oldTime = GAME.TIME.now();
num track = 0;
num timerDelay = 250; # Initial timer (0.25s)
num timerStepper = 50; # Defines the step count for the timer in ms
num gridStepper = 1; # Defines the step count for tile size in px
num maxGrid = 100; # Maximum tile size amount in px
num minGrid = 20; # Minimum tile size amount in px
obj grid = {
x: 0,
y: 0,
w: 0,
h: 0,
scale: 30, # Initial tile size (lower = more tiles)
tileColor: "#3e4240",
backgroundColor: "#000000",
borderColor: "#707070",
hoverColor: "#464a48",
lifeColor: "#5dde6c"
};
obj ui = {
height: 99
};
num[][] tiles = num[][];
# Counts how many live neighors each tile has
num action neighbors(num x, num y){
num neighborCount = 0;
# top
if(y != 0 && tiles[x][y - 1] == 1){
neighborCount++;
};
# bottom
if(y != (num) grid.h - 1 && tiles[x][y + 1] == 1){
neighborCount++;
};
# left
if(x != 0 && tiles[x - 1][y] == 1){
neighborCount++;
};
# right
if(x != (num) grid.w - 1 && tiles[x + 1][y] == 1){
neighborCount++;
};
# top left
if(y != 0 && x != 0 && tiles[x - 1][y - 1] == 1){
neighborCount++;
};
# top right
if(y != 0 && x != (num) grid.w - 1 && tiles[x + 1][y - 1] == 1){
neighborCount++;
};
# bottom left
if(y != (num) grid.h - 1 && x != 0 && tiles[x - 1][y + 1] == 1){
neighborCount++;
};
# bottom right
if(y != (num) grid.h - 1 && x != (num) grid.w - 1 && tiles[x + 1][y + 1] == 1){
neighborCount++;
};
return neighborCount;
}
# Advances to the next generation -- Updates board
action tick(){
obj[] buffer = obj[];
for(num i = 0; i < (num) grid.w; i++){
for(num j = 0; j < (num) grid.h; j++){
num count = neighbors(i, j);
if((count == 2 || count == 3) && tiles[i][j] == 1){
addTo buffer {x: i, y: j, val: 1};
};
if(count == 3 && tiles[i][j] == 0){
addTo buffer {x: i, y: j, val: 1};
};
if(count < 2 && tiles[i][j] == 1) {
addTo buffer {x: i, y: j, val: 0};
};
if(count > 3 && tiles[i][j] == 1) {
addTo buffer {x: i, y: j, val: 0};
};
}
}
# Prevents neighbors from changing while updating -- Updates all changes at once
for(num i = 0; i < lengthOf buffer; i++){
tiles[(num) buffer[i].x][(num) buffer[i].y] = (num) buffer[i].val;
}
}
# Creates the grid array according to screen size
action makeGrid(){
window = GAME.OVERLAY.getSize();
(num) window.height -= (num) ui.height;
grid.w = Math.floor((num) window.width / (num) grid.scale);
grid.h = Math.floor((num) window.height / (num) grid.scale);
grid.x = ((num) window.width - (num) grid.scale * Math.floor((num) window.width / (num) grid.scale))/2; # Always center board in the center
grid.y = ((num) window.height - (num) grid.scale * Math.floor((num) window.height / (num) grid.scale))/2;
# Grid is defined as a nested array -- tiles[x][y]
tiles = num[][];
for(num i = 0; i < (num) grid.w; i++) {
num[] tempArray = num[];
for(num j = 0; j < (num) grid.h; j++) {
addTo tempArray 0;
}
addTo tiles tempArray;
}
# tiles[
# [0,0,0,0],
# [0,0,0,0],
# [0,0,0,0]
# ]
}
# Saves previous live tiles in buffer and re-adjusts grid on scale/window change
action updateGrid(){
obj[] buffer = obj[];
window = GAME.OVERLAY.getSize();
(num) window.height -= (num) ui.height;
grid.w = Math.floor((num) window.width / (num) grid.scale);
grid.h = Math.floor((num) window.height / (num) grid.scale);
grid.x = ((num) window.width - (num) grid.scale * Math.floor((num) window.width / (num) grid.scale))/2;
grid.y = ((num) window.height - (num) grid.scale * Math.floor((num) window.height / (num) grid.scale))/2;
for(num i = 0; i < lengthOf tiles; i++){
for(num j = 0; j < lengthOf tiles[i]; j++){
if(tiles[i][j] == 1){
addTo buffer {x: i, y: j};
}
}
}
tiles = num[][];
for(num i = 0; i < (num) grid.w; i++) {
num[] tempArray = num[];
for(num j = 0; j < (num) grid.h; j++) {
addTo tempArray 0;
}
addTo tiles tempArray;
}
# Load old live tiles
for(num i = 0; i < lengthOf buffer; i++){
tiles[(num) buffer[i].x][(num) buffer[i].y] = 1;
}
}
# Create GUI and set custom controls
public action start() {
GAME.DEFAULT.disablePlayerBehaviour();
GAME.INPUTS.disableDefault();
GAME.UI.hideDefault();
GAME.UI.hideCrosshair();
GAME.INPUTS.freeMouse();
str defaultBackground = "background-color: black;width: 30px;height: 30px;border-radius: 15px;margin: 5px;font-family: sans-serif;color: white;";
str customValues = "color: white;background: black;font-family: sans-serif;font-weight: bold;font-size: 18px;width: 50px;height: fit-content;padding: 11px;border-radius: 10px;display: inline-grid;";
str startMenu = GAME.UI.addDIV(
"startMenu",
true,
"position: absolute;background: #2b2b2b;width: 100%;height: 100%;display: grid;justify-content: center;align-items: center;justify-items: center;z-index:1000;"
);
str title = GAME.UI.addDIV(
"title",
true,
"text-align: center;font-size: 70px;color: white;font-family: monospace;",
"startMenu"
);
str startBtn = GAME.UI.addDIV(
"startBtn",
true,
"color: black;font-size: 65px;font-family: sans-serif;border: 1px solid white;border-radius: 19px;padding: 10px;background: white;font-weight: bold;user-select: none;width: fit-content;height: fit-content;",
"startMenu"
);
str ruleText = GAME.UI.addDIV(
"ruleText",
true,
"text-align: center; color: white;width: 900px;font-size: 25px;white-space: break-spaces;font-family: sans-serif;",
"startMenu"
);
GAME.UI.updateDIVText(
"title",
"Conways Game of Life"
);
GAME.UI.updateDIVText(
"startBtn",
"START"
);
GAME.UI.updateDIVText(
"ruleText",
"Any live cell with fewer than two live neighbours dies, as if by underpopulation. Any live cell with two or three live neighbours lives on to the next generation. Any live cell with more than three live neighbours dies, as if by overpopulation. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction."
);
str ui = GAME.UI.addDIV(
"ui",
true,
"height: 100px;width: 100%;background: #2f2f2f;position: absolute;bottom: 0px;display: grid;justify-content: center;align-content: center;grid-auto-flow: column;gap: 9px;align-items: center;user-select: none;"
);
str backgroundClear = GAME.UI.addDIV(
"backgroundClear",
true,
"background-color: black;width: 70px;height: 70px;border-radius: 15px;margin: 5px;float: right;",
"ui"
);
str startGui = GAME.UI.addDIV(
"startGui",
true,
"color: white;background: black;font-family: sans-serif;font-weight: bold;font-size: 40px;width: 137px;height: fit-content;padding: 11px;border-radius: 10px;display: inline-grid;",
"ui"
);
GAME.UI.updateDIVText(
"startGui",
"START"
);
str backgroundNext = GAME.UI.addDIV(
"backgroundNext",
true,
"background-color: black;width: 70px;height: 70px;border-radius: 15px;margin: 5px;float: right;",
"ui"
);
str next = GAME.UI.addDIV(
"next",
true,
"background-color: #ffffff;clip-path: polygon(100% 50%, 0 100%, 25% 49%, 0 0);width: 50%;height: 50%;position: relative;top: 23%;left: 32%;",
"backgroundNext"
);
str clear = GAME.UI.addDIV(
"clear",
true,
"background-color: #ffffff;clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);width: 50%;height: 50%;position: relative;top: 24%;left: 26%;",
"backgroundClear"
);
str backgroundRemove = GAME.UI.addDIV(
"backgroundRemove",
true,
defaultBackground + "font-size: 23px;",
"ui"
);
str timer = GAME.UI.addDIV(
"timer",
true,
customValues,
"ui"
);
GAME.UI.updateDIVText(
"timer",
toStr (timerDelay / 1000) + "s"
);
str backgroundAdd = GAME.UI.addDIV(
"backgroundAdd",
true,
defaultBackground + "font-size: 27px;",
"ui"
);
GAME.UI.updateDIVText(
"backgroundAdd",
"+"
);
GAME.UI.updateDIVText(
"backgroundRemove",
"-"
);
str backgroundRemoveScale = GAME.UI.addDIV(
"backgroundRemoveScale",
true,
defaultBackground + "font-size: 23px;",
"ui"
);
str scale = GAME.UI.addDIV(
"scale",
true,
customValues,
"ui"
);
str backgroundAddScale = GAME.UI.addDIV(
"backgroundAddScale",
true,
defaultBackground + "font-size: 27px;",
"ui"
);
GAME.UI.updateDIVText(
"backgroundAddScale",
"+"
);
GAME.UI.updateDIVText(
"scale",
toStr grid.scale + "px"
);
GAME.UI.updateDIVText(
"backgroundRemoveScale",
"-"
);
}
# Function manager for GUI and keybinds
action guiFunction(str function){
if(function == "addTime"){
if(timerDelay < 10000){
timerDelay += timerStepper;
GAME.UI.updateDIVText("timer", toStr (timerDelay / 1000) + "s");
}
};
if(function == "removeTime"){
if(timerDelay > timerStepper){
timerDelay -= timerStepper;
GAME.UI.updateDIVText("timer", toStr (timerDelay / 1000) + "s");
}
};
if(function == "toggleTimer"){
if(pauseState == false){
GAME.UI.updateDIVText("startGui","START");
pauseState = true;
} else {
GAME.UI.updateDIVText("startGui","STOP");
pauseState = false;
};
};
if(function == "scaleIncrease"){
if((num) grid.scale < maxGrid){
(num) grid.scale += gridStepper;
GAME.UI.updateDIVText("scale", toStr grid.scale + "px");
updateGrid();
}
};
if(function == "scaleDecrease"){
if((num) grid.scale > minGrid){
(num) grid.scale -= gridStepper;
GAME.UI.updateDIVText("scale", toStr grid.scale + "px");
updateGrid();
}
};
if(function == "clearGrid"){
GAME.UI.updateDIVText("startGui","START");
pauseState = true;
makeGrid();
};
}
public action update(num delta) {
# Runs evolution timer
if (pauseState == false && GAME.TIME.now() - oldTime > timerDelay){
tick();
oldTime = GAME.TIME.now();
}
# Right-click and hold function
if(mouseReleased == false) {
for(num i = 0; i < (num) grid.w; i++){
for(num j = 0; j < (num) grid.h + 1; j++){
if((num) pos.x > (((num) grid.scale * i) + (num) grid.x) && (num) pos.x < (((num) grid.scale * i)) + (num) grid.scale + (num) grid.x && (num) pos.y > (((num) grid.scale * j) + (num) grid.y) && (num) pos.y < (((num) grid.scale * j)) + (num) grid.scale + (num) grid.y){
if(first == false){
track = tiles[i][j];
first = true;
};
if(tiles[i][j] != track){
tiles[i][j] = track;
}
if(pauseState == false) {
guiFunction("toggleTimer");
};
}
}
}
}
}
# Updates all overlay elements -- Makes board visible
public action render(num delta) {
window = GAME.OVERLAY.getSize();
(num) window.height -= (num) ui.height;
if ((Math.floor((num) window.width) != Math.floor((num) oldWindow.width) || Math.floor((num) window.height) != Math.floor((num) oldWindow.height)) && initialized == true){
GAME.log(Math.floor((num) window.height), Math.floor((num) oldWindow.height));
GAME.log("updateWindow");
updateGrid();
oldWindow.width = (num) window.width;
oldWindow.height = (num) window.height;
}
pos = GAME.INPUTS.mousePosOverlay();
GAME.OVERLAY.drawRect(
0,
0,
window.width,
window.height,
false,
grid.backgroundColor,
1
);
for(num i = 0; i < lengthOf tiles; i++){
for(num j = 0; j < lengthOf tiles[i]; j++){
if((num) pos.x > (((num) grid.scale * i) + (num) grid.x) && (num) pos.x < (((num) grid.scale * i)) + (num) grid.scale + (num) grid.x && (num) pos.y > (((num) grid.scale * j) + (num) grid.y) && (num) pos.y < (((num) grid.scale * j)) + (num) grid.scale + (num) grid.y){
GAME.OVERLAY.drawRect(((num) grid.scale * i) + (num) grid.x, (j * (num) grid.scale) + (num) grid.y, (num) grid.scale, (num) grid.scale, 0, (str) grid.hoverColor, 1);
}
if(tiles[i][j] == 1){
GAME.OVERLAY.drawRect(((num) grid.scale * i) + (num) grid.x, (j * (num) grid.scale) + (num) grid.y, (num) grid.scale, (num) grid.scale, 0, (str) grid.lifeColor, 1);
} else {
GAME.OVERLAY.drawRect(((num) grid.scale * i) + (num) grid.x, (j * (num) grid.scale) + (num) grid.y, (num) grid.scale, (num) grid.scale, 0, (str) grid.tileColor, 1);
}
}
}
for(num i = 0; i < (num) grid.w + 1; i++){
for(num j = 0; j < (num) grid.h + 1; j++){
GAME.OVERLAY.drawLine((num) grid.scale * i + (num) grid.x, (num) grid.y, (num) grid.scale * i + (num) grid.x, (num) window.height - (num) grid.y, 1, (str) grid.borderColor, 1);
GAME.OVERLAY.drawLine((num) grid.x, j * (num) grid.scale + (num) grid.y, (num) window.width - (num) grid.x, j * (num) grid.scale + (num) grid.y, 1, (str) grid.borderColor, 1);
};
}
}
# Checks if GUI element was clicked -- Timer prevents propagation
public action onDIVClicked(str id) {
if(GAME.TIME.now() - oldTime > 10){
if (id == "startBtn") {
GAME.UI.updateDIV(
"startMenu",
"display",
"none"
);
makeGrid();
initialized = true;
};
if (id == "backgroundNext" || id == "next") {
tick();
};
if (id == "backgroundClear" || id == "clear") {
guiFunction("clearGrid");
};
if (id == "backgroundAddScale") {
guiFunction("scaleIncrease");
};
if (id == "backgroundRemoveScale") {
guiFunction("scaleDecrease");
};
if (id == "backgroundAdd") {
guiFunction("addTime");
};
if (id == "backgroundRemove") {
guiFunction("removeTime");
};
if (id == "startGui") {
guiFunction("toggleTimer");
};
oldTime = GAME.TIME.now();
}
}
# Toggles tile alive or empty
public action onMouseClick(num button, num x, num y) {
if(button == 1) {
for(num i = 0; i < (num) grid.w; i++){
for(num j = 0; j < (num) grid.h + 1; j++){
if((num) pos.x > (((num) grid.scale * i) + (num) grid.x) && (num) pos.x < (((num) grid.scale * i)) + (num) grid.scale + (num) grid.x && (num) pos.y > (((num) grid.scale * j) + (num) grid.y) && (num) pos.y < (((num) grid.scale * j)) + (num) grid.scale + (num) grid.y){
if(tiles[i][j] == 1){
tiles[i][j] = 0;
} else {
tiles[i][j] = 1;
};
if(pauseState == false) {
guiFunction("toggleTimer"); # Stops timer if grid is clicked
};
}
}
}
}
if (button == 3){
mouseReleased = false;
}
}
public action onMouseUp(num button, num x, num y) {
mouseReleased = true;
first = false;
}
public action onKeyPress(str key, num code) {
if(code == 39 || code == 68){ # D or Right Arrow
tick();
};
if(code == 32){ # Space
guiFunction("toggleTimer");
};
if(code == 8){ # Backspace
guiFunction("clearGrid");
};
if(code == 38 || code == 87){ # W or Up Arrow
guiFunction("addTime");
};
if(code == 40 || code == 83){ # S or Down Arrow
guiFunction("removeTime");
};
}
public action onMouseScroll(num dir) {
if (dir == 1){ # Scroll up -- Zoom in
guiFunction("scaleIncrease");
}
if (dir == -1){ # Scroll up -- Zoom out
guiFunction("scaleDecrease");
}
}
public action onKeyHeld(str key, num code) {}
public action onKeyUp(str key, num code) {}
public action onNetworkMessage(str id, obj data) {}
Server Script
# Server Script runs only on Hosted server & not in test mode
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
}
# Runs every game tick
public action update(num delta) {
}
# Player spawns in
public action onPlayerSpawn(str id) {
}
# Player update
public action onPlayerUpdate(str id, num delta, static obj inputs) {
}
# Called from Custom Trigger Action
public action onCustomTrigger(str playerID, str customParam) {
}
# Server receives network message
public action onNetworkMessage(str id, obj data, str playerID) {
}
# When a player leaves the server
public action onPlayerLeave(str playerID) {
}
Custom Geometry
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
# Runs when the game starts
public action start() {
# Create Custom Geometry vertices:
GAME.SCENE.addCustom("", "#00ffff", num[
0.5, 0.5, 0.5,
-0.5, 0.5, 0.5,
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
0.5, 0.5, -0.5,
-0.5, 0.5, -0.5,
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5
], 0, 10, 0, 10, 10, 10);
}
Attaching
Client Script
# Client Script runs only on the client
# KrunkScript Copyright (C) Yendis Entertainment Pty Ltd
#
# Add custom actions here
obj leftPalm = {};
obj rightPalm = {};
obj leftCube = {};
obj rightCube = {};
# Player spawns in
public action onPlayerSpawn(str id) {
obj pl = GAME.PLAYERS.getSelf();
if (notEmpty pl) {
obj dat = (obj) pl.getAsset();
# GET BONES
leftPalm = (obj) dat.getBone("Palm2L");
rightPalm = (obj) dat.getBone("Palm2R");
# CREATE CUBES TO ATTACH
leftCube = GAME.SCENE.addCube("13075", "#00ffff", 0, 0, 0, 0.01, 0.01, 0.01, {textureStretching:true});
rightCube = GAME.SCENE.addCube("13075", "#ff0000", 0, 0, 0, 0.01, 0.01, 0.01, {textureStretching:true});
# ATTACH TO PALMS
leftCube.attachTo(leftPalm, 0, 0, 0);
rightCube.attachTo(rightPalm, 0, 0, 0);
}
}