Scripting in Construct 3

run on the loading screen

runOnStartup(async runtime =>
{
  // This code runs on startup with
  // the 'runtime' variable available
});

Runtime events

runOnStartup(async runtime =>
{
	runtime.addEventListener("beforeprojectstart", () => OnBeforeProjectStart(runtime));
});

async function OnBeforeProjectStart(runtime)
{
	runtime.addEventListener("tick", () => Tick(runtime));
}

function Tick(runtime)
{
	// Code to run every tick
}

Function cheatsheet

一些封装好的函数,复制粘贴到事件表,开箱即用。

检查精灵是否存在动画名

Test ObjectType Animation Exist Functions.hasAnimation(Sprite.ObjectTypeName, "Default")

{"is-c3-clipboard-data":true,"type":"events","items":[{"functionName":"hasAnimation","functionDescription":"","functionCategory":"","functionReturnType":"number","functionCopyPicked":false,"functionIsAsync":false,"functionParameters":[{"name":"objectType","type":"string","initialValue":"","comment":""},{"name":"name","type":"string","initialValue":"","comment":""}],"eventType":"function-block","conditions":[],"actions":[{"type":"script","script":"runtime.setReturnValue(runtime.objects[localVars.objectType].getAllAnimations().some(a => a.name.includes(localVars.name)) ? 1 : 0);"}]}]}

检查场景名称是否存在

Test Layout Name Exist Functions.hasLayoutName("Layout 1")

{"is-c3-clipboard-data":true,"type":"events","items":[{"functionName":"hasLayoutName","functionDescription":"","functionCategory":"","functionReturnType":"number","functionCopyPicked":false,"functionIsAsync":false,"functionParameters":[{"name":"name","type":"string","initialValue":"","comment":""}],"eventType":"function-block","conditions":[],"actions":[{"type":"script","script":"try {\n    runtime.getLayout(localVars.name);\n    runtime.setReturnValue(1);\n} catch (error) {\n    runtime.setReturnValue(0);\n}"}]}]}
方法二: 遍历所有对象

{"is-c3-clipboard-data":true,"type":"events","items":[{"functionName":"hasLayoutName","functionDescription":"","functionCategory":"","functionReturnType":"number","functionCopyPicked":false,"functionIsAsync":false,"functionParameters":[{"name":"name","type":"string","initialValue":"","comment":""}],"eventType":"function-block","conditions":[],"actions":[{"type":"script","script":"const result = +runtime.getAllLayouts().some(({ name }) => name.includes(localVars.name));\n\nruntime.setReturnValue(result);"}]}]}

获取对象Effect参数

Get Object effect parameter Functions.getEffectParameter(Sprite.UID, "AdjustHSL", 0)

{"is-c3-clipboard-data":true,"type":"events","items":[{"functionName":"getEffectParameter","functionDescription":"","functionCategory":"","functionReturnType":"any","functionCopyPicked":false,"functionIsAsync":false,"functionParameters":[{"name":"UID","type":"number","initialValue":"0","comment":""},{"name":"effectName","type":"string","initialValue":"","comment":""},{"name":"parameterIndex","type":"number","initialValue":"0","comment":""}],"eventType":"function-block","conditions":[],"actions":[{"type":"script","script":"const inst = runtime.getInstanceByUid(localVars.UID);\n\nconst effect = inst.effects.find(e => e.name === localVars.effectName);\n\nif (!effect) return;\n\nconst param = effect.getParameter(localVars.parameterIndex);\n\nconst result = Array.isArray(param) ? param.map(v => Math.round(v * 255)).join(',') : param * 100;\n\nruntime.setReturnValue(result);"}]}]}

Debug


StringSub("{0} objects | {1} FPS | {2}% CPU | {3}% GPU | {4}", objectcount,PlatformInfo.TicksPerSecond, roundToDp(cpuutilisation*100,1), roundToDp(gpuutilisation*100,1), PlatformInfo.Renderer)

expDecay

Function.expDecay(0, 100, 25)

{"is-c3-clipboard-data":true,"type":"events","items":[{"functionName":"expDecay","functionDescription":"","functionCategory":"","functionReturnType":"number","functionCopyPicked":false,"functionIsAsync":false,"functionParameters":[{"name":"a","type":"number","initialValue":"0","comment":""},{"name":"b","type":"number","initialValue":"1","comment":""},{"name":"decay","type":"number","initialValue":"16","comment":"1-25"}],"eventType":"function-block","conditions":[],"actions":[{"id":"set-function-return-value","objectClass":"Functions","parameters":{"value":"b + (a-b) * exp(-decay*dt)"}}]}]}

发送信号

runtime.signal("tag");

等待信号

runtime.waitForSignal("tag");

全局变量(Global variable)

用脚本获取全局变量。直接对它进行赋值操作也是可以的。

runtime.globalVars.Variable1

局部变量(Local variable)

在事件条范围内,用脚本获取本地变量。备注,函数的传入参数(`Function parameter`) 也是通过它来获取。

localVars.Variable2

提示,在某些情况下,变量名称可能不是有效的标识符,例如可能跟保留关键字冲突了。此时你还可以用字符串来引用这个变量。

runtime.globalVars["Variable1"]
localVars["Variable2"]
instVars["Variable3"]

调用函数(call function)

调用事件表的自定义函数

调用函数

runtime.callFunction("add", 1)

设置函数返回值

runtime.setReturnValue(value);

获取函数的参数

localVars.Parameter0

解构赋值

const { a, b } = localVars;

在 HTML 调用 C3 Function

c3_callFunction("Function1");

通过字符串调用函数

const function = "AddValue, 123, ABC"

runtime.callFunction(...localVars["command"]
.split(",").map(e => e.trim()))
.map(e => (isNaN(e) ? e : Number(e)))
);  

触发事件

const event = C3.Event("")
const object = runtime.objects.Sprite.getFirstInstance();
object.dispatchEvent(event);

挑选对象

按UID挑选

runtime.getInstanceByUid();

按名称挑选

runtime.objects["Sprite"];

获取第一个实例对象(First Instance Object)

runtime.objects.Sprite.getFirstInstance()

获取所有实例对象(All Instances objects)

runtime.objects.Sprite.getAllInstances()

获取实例对象的实例变量

const player = runtime.objects.Sprite.getFirstInstance();
const score = player.instVars["Score"];
// console.log(score)

获取实例对象的滤镜参数

这里封装了一个 function,返回 any 类型。带有 3 个参数: UID, effectName, parameterIndex
const inst = runtime.getInstanceByUid(localVars.UID);

const effect = inst.effects.find(e => e.name === localVars.effectName);

if (!effect) return;

const param = effect.getParameter(localVars.parameterIndex);

const result = Array.isArray(param) ? param.map(v => Math.round(v * 255)).join(',') : param;

runtime.setReturnValue(result);
{"is-c3-clipboard-data":true,"type":"events","items":[{"functionName":"getEffectParameter","functionDescription":"","functionCategory":"","functionReturnType":"any","functionCopyPicked":false,"functionIsAsync":false,"functionParameters":[{"name":"UID","type":"number","initialValue":"0","comment":""},{"name":"effectName","type":"string","initialValue":"","comment":""},{"name":"parameterIndex","type":"number","initialValue":"0","comment":""}],"eventType":"function-block","conditions":[],"actions":[{"type":"script","script":"const inst = runtime.getInstanceByUid(localVars.UID);\n\nconst effect = inst.effects.find(e => e.name === localVars.effectName);\n\nif (!effect) return;\n\nconst param = effect.getParameter(localVars.parameterIndex);\n\nconst result = Array.isArray(param) ? param.map(v => Math.round(v * 255)).join(',') : param;\n\nruntime.setReturnValue(result);"}]}]}

更改实例对象的滤镜参数

const player = runtime.objects.Sprite.getFirstInstance();
player.effects[0].setParameter(0, 100);

获取实例对象的行为属性(Instance behaviors)

const player = runtime.objects.Sprite.getFirstInstance();
const playerBehavior = player.behaviors.Platform;
// console.log(playerBehavior.maxSpeed)

在某些情况下,有些名称可能不是有效的标识符,此时,还可以用字符串来引用。

instance.behaviors["8Direction"]["maxSpeed"]

遍历所有实例对象的实例变量

遍历所有 Sprite 对象的实例变量 Score,同时销毁所有变量小于 60 的实例。

for (const obj of runtime.objects.Sprite.instances())
{
  const score = obj.instVars["Score"];

  if (score <= 60) 
  {
    obj.destroy();
  }
}

在某些情况下,有些名称可能不是有效的标识符,此时,还可以用字符串来引用。

const textObject = runtime.objects.Text;
const textInstance = textObject.getFirstPickedInstance();
textInstance.text = "Hello World"

也可以通过 filter来实现条件筛选。

例如下方的代码,用于选出布尔型 foobar 是开启状态下 obj 实例对象。

const instances = runtime.objects.Sprite.getAllInstances();
const picked = instances.filter(obj => obj.instVars.foobar); // filter based on instance Variable

获取所有满足条件的实例

// 获取所有 Sprite 实例
const instances = runtime.objects.Sprite.getAllInstances();
// 使用筛选逻辑过滤实例
const picked = instances.filter(obj => {
  const score = obj.instVars["Score"];   // 获取每个实例的分数
  return score <= 60;   // 返回分数小于或等于60的实例
});

实例对象场景属性

文档

获取碰撞框

const instance = runtime.objects.Sprite.getFirstInstance();
console.log(instance.getBoundingBox())

检测对象是否可以被另一个对象完全重叠

for (const inst of runtime.objects.Sprite.pickedInstances()) {
	const instBBox = inst.getBoundingBox();
    const candidates = runtime.collisions.getCollisionCandidates(runtime.objects.Sprite2, instBBox)
    for (const candidateInst of candidates) {
		const candidateBBox = candidateInst.getBoundingBox();
		if (candidateBBox.left <= instBBox.left && candidateBBox.right >= instBBox.right && candidateBBox.top <= instBBox.top && candidateBBox.bottom >= instBBox.bottom) {
			inst.destroy()
		}
	}
}

编辑文本

const textObject = runtime.objects.Text;
const textInstance = textObject.getFirstPickedInstance();
  
textInstance.text = "Hello World"
}

AJAX

AJAX 加载文件

在脚本中代替事件表AJAX加载文件的操作。

async function OnBeforeProjectStart(runtime)
  {
    const InfoData = await runtime.assets.fetchJson('info.json')
    runtime.objects.JSON.getFirstInstance().setJsonDataCopy(InfoData);
  }
}
const response = await fetch("file.json");
const myJson = await response.json();
console.log("Fetched JSON: ", myJson);

JSON

JSON 读取数据

const dataInst = runtime.objects.JSON.getFirstInstance();
  const data = dataInst.getJsonDataCopy();
console.log(data['Name'])

JSON 更改数据

这里引用了一个实例变量 name
const dataInst = runtime.objects.JSON.getFirstInstance();
const data = dataInst.getJsonDataCopy();
data.name = localVars.name;
dataInst.setJsonDataCopy(filterItems);

Array

读取 Array 数据

const myArray = runtime.objects.Array.getFirstInstance();
myArray.setAt(0, 1)
console.log(myArray.getAt(0, 0))

Dictionary

获取 Dictionary 数据

const myData = runtime.objects.Dictionary.getFirstInstance().getDataMap();
console.log(MyData.get('Key'))
遍历字典数据
const data = runtime.objects.Dictionary.getFirstInstance().getDataMap();
for (const [key, value] of data) {
	console.log(`${key}: ${value}`);
}

其他

转置数组
const array = JSON.parse(localVars.arrayJson);
const [width, height, depth] = array["size"];
array["size"] = [height, width, depth];
array["data"] = array["data"][0].map((_, y) => array["data"].map(row => row[y]));
runtime.setReturnValue(JSON.stringify(array));
将C3的Array文件转换成JSON 在一个 Function 中的脚本块,Function 带有一个 String 类型的参数 c3ArrayFile,用于从在 AJAX 加载 Array文件时,将 AJAX.LastData 数据传入到这个 Function 中。 Function 具有 String 类型的返回值,传出的是一个可被解析为 JSON 的字符串,用于在 JSON 对象中使用 Parse JSON string 动作时,使用 Functions.convertC3ArrayToJson(AJAX.LastData) 进行解析。
const { size, data } = JSON.parse(localVars.c3ArrayFile);
const [, totalRows] = size;
let output = [];
const columnHeaders = data.map(column => column[0][0].trim());
for (let rowIndex = 1; rowIndex < totalRows; rowIndex++) {
	let rowObject = {};
	columnHeaders.forEach((header, columnIndex) => {
		if (!header) return;
		let cellValue = data[columnIndex][rowIndex][0];
		rowObject[header] = typeof cellValue === 'string' ? cellValue.trim() : cellValue;
	});
	if (Object.values(rowObject).some(value => value !== "")) {
		output.push(rowObject);
	}
}
runtime.setReturnValue(JSON.stringify(output));

全局作用域

可以在任何地方使用它,如事件表脚本块、JS文件等,可以用于引用第三方库的初始化对象,并在后续使用。
globalThis.Variable1 = 1

鼠标

获取鼠标的坐标
// getMousePosition(layer);
const [mouseX, mouseY] = runtime.mouse.getMousePosition(0);

键盘

获取键盘的按键
const keyboard = runtime.keyboard;
if (keyboard.isKeyDown("ArrowRight"))
{
	player.x += playerSpeed * dt;
	player.angleDegrees = 0;
}
屏蔽特定按键
window.addEventListener("keydown", e =>
{
	if (e.key === "Tab")
		e.preventDefault();
});

其他函数

辅助函数

生成指定范围内的随机整数
const randInt = (min, max) => {
	return Math.floor(Math.random() * (max - min + 1)) + min;
};
// 例子
let a = randInt(1, 10); // 生成 1 到 10 之间的随机整数
let b = randInt(50, 100); // 生成 50 到 100 之间的随机整数
let c = randInt(-10, 10); // 生成 -10 到 10 之间的随机整数
console.log(c);
Construct by 官方文档
图书馆
编辑