Войти
GodotФорумВопросы

Не работает SetProcess()

#0
20:19, 9 дек 2023

Реализовано создание экземпляров "сцен" внутри родительской сцены (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(".", "");
    }
}
#1
20:57, 9 дек 2023

Ломбард
Портянку не читал... но на всякий случай, может ты вызываешь 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.

#2
(Правка: 21:16) 21:07, 9 дек 2023

К сожалению, проблема не в этом. SetProcess(false) для текущей ноды вызывается при переключении на другую, когда текущая уже прогружена

#3
21:33, 9 дек 2023

Ломбард
> SetProcess(false) вызывается при переключении на другую сцену, когда текущая уже прогружена
А где?

Просто ты вывалил какую-то кучу добра, и думаешь, что у Godot баг, а между тем, это легко тетстируется, в отличие от твоего кода.

func _ready() -> void:
  set_process(false)

func _process(delta: float) -> void:
  print("_process")

Если надписей  "_process" нет - значит баг в твой куче. Ставь логи.


p.s. И я вообще не понял, зачем тебе у панели отключать process, не говоря уже про пул.

#4
21:46, 9 дек 2023

Ломбард
Не знаю, чего у тебя такие сложности. // я не увидел функцию _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);

#5
(Правка: 22:35) 22:24, 9 дек 2023

Ну товарищи, конечно я ставил логи. И самое интересное, что я делал проверку, в процессе ли нода

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 и этот пре-загружэный пакет - будет в басейне плавать.
Можно и так. У меня нода при первом открытии инстанциировалась, а при закрытии добавлялась в бассейн, и если второй раз ее открыть, то уже с бассейна

#6
23:17, 9 дек 2023

Ломбард
Я тебе дал элементарный тест. Если он не проходит - это баг.
Если проходит - ты неправильно озаглавил тему.

> И самое интересное, что я делал проверку, в процессе ли нода
Самое интересное, ты не рассказал, зачем тебе манипулировать процессом.
Какой регулярный код там на панели крутится?

Я вижу вариацию проблемы XY.

#7
(Правка: 14:03) 13:56, 10 дек 2023

Der FlugSimulator
> Я тебе дал элементарный тест. Если он не проходит - это баг.
У меня сцена инстанциируется через код, и у нее есть свой скрипт.
При ее закрытии, мне нужно вызвать для нее (для объекта класса Control) SetProcess(false), что я и делаю, но процесс продолжает бежать
И годот считает, что процесс не работает!
Судя по всему, в этом месте баг, при работе с packedscene. При чем тут скрипт, который выключает процесс сам у себя?

Der FlugSimulator
> Самое интересное, ты не рассказал, зачем тебе манипулировать процессом.
Планируются довольно сложные элементы, которые должны быть поверх основной сцены...
Фактически сцена в сцене

#8
16:02, 10 дек 2023

Ломбард
> Судя по всему, в этом месте баг, при работе с packedscene. При чем тут скрипт, который выключает процесс сам у себя?

Я не вижу конкретного кода, по которому можно "судить".

Ты написал "Не работает SetProcess()" - я тебе показал как это проверить.
Ты видимо не проверил, но утверждаешь, что в Godot баг. Это можно делать только если у тебя есть minimal reproducible example.
Он у тебя есть?

Ломбард
> Планируются довольно сложные элементы ...

Ты с простым разберись сначала. У тебя какой то оверинжиниринг, с которым ты не можешь совладать и даже объяснить.
Никто за тебя этого делать не будет. Тем более, "ванговать по фотографии".

#9
16:29, 10 дек 2023

Ломбард
Не упирайся в проблемы - обходи их как-нибудь.
Баг или не баг - пофигу.

var now_on_top = какое-то событие, просит поднять диалог вышэ других;
pano.move_child( now_on_top, -1) # вероятно, поднимем вышэ всех

Или сделай особую пустую ноду spec_solo - переносим туда, и обратно, в гараж.

var new_solo = достать из спрятаной ноды;
new_solo.reparent( spec_solo); # единственый чилд, поверх всех.
#10
22:03, 11 дек 2023

Der FlugSimulator
> Ты с простым разберись сначала. У тебя какой то оверинжиниринг, с которым ты не
> можешь совладать и даже объяснить.
Тоже сначала пытался вникнуть в код, но слишком сложен для готода и не только, я когда под юнити пишу такую сложную логику стараюсь обходить без особого настроения .
зы сделал на gd скрипте все работает. Создаю 2к обьекты отключаю\включаю фпс приятно большой ))

#11
0:05, 12 дек 2023

DanQuimby
> сначала пытался вникнуть в код, но слишком сложен для готода

Он не сложен. Он переусложнён, непонятно зачем.
Я до сих пор не понял, нафиг делать пул для контролов.

Смысл пула - чтобы экономить на времени создания объекта.
А панель, какая бы она там ни была - это не тяжелый запрос к БД

#12
0:35, 12 дек 2023

Der FlugSimulator
Допустим, мне нужно добавить на сцену ноду, в которой 40 элементов, внутри которых ещё по 5.
Для каждого из 40 элементов, каждый элемент заранее создаётся и кладется в пул, и он заполняется из базы данных по алгоритму

А проблема, судя по всему, была в приведении типа Control к packed scene. Годот не ругался на это, но при попытке создать ту же историю немного по другому, в этом моменте годот уже заметил что что-то не так.
Надоела возня с костылями... жаль у меня недостаточно знаний и силы воли чтобы сделать без движка

#13
1:19, 12 дек 2023

Ломбард
> 40 элементов, внутри которых ещё по 5.

Я без проблем создаю 300+ котнролов за 1 раз, и еще линни (на рисунке 570 2D нод). По сути мгновенно.
Это правда G3, у G4 контролы чуть тяжелей.

+ Показать

И без пула не умер.

GodotФорумВопросы