Nekoscript
介绍
NekoScript 是为 Nekoplay 游戏开发设计的自定义脚本语言。其语法轻量、表达性强,对于熟悉 JavaScript 的开发者来说很容易上手,同时包含专为游戏逻辑设计的独特特性。
目录
语法
变量
数据类型
运算符
条件语句
循环
游戏导向
函数
数据结构
常量
JavaScript 对象和函数
语法 →
语法
注释
Nekoscript 使用与其他语言如 JavaScript 或 C 相同的注释风格:
// 单行注释
/*
多行
注释
*/
/**
MEOW: TODO: FIXME: NOTE:
BUG: OPTIMIZE: HACK: XXX:
@example {type} Name - https://jsdoc.app/
*/
大小写敏感
NekoScript 区分大小写。这意味着 Variable, variable 和 VARIABLE 是三个不同的标识符。
行结束
不需要在每行末尾使用 ;。解释器认为换行足以分隔指令,除非在可能产生歧义的上下文中。
{
foo()
bar()
}
{
foo(); bar();
}
变量 →
变量
在 NekoScript 中有两种主要的变量声明方式:local 和 global。与 JavaScript 不同,不使用 let, const 或 var。
local
用于声明作用域限于定义它们的块(如函数或控制结构 if, for 等)的变量。
此变量仅存在于定义它的块内。
func greet() {
local myVariable = "Hello "
local my_variable = "World!"
meow(myVariable + my_variable)
}
greet()
global
用于声明可以从代码任何部分访问的变量。
global group_name = "BlackPink"
global name_list = ["Jennie", "Jisoo", "Rose", "Lisa"]
global group = {
name: group_name,
members: name_list,
}
从 Nekoplay 1.3.0 开始,您可以在不使用 global 关键字的情况下声明全局变量。
group_name = "Itzy"
leader_name = "Yeji"
多重定义
可以同时赋值多个变量,甚至可以交换它们的值
local x, y = $camera
a, b, = b, a
重新赋值
变量可以在之后重新赋值:
local player_name
global score = 0 // No puedes definir globales vacias;
player_name = "Miku"
score = 75
分配随机值 ?[]
您可以直接为变量分配随机值。
local group_name = ?['BlackPink', 'Itzy', 'TripleS']
meow(group_name)
local songs = ['Pink Venom', 'Swipe', 'Generation']
defaultSong = ?songs
self
Nekoscript utiliza la palabra clave para referirse al contexto actual de objeto o función.
player = {
name: "Lia",
song: "Blossom",
current_song: self.song,
}
数据类型 →
数据类型
NekoScript 使用多种数据类型,与其他语言类似,但在书写和使用方式上有一些关键差异。
数字
数字可以是整数或小数,按常规方式书写:
myNumber = 69
myDecimal = 4.20
文本 (字符串)
字符串可以用单引号、双引号或模板字面量书写:
global group = ["Yeji", "Lia", "Ryujin", "Chaeryeong", "Yuna"]
local current_song = 'Dalla Dalla'
用单引号书写的字符串可以使用${}访问对象
local song = "Wannabe"
meow('Current Song: ${song}')
// Current Song: Wannabe
Single quotes can also be used to write multiline strings
song_lyrics = 'I’m so bad bad 차라리 이기적일래
눈치 보느라 착한 척
상처받는 것보다 백번 나아'
布尔值
NekoScript 使用标准布尔值:true 和 false
settings.music = false
player.isAlive = true
数组 (Arrays) 和对象
可以定义数组(列表)和对象(字典或映射),我们将在第8节深入探讨。现在先看一个简单示例:
local names = ["Jennie", "Jisoo", "Chaeyoung", "Lalisa"]
local maknae = {
name: "Yuna"
birthday: "9 of December"
}
空值 / 未定义
NekoScript 基于 JavaScript,使用 null 和 undefined。
task = null
settings.song = undefined
typeof - 查询对象的类型
您可以使用typeof查询对象的类型
typeof <object>
local songs = ["Generation", "Rising", "Dimension", "Colorful"]
typeof songs
// array
以字符串形式返回类型:
"boolean"
"string"
"number"
"array"
"object"
"function"
"image"
"audio"
"input"
"button"
"progress"
"body"
"material"
"joint"
运算符 →
运算符
NekoScript 提供了一系列运算符,用于执行数学、逻辑和比较操作。
算术运算符
用于执行基本数学运算:
+加法a + b
-减法a - b
*乘法a * b
/除法a / b
%取模a % b
**幂运算a ** b
比较运算符
用于比较值
==相等a == b
!=不等a != b
>大于a > b
<小于a < b
>=大于等于a >= b
<=小于等于a < b
逻辑运算符
用于组合条件和布尔值:
&&与a && b
||或a || b
!非!a
赋值运算符
赋值运算符将操作与重新赋值结合起来:
=简单赋值
+=相加并赋值
-=相减并赋值
*=相乘并赋值
/=相除并赋值
条件语句 →
条件语句
条件结构允许仅在满足特定条件时执行代码的某些部分。
if
评估一个条件,如果为真则执行代码块:
if <condition> { <block> }
if score > 999 {
endLevel("Perfect!");
}
elseif
如果第一个条件不满足,允许评估第二个条件:
} elseif <condition> { <block> }
if score > 999 {
endLevel("Perfect!");
} else if score > 599 {
endLevel("Good!");
}
else
如果前面的条件都不满足,则执行一个代码块:
} else { <block> }
if level.ready {
player.animation('Lets a go!')
} else {
player.animation('idle')
}
otherwise
功能类似于else但更正式。
} otherwise { <block> }
if level.ready {
player.greet('Good Morning!')
} otherwise {
player.animation('salute')
}
switch
评估同一条件的多个情况。与case、default和break一起使用
▹case定义一个可能的操作
▹default定义默认操作
▹break停止检查其他情况
switch <expression> {
case <expression>:
<block>
break;
default:
<block>;
}
local group_name = "TripleS"
global number_of_members = 0
switch (group_name) {
case "BlackPink":
number_of_members = 4
break;
case "Itzy":
number_of_members = 5
break;
case "TripleS":
number_of_members = 24
break;
}
meow('${group_name} has ${number_of_members} members!')
// TripleS has 24 members!
循环 →
循环
NekoScript 提供了多种循环结构来多次执行代码块。
经典 for
允许手动控制变量的初始化、条件和更新。用逗号分隔,不用分号,也不带括号:
for <initial operation>, <condition>, <repeated operation> { <block> }
for local i = 0, i < 10, i++ {
print(i);
}
数组 for...in
遍历数组元素:
for <element> in <array> { <block> }
for <element>, <index> in <array> { <block> }
for name in game.players {
print(name);
}
for player, position in game.leaderboard {
player.rank = calculateRank(position)
}
for...of
遍历对象的属性:
for <key> of <object> { <block> }
for <key>, <value> of <object> { <block> }
for icon of game.powerups {
draw.image(icon.texture, icon.dim)
}
for name, mission of game.missions {
print(`${name}: ${mission}`);
}
while
只要条件满足就重复代码块:
while <condition> { <block> }
while score < 1000 {
score++
}
repeat
是 for 和 while 的简单替代方案,语法非常简洁:
repeat <times> { <block> }
repeat <times>, <identifier> { <block> }
repeat 10 {
player.life++
}
repeat 12, factor {
score += factor
}
forever
重复该块直到时间结束或程序崩溃:
forever { <block> }
local i = 0;
forever {
i += 1
}
Usar forever con precaución ⚠: Congelará tu navegador.
游戏导向 →
游戏导向
Nekoscript包含利用@dt(增量时间)访问设计的游戏开发控制结构。
sometimes
以50%的概率执行代码块。
sometimes { <block> }
sometimes {
spawnPowerUp()
}
maybe
以自定义概率执行代码块。
maybe <probability> { <block> }
maybe 20 {
spawnPowerUp()
} // 20% 概率
maybe 75 {
spawnPowerUp()
} // 75% 概率
every
允许每几秒执行一次代码块。
every <seconds> { <block> }
every 3 {
background.pulse()
}
every 10 {
dynamicTiles.flip()
} otherwise {
dynamicTiles.move()
}
wait
延迟执行一个块,直到连续秒数过去。
wait <seconds> { <block> }
wait 5 {
levelStart()
}
wait 200 {
spawnBoss()
} otherwise {
updateEnemies()
}
message
定义全局状态
message <message>
message "levelReady"
remove..."message"
移除全局状态
remove <message>
remove "levelLoading"
on..."message"
检查全局状态
on <message> { <block> }
on "levelReady" {
startStage()
}
once
仅执行一次块
once { <block> }
once {
resetCounters()
}
once..."message"
通过检查全局状态仅执行一次块。
once <message> { <block> }
once "levelLoading" {
spawnEnemies()
}
twice
尽管名称如此,该指令像once一样只执行一次块,但如果不是连续调用,可以再次执行。
twice { <block> }
twice {
player.jump()
}
函数 →
函数
NekoScript 允许以清晰简洁的方式定义和使用函数。函数可以有名称或匿名(无名称),并且可以接受任意数量的参数。
声明函数
在 nekoscript 中使用关键字 func 声明函数
purr <name>(<arguments>) { <function> }
func toggleColor(col) {
set.color(col == #f0f ? #000 : #f0f)
}
toggleColor(#f0f)
purr toggleColor(col) {
set.color(col == #f0f ? #000 : #f0f)
}
toggleColor(#f0f)
匿名函数
也可以定义没有名称的函数,适用于将它们赋值给变量或对象:
purr (<arguments>) { <function> }
global transform_size = purr (size, scale) {
return size * scale;
}
local finalSize = transform_size(64, 8);
global transform_size = func (size, scale) {
return size * scale;
}
local finalSize = transform_size(64, 8);
箭头函数 (arrow functions)
<arguments> => { <function> }
NekoScript 允许使用箭头式语法 (=>) 定义快速函数。这种形式非常适合回调或单行函数:
local addScore = score => score + 10;
也适用于多个参数:
local calculateDamage = (attack, defense) => attack - defense;
在像 .each 这样的方法中,你可以这样写:
global name_list = ["Lis", "米歇尔", "지운", "ちぇりー"]
name_list.each(name => {
print(`${name} is ready!`);
});
async & await
在 NekoScript 中可以使用关键字 async 定义异步函数。这些函数允许等待需要时间的操作。
只有当你在其中使用 await 时,async 函数才有用。只能在 async 函数内使用 await。async 函数的结果是一个 promise。
async func fetchPlayerData(player_id) {
local playerData = await get.json(server + player_id);
return parseData(playerData)
}
fetchPlayerData('Xxtnkii69xX')
return
在函数内部使用,指示该函数应返回的值。当执行 return 时,函数立即停止并返回该值到调用处。
purr get_performance(score, bonus) {
return 100 / (score + bonus);
}
local playerPerformance = get_performance(player.score, level.bonus);
数据结构 →
数据结构
NekoScript 提供两种主要数据结构:数组(也称为列表)和对象(具有键值属性的结构)。这些结构允许存储和组织信息。
数组 (arrays)
数组是有序的值集合。使用方括号 [ ] 定义,元素用逗号 , 分隔:
global name_list = ["Yeji", "Lia", "Ryu", "Chaeryeong", "Yuna"]
可以通过索引访问其元素,从0开始:
name_list[3]
// Chaeryeong
也可以在固定索引处定义元素:
name_list[3] = "Ryujin";
methods
[].push()
在末尾添加元素并返回新长度
[1, 2, 3].push(4)
// [1, 2, 3, 4]
// 4
[].pop()
删除最后一个元素并返回它
[1, 2, 3].pop()
// [1, 2]
// 3
[].shift()
删除第一个元素并返回它
[1, 2, 3].shift()
// [2, 3]
// 1
[].unshift()
在开头添加元素并返回新长度
[1, 2, 3].unshift(0)
// [0, 1, 2, 3]
// 4
[].join()
将元素连接成字符串
["a", "b", "c"].join('-')
// a-b-c
[1, 2, 3].join('_')
// 1_2_3
[].slice()
返回列表的一部分
[1, 2, 3, 4, 5].slice(1, 3)
// [2, 3]
[].splice()
删除或替换元素并返回被删除的元素
[1, 2, 3, 4, 5].splice(2, 1, 'three')
// [1, 2, 'three', 4, 5]
// 3
[1, 2, 3, 4, 5].splice(1, 2)
// [1, 4, 5]
// [2, 3]
[].includes()
检查数组是否包含某个值
['alpha', 'bravo', 'charlie'].includes('bravo')
// true
[].map()
返回对每个元素应用函数结果的新数组
[1, 2, 3].map(x => x * 2)
// [2, 4, 6]
[].filter()
返回满足条件的元素组成的新数组
[1, 2, 3, 4].filter(x => x * 2 == 4)
// [2]
[].find()
返回满足条件的第一个元素
[1, 2, 3, 4].find(x => x == 2)
// 2
[].each()
为每个元素执行一个函数
[1, 2, 3, 4].each(n, index => print('Number' + n + 'at index: ' + index))
properties
[].length
返回元素的数量
['a', 'b', 'c'].length
// 3
对象
对象是属性的集合,用花括号 {} 定义,键和值用冒号 : 定义并用逗号 , 分隔:
local inventory = {
weapon: "Magic Wand",
armor: "Iron Armor",
potions: ["Health", "Mana"],
}
可以使用点号 . 访问属性:
inventory.weapon
// Magic Wand
inventory["armor"]
// Iron Armor
也可以修改它:
inventory.weapon = "Dark Sword"
这些结构可以嵌套、组合并自由用作函数参数、计算结果或游戏状态的一部分。
array = [
{},
[],
{};
]
object = {
array: [],
object: {},
}
常量 →
引擎常量
NekoScript 包含一系列内置的特殊常量。这些常量始终可用,并提供对系统、游戏、场景信息和其他内部实用程序的直接访问。
变量
这些变量表现为实时值。你可以随时读取它们以获取引擎或游戏当前状态的信息。
$dt增量时间:自上一帧以来经过的时间。Number
$fps每秒帧数 (Frames Per Second)。Number
$camera当前摄像机位置,作为数组 [x, y]。Array
$scene当前场景的名称(字符串)。String
$time自游戏启动以来的总时间(秒)。Number
$sceneTime自当前场景加载以来的时间(秒)。Number
$keys按下的键列表(按名称:"ArrowUp"、"z"等)。Array
$scancodes按下的键列表(按物理键盘代码)。Array
$messages消息列表。Array
$left屏幕的最左侧点。Number
$right屏幕的最右侧点。Number
$top屏幕的最高点。Number
$down屏幕的最低点。Number
$width游戏的水平尺寸。Number
$height屏幕的垂直尺寸。Number
这些变量可以直接在任何表达式中使用。
函数
这些函数允许执行与引擎相关的特殊转换或计算。
$position(x, y)将固定坐标转换为引擎坐标(用于摄像机、缩放、比例等)。Array
$size(w, h)根据系统比例调整大小。Array
Javascript →
可用的 JavaScript 函数和对象
NekoScript 构建在 JavaScript 基础之上,因此许多标准语言的函数和对象都可用。
标准函数
这些可能是游戏开发中最有用的函数。
Math用于数学运算的静态属性和方法。Math ↗
String用于操作文本的对象。String ↗
Number用于操作数值的常量和方法。Number ↗
JSON用于将值转换为 Json 和从 Json 转换的静态方法。Json ↗
RegExp操作文本模式。RegExp ↗
Date表示时间中的一个时刻。Date ↗
Boolean表示逻辑运算结果的值Boolean ↗
Array多个值(变量、对象、其他数组)的集合,使用单一名称Array ↗
Object具有键名的值或实体(变量、对象、数组、函数、方法)的集合。Object ↗
示例:
local gameSave = {
score: Math.round(99.5),
isComplete: false, // Boolean
name: getMember().replace(/Member\s*/, ""), // RegExp
playerLevel: Number.parseInt("99"),
keyItems: ["Microphone", "Tea Set"], // Array
lastSave: new Date(),
}; // Object
local savedDataString = JSON.stringify(gameSave);
基本全局函数
这些函数在 JavaScript 中可用:
isFinite()
inNaN()
parseFloat()
parseInt()
特殊值
undefined
NaN
Infinity
全局对象
ObjectBooleanSymbol
数字和日期NumberBigIntMathDate
文本和正则表达式StringRegExp
结构ArrayMapSetWeakMapWeakSet
特殊类型Int8ArrayUint8ArrayUint8ClampedArrayInt16ArrayUint16ArrayInt32ArrayUint32ArrayBigInt64ArrayBigUint64ArrayFloat32ArrayFloat64Array
并发PromiseAsyncFunction
实用工具JSONReflectProxyIntl
标准错误
Error
TypeError
ReferenceError
SyntaxError
RangeError
URIError
EvalError
AggregateError