MD3 - improve animations support

Okay, I tried that advice and now get an error unrelated to it:

I figure it’s just to do with me using the wrong syntax for this particular situation, even though you normally use the period for accessing a scene’s property, and I believe I should be perfect after this one is fixed, but you never know with code errors.

Hi, I got these errors instead now after fixing that one.

Does anyone know why AvatarHiearchy is not recognized in the current code?

I made sure to use the correct syntax for accessing a scene’s property initially, but had to change the period to a colon after I was given the compiler error in the post above (which again, makes zero sense because you are supposed to put the period to access a scene’s property).

unit GameViewMain;

interface

uses Classes,
  CastleVectors, CastleComponentSerialize,
  CastleUIControls, CastleControls, CastleKeysMouse,
  CastleThirdPersonNavigation;

type
  TMyThirdPersonNavigation = class(TCastleThirdPersonNavigation)
  TMyThirdPersonNavigation:AvatarHierarchy := MyTransform;
  protected
    procedure SetAnimation(const AnimationNames: array of String); override;
  end;

  { Main view, where most of the application logic takes place. }
  TViewMain = class(TCastleView)
  published
    { Components designed using CGE editor.
      These fields will be automatically initialized at Start. }
    LabelFps: TCastleLabel;
    ExampleLegsScene, ExampleTorsoScene: TCastleScene;
  public
    constructor Create(AOwner: TComponent); override;
    procedure Start; override;
    procedure Update(const SecondsPassed: Single; var HandleInput: Boolean); override;
    function Press(const Event: TInputPressRelease): Boolean; override;
  end;

var
  ViewMain: TViewMain;

implementation

uses SysUtils;

{ TMyThirdPersonNavigation ---------------------------------------------------- }

procedure TMyThirdPersonNavigation.SetAnimation(const AnimationNames: array of String);
begin
  if AnimationNames[0] = 'idle' then
  begin
    ExampleLegsScene.PlayAnimation('idle', true);
    ExampleTorsoScene.PlayAnimation('idle', true);
  end else
  if AnimationNames[0] = 'walk' then
  begin
    ExampleLegsScene.PlayAnimation('walk', true);
    ExampleTorsoScene.PlayAnimation('walk', true);
  end;
end;

{ TViewMain ----------------------------------------------------------------- }

constructor TViewMain.Create(AOwner: TComponent);
begin
  inherited;
  DesignUrl := 'castle-data:/gameviewmain.castle-user-interface';
end;

procedure TViewMain.Start;
var
  MyNavigation: TMyThirdPersonNavigation;
begin
  inherited;
  MyNavigation := TMyThirdPersonNavigation.Create(FreeAtStop);
  // TODO: Remember to assign MyNavigation properties
end;

procedure TViewMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
begin
  inherited;
  { This virtual method is executed every frame (many times per second). }
  Assert(LabelFps <> nil, 'If you remove LabelFps from the design, remember to remove also the assignment "LabelFps.Caption := ..." from code');
  LabelFps.Caption := 'FPS: ' + Container.Fps.ToString;
end;

function TViewMain.Press(const Event: TInputPressRelease): Boolean;
begin
  Result := inherited;
  if Result then Exit; // allow the ancestor to handle keys

  { This virtual method is executed when user presses
    a key, a mouse button, or touches a touch-screen.
    Note that each UI control has also events like OnPress and OnClick.
    These events can be used to handle the "press", if it should do something
    specific when used in that UI control.
    The TViewMain.Press method should be used to handle keys
    not handled in children controls.
  }

  // Use this to handle keys:
  {
  if Event.IsKey(keyXxx) then
  begin
    // DoSomething;
    Exit(true); // key was handled
  end;
  }
end;

end.

This statement does not seem correct. But allow me a note: this is not about the complexity of CGE but rather about the syntax of Pascal. I’m not a good programmer and therefore I don’t want to teach someone when I myself haven’t finished learning yet (and when does it ever end? :grinning:).
In your place I would take a couple of days to read michalis’ guide (I did, I’ve been using Pascal for a long time, but why not?).
Also I noticed that sometimes you repeat the same mistakes. For example your previous code class declarations after michalis had just explained to you that it was the wrong way to do it.
Again, it’s not a criticism, we all want to see the results of our work soon and we don’t take the time to get it right. Note this detail too:

one way to achieve it is to define and initialize additional fields in TMyThirdPersonNavigation , like `ExampleLegsScene, ExampleTorsoScene: TCastleScene

You put them in TViewMain, not TMyThirdPersonNavigation.
Another detail you should clarify:

MyNavigation.AvatarHierarchy := MyTransform; // MyTransform here is just an example

What is myTransform?

In any case, let’s wait for michalis :wink:

Thank you for that feedback. By no means am I offended; I know no one is a perfect programmer, especially if you are only a beginner without the advanced knowledge like me.

That clarification about me putting something in the wrong spot is very good feedback, cause that is definitely not something I noticed at first like you already figured out.

The MyTransform was just because I was copying the code verbatim, but maybe it’s meant to reference an actual transform like “HumanBase1?”

Unfortunately, I am getting the same exact error as the previous error report even with the fix, so we’re just going to have to sit back and wait for michaelis to clarify what else I am doing wrong.

unit GameViewMain;

interface

uses Classes,
  CastleVectors, CastleComponentSerialize,
  CastleUIControls, CastleControls, CastleKeysMouse,
  CastleThirdPersonNavigation;

type
  TMyThirdPersonNavigation = class(TCastleThirdPersonNavigation)
  TMyThirdPersonNavigation:AvatarHierarchy := HumanBase1;
  protected
    procedure SetAnimation(const AnimationNames: array of String); override;
  published
    ExampleLegsScene, ExampleTorsoScene: TCastleScene;
  end;

  { Main view, where most of the application logic takes place. }
  TViewMain = class(TCastleView)
  published
    { Components designed using CGE editor.
      These fields will be automatically initialized at Start. }
    LabelFps: TCastleLabel;
  public
    constructor Create(AOwner: TComponent); override;
    procedure Start; override;
    procedure Update(const SecondsPassed: Single; var HandleInput: Boolean); override;
    function Press(const Event: TInputPressRelease): Boolean; override;
  end;

var
  ViewMain: TViewMain;

implementation

uses SysUtils;

{ TMyThirdPersonNavigation ---------------------------------------------------- }

procedure TMyThirdPersonNavigation.SetAnimation(const AnimationNames: array of String);
begin
  if AnimationNames[0] = 'idle' then
  begin
    ExampleLegsScene.PlayAnimation('idle', true);
    ExampleTorsoScene.PlayAnimation('idle', true);
  end else
  if AnimationNames[0] = 'walk' then
  begin
    ExampleLegsScene.PlayAnimation('walk', true);
    ExampleTorsoScene.PlayAnimation('walk', true);
  end;
end;

{ TViewMain ----------------------------------------------------------------- }

constructor TViewMain.Create(AOwner: TComponent);
begin
  inherited;
  DesignUrl := 'castle-data:/gameviewmain.castle-user-interface';
end;

procedure TViewMain.Start;
var
  MyNavigation: TMyThirdPersonNavigation;
begin
  inherited;
  MyNavigation := TMyThirdPersonNavigation.Create(FreeAtStop);
  // TODO: Remember to assign MyNavigation properties
end;

procedure TViewMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
begin
  inherited;
  { This virtual method is executed every frame (many times per second). }
  Assert(LabelFps <> nil, 'If you remove LabelFps from the design, remember to remove also the assignment "LabelFps.Caption := ..." from code');
  LabelFps.Caption := 'FPS: ' + Container.Fps.ToString;
end;

function TViewMain.Press(const Event: TInputPressRelease): Boolean;
begin
  Result := inherited;
  if Result then Exit; // allow the ancestor to handle keys

  { This virtual method is executed when user presses
    a key, a mouse button, or touches a touch-screen.
    Note that each UI control has also events like OnPress and OnClick.
    These events can be used to handle the "press", if it should do something
    specific when used in that UI control.
    The TViewMain.Press method should be used to handle keys
    not handled in children controls.
  }

  // Use this to handle keys:
  {
  if Event.IsKey(keyXxx) then
  begin
    // DoSomething;
    Exit(true); // key was handled
  end;
  }
end;

end.

Because this, as far as I can understand, continues to be wrong. The statement doesn’t seem to make sense.

This would sound correct:

MyNavigation.AvatarHierarchy := HumanBase1;

According to Michalis:

AvatarHierarchy is a property of type TCastleTransform (see API docs Castle Game Engine: CastleThirdPersonNavigation: Class TCastleThirdPersonNavigation ) . So you just set it like

MyNavigation.AvatarHierarchy := MyTransform; // MyTransform here is just an example

Have you tried changing the code again?
Also, does your code know what HumanBase1 is? Otherwise there is no difference with MyTransform. I really believe you need to load the 3D model before you can use it, maybe in TViewMain.Create. I haven’t used CGE in a while, but you might want to take a look at the examples to see where and how your model needs to be initialized.

That is actually what I tried initially, but got an error with the period being in place of where a colon should be (again, makes no sense if you follow the rules of Pascal for accessing a scene’s properties!)

Maybe it was because, however, I used the wrong reference after the period, like it was when I copy pasted the old one before HumanBase1, and I can try the syntax again now?

And that makes sense that the model needs to be initalized before I can use it - very good catch.

Yes, in my opinion the error is raised by the compiler not because the dot needs to be replaced with a colon, but rather because it doesn’t recognize what follows (MyTransform or HumanBase1).
I’m pretty sure if you can initialize the model correctly, the error will go away :slightly_smiling_face:

Okay, unfortunately I am getting more errors, in particular I get an error about IMPLEMENTATION being expected instead if I try initializing the HumanBase before accessing it in the code, and I get a whole slew of different errors worse than the current ones shown above, like about AvatarHiearchy not being defined, if I put the call to HumanBase1 in the protected section after the section where I declare all the TCastleScenes instead.

Finally, if I leave the order like it is but add the initiialization of HumanBase1 to the list with the Torso and Leg parts etc, I get the same exact errors as in the previous posts.

Does anyone know the proper solution to how to initalize it?

Looking at the latest code, you placed the line

inside the class declaration. This doesn’t make sense for a few reasons

  • it’s an assignment (so it is something you are supposed to use inside a function/procedure/method body, not in class declaration).

  • you treat AvatarHierarchy as a class property, when it’s a property of an instance, as I said and shown.

  • HumanBase1 is not defined.

It seems you’re trying to write code by trail and error, but I’m afraid this will not work effectively (in Pascal or other languages). You are also in the middle of a bigger task (using navigation class and MD3) which is not a good way to learn.

Please instead consult the Pascal resources I pointed out in this thread. I have also created today an additional page on our website with more Pascal learning materials: More Resources to Learn Pascal | Castle Game Engine . It has a number of good resources, in particular “Free Object Pascal Handbook by Marco Cantu” ( Free Object Pascal Handbook by Marco Cantu - Embarcadero ).

They show how to declare classes, fields and how they work. I would advise to test these things on simple programs (not even related to CGE), e.g. create a class TApple with 2 fields like Weight and Radius, and play with them. Add sample fields, add sample virtual methods, see how it all “plays out” in a simple case, independently of existing CGE classes.

Once you have some simple test applications using (your own) basic classes, then get back to this subject. You need to know how to declare and use fields in a Pascal class to use CGE effectively :slight_smile:

Okay, thank you for pointing me directly toward the resources that can answer my questions. I will try them and see how it goes, but I believe if I read it thoroughly I should have no issues.

Okay, I get these errors instead even though I make sure to follow all the examples to a T.

For example, I added the HumanBase1 in the same exact way as I was told to do earlier for the other things, and I saw in the reference examples that as long as you intialize the objects in the class declaration, you can use them wihout having to re-declare them in the procedure.

Am I missing something again?

unit GameViewMain;

interface

uses Classes,
  CastleVectors, CastleComponentSerialize,
  CastleUIControls, CastleControls, CastleKeysMouse,
  CastleThirdPersonNavigation;


type
  TMyThirdPersonNavigation = class(TCastleThirdPersonNavigation)
  protected
    procedure SetAnimation(const AnimationNames: array of String); override;
    procedure AssignAvatar;
  published
    ExampleLegsScene, HumanBase1, ExampleTorsoScene: TCastleScene;
  end;

  { Main view, where most of the application logic takes place. }
  TViewMain = class(TCastleView)
  published
    { Components designed using CGE editor.
      These fields will be automatically initialized at Start. }
    LabelFps: TCastleLabel;
  public
    constructor Create(AOwner: TComponent); override;
    procedure Start; override;
    procedure Update(const SecondsPassed: Single; var HandleInput: Boolean); override;
    function Press(const Event: TInputPressRelease): Boolean; override;
  end;

var
  ViewMain: TViewMain;

implementation

uses SysUtils;

{ TMyThirdPersonNavigation ---------------------------------------------------- }

procedure TMyThirdPersonNavigation.SetAnimation(const AnimationNames: array of String);
begin
  if AnimationNames[0] = 'idle' then
  begin
    ExampleLegsScene.PlayAnimation('idle', true);
    ExampleTorsoScene.PlayAnimation('idle', true);
  end else
  if AnimationNames[0] = 'walk' then
  begin
    ExampleLegsScene.PlayAnimation('walk', true);
    ExampleTorsoScene.PlayAnimation('walk', true);
  end;
end;

procedure TMyThirdPersonNavigation.AssignAvatar;
begin
  TMyThirdPersonNavigation:AvatarHierarchy := HumanBase1;
end;

{ TViewMain ----------------------------------------------------------------- }

constructor TViewMain.Create(AOwner: TComponent);
begin
  inherited;
  DesignUrl := 'castle-data:/gameviewmain.castle-user-interface';
end;

procedure TViewMain.Start;
var
  MyNavigation: TMyThirdPersonNavigation;
begin
  inherited;
  MyNavigation := TMyThirdPersonNavigation.Create(FreeAtStop);
  // TODO: Remember to assign MyNavigation properties
end;

procedure TViewMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
begin
  inherited;
  { This virtual method is executed every frame (many times per second). }
  Assert(LabelFps <> nil, 'If you remove LabelFps from the design, remember to remove also the assignment "LabelFps.Caption := ..." from code');
  LabelFps.Caption := 'FPS: ' + Container.Fps.ToString;
end;

function TViewMain.Press(const Event: TInputPressRelease): Boolean;
begin
  Result := inherited;
  if Result then Exit; // allow the ancestor to handle keys

  { This virtual method is executed when user presses
    a key, a mouse button, or touches a touch-screen.
    Note that each UI control has also events like OnPress and OnClick.
    These events can be used to handle the "press", if it should do something
    specific when used in that UI control.
    The TViewMain.Press method should be used to handle keys
    not handled in children controls.
  }

  // Use this to handle keys:
  {
  if Event.IsKey(keyXxx) then
  begin
    // DoSomething;
    Exit(true); // key was handled
  end;
  }
end;

end.

That line is still not valid Pascal. You used :, you used class name TMyThirdPersonNavigation as if to assign a class property.

To solve the 1st error message also add CastleScene to the uses clause.

Please read the resources we pointed in this thread. And start from small examples. Don’t try to do this task (combining 3rd-person navigation with MD3 animations) by trial and error. To reiterate most important links:

Okay, thank you for the links! It is really weird/annoying that the colon assignment works in some spots and not others, but I guess that’s just programming life for you.

And actually, I was right about something not making sense even if you read the manual, namely that the error about TMyThirdPersonNavigation not accepting class properties doesn’t make any sense, because it is defined as a class explicitly/word for word in the manual, like in the scripting API like in the page I linked above.

Do you know if it’s supposed to be something else but says class in the manual?

I also know already about how the uses clause works, so for example I know that it lets you use the code that comes pre-packaged in the unit and without it the code is meaningless; I understand that already, it’s just tricky because the scripting API is really weird and doesn’t mention all of the uses for all the classes, especially the newer ones, which is what I was explaining earlier.

Yeah, and now it’s telling me what yo were telling me about TThirdPersonNavigation is not a class, and only class properties may be accessed in the syntax I used (which again is exactly what you said above).

This literally makes no sense, however, even after you read the manual, because it tells you TThirdPersonNavigation is defined as a class specifcally in the scripting API.

Okay, I re-read the page and it still stands that you explicitly define it with keywords like “type custom name = class(classdefined)”,

But it turns out that I mis-read it initially and you actually use TCastleMouseLookNavigation in the parenthesis part of the class definition and not TCastleThirdPersonNavigation.

However, now it’s telling me that TCastleMouseLookNavigation is an identifier not found, which I call absolute BS on, because as I just said it’s given to you word for word on the reference page.

Is it perhaps that I am missing another uses unit? Again, on the page it’s not given if that is the problem. Under Unit, it only says TCastleThirdPersonNavigation, and not anything else at all, period.

Link to Scripting API not giving me all the same help you give me

TCastleThirdPersonNavigation is an identifier in unit CastleThirdPersonNavigation as it says on Castle Game Engine: CastleThirdPersonNavigation: Class TCastleThirdPersonNavigation .

Other than that, I really recommend to follow the documentation (both CGE and Pascal) mentioned in my older post :

And start smaller. Start from simple FPC command-line programs to test various Pascal class features. Check out CGE examples (that are tested for compilation + run) link from above pages, do small modifications there once you understand them.

You are now in the middle of a complicated task (using 3rd-person navigation with MD3). Learning in the middle of this complicated task how to properly use assignment, what is a class property vs instance property etc. – this is not efficient.