About TCastleTransform Remove from TCastleViewport

when game over check Clear All TCastleTransform;

First : TCastleTransformend.Exists:= false;
Secound : TCastleTransform.Update(const SecondsPassed: Single; var RemoveMe: TRemoveType); RemoveMe := rtRemove;

First = Second?

procedure TTowerManager.ClearAllTower;
var
  i : Integer;
  Tower : TTower;
begin
  for i:= FTowerList.Count - 1 downto 0 do
  begin
    Tower := FTowerList.Items[i];
    if Assigned(Tower) then
    begin
      if Tower.Exists then
      begin
        Tower.Exists := False; //need to TCastleViewport.Items.Remove(Tower) ?
      end;
      FreeAndNil(Tower);
    end;
  end;
end; 

What is TTower ? how do you create them? If you pass an owner to constructor Create (I assume TTower is descendant of TCastleTransform), then Owner will be responsible for their Freeing. If you create them manually and do not pass Owner, but instead store in some List (FTowerList) then you need manually control what you do.

Items object of viewport is not responsible for freeing, it is about being a parent to them and showing them inside a viewport.

1 Like

Thank you ! I asked the wrong question!

TTower = class(TCastleTransform)

TTower.Create;

TCastleViewport.Items.Add(TTower);

dosomething;

TTower.Exists : =false; TCastleViewport.Items.Remove(TTower); this is my question!

FreeAndNil(TTower);

I still don’t get what IS your question, you’re just showing the code.

But, Yes, you need to remove your object from Items before doing Free, you don’t even need to do Exists := false.

1 Like

Admittedly I’m also not clear about the question:) You’re posting pieces of code or pseudo-code, and they only show parts of the code, it is not clear where is the problem / where is the question :slight_smile:

What are you actually trying to do? Provide more description what is your ultimate goal, so we don’t have XY problem and we don’t guess :slight_smile: If something is not working for you? Provide a compete testcase where things fail to compile / work.

Some information:

  1. Setting Exists to false means that the component remains in the tree (e.g. in Viewport.Items, if that’s where it was added), but behaves as if it wasn’t there. So it will not be rendered (visible), will not collide etc., but is still on Viewport.Items, so it’s easy to make it active again.
  2. Removing from Viewport.Items means that the component is not part of the viewport anymore (and setting Exists to false is not necessary in this case). So it will not be rendered (visible), will not collide etc.
  3. Independently of above, you need to free the component at some time. Freeing it → makes AD 1 and AD 2 unnecessary, freed component is automatically removed from the viewport .
1 Like

Thank you! I Fix It.

1 Like

Note: I didn’t review your code completely, but I understand your question is fixed. Cool!

Some notes from scanning your code:

  1. Doing Self.Exists:= False, FScene.Exists:= False before FNeedFree := True; seems unnecessary. If you will soon free this instance (which also frees FScene), then it doesn’t matter what will be their Exists value. (unless they get rendered for 1 frame with invalid state?)

  2. This looks dangerous:

This looks wriong – you are freeing the instance of Self, but this Update is running inside a loop that iterates over components, and your component is part of that list. This is a problem – by freeing yourself, you decrease the loop count, and undefined things (crashes, missing calls to other updates) may happen.

The RemoveMe parameter was introduced to Update as one way to avoid this mistake. Instead of Self.Free, do Remove := rtRemove. I realize that you maybe had some issues with this (judging from your first post in this thread), then we have to explore then and fix – simply doing Self.Free is really dangerous, even if it seems to work. See TCastleTransform.Update docs.

An even simpler solution is to use TCastleTransform.RemoveDelayed (with FreeInstance=true) or ApplicationProperties.FreeDelayed.

My recommendation:

  • remove FNeedFree field and tracking
  • remove TScene4.Update method
  • make TScene4.OnScene4WalkStopped just call ApplicationProperties.FreeDelayed(Self), nothing more.