Navigation Issue Between Files

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 :slight_smile:

  • 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 :slight_smile: No worries, I also didn’t know how to create classes long time ago :slight_smile: We have resources to learn, please read them and experiment with creating classes in Object Pascal:

1 Like