Design for units

Good day!
Is I understand right, that DesignedComponent is function of the TCastleView means that I can load disigned components only if Iuse child class of TCastleView?
For example this is class of inventory display.

  TInventoryDisplay = class(TCastleUserInterface)
  private
    DisplayScroll : TCastleScrollView;
    Area1, Area2 : TCastleUserInterface;
    Display1, Display2 : TItemsDisplay;

    ControlArea : TCastleRectangleControl;
    Description : TCastleLabel;
    RotationButton : TCastleButton;
  public
    constructor Create(AOwner: TComponent); override;
    procedure FillingOfTheDScroll(inv1, inv2: TInventory);
    procedure AddItemToDScroll(item: TItemDisplay);
    procedure DelItemToDScroll(item: Integer);
    procedure RotateInventory(Sender : TObject);
    procedure From1to2(ItemNum: Integer);
    procedure From2to1(ItemNum: Integer);
    destructor Destroy; override;
  end;

For now I use too much code to group it all rightly. So, if I create this class in special unit and want to construct in CGE Editor, I have only one approach:

TInventoryDisplay = class(TCastleView)

When I did it and run project I get message:

DesignedComponent can only be used if the design was loaded, which means that TCastleView has started and DesignUrl is not empty.
There are few parts of code:

constructor TInventoryDisplay.Create(AOwner: TComponent);
begin
  inherited;
  DesignUrl := 'castle-data:/gameviewmain.castle-user-interface';

end;

procedure TInventoryDisplay.Start;
begin
  inherited;      
  RotationButton := DesignedComponent('Button') as TCastleButton;
  ControlArea.InsertFront(RotationButton);
  RotationButton.OnClick := @RotateInventory;
end;

And this in the Main unit:

  Player_Inv := TInventoryDisplay.Create(Char_Items_Disp_Area);
  Player_Inv.Start;
  Player_Inv.FillingOfTheDScroll(Character_Items, Containers_Items);

So, what the problem with this?..
Thanks!

I must admit I do not fully understand what exactly you are trying to achieve (in other words, how exactly you envision usage of TInventoryDisplay) . Guessing from the function names, this is a single inventory item, right?

Unfortunately too tired right now to explain in more detail, but more likely you are looking for something like Components to reuse a design in other designs | Manual | Castle Game Engine - if you have designed them using Editor and placed them in your View.

You can load the designs by code through Castle Game Engine: CastleControls: Class TCastleDesign - this may be more flexible than the previous path.

1 Like

Thanks, I began to understand.

1 Like

One additional question is how to get access to the objects “inside” of design from code?
I can’t just create Design and use the button, I must manually create Button and set they personal URL from Design?..
Sorry, if I explained a little confused. For Example, I need to use ControllButton from here:
Снимок экрана_2024-02-09_12-06-44

I must do approximately this:

ContBut : TCastleButton;
...
ContBut := TCastleButton.Create(Somewhere);
ContBut := DesignedComponent('ControlButton'') as TCastleButton;

, or I can use all objects Inside the Design, when that is already created (without additional manipulation)?

  1. The method TCastleView.DesignedComponent can only be used between design Start and Stop. I think this answers your original question? :slight_smile:

  2. Moreover, to be clear, you can load designs (files like .castle-user-interface) without TCastleView.

2.1. One approach is mentioned by @eugeneloza , Components to reuse a design in other designs | Manual | Castle Game Engine . This means you use class like TCastleDesign, point the URL to a design file, and then use TCastleDesign.DesignedComponent. Like

var
  MyDesign: TCastleDesign;
  MyButtonFromDesign: TCastleButton;
begin
  MyDesign := TCastleDesign.Create(...);
  MyDesign.Url := 'castle-data:/button_design.castle-user-interface';
  MyButtonFromDesign := MyDesign.DesignedComponent('MyButton') as TCastleButton;

  // remember to add it to some parent, e.g. from view, to make it visible
  SomeParent.InsertFront(MyDesign);

2.2. Or you can deserialize the design files using UserInterfaceLoad. You then set at owner of the loaded things, and you can use FindRequiredComponent on t, like this:

var
  OwnerOfLoadedComponents: TComponent;
  MyDesignRoot: TCastleUserInterface;
  MyButtonFromDesign: TCastleButton;
begin
  OwnerOfLoadedComponents := TComponent.Create(...);
  MyDesignRoot := UserInterfaceLoad('castle-data:/button_design.castle-user-interface', OwnerOfLoadedComponents);
  MyButtonFromDesign := OwnerOfLoadedComponents.FindRequiredComponent('MyButton') as TCastleButton;

  // remember to add it to some parent, e.g. from view, to make it visible
  SomeParent.InsertFront(MyDesignRoot);

I would usually recommend using TCastleDesign over UserInterfaceLoad, the API is a bit more straightforward, though deep down they do the same thing.

1 Like

P.S. Take a look at example examples/advanced_editor/advanced_loading_designs in CGE sources, castle-engine/examples/advanced_editor/advanced_loading_designs at master · castle-engine/castle-engine · GitHub . It shows some stuff we mention above, and has a README that provides an overview.

1 Like

Thanks, examples added understanding!
For now it looks enough simple to use :upside_down_face:

1 Like