Playing part of an animation xml file

Thanks. But then I thought it would be easier to load all animations once and then play a range by setting the first animation and last animation, because else I would have to make Runanimation lists of every possible ‘turnaround’ sequence.
I have now made this: it loads all animations and they get a number. Then it searches for the first (which is the current) and last animation and plays all animations between until it reaches the last set animation. This works okay, except it makes the character sprite turn the longest “route.”
For instance ‘standfront’ is the first animation in the list: 0
So if the sprite has to turn from 'standse (facing southeast) to standfront, it should play forward (clockwise).
But it does not because 'standse’ has number 14 in the list, so it plays backward (counterclockwise).

So if the LastAnimation is close to FirstAnimation it should take a ‘short turn’ instead of turning the sprite all around.
So I guess there should be something edited in the SetAnimationRange procedure to achieve this.
Any idea?
:slight_smile:



type
  TStateMain = class(TUIState)
  private
    Viewport: TCastleViewport;
    AnimList: TStrings;
    FirstAnim, NextAnim, LastAnim: Integer;
    StopAnim: Boolean;
    FirstName: string;
    FirstAnimationName, LastAnimationName: String;
    PlayForward: Boolean;

    procedure SetAnimationRange;
    procedure StandSW(Sender: TObject);
    procedure StandLeft(Sender: TObject);
    procedure StandNW(Sender: TObject);
    procedure StandBack(Sender: TObject);
    procedure StandNE(Sender: TObject);
    procedure StandRight(Sender: TObject);
    procedure StandSE(Sender: TObject);
    procedure StandFront(Sender: TObject);

    procedure RunNextAnimation(const Scene: TCastleSceneCore; const Animation: TTimeSensorNode);
   public
    constructor Create(AOwner: TComponent); override;
    procedure Start; override;
    procedure RunAllAnimations(const Scene: TCastleSceneCore);
    procedure RunAnimationList(const Scene: TCastleSceneCore; const AnimNames: array of String);
  end;

var
  StateMain: TStateMain;
  Button1, Button2, Button3, Button4, Button5, Button6, Button7, Button8: TCastleButton;
  Label1, Label2: TCastleLabel;
  Scene1: TCastleScene;


implementation

uses SysUtils;

{ TStateMain ----------------------------------------------------------------- }

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


procedure TStateMain.SetAnimationRange;
var A, B: Integer;
List: array[0..20] of string;
begin
  AnimList := Scene1.AnimationsList;
  For A :=  0 to AnimList.Count-1 do
  begin
    List[A] := AnimList[A];
    if List[A] = FirstAnimationName then FirstAnim := A;
  end;

   For B :=  0 to AnimList.Count-1 do
  begin
    List[B] := AnimList[B];
    if List[B] = LastAnimationName then LastAnim := B;
  end;

 if FirstAnim > LastAnim then PlayForward := False;
 if FirstAnim < LastAnim then PlayForward := True;

 NextAnim := FirstAnim;

 end;


procedure TStateMain.Start;
begin

  inherited;

  Viewport := DesignedComponent ('Viewport') as TCastleViewport;

  Button1 := DesignedComponent('Button1') as TCastleButton;
  Button2 := DesignedComponent('Button2') as TCastleButton;
  Button3 := DesignedComponent('Button3') as TCastleButton;
  Button4 := DesignedComponent('Button4') as TCastleButton;
  Button5 := DesignedComponent('Button5') as TCastleButton;
  Button6 := DesignedComponent('Button6') as TCastleButton;
  Button7 := DesignedComponent('Button7') as TCastleButton;
  Button8 := DesignedComponent('Button8') as TCastleButton;

  Button1.Caption := 'standsw';
  Button2.Caption := 'standleft';
  Button3.Caption := 'standnw';
  Button4.Caption := 'standback';
  Button5.Caption := 'standne';
  Button6.Caption := 'standright';
  Button7.Caption := 'standse';
  Button8.Caption := 'standfront';

  Button1.OnClick:= @StandSW;
  Button2.OnClick:= @StandLeft;
  Button3.OnClick:= @StandNW;
  Button4.OnClick:= @StandBack;
  Button5.OnClick:= @StandNE;
  Button6.OnClick:= @StandRight;
  Button7.OnClick:= @StandSE;
  Button8.OnClick:= @StandFront;

  Scene1 := DesignedComponent('Scene') as TCastleScene;
  Viewport.Items.Add(Scene1);

  Scene1.Translation:= Vector3(800,300,0);

FirstName := 'john';

 Scene1.URL:= 'castle-data:/' + FirstName + '_idle.starling-xml';

FirstAnimationName := FirstName + 'standfront';
FirstAnim := 0;
SetAnimationRange;

end;

procedure TStateMain.StandSW(Sender: TObject);
begin
  LastAnimationName := FirstName + Button1.Caption;
  SetAnimationRange;
  RunAllAnimations(Scene1);
end;

procedure TStateMain.StandLeft(Sender: TObject);
begin
  LastAnimationName := FirstName + Button2.Caption;
  SetAnimationRange;
  RunAllAnimations(Scene1);
end;

procedure TStateMain.StandNW(Sender: TObject);
begin
  LastAnimationName := FirstName + Button3.Caption;
  SetAnimationRange;
  RunAllAnimations(Scene1);
end;

procedure TStateMain.StandBack(Sender: TObject);
begin
  LastAnimationName := FirstName + Button4.Caption;
  SetAnimationRange;
  RunAllAnimations(Scene1);
end;

procedure TStateMain.StandNE(Sender: TObject);
begin
  LastAnimationName := FirstName + Button5.Caption;
  SetAnimationRange;
  RunAllAnimations(Scene1);
end;

procedure TStateMain.StandRight(Sender: TObject);
begin
  LastAnimationName := FirstName + Button6.Caption;
  SetAnimationRange;
  RunAllAnimations(Scene1);
end;

procedure TStateMain.StandSE(Sender: TObject);
begin
  LastAnimationName := FirstName + Button7.Caption;
  SetAnimationRange;
  RunAllAnimations(Scene1);
end;

procedure TStateMain.StandFront(Sender: TObject);
begin
  LastAnimationName := FirstName + Button8.Caption;
  SetAnimationRange;
  RunAllAnimations(Scene1);
end;

procedure TStateMain.RunNextAnimation(const Scene: TCastleSceneCore; const Animation: TTimeSensorNode);
var
  NextAnimationName: String;
  Params: TPlayAnimationParameters;
begin
   if StopAnim then
  begin
    FirstAnim := NextAnim; // remember the index value of LastAnimationName so next call to RunAnimationList will start from this animation
    FirstAnimationName := LastAnimationName; // next call to RunNextAnimation should start with this animation
    StopAnim := False; // reset
    Exit;
  end;

  NextAnimationName := AnimList[NextAnim];
  if NextAnimationName = LastAnimationName then StopAnim := True else

  begin
    if PlayForward then Inc(NextAnim);
    if PlayForward = False then
    begin
      if NextAnim = 0 then NextAnim := AnimList.Count;
      Dec(NextAnim);
    end;
  end;

  Params := TPlayAnimationParameters.Create;
  try
    Params.Name := NextAnimationName;
    Params.Forward:= PlayForward;
    Params.StopNotification := @RunNextAnimation;
    Scene.PlayAnimation(Params);

    finally FreeAndNil(Params) end;
    end;

end.

It seems you want to calculate whether to go backward or forward through the animation list. There are various ways to go backward, but I would suggest the simplest route for you: just reverse the animation list when copying it at the beginning of RunAnimationList.

To decide whether to go forward or backward, you have to compare 2 distances: one is going forward through the animation list (“index of last animation - index of first animation”, and if it’s negative add the “count of animations”), the other distance is going backward (which should be equal “count of animations - distance of going forward”). Choose the option with the shortest distance.

I do not follow your code, I admit I don’t know what you want to do by SetAnimationRange. Looks like you want to hack RunAllAnimations into doing something contrary to its name (which would be “run all animations in the scene”). I would rather advise to go back to the working RunAnimationList that you had. Build new concepts on top of the existing concepts that you have working reliably.

Yes! This is what I was looking for but I could not get it working because I could not figure out how to calculate the backward playing distance.
Thanks!

Now I have this and it works perfectly :slight_smile:

procedure TStateMain.SetAnimationRange;
var A, B, AnimCountForward, AnimCountBackward: Integer;
List: array[0..20] of string;
begin
  AnimList := Scene1.AnimationsList;
  For A := 0 to AnimList.Count-1 do
  begin
    List[A] := AnimList[A];
    if List[A] = FirstAnimationName then FirstAnim := A;  // find current animationame and remember its index
  end;

  For B := 0 to AnimList.Count-1 do
  begin
    List[B] := AnimList[B];
    if List[B] = LastAnimationName then LastAnim := B;  // find destination animation and remember its index
  end;

  AnimCountForward := LastAnim - FirstAnim;  // count the forward number of animations between them
  If AnimCountForward < 0 then AnimCountForward := (AnimCountForward + AnimList.Count);

  AnimCountBackward := AnimList.Count - AnimCountForward; // count the backward number of animations between them
  if AnimCountBackward < AnimCountForward then PlayForward := False else PlayForward := True;

Yes, I should have named it like RunAnimationRange.

Ok, maybe I will try this for the above, but since it works at last now I dont’ dare to change again :slight_smile:
I think I will use RunAnimationList for playing certain animations in the sprite sheet and ‘RunAnimationRange’ only for turning the sprite.