Скрипты C#: от сборки до проверенного результата
Запускаемый пример: динамический вызов, сигнал, формула и UTF-8-файл.
Запустить законченный пример
Нужен C#-проект Reckfell.NET.Sdk 1.0.0 с net10.0. Настройка C# описывает создание и сборку проекта.
- Сохраните четыре файла ниже в проект с точными именами.
- Выполните dotnet build в папке .csproj.
- Создайте уровень с корневым Entity, прикрепите ScriptToolsExample.cs и сохраните как script_tools.rscene.
- Запустите этот уровень. В Output ожидается SCRIPT_TOOLS count=5; signal=5; damage=25; ok=True.
Пример также записывает user://script-tools-note.txt со строкой Reckfell: 25. Это тестовая заметка, не состояние всей игры. CounterService удаляется после проверки.
NumericExpression.cs
using System;
using Reckfell;
public sealed class NumericExpression : IDisposable
{
private readonly Expression _expression = new();
private bool _parsed;
private int _inputCount;
public bool TryParse(string formula, string[] names, out string error)
{
_parsed = false;
error = "";
if (_expression.Parse(formula, names) != Error.Ok)
{
error = _expression.GetErrorText();
return false;
}
_inputCount = names.Length;
_parsed = true;
return true;
}
public bool TryEvaluate(double[] values, out double number, out string error)
{
number = 0;
error = "";
if (!_parsed || values.Length != _inputCount)
{
error = "Parse first and supply one value per input name.";
return false;
}
using var inputs = new Reckfell.Collections.Array();
foreach (double value in values)
{
if (!double.IsFinite(value))
{
error = "Inputs must be finite numbers.";
return false;
}
inputs.Add(value);
}
using Variant result = _expression.Execute(inputs, null, false, true);
if (_expression.HasExecuteFailed())
{
error = _expression.GetErrorText();
return false;
}
if (result.VariantType is not (Variant.Type.Int or Variant.Type.Float) ||
!double.IsFinite(result.AsDouble()))
{
error = "Expected a finite numeric result.";
return false;
}
number = result.AsDouble();
return true;
}
public void Dispose() => _expression.Dispose();
}TextNoteStore.cs
using System.Text;
using Reckfell;
public static class TextNoteStore
{
public const string Path = "user://script-tools-note.txt";
public const int MaximumBytes = 4096;
public static bool TryWrite(string text, out Error error)
{
error = Error.InvalidParameter;
if (Encoding.UTF8.GetByteCount(text) > MaximumBytes) return false;
using var file = FileAccess.Open(Path, FileAccess.ModeFlags.Write);
if (file is null)
{
error = FileAccess.GetOpenError();
return false;
}
if (!file.StoreString(text))
{
error = file.GetError();
return false;
}
file.Flush();
error = file.GetError();
return error == Error.Ok;
}
public static bool TryRead(out string text, out Error error)
{
text = "";
using var file = FileAccess.Open(Path, FileAccess.ModeFlags.Read);
if (file is null)
{
error = FileAccess.GetOpenError();
return false;
}
if (file.GetLength() > MaximumBytes)
{
error = Error.InvalidData;
return false;
}
string candidate = file.GetAsText();
error = file.GetError();
if (error != Error.Ok && error != Error.FileEof) return false;
text = candidate;
error = Error.Ok;
return true;
}
}CounterService.cs
using Reckfell;
public partial class CounterService : Entity
{
[Signal] public delegate void CountChangedEventHandler(int count);
private int _count;
public int Count => _count;
public bool TryAdd(int amount)
{
if (amount <= 0 || amount > 1000 - _count) return false;
_count += amount;
EmitSignal(SignalName.CountChanged, _count);
return true;
}
}ScriptToolsExample.cs
using Reckfell;
public partial class ScriptToolsExample : Entity
{
public bool Succeeded { get; private set; }
public override void _Ready()
{
var counter = new CounterService();
AddChild(counter);
int observed = 0;
counter.CountChanged += value => observed = value;
bool added = counter.Call(CounterService.MethodName.TryAdd, 5).AsBool();
using var expression = new NumericExpression();
bool parsed = expression.TryParse("base_damage * multiplier",
new[] { "base_damage", "multiplier" }, out string error);
double damage = 0;
bool evaluated = parsed && expression.TryEvaluate(new[] { 20.0, 1.25 }, out damage, out error);
bool saved = TextNoteStore.TryWrite("Reckfell: 25", out Error writeError);
bool read = TextNoteStore.TryRead(out string text, out Error readError);
Succeeded = added && counter.Count == 5 && observed == 5 && evaluated &&
damage == 25 && saved && read && text == "Reckfell: 25";
ReckfellApi.Print($"SCRIPT_TOOLS count={counter.Count}; signal={observed}; damage={damage}; ok={Succeeded}");
if (!Succeeded) ReckfellApi.PrintErr($"SCRIPT_TOOLS failed: {error}; write={writeError}; read={readError}");
counter.Free();
}
}Разобрать по отдельности
C# и Variant · Формулы · Файлы · Логи · Чтение API · Шаблоны
Если ok=False, разберите напечатанные ошибки по этапам: динамический вызов, разбор/вычисление, открытие/запись/чтение файла. Не продолжайте обработку результата, который уже вернул отказ.
Документация Reckfell EngineНаверх ↑