Вопрос или проблема
На движке Godot я создал генератор, который размещает на карте различные ссылки на объекты и проверяет, доступны ли позиции. Он полностью основан на коде C# и использует классический экспорт для Windows. Генерация требует много вычислений, поэтому ожидайте некоторой задержки, прежде чем расчеты будут завершены. Экспортированная версия не читает код правильно, поэтому генератор не функционирует.
Вот экспортированный результат, когда карта рисуется:
4 запомненных актива 4 4 14 14 4 4 || позиции 14 0 0 14 || активы на карте 0 0 0 0
ОШИБКА: System.ArgumentOutOfRangeException: Указанный аргумент находится вне диапазона допустимых значений. (Параметр ‘index’)
в Godot.Collections.Array.GetVariantBorrowElementAt(Int32 index, godot_variant& elem)
в Godot.Collections.Array`1.get_Item(Int32 index)
в Godot.Node.InvokeGodotClassMethod(godot_string_name& method, NativeVariantPtrArgs args, godot_variant& ret)
в WorldBehaviour2.InvokeGodotClassMethod(godot_string_name& method, NativeVariantPtrArgs args, godot_variant& ret) 4
в Godot.Bridge.CSharpInstanceBridge.Call(IntPtr godotObjectGCHandle, godot_string_name* method, godot_variant** arg)
в: void Godot.NativeInterop.ExceptionUtils.LogException(System.Exception) (:0)
А вот ожидаемый результат в симуляции:
4 запомненных актива 4 4 4 4 4 4 || позиции 0 0 0 0 || активы на карте 0 0 0 0
позиции на карте 31 диаметр [49, 74, 70, 51, 72, 66, 47, 73, 51, 81, 67, 52, 83, 46, 81, 46, 60, 69, 56, 69, 43, 63, 51, 57, 45, 52, 63, 58, 48, 45, 46]
Запомненные элементы кажется, размещены в другом списке без видимой причины, и код не использует специфическую библиотеку, которая может быть неправильно экспортирована.
Код слишком длинный, поэтому я дам вам краткое описание логики:
public override void _Ready() {
SetDefault();
CallDeferred(nameof(DrawLargeMap));
}
// Как только код запускается
public void DrawLargeMap() {
DrawCoverage(100); // Генерирует 100 Vector3 и добавляет их в список Vector3
// Для каждой позиции мы выбираем элементы
for (int i = 0 ; i < spots.Count ; i++) {
SelectObj(context[i]); // выбираем объекты из списка
// Для каждого выбранного объекта мы определяем свойства объекта в списке
for (int j = 0 ; j < selectedObj.Count ; j++) {
SelectOnMap(i, selectedObj[j]); // выбираем объекты, которые еще не нарисованы в списке
(...)
SetOnMap(); // Устанавливаем объект в некоторый список, который определяет его свойства
}
}
}
Каждый вызываемый метод – это базовый метод void с параметрами, без асинхронности и прочего, каждый список выглядит так:
[Export]
public Godot.Collections.Array<Vector3> spots { get; set; }
[Export]
public Godot.Collections.Array<int> spotDiameter { get; set; }
public Godot.Collections.Array<Vector3> spotMap { get; set; }
[Export]
public Godot.Collections.Array<Vector3> context { get; set; }
Я пытался решить это неожиданное поведение, уменьшая затраты на генерацию карты, заменив “call deferred” на более простой метод вызова, и даже добавляя его в обновление, кажется, что проблема остается прежней, код просто ведет себя иначе.
Я не могу запускать каждый метод отдельно с конкретным call deferred без нарушения логики (необходимо читать в определенном порядке), и мне не нужно запускать это в реальном времени.
Единственная известная проблема, возможно, заключается в системе списков или низком уровне чтения памяти, который может использоваться не так, как я предполагал в окружении dotnet, я не знаю ответа.
Кажется, что потоки можно использовать, но я не совсем уверен, как это сделать или полезно ли это здесь…
Есть ли какие-либо известные проблемы с экспортом C# при экспорте для Windows в Godot 4.3, или какие-либо настройки безопасности/чтения памяти, когда это упаковано как продукт?
Ответ или решение
Проблема, с которой вы столкнулись, связана с исключением System.ArgumentOutOfRangeException
, возникающим при обращении к элементам массива в вашем C# скрипте в Godot (4.3) при экспорте для Windows. Это часто указывает на то, что вы пытаетесь получить доступ к элементу массива, индекс которого выходит за пределы допустимых значений. Давайте проанализируем возможные причины и пути решения вашей проблемы.
1. Проверка индексов массивов
Первое, что следует сделать, это убедиться, что все индексы, используемые для обращения к массиву, находятся в пределах допустимых значений. Это особенно важно в коде:
for (int i = 0; i < spots.Count; i++) {
SelectObj(context[i]); // Возможно, здесь происходит ошибка
...
}
Здесь нужно добавить проверку на количество элементов в массиве context
перед доступом к context[i]
. Например:
if (i < context.Count) {
SelectObj(context[i]);
} else {
GD.PushError($"Индекс {i} выходит за пределы массива context");
}
2. Инициализация массивов
Убедитесь, что все массивы, такие как spots
, context
, selectedObj
, и другие, правильно инициализированы перед их использованием. Возможно, вам необходимо убедиться, что все обозначенные как Export
массивы действительно имеют значения, заданные в редакторе.
3. Асинхронное выполнение и использование CallDeferred
Хотя вы упомянули, что не используете асинхронные методы, метод CallDeferred
может вести себя иначе при выполнении в экспортированной версии игры. Попробуйте упростить выполнение кода. Например, вместо CallDeferred
, можно попробовать вызвать метод напрямую в _Ready
, если вы уверены, что данные инициализированы.
Если необходимо сохранить порядок выполнения, вы можете использовать yield
(ассинхронные вызовы), чтобы дать возможность другим операциям завершиться. Например:
public IEnumerator _Ready() {
SetDefault();
yield return null; // Ждем одного кадра
DrawLargeMap();
}
4. Логирование и отслеживание
Добавьте больше логов (например, GD.Print
), чтобы отслеживать, какие значения имеют ваши массивы в разных частях кода. Это поможет понять, где именно происходит сбой.
5. Использование потоков
Если у вас возникают проблемы с длительными вычислениями, вы можете рассмотреть возможность использования потоков. Однако важно помнить, что Godot не является потокобезопасным, поэтому доступ к объектам Godot из потока может привести к ошибкам. Если решите использовать потоки, убедитесь, что вы взаимодействуете с Godot API только из основного потока.
6. Проверьте на наличие известных ошибок экспорта
Поскольку вы используете Godot 4.3, стоит проверить, не зарегистрированы ли известные ошибки или проблемы с экспортом C# версии. Время от времени могут появляться обновления или патчи, исправляющие такие проблемы, поэтому стоит следить за официальным репозиторием и форумами.
Заключение
Подводя итог, рекомендуется в первую очередь тщательно проверить ваши массивы на предмет корректной инициализации и использования индексов. Логирование поможет вам наглядно увидеть, где данные могут оказываться некорректными. Если проблема не решается, попробуйте временно упростить структуру кода и избежать потенциальных ошибок, связанных с асинхронными вызовами и доступом к объектам игры в потоках.
Если у вас возникнут дополнительные вопросы или понадобятся дальнейшие разъяснения, пожалуйста, дайте знать!