I would like to use a string name to replace a scene name in a procedure so I don’t have to use if…then for every similar statement.
original:
If Player.Transform.WorldBoundingBox.RectangleXY.Collides(westbeach_downplane.WorldBoundingBox.RectangleXY) then
idea:
Location.Name := 'westbeach';
if Player.Transform.WorldBoundingBox.RectangleXY.Collides(Location.Name + '_downplane' + .WorldBoundingBox.RectangleXY) then // this obviously not works because .Worldboundingbox is a scene method, not a string).
Where Location.Name is a string, depending on the location.
Each location has a number of planes that set the borders of the location for limiting movement of a character sprite.
(westbeach, eastbeach, northbeach etc.)
However, please note that while this will work, this is not the best way to. A more efficient way would be to handle everything related to the location through such sort of containers. E.g. like this:
type
TLocation = class(TObject)
private
// Private stuff needed to handle internally
public
// Public variables; including Scene and a lot of other information related to the scene
LocationName: String;
GlobalX, GlobalY: Integer;
... // and anything else related to this location grouped together
Scene: TCastleScene;
end;
TLocationsDictionary = specialize TObjectDictionary<string, TLocation>;
And then you’ll work with Player.Transform.WorldBoundingBox.RectangleXY.Collides(Location[CurrentLocation] .WorldBoundingBox.RectangleXY) and have easy and unified access to everything related to the location in one call.
type TLocation = class(TObject) // (TComponent)
public
Name: String;
LongDescription, ShortDescription: String;
end;
TLocationsDictionary = specialize TObjectDictionary<string, TLocation>;
procedure TAvatar.Border_Collision;
begin
with StatePlay do
begin
Location.Name:= Location.Name + '_topplane'; // the stringname is defined in a different procedure
// if Player.Transform.WorldBoundingBox.RectangleXY.Collides(westbeach_topplane.WorldBoundingBox.RectangleXY) then...
if Player.Transform.WorldBoundingBox.RectangleXY.Collides(Location[Location.Name].WorldBoundingBox.RectangleXY) then...
end;
end;
Yes, because your Location doesn’t have a WorldBoundingBox. (It was my typo above :D). To have it you either need to have some TCastleScene inside TLocation and then ask it as Locations[Location.Name].Scene.WorldBoundingBox
Or alternatively (it’s possible, though I’d not recommend doing it this way) you can have TLocation = class(TCastleScene) - then your TLocation can just be rendered on the Screen. However, it may cause some unexpected consequences.
Note 1 - you don’t need to ask Locations[Location.Name] if you already have Location class you can just ask Location.Scene).
Note 2 - don’t do Location.Name:= Location.Name + '_topplane' - by this you modify Location.Name - i.e. imagine initially you’ve had Location.Name = 'westbeach'. Now you modify the Name and get Location.Name = 'westbeach_topplane', then you modify it again (e.g. when switching locations) and you get unexpected Location.Name = 'westbeach_topplane_topplane'
You’d be much better to use Locations[Location.Name + '_topplane'].Scene.Xxxxxx - this way Location.Name will always stay the same.
Check Messages window for errors. It most likely has some compilation issues. You might need to press F9 to start compiling for Messages window to update properly (don’t worry about unfinished line, it fails somewhere above, e.g. it can say something like “Unknown identifier TCastleScene” and you should include it somewhere in the uses section.
UPD: Ah, it may say “Cyclic reference detected” or something similar complaining about Generics.Collections. Unfortunately it’s the bug of that specific unit and sometimes you get Code Tools (the utility responsible for the hint in your screenshot) failing to parse your code properly. Just write next part of the code (.Scene) manually, it should compile without issues - this bug affects only hints.
First error notifies you that indeed you don’t have the container Locations. You need to 1) declare it and 2) fill it with your locations information.
You should declare it somewhere globally. E.g. just as a top level variable in interface of the Unit that deals with locations.
Then you should create it - only once, not every time it is requested.
And finally you should fill it in with proper data about your locations. For this feature to work in your favor - you should have there all your locations loaded and ready for action.
The second error complains it cannot find location named 'westbeach_topplane' in Locations.
So it should be something like this (again, it’s a pseudocode, don’t rely it’ll work out-of-the-box, you’ll need to adjust it to fit for your purpose :))
interface
type
TLocation = class...
...
end;
TLocationsDictionary = specialize TObjectDictionary<string, TLocation>;
var
Locations: TLocationsDictionary;
procedure LoadLocations; // this is a "forward declaration", you should call it at the beginning of the game, e.g. in GameInitialize
implementation
procedure LoadLocations;
var
Location: TLocation;
begin
Locations := TLocationsDictionary.Create([doOwnsValues];
// Here I hardcoded the locations information, it's convenient when beginning the development
// Later it'd be better to load those from some game data (like Text, JSON or XML file) where it's easier to edit
Location := TLocation.Create;
Location.Name = 'westbeach';
Location.Scene := westbeach_topplane; // Maybe directly TCastleScene.Create('castle-data:/locations/westbeach.X3D');
Locations.Add(Location.Name, Location);
Location := TLocation.Create;
Location.Name = 'eastbeach';
Location.Scene := eastbeach_topplane;
Locations.Add(Location.Name, Location);
end;
...(here you can work with Locations)...
finalization
FreeAndNil(Locations);
End.
Note that here you will access Location by it’s regular name. So, you’ll need to use Locations['westbeach'] without any additional '_topplane'.