Need Help Understanding how to Assign a Transform as an Avatar of a Third Person Navigation Camera

Hi everyone,

I started a brand new project, with no prior experience with the Castle Game Engine and tried to assign a Transform object under the “Avatar” property of a Third Person Navigation object.

I couldnt use or see any Transform objects when I clicked on the drop down menu, only Scene objects.

I need Transform objects as targets specifically, because I want to use models with multiple parts animated seperately from each other, which Scenes dont support.

Does anyone know the correct way to assign a Transform specifically as an avatar/target of a Third Person Navigation camera, and not a Scene as the game engine currently supports?

Thank you very much in advance.

Hi!

For a model composed from multiple scenes, you most likely want to:

  • leave Avatar unassigned (nil)
  • set AvatarHierarchy to the “root” transformation of your avatar.

Moreover, in case you have multiple scenes and you want to control which animations on which scenes run, you will have to introduce your own class, descending from TCastleThirdPersonNavigation. In that class, override SetAnimation virtual method (
Castle Game Engine: CastleThirdPersonNavigation: Class TCastleThirdPersonNavigation ), and run proper animations on the child scene, using any algorithm you like For example:

procedure TMyThirdPersonNavigation.SetAnimation(const AnimationNames: array of String);
begin
  // Apply desired animation in any way.
  // You are most interested in AnimationNames[0].
  // Over-simplifying, you can think that default implementation does "Avatar.AutoAnimation := AnimationNames[0]"
  if AnimationNames[0] = 'walk' then
  begin
    MyView.SceneLegs.AutoAnimation := 'legs_walk_animation';
    MyView.SceneTorso.AutoAnimation := 'torso_walk_animation';
  end else
  if AnimationNames[0] = 'idle' then
  begin
    ...
  end;
end;

( This is not an example ready to be copy-pasted naturally – you wil have to adjust it to your use-case. )

See the API documentation of TCastleThirdPersonNavigation – Castle Game Engine: CastleThirdPersonNavigation: Class TCastleThirdPersonNavigation .

See also this answer in long past thread: MD3 - improve animations support - #71 by michalis

Thank you so much for the thoughtful and helpful response! I will definitely follow your instructions right away. :slight_smile:

Okay, so trying to follow the way a class constructor is done using actual classes, like in TViewMain that comes with a blank project, I ran into an error on line 37, 17 about “forward declaration not solved” despite it being declared and used in the same exact way the existing classes did for their constructors, and I made sure to read the documentation on the particular class I want to make sure it actually does do the constructor in the way I specified, and it says it does when you read further down the page.

Full code for reference:

{ Main view, where most of the application logic takes place.

  Feel free to use this code as a starting point for your own projects.
  This template code is in public domain, unlike most other CGE code which
  is covered by BSD or LGPL (see https://castle-engine.io/license). }
unit GameViewPlay;

interface

uses Classes,
  CastleComponentSerialize, CastleUIControls, CastleControls,
  CastleKeysMouse, CastleViewport, CastleScene, CastleVectors, CastleCameras,
  CastleTransform, CastleInputs, CastleThirdPersonNavigation, CastleDebugTransform,
  CastleSceneCore,
  GameEnemy;

type
  { 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. }
    MainViewport: TCastleViewport;
    ThirdPersonNavigation: TCastleThirdPersonNavigation;
    SceneLevel: TCastleScene;
  private
    Enemies: TEnemyList;
  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;

  TThirdPersonNav = class(TCastleThirdPersonNavigation)
  public
    constructor Create(AOwner: TComponent); override;
    procedure SetAnimation(const AnimationNames: array of String);
  end;

  MyThirdPersonNav = class(TThirdPersonNav)
  public
    constructor Create(AOwner: TComponent); override;
    procedure SetAnimation(const AnimationNames: array of String);
  end;

var
  ViewMain: TViewMain;
  ThirdPersonNav: MyThirdPersonNav;

implementation

uses SysUtils;

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

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

constructor MyThirdPersonNav.Create(AOwner: TComponent);
begin
  inherited;
end;

procedure TViewMain.Start;
begin
  inherited;
end;

procedure MyThirdPersonNav.SetAnimation(const AnimationNames: array of String);
begin

end;

procedure TViewMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
begin
  inherited;
  { This virtual method is executed every frame (many times per second). }
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.

Does anyone know where I am going wrong? Again, you will notice how TViewMain, a class that was there already, was created in the same exact way and didn’t give any errors.

Oh wait, I think I figured it out!

I did “MyThirdPersonNav” without doing “TThirdPersonNav” later in the code, after I first declare the classes; I didn’t actually initialize it.

And now the answer works fine now that I figured out those details, like that I have to initialize the procedure and constructor bodies for each class after I initially declare them, but the catch is even after assigning AvatarHiearchy to the AvatarTransform and not Avatar to one particular scene of the Avatar, I get a blank screen, most likely because it is covered entirely by something.

I will post the entire game/project on GitHub and hopefully you guys will be able to figure out why this is.

Here is the direct link to the project, updated with everything I did as of just today/now:

h ttps://github.com/JPF12141999/Toy-Story-2-Ripoff-Game

Because I am still very new, I cannot post links directly and so you will just have to copy paste the project address into your browser, without the space after the first “h”.

Okay, so I made an educated guess that perhaps I was using the wrong distance to the avatar, but unfortunately that didn’t change anything even when I made the distance a ridiculously large number like 90.

Does anyone know what else I might be doing wrong?

Moderator note:

It is obvious you are the same user as @JPF12141999 . Please use your original account – nothing to be gained by you pretending to come from a new account.

As a bonus, if you use your original account instead of the new one, you will be able to post URLs without issues.

It was kind of obvious that you’re the same user as @JPF12141999 before (given you ask exactly the same question as in 4 existing threads on this forum, 2 threads on Lazarus forums, and similar threads on at least 2 other forums on the Internet, and given you come from same IP). It is even more obvious now since you point to the same GitHub project :slight_smile: