Suggestion to Extend the Tag Property of Components

Hello @Michalis,
It would be great if, in addition to the usual Tag, components could also have a TagString or even TagFloat, etc.

What’s wrong with extending the component yourself to add such fields? And if for some reasons you don’t want to, you can always create a new behavior with those fields and attach it to component.
Note that you can register your own extended components / behaviors to the editor if you want to use them in the editor.

2 Likes

Regarding behaviors, I don’t have information — thank you for the link you provided.
As for extending components, I don’t know how it works in FPC and CGE, but I have written many components for Delphi.

I do not want to add properties like TagString, TagFloat, as it doesn’t seem to me a class design approach that “scales” (works with bigger and bigger applications).

That is:

  • If we add TagFloat etc., and you will use them heavily, soon you will need e.g. 2 floats, and then TagFloat2 and so on will need to appear.

  • The names of such properties are inherently unclear – every use-case uses TagXxx for something else. It’s the opposite of self-documenting identifiers :slight_smile: You will use a number of TagXxx properties, and it will not be clear from the code which instance uses what and for what purpose.

  • It’s inherently “never going to be enough”. You will not have tags typed with all possible classes, records, including your own.

Instead, one can follow existing OOP best practices to extend classes. Namely, inheritance and composition. They allow to have clear names for your things (properties, fields, methods…), proper types, and as much (or as little) additional information as necessary for a particular task. I believe they are better answer:

  • The behaviors mentioned above are a simplest way to do “composition”. You can add information and functionality to existing TCastleTransform instances, and it’s flexible, you can name and type them properly.

    Aside from linked manual page, please also read our tutorial “bad chess” which shows a simplest usage of behaviors.

  • Other ways to use composition are possible - put TCastleTransform and it’s descendants into parent TCastleTransform, put TCastleUserInterface and it’s descendants into parents, arrange any instances in lists and containers.

  • And you can inherit from almost any CGE class in a useful way and extend it’s functionality.

I have used Behaviors.
The decision to add it is up to you; however, whether using Behaviors or inheritance, in many cases there is a need to configure certain things at design time, which is not easily possible with these two approaches.

Eh I kinda disagree. Both behaviors and custom components are easy to use at design time. To sum it up:

  • Register your new behaviors / components at initialization block: RegisterSerializableComponent(TMyCustomComponent, ‘My Custom Component’);

  • Add your units with said new components to your project’s CastleEngineManifest.xml

  • Rebuild a custom castle-editor for your project via build tool: castle-engine editor.

Now you can use your new behaviors / custom components at design time in castle-editor just like the default components.

(Of course if you use Delphi then you need to install both FPC / Lazarus for this to work, but I believe you already did that in your android thread :slight_smile: )

2 Likes

Yes, I have installed FPC / Lazarus.
However, I really prefer to focus on my own work rather than building a custom castle-editor.
In any case, I did the initializations in procedure TViewPlayWorld.Start, which ended up being somewhat complex and extensive.

I agree with @kagamma , the setup of behaviors is quite simple, I create them for useful component-extensions, to be easily setup via editor. For example, here is the ColorBehavior, no Update method, just to store different colors alongside the component which meant to be dynamically recolored based on some conditions/statuses:

  TColorBehavior = class(TCastleBehavior)
  strict private
    FColor: TCastleColor;
    FColorPersistent: TCastleColorPersistent;
    function GetColorPersistent(): TCastleColor;
    procedure SetColorPersistent(const AValue: TCastleColor);
  public
    constructor Create(AOwner: TComponent); override;
    function PropertySections(const APropertyName: String): TPropertySections; override;
    property Color: TCastleColor read FColor write FColor;
  published
    property ColorPersistent: TCastleColorPersistent read FColorPersistent;
  end;

on Start I store the values (presetup in editor) for usage:

LBehaviors := MyComponent.FindAllBehaviors(TColorBehavior);
  for LColorBehavior in LBehaviors do
    MyComponent.CustomColors[LColorBehavior.Name] := TColorBehavior(LColorBehavior).Color;  

btw, I use behaviors also in standard way, with Update method, to change translation of Component, for example, I have like around 10 different kind of behaviors in my game, which are good to adjust from editor, I have even special property for them

property ActiveInEditor: Boolean read FActiveInEditor write FActiveInEditor default False;
...
procedure TMyBehavior.Update(...);
begin
  inherited;
{$IFDEF CASTLE_DESIGN_MODE}
  if not ActiveInEditor then Exit;
{$ENDIF}

which allows to enable and disable them in editor (in the game they are always on), so they don’t actually affect translations or whatever they supposed to, when you already set them up in editor, bc otherwise they distract you.

Moreover there is also a way to store “dictionaries”/configs of complex types with the same mechanism of registration , but of different inheritance: TCastleComponent, fully editable from editor as well (check examples\2d_games\platformer\data\sounds.castle-component for example)

1 Like

You don’t need to do anything specific to build a custom editor, bc when you launch a project which contains custom components (it is defined in CastleEngineManifest.xml , there is an article in manual on how-to), it asks you to build/rebuild it automatically, you just click and wait a bit and get it with all brand-new features :slight_smile:

2 Likes

I did this:

TSceneBehavior = class(TCastleBehavior)
public
  deg : Int16;
  I : Int32;
  J : Int32;
  fileName : string;

// procedure Update(const SecondsPassed: Single; var RemoveMe: TRemoveType); override;
end;

For now my problem is solved, and I thank everyone who took the time to guide and explain things.

1 Like

Thanks everyone! Just adding a link to the manual page about “custom components” that expands the advises from this thread, how to make your own component (like a behavior) available in the editor at design-time: Custom Components | Manual | Castle Game Engine .

2 Likes