Nekoscript
Introducción
NekoScript es un lenguaje esotérico de scripting diseñado para la creación de videojuegos en Nekoplay. Su sintaxis es ligera, expresiva y familiar para quienes conocen JavaScript, pero incluye características propias que lo hacen más adecuado para la escritura de lógica de juego.
Contenidos
Sintaxis
Variables
Tipos de Datos
Operadores
Condicionales
Loops
Orientado a juegos
Funciones
Estructuras de Datos
Constantes
Objetos y Funciones de Javascript
Sintaxis →
Sintaxis
Comentarios
Nekoscript utiliza el mismo estilo de comentarios que otros lenguajes como javascript o C:
// Comentario de una línea
/*
Comentario
multilinea
*/
/**
MEOW: TODO: FIXME: NOTE:
BUG: OPTIMIZE: HACK: XXX:
@example {type} Name - https://jsdoc.app/
*/
Sensibilidad a mayúsculas
NekoScript es sensible a mayúsculas y minúsculas. Esto significa que Variable, variable y VARIABLE son tres identificadores distintos.
Final de línea
No es obligatorio utilizar ; al final de cada línea. El intérprete considera que una nueva línea es suficiente para separar instrucciones, salvo en contextos ambiguos.
{
foo()
bar()
}
{
foo(); bar();
}
Variables →
Variables
En NekoScript existen dos formas principales de declarar variables: local y global. A diferencia de JavaScript, no se utilizan let, const o var.
local
Se utiliza para declarar variables cuyo alcance está limitado al bloque donde se definen, como una función o una estructura de control (if, for, etc).
Esta variable solo existe dentro del bloque donde fue definida.
func greet() {
local myVariable = "Hello "
local my_variable = "World!"
meow(myVariable + my_variable)
}
greet()
global
Se utiliza para declarar variables accesibles desde cualquier parte del código.
global group_name = "BlackPink"
global name_list = ["Jennie", "Jisoo", "Rose", "Lisa"]
global group = {
name: group_name,
members: name_list,
}
Desde Nekoplay 1.3.0 puedes declarar variables globales sin la palabra global.
group_name = "Itzy"
leader_name = "Yeji"
Definiciones multiples
Puedes asignar varias variables a la vez, incluso intercambiando sus valores
local x, y = $cam
a, b, = b, a
Reasignar
Las variables pueden ser reasignadas posteriormente:
local player_name
global score = 0 // No puedes definir globales vacias;
player_name = "Miku"
score = 75
Asignar un valor random ?[]
Puedes asignar un valor random a una variable directamente.
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,
}
Tipos de Datos →
Tipos de Datos
NekoScript utiliza una variedad de tipos de datos, similares a los de otros lenguajes, pero con algunas diferencias clave en su forma de escritura y uso.
Números
Los números pueden ser enteros o decimales, escritos de forma habitual:
myNumber = 69
myDecimal = 4.20
Texto (Strings)
Las strings se escriben con comillas dobles o simples:
global group = ["Yeji", "Lia", "Ryujin", "Chaeryeong", "Yuna"]
local current_song = 'Dalla Dalla'
Las strings escritas en comillas simples pueden acceder a objetos usando ${}
local song = "Wannabe"
meow('Current Song: ${song}')
// Current Song: Wannabe
Las comillas simples también sirven para escribir strings multilinea
song_lyrics = 'I’m so bad bad 차라리 이기적일래
눈치 보느라 착한 척
상처받는 것보다 백번 나아'
Booleanos
NekoScript utiliza los valores booleanos estándar: true & false
settings.music = false
player.isAlive = true
Arreglos (Arrays) y objetos
Se pueden definir arreglos (listas) y objetos (diccionarios o mapas), lo veremos a profundidad en la sección 8. Por ahora, un ejemplo rápido:
local names = ["Jennie", "Jisoo", "Chaeyoung", "Lalisa"]
local maknae = {
name: "Yuna"
birthday: "9 of December"
}
Nulo / indefinido
NekoScript está basado en JavaScript, utiliza null y undefined.
task = null
settings.song = undefined
typeof - Consultar el tipo de un objeto
Puedes consultar el tipo del objeto usando typeof
typeof <object>
local songs = ["Generation", "Rising", "Dimension", "Colorful"]
typeof songs
// array
Devuelve el tipo como string:
"boolean"
"string"
"number"
"array"
"object"
"function"
"image"
"audio"
"input"
"button"
"progress"
"body"
"material"
"joint"
Operadores →
Operadores
NekoScript ofrece una serie de operadores que permiten realizar operaciones matemáticas, lógicas y de comparación.
Operadores Aritméticos
Se utilizan para realizar operaciones matemáticas básicas:
+Sumaa + b
-Restaa - b
*Multiplicacióna * b
/Divisióna / b
%Residuoa % b
**Potenciaa ** b
Operadores de Comparación
Se utilizan para comparar valores
==Igualdada == b
!=Desigualdada != b
>Mayor quea > b
<Menor quea < b
>=Mayor o igual quea >= b
<=Menor o igual quea < b
Operadores Lógicos
Para combinar condiciones y valores booleanos:
&&Ya && b
||Oa || b
!Not!a
Operadores de Asignación
Los operadores de asignación combinan operaciones con reasignación:
=Asignación simple
+=Suma y asigna
-=Resta y asigna
*=Multiplica y asigna
/=Divide y asigna
Condicionales →
Condicionales
Las estructuras condicionales permiten ejecutar ciertas partes del código solo si se cumplen determinadas condiciones.
if
Evalúa una condición y ejecuta el bloque si esta es verdadera:
if <condition> { <block> }
if score > 999 {
endLevel("Perfect!");
}
elseif
Permite evaluar una segunda condición si la primera no se cumple:
} elseif <condition> { <block> }
if score > 999 {
endLevel("Perfect!");
} else if score > 599 {
endLevel("Good!");
}
else
Ejecuta un bloque si ninguna de las condiciones anteriores se cumplió:
} else { <block> }
if level.ready {
player.animation('Lets a go!')
} else {
player.animation('idle')
}
otherwise
Funciona similar a else pero es más formal.
} otherwise { <block> }
if level.ready {
player.greet('Good Morning!')
} otherwise {
player.animation('salute')
}
switch
Evaluar múltiples casos de una misma condición. Se usa junto con case, default & break
▹case define una posible acción
▹default define la acción por defecto
▹break termina de comprobar otros casos
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!
Loops →
Loops
NekoScript ofrece una variedad de estructuras de repetición para ejecutar bloques de código múltiples veces.
for clásico
Permite controlar manualmente la inicialización, condición y actualización de una variable:
for <initial operation>, <condition>, <repeated operation> { <block> }
for local i = 0, i < 10, i++ {
print(i);
}
for...in para Arrays
Itera sobre los elementos de un arreglo:
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 para objetos
Recorre las propiedades de un objeto:
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
Repite el bloque mientras se cumpla la condición:
while <condition> { <block> }
while score < 1000 {
score++
}
repeat
Es una alternativa simple a for y while, con una sintaxis muy limpia:
repeat <times> { <block> }
repeat <times>, <identifier> { <block> }
repeat 10 {
player.life++
}
repeat 12, factor {
score += factor
}
forever
Repite el bloque hasta el fin del tiempo o hasta que el rpograma se rompa:
forever { <block> }
local i = 0;
forever {
i += 1
}
Usar forever con precaución ⚠: Congelará tu navegador.
Orientado a juegos →
Orientado a juegos
Nekoscript incluye estructuras de control diseñadas para el desarrollo de juegos aprovechando el acceso a @dt (delta time).
sometimes
Ejecutar un bloque de código con probabilidad del 50%.
sometimes { <block> }
sometimes {
spawnPowerUp()
}
maybe
Ejecutar un bloque de código con probabilidad personalizada.
maybe <probability> { <block> }
maybe 20 {
spawnPowerUp()
} // probabilidad del 20%
maybe 75 {
spawnPowerUp()
} // probabilidad del 75%
every
Permite ejecutar un bloque de código cada cierto número de segundos.
every <seconds> { <block> }
every 3 {
background.pulse()
}
every 10 {
dynamicTiles.flip()
} otherwise {
dynamicTiles.move()
}
wait
Retrasa la ejecución de un bloque hasta quue haya pasado una cantidad de segundos continuos.
wait <seconds> { <block> }
wait 5 {
levelStart()
}
wait 200 {
spawnBoss()
} otherwise {
updateEnemies()
}
message
Define un estado global
message <message>
message "levelReady"
remove..."message"
Elimina un estado global
remove <message>
remove "levelLoading"
on..."message"
Comprueba un estado global
on <message> { <block> }
on "levelReady" {
startStage()
}
once
Ejecuta un bloque una sola véz
once { <block> }
once {
resetCounters()
}
once..."message"
Ejecuta un bloque una sola véz comprobando un estado global.
once <message> { <block> }
once "levelLoading" {
spawnEnemies()
}
twice
A pesar del nombre, esta instrucción ejecuta un bloque una sola véz como once pero puede ejecutarse de nuevo si no se llama en secuencia.
twice { <block> }
twice {
player.jump()
}
Funciones →
Funciones
NekoScript permite definir y utilizar funciones de manera clara y concisa. Las funciones pueden tener nombre o ser anónimas (sin nombre), y pueden recibir cualquier número de argumentos.
Declarar un función
En nekoscript se utilizan la palabras clave func y purr para declarar funciones
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)
Funciones anónimas
También se pueden definir funciones sin nombre, útiles para asignarlas a variables u objetos:
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);
Funciones flecha (arrow functions)
<arguments> => { <function> }
NekoScript permite definir funciones rápidas con una sintaxis tipo flecha (=>). Esta forma es ideal para callbacks o funciones de una sola línea:
local addScore = score => score + 10;
También funciona con múltiples parámetros:
local calculateDamage = (attack, defense) => attack - defense;
Y en métodos como .each puedes escribir:
global name_list = ["Lis", "米歇尔", "지운", "ちぇりー"]
name_list.each(name => {
print(`${name} is ready!`);
});
async & await
En NekoScript es posible definir funciones asincrónicas usando la palabra clave async. Estas funciones permiten esperar operaciones que toman tiempo.
las funciones async solo son útiles cuando usas await dentro de ellas. Solo puedes usar await dentro de una función async. El resultado de una función async es una promesa.
async func fetchPlayerData(player_id) {
local playerData = await get.json(server + player_id);
return parseData(playerData)
}
fetchPlayerData('Xxtnkii69xX')
return
Se utiliza dentro de una función para indicar el valor que esa función debe devolver. Cuando se ejecuta un return, la función se detiene inmediatamente y entrega ese valor al lugar donde fue llamada.
purr get_performance(score, bonus) {
return 100 / (score + bonus);
}
local playerPerformance = get_performance(player.score, level.bonus);
Estructuras de datos →
Estructuras de Datos
NekoScript ofrece dos estructuras de datos principales: arreglos (también conocidos como listas) y objetos (estructuras con propiedades clave-valor). Estas estructuras permiten almacenar y organizar información.
Arreglos (arrays)
Un arreglo es una colección ordenada de valores. Se define utilizando corchetes [ ], separando los elementos por comas ,:
global name_list = ["Yeji", "Lia", "Ryu", "Chaeryeong", "Yuna"]
Se puede acceder a sus elementos mediante índices, comenzando desde 0:
name_list[3]
// Chaeryeong
Y también se puede definir un elemento en un index fijo:
name_list[3] = "Ryujin";
methods
[].push()
Agrega un elemento al final y devuelve nueva longitud
[1, 2, 3].push(4)
// [1, 2, 3, 4]
// 4
[].pop()
Elmina el último elemento y lo devuelve
[1, 2, 3].pop()
// [1, 2]
// 3
[].shift()
Elmina el primer elemento y lo devuelve
[1, 2, 3].shift()
// [2, 3]
// 1
[].unshift()
Agrega un elemento al inicio y devuelve nueva longitud
[1, 2, 3].unshift(0)
// [0, 1, 2, 3]
// 4
[].join()
Une los elementos en una string.
["a", "b", "c"].join('-')
// a-b-c
[1, 2, 3].join('_')
// 1_2_3
[].slice()
Devuelve una porción de la lista
[1, 2, 3, 4, 5].slice(1, 3)
// [2, 3]
[].splice()
Eliminar o remplazar elementos y devuelve el elemento eliminado
[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()
Comprobar si una Array contiene un valor
['alpha', 'bravo', 'charlie'].includes('bravo')
// true
[].map()
Devuelve un nuevo array con el resultado de aplicar la función a cada elemento.
[1, 2, 3].map(x => x * 2)
// [2, 4, 6]
[].filter()
Devuelve un nuevo array con los elementos que cumplen la condición.
[1, 2, 3, 4].filter(x => x * 2 == 4)
// [2]
[].find()
Devuelve el primer elemento que cumple con la condición
[1, 2, 3, 4].find(x => x == 2)
// 2
[].each()
Ejecuta una función para cada elemento
[1, 2, 3, 4].each(n, index => print('Number' + n + 'at index: ' + index))
properties
[].length
Devuelve el número de elementos
['a', 'b', 'c'].length
// 3
Objetos
Los objetos son colecciones de propiedades, definidas entre llaves {} con clave y valor definidos con dos puntos : y separados com coma ,:
local inventory = {
weapon: "Magic Wand",
armor: "Iron Armor",
potions: ["Health", "Mana"],
}
Puedes acceder a una propiedad usando el punto . o el nombre:
inventory.weapon
// Magic Wand
inventory["armor"]
// Iron Armor
Y también modificarla:
inventory.weapon = "Dark Sword"
Estas estructuras pueden anidarse, combinarse y usarse libremente como argumentos de funciones, resultados de cálculos o partes de estados de juego.
array = [
{},
[],
{};
]
object = {
array: [],
object: {},
}
Constantes →
Constantes del Motor
NekoScript incluye una serie de constantes especiales integradas en el entorno de ejecución. Estas constantes están siempre disponibles y proporcionan acceso directo a información del sistema, del juego, de la escena, y otras utilidades internas.
Variables
Estas variables se comportan como valores en tiempo real. Puedes leerlas en cualquier momento para obtener información del estado actual del motor o del juego.
$dtDelta Time: el tiempo transcurrido desde el último fotograma.Number
$fpsCuadros por segundo (Frames Per Second).Number
$camPosición actual de la cámara, como un arreglo [x, y].Array
$sceneNombre (String) de la escena actual.String
$timeTiempo total desde que el juego fue iniciado (en segundos).Number
$sceneTimeTiempo desde que la escena actual fue cargada (en segundos).Number
$keysLista de teclas presionadas (por nombre: "ArrowUp", "z", etc).Array
$scancodesLista de teclas presionadas (por código físico del teclado).Array
$messsagesLista de mensajes.Array
$leftEl punto más a izquierda de la pantalla.Number
$rightEl ponto más a la derecha de la pantalla.Number
$topEl punto más arriba en la pantalla.Number
$downEl punto más abajo en la pantalla.Number
$widthEl tamaño horizontal del juego.Number
$heightEl tamaño vertical de la pantalla.Number
Estas variables pueden ser utilizadas directamente en cualquier expresión.
Funciones
Estas funciones permiten realizar transformaciones o cálculos especiales relacionados con el motor.
$position(x, y)Convierte coordenadas fijas a coordenadas del motor (por cámara, zoom, escala, etc.).Array
$size(w, h)Ajusta el tamaño a las proporciones del sistema.Array
Javascript →
Funciones y Objetos de JavaScript disponibles
NekoScript está construido sobre una base de JavaScript, por lo que muchas de las funciones y objetos estándar del lenguaje están disponibles.
Funciones estandar
Estas son quizás las funciones más útiles para el desarrollo de videojuegos.
MathPropiedades estáticas y métodos para operaciones matemáticas.Math ↗
StringObjeto usado para manipular texto.String ↗
NumberConstantes y metodos para manipular valores numéricos.Number ↗
JSONMétodos estáticoss para convertir valores hacia y desde Json.Json ↗
RegExpManipular patrones de texto.RegExp ↗
DateRepresenta un momento en el tiempo.Date ↗
BooleanValores que representan el resultado de una operación lógicaBoolean ↗
ArrayColección de multiples valores (variables, objetos, otras arrays) bajo un solo nombreArray ↗
ObjectColección de valores o entidades (variables, objetos, arrays, fuciones, métodos) con nombres clave.Object ↗
Ejemplo:
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);
Funciones globales básicas
Estas funciones están disponibles tal como en JavaScript:
isFinite()
inNaN()
parseFloat()
parseInt()
Valores especiales
undefined
NaN
Infinity
Objetos globales
ObjectBooleanSymbol
Números y FechasNumberBigIntMathDate
Texto y RegExStringRegExp
EstructurasArrayMapSetWeakMapWeakSet
Tipos especialesInt8ArrayUint8ArrayUint8ClampedArrayInt16ArrayUint16ArrayInt32ArrayUint32ArrayBigInt64ArrayBigUint64ArrayFloat32ArrayFloat64Array
ConcurrenciaPromiseAsyncFunction
UtilidadesJSONReflectProxyIntl
Errores estandar
Error
TypeError
ReferenceError
SyntaxError
RangeError
URIError
EvalError
AggregateError