Good day!
Currently in my project there are pair of entities united by a common interface
{ Interface to interact with all Transforms bounded to Essences }
IBoundSpriteInterface = interface
['{6777E00A-8110-471E-B6A4-6B0B8C453B55}']
function ReturnMenu: TCastleDesign;
end;
When clicked, they return the corresponding design to the main game scene module as TCastleDesign. After that, I adds player’s hero by another Interface to this TCastleDesign to interaction with game object. For barter with NPC or for the crate looting, as example. I use TCastleDesign as bridge between player and game objects with two interfaces from the two sides.
if Event.IsMouseButton(buttonLeft)
and (EnvironmentViewport.TransformUnderMouse is TBoundImageTransform) then
begin
InterfaceDesign := (EnvironmentViewport.TransformUnderMouse as IBoundSpriteInterface).ReturnMenu;
(InterfaceDesign as IHeroConnector).ConnectHero(PlayableHero);
InsertFront(InterfaceDesign);
end;
This method looked scalable and universal until I made a separate TCastleView for the interiors of buildings. Now I just want to call Container.View for choose building’s TCastleView with interior (when I click to this building sprite, as with characters e.t.c). So, my current interface don’t works.
The questions are:
How optimal is my way of interacting with game objects? Perhaps there is a more elegant and universal alternative?
If I stop at this method, I need to cast all returned objects to some common type to easily output the result of the click? What is better to return by this interface instead of TCastleDesign? I think it will bad way to change TCastleView in the any case…
Or single interface into the complex game is utopia and I best to add if else into the mouse button event?
I don’t ask 100% complete solution, of course, question more about the game patterns.
Thanks!
Hm, I feel I don’t understand your design enough to answer something constructive, sorry
Looking at your code snippet: you check if EnvironmentViewport.TransformUnderMouse is TBoundImageTransform (a class) and then you typecast it to an interface (IBoundSpriteInterface), I understand assuming that IBoundSpriteInterface is implemented by TBoundImageTransform. OK, but then why is IBoundSpriteInterface useful? And what is IHeroConnector?
I’m afraid I do not understand this question either, sorry I don’t understand what you mean by " call Container.View for choose building’s TCastleView with interior".
I’m sorry, I will need more information about your design to answer something intelligent.
In general, using Pascal interfaces is of course OK, and there are many ways to code something like “various selectable components may define a menu”. E.g. you can as well use behaviors (see Behaviors | Manual | Castle Game Engine , tutorial describes them: Tutorial: 3D physics fun (aka "the bad way to play chess") | Castle Game Engine ). I’m not saying that behaviors are a better design in this case – I don’t understand your “problem space” yet properly to advise anything I’m just pointing to various options.
I use this interface because I created three different child classes of the game essences. So, this Interface can create menu for interaction with different things (as TCastleDesign)…
… and, after that, I need to bind this menu with player character. I use another Interface, because I need different procedures to interaction with different things.
I planned use this idea for all game objects. Full algorithm is:
player click on the sprite
sprite is bounded with some game essence. I need Interface, because game don’t know, what this essence is - character, container or something else.
interface realization of this specific essence return TCastleDesign for interaction with it.
In the next step another interface bounds player with this TCastleDesign.
But now I think it is not optimal. For example, I don’t need return some TCastleDesign. For example, I want just change the scene or something else.
@michalis , as I understand, I can add different behaviors to different game essences, but call them in the same procedure (with mouse click on the sprite in my case)?
It seems some close to Interfaces idea - creating the classes composition without the problem of multiple inheritance?
For now I created my current interface method more universal, as procedure.
I made mistake in designing, but for now it is Ok, I think.
Nevertheless I interested is the Behaviors are good alternative to Interfaces in more of cases?..
The idea is that you can add multiple behaviors to one TCastleTransform. The behavior can express any “property of TCastleTransform that may be shared by various TCastleTransform instances”. For example it can be “logic of creature moving”. Or it can be just some information, like “TBuildingBehavior that describes properties of this building (and is added only to TCastleTransform instances that represent buildings)”.
Indeed, as you say, the pattern allows to use composition – you define a set of behaviors (like “this is a building”, “this is a creature”, “this is an alive creature”, “this is a player”) and then freely mix the behaviors at run-time, adding and removing them as you wish from particular objects.
It can be seen as an alternative to interfaces in some cases. Interfaces allow you to “mark” certain classes (unrelated when it comes to class inheritance) as sharing some API. Behaviors can be used to do the same thing, in a way. For example, you cab check whether something is a building, and access building-specific methods/properties like this:
var
Building: TBuildingBehavior;
begin
Building := MyViewport.TransformUnderMouse.FindBehavior(TBuildingBehavior)
as TBuildingBehavior;
if Building <> nil then
begin
// mouse is over a TCastleTransform with TBuildingBehavior.
// Now we can call methods of Buiding and/or access its fields and properties.
end;
end;