Реализовано создание экземпляров "сцен" внутри родительской сцены (PopUp панель).
При смене панели, предыдущая идет плавать в ObjectPool, и не должна в это время работать, поэтому объекту выставляется SetProcess(false).
Но вот, _Process продолжает работать.
public void PoolObject(Node obj)
{
if(obj is Control)
{
Control control = obj as Control;
control.SetProcess(false);
if (!control.IsProcessing())
{
GD.Print("NOT PROCESSING");
}
control.Visible = false;
_pooledObjects.Add(control);
}
}
В консоль выводит "NOT PROCESSING", но процесс идёт...
Полный код(почти)
SceneManager:
using Godot;
using System.Collections.Generic;
using System.Linq;
public class SceneManager : InstanceList<SceneManager>
{
private List<SceneInstanceModel> _sceneInstanceModels = new List<SceneInstanceModel> ();
private ObjectPool _objectPool;
[Export] public int SingletonLayer = 0;
public override void _Ready()
{
Init(SingletonLayer);
_objectPool = ObjectPool.Instance(SingletonLayer);
}
public void ShowScene(string sceneName, SceneManagerShowEnum behaviour = SceneManagerShowEnum.KEEP_PREVIOUS, SceneManagerWhereToInstantiate whereToInstantiate = SceneManagerWhereToInstantiate.INSTANTIATE_IN_PARENT, SceneManagerCanDuplicate canDuplicate = SceneManagerCanDuplicate.CAN_NOT_DUPLICATE)
{
Node sceneInstance = null;
var lastInstance = GetLastInstance();
if (canDuplicate == SceneManagerCanDuplicate.CAN_NOT_DUPLICATE && _sceneInstanceModels.Any(obj => obj.SceneName == sceneName)) //lastInstance != null ? sceneName == lastInstance.SceneName : false)
{
GD.Print("SceneManager: Try to duplicate, but choosen option 'CAN_NOT_DUPLICATE'");
return;
}
else
{
if (whereToInstantiate == SceneManagerWhereToInstantiate.INSTANTIATE_IN_ROOT)
{
sceneInstance = _objectPool.GetObjectFromPool(sceneName);
}
if (whereToInstantiate == SceneManagerWhereToInstantiate.INSTANTIATE_IN_PARENT)
{
sceneInstance = _objectPool.GetObjectFromPool(sceneName, GetParent());
}
}
if (sceneInstance != null)
{
if (behaviour == SceneManagerShowEnum.POOL_PREVIOUS && AnySceneShowing())
{
lastInstance = GetLastInstance();
_sceneInstanceModels.Remove(lastInstance);
if (lastInstance.SceneInstance != null)
{
_objectPool.PoolObject(lastInstance.SceneInstance);
}
}
if (behaviour == SceneManagerShowEnum.HIDE_PREVIOUS && AnySceneShowing())
{
lastInstance = GetLastInstance();
Control control = lastInstance.SceneInstance as Control;
if (control != null)
{
control.SetProcess(false);
control.Visible = false;
}
}
_sceneInstanceModels.Add(new SceneInstanceModel
{
SceneName = sceneName,
SceneInstance = sceneInstance
});
}
else
{
GD.Print($"Error: SceneManager: Trying to use scene named '{sceneName}', but this is not found in the ObjectPool");
}
GD.Print(
$"SceneManager: _pooledObjects.Count = {_objectPool.GetPooldeObjectsCount()}; Singleton Layer: {SingletonLayer};\n" +
$"\tACTION: ShowScene(); Instance name = {sceneInstance.Name};"
);
}
public void CloseLastScene(SceneManagerHideEnum behaviour = SceneManagerHideEnum.KEEP_IN_POOL)
{
if(AnySceneShowing())
{
var lastInstance = GetLastInstance();
if (behaviour == SceneManagerHideEnum.KEEP_IN_POOL)
{
_sceneInstanceModels.Remove(lastInstance);
_objectPool.PoolObject(lastInstance.SceneInstance);
if (AnySceneShowing())
{
lastInstance = GetLastInstance();
Control control = lastInstance.SceneInstance as Control;
if (control != null && !control.IsProcessing())
{
control.SetProcess(true);
control.Visible = true;
}
}
}
if (behaviour == SceneManagerHideEnum.CLOSE)
{
GD.Print($"\nSceneManager: CloseLastScene: DONT_KEEP_IN_POOL");
lastInstance.SceneInstance.QueueFree();
}
}
}
public void HideAllScenes(SceneManagerHideEnum behaviour = SceneManagerHideEnum.KEEP_IN_POOL)
{
if (AnySceneShowing())
{
for(int i = 0; i < _sceneInstanceModels.Count; i++)
{
CloseLastScene(behaviour);
}
}
}
SceneInstanceModel GetLastInstance()
{
if (AnySceneShowing())
return _sceneInstanceModels[_sceneInstanceModels.Count - 1];
else
return null;
}
public bool AnySceneShowing()
{
return GetAmountScenesInQueue() > 0;
}
public int GetAmountScenesInQueue()
{
return _sceneInstanceModels.Count;
}
}ObjectPool:
using Godot;
using System.Collections.Generic;
using System.Linq;
public class ObjectPool : InstanceList<ObjectPool>
{
[Export] public List<string> PrefabsForPool;
private List<Node> _pooledObjects = new List<Node>();
[Export] public int SingletonLayer = 0;
public override void _Ready()
{
Init(SingletonLayer);
}
public Node GetObjectFromPool(string objectPath)
{
var instance = _pooledObjects.FirstOrDefault(obj => obj.Name == GetStringWitoutSymbols(objectPath));
if (instance != null)
{
Control control = instance as Control;
_pooledObjects.Remove(control);
control.SetProcess(true);
control.Visible = true;
control.Raise();
return instance;
}
var prefab = GD.Load<PackedScene>(PrefabsForPool.FirstOrDefault(obj => obj == objectPath));
if (prefab != null)
{
var newInstance = (Control)prefab.Instance();
newInstance.Name = objectPath;
GetTree().Root.AddChild(newInstance);
return newInstance;
}
GD.Print("Error: Object pool: doesnt have an object named: " + objectPath);
return null;
}
public Node GetObjectFromPool(string objectPath, Node _parent)
{
var instance = _pooledObjects.FirstOrDefault(obj => obj.Name == GetStringWitoutSymbols(objectPath));
if (instance != null)
{
Control control = instance as Control;
_pooledObjects.Remove(control);
control.SetProcess(true);
control.Visible = true;
return instance;
}
var prefab = GD.Load<PackedScene>(PrefabsForPool.FirstOrDefault(obj => obj == objectPath));
if (prefab != null)
{
var newInstance = (Control)prefab.Instance();
newInstance.Name = objectPath;
_parent.AddChild(newInstance);
newInstance.Raise();
newInstance.RectPosition = Vector2.Zero;
newInstance.MarginBottom = 0;
newInstance.MarginTop = 0;
newInstance.MarginLeft = 0;
newInstance.MarginRight = 0;
return newInstance;
}
GD.Print("Error: Object pool: doesnt have an object named: " + objectPath);
return null;
}
public void PoolObject(Node obj)
{
if(obj is Control)
{
Control control = obj as Control;
control.SetProcess(false);
if (!control.IsProcessing())
{
GD.Print("NOT PROCESSING");
}
control.Visible = false;
_pooledObjects.Add(control);
}
}
public void RemoveObject(Node obj)
{
obj.QueueFree();
}
public int GetPooldeObjectsCount()
{
return _pooledObjects.Count;
}
public string GetStringWitoutSymbols(string str)
{
return str.Replace("/", "").Replace(":", "").Replace(".", "");
}
}Ломбард
Портянку не читал... но на всякий случай, может ты вызываешь set_process() у новой ноды до _ready() ?
Тогда так нельзя. Используй call_deferred()
https://docs.godotengine.org/en/stable/classes/class_node.html#cl… d-set-process
Any calls to this before _ready will be ignored.
К сожалению, проблема не в этом. SetProcess(false) для текущей ноды вызывается при переключении на другую, когда текущая уже прогружена
Ломбард
> SetProcess(false) вызывается при переключении на другую сцену, когда текущая уже прогружена
А где?
Просто ты вывалил какую-то кучу добра, и думаешь, что у Godot баг, а между тем, это легко тетстируется, в отличие от твоего кода.
func _ready() -> void: set_process( false) func _process( delta: float) -> void: print( "_process")
Если надписей "_process" нет - значит баг в твой куче. Ставь логи.
p.s. И я вообще не понял, зачем тебе у панели отключать process, не говоря уже про пул.
Ломбард
Не знаю, чего у тебя такие сложности. // я не увидел функцию _process
Я имею ввиду - нафига тебе такие сложности ?
Выкидывай нафиг не-нужные сцены, и загружай их заново.
Сделай preload и этот пре-загружэный пакет - будет в басейне плавать.
Когда захочеш его выловить, и пустить в работу - вызывай нечто
var new_panel = preloaded_panel_pack.instantiate()
https://docs.godotengine.org/en/stable/classes/class_packedscene.html
// ---
Попробуй выставить свойство контроль.Мышка = игнор,
и клики не должны задевать этот элемент.
Ну и не ясно - как ты понял, что работают _process
Сделай свойство
func _process
if my_stop: return;
// my_stop = true // вместо control.SetProcess(false);
Ну товарищи, конечно я ставил логи. И самое интересное, что я делал проверку, в процессе ли нода
public void PoolObject(Node obj)
{
if(obj is Control)
{
Control control = obj as Control;
control.SetProcess(false);
if (!control.IsProcessing())
{
GD.Print("NOT PROCESSING");
}
control.Visible = false;
_pooledObjects.Add(control);
}
}И мне писало что NOT PROCESSING, но при этом процесс продолжал писать в вывод
slatazan
> Сделай preload и этот пре-загружэный пакет - будет в басейне плавать.
Можно и так. У меня нода при первом открытии инстанциировалась, а при закрытии добавлялась в бассейн, и если второй раз ее открыть, то уже с бассейна
Ломбард
Я тебе дал элементарный тест. Если он не проходит - это баг.
Если проходит - ты неправильно озаглавил тему.
> И самое интересное, что я делал проверку, в процессе ли нода
Самое интересное, ты не рассказал, зачем тебе манипулировать процессом.
Какой регулярный код там на панели крутится?
Я вижу вариацию проблемы XY.
Der FlugSimulator
> Я тебе дал элементарный тест. Если он не проходит - это баг.
У меня сцена инстанциируется через код, и у нее есть свой скрипт.
При ее закрытии, мне нужно вызвать для нее (для объекта класса Control) SetProcess(false), что я и делаю, но процесс продолжает бежать
И годот считает, что процесс не работает!
Судя по всему, в этом месте баг, при работе с packedscene. При чем тут скрипт, который выключает процесс сам у себя?
Der FlugSimulator
> Самое интересное, ты не рассказал, зачем тебе манипулировать процессом.
Планируются довольно сложные элементы, которые должны быть поверх основной сцены...
Фактически сцена в сцене
Ломбард
> Судя по всему, в этом месте баг, при работе с packedscene. При чем тут скрипт, который выключает процесс сам у себя?
Я не вижу конкретного кода, по которому можно "судить".
Ты написал "Не работает SetProcess()" - я тебе показал как это проверить.
Ты видимо не проверил, но утверждаешь, что в Godot баг. Это можно делать только если у тебя есть minimal reproducible example.
Он у тебя есть?
Ломбард
> Планируются довольно сложные элементы ...
Ты с простым разберись сначала. У тебя какой то оверинжиниринг, с которым ты не можешь совладать и даже объяснить.
Никто за тебя этого делать не будет. Тем более, "ванговать по фотографии".
Ломбард
Не упирайся в проблемы - обходи их как-нибудь.
Баг или не баг - пофигу.
var now_on_top = какое-то событие, просит поднять диалог вышэ других; pano.move_child( now_on_top, -1) # вероятно, поднимем вышэ всех
Или сделай особую пустую ноду spec_solo - переносим туда, и обратно, в гараж.
var new_solo = достать из спрятаной ноды; new_solo.reparent( spec_solo); # единственый чилд, поверх всех.
Der FlugSimulator
> Ты с простым разберись сначала. У тебя какой то оверинжиниринг, с которым ты не
> можешь совладать и даже объяснить.
Тоже сначала пытался вникнуть в код, но слишком сложен для готода и не только, я когда под юнити пишу такую сложную логику стараюсь обходить без особого настроения .
зы сделал на gd скрипте все работает. Создаю 2к обьекты отключаю\включаю фпс приятно большой ))
DanQuimby
> сначала пытался вникнуть в код, но слишком сложен для готода
Он не сложен. Он переусложнён, непонятно зачем.
Я до сих пор не понял, нафиг делать пул для контролов.
Смысл пула - чтобы экономить на времени создания объекта.
А панель, какая бы она там ни была - это не тяжелый запрос к БД
Der FlugSimulator
Допустим, мне нужно добавить на сцену ноду, в которой 40 элементов, внутри которых ещё по 5.
Для каждого из 40 элементов, каждый элемент заранее создаётся и кладется в пул, и он заполняется из базы данных по алгоритму
А проблема, судя по всему, была в приведении типа Control к packed scene. Годот не ругался на это, но при попытке создать ту же историю немного по другому, в этом моменте годот уже заметил что что-то не так.
Надоела возня с костылями... жаль у меня недостаточно знаний и силы воли чтобы сделать без движка
Ломбард
> 40 элементов, внутри которых ещё по 5.
Я без проблем создаю 300+ котнролов за 1 раз, и еще линни (на рисунке 570 2D нод). По сути мгновенно.
Это правда G3, у G4 контролы чуть тяжелей.
И без пула не умер.
Тема в архиве.