I’m working on a project and moved the navigation to a separate file, and then linked them with Regulation.Nav := Nav
in my play file, and everything I pass works except making the binds clear.
Source code attached, I’m sure I’m overlooking something.
ACompleteForfeit-0.1-src.zip (29.7 MB)
The problem is that you call Regulator.InitAddon before setting the Movement’s Nav property. Then Movement.Init assumes that Nav was set but it isn’t yet.
procedure TViewOffice.Start;
begin
{...}
Regulation.InitAddOn;
Movement.Nav := Nav;
Movement.Camera := Camera;
end;
Just replace the last 3 lines so you call them in a right order:
Movement.Nav := Nav;
Movement.Camera := Camera;
Regulation.InitAddOn;
You could also set the properties to nil
at TMovement.Create or somewhere during initialization. Then it’ll be easy to check if they hold an object.
1 Like
Like I said, probably something I overlooked. TYSM!
1 Like
Note that all the instance fields are automatically initialized to nil
in Object Pascal, no need to do this explicitly.
type
TMyClass = class
Int: Integer;
AnotherObject: TObject;
end;
var
My: TMyClass.
begin
M := TMyClass.Create;
Writeln(M.Int); // guaranteed zero
Writeln(M.AnotherObject <> nil); // guaranteed false
FreeAndNil(M);
end.
Looking at the source code of @haoshef , I see an additional problem. I hope my comments below will be helpful to learn more about the Pascal 
-
You never create any instance of the TMovement
class, that is: you never call TMovement.Create
. The global variable Movement: TMovement;
has always value of nil
in your application, from what I can see you never assign anything to it (you never do sthg like Movement := TMovement.Create
). Calling any method of such class or accessing any field of it is an error.
-
Things happen to work because you made all the fields “class fields”, and you don’t use virtual methods, so you have
class var Viewport: TCastleViewport;
class var Nav: TCastleWalkNavigation;
class var Camera: TCastleCamera;
But in this case, assessing them by (uninitialized) global variable Movement: TMovement;
is not necessary.
-
In your application, you defined a number of other own classes (TButtons
, TForfeitNav
, TGameRegulation
) but you never create instances of them, and they all work following the above workflow: all fields are class var
, all methods are non-virtual. The global variables
Buttons: TButtons;
Regulation: TGameRegulation;
are never initialized, they are always nil
, but things happen to work because calling non-virtual methods and accessing class fields is OK, nothing actually “deferences” the invalid nil
pointers.
-
Recommendation:
-
Either delete the global variables, like Movement: TMovement;
, and use only class fields and class methods, like TMovement.Init
.
-
Or make the Viewport
(and other fields in TMovement
) regular fields (not class var
), and create the class like Movement := TMovement.Create
and destroy it at the appropriate moment (e.g. in a finalization
of some unit). And instead of inventing your own method Init
, define constructor Create
to initialize the class fields.
Do the above consistently for all of your own classes, like TButtons
, TForfeitNav
, TGameRegulation
.
-
Possibly, you are unsure how to do the above, it’s OK! If you’ve never done it, then my advise “create the class” may sound like a black magic incantation
No worries, I also didn’t know how to create classes long time ago
We have resources to learn, please read them and experiment with creating classes in Object Pascal:
1 Like