Mounting Multiple Animated Sprites as one "character"
by Bob Dobbs · in Torque X 2D · 05/31/2010 (3:44 am) · 7 replies
Hi, This sounds like a fairly simple concept and I'm sure its doable but I just seem to be missing something small.
I want to make a single character made of three parts; Head, Torso and Legs. Each of these is are an animated sprite. I've mounted the animated Head and Legs sprites to the Torso animated sprite. Each of the mounted parts has its own component called "AnimatedCharacterComponent" and I set mounting options to "Owned by Mount" and "Track Rotation"
The idea is when the users moves north or south the Head, Torso and Legs will animate and all sprites move. Later if the character is "running" I would change the relevant body parts.for East, West, NW, etc I will just rotate the main torso sprite.
In the code I do the following attached below. I thought at first of animating all parts with a single public method "PlayMyAnimation", then tried doing the animations "individually" - PlayHeadAnimation,PlayTorsoAnimation,PlayLegsAnimation but that doesn't seem to work either. Tried the body parts on separate layers, with & without "AnimatedCharacterComponent" but just doesn't seem to want to work in harmony.The sprites just jitter and fail to move or animate. Individually without the mounts or other parts the code works fine. What am I Missing ? Would really appreciate any pointers or advice on this kind of thing, been scratching my head on it all day !
I want to make a single character made of three parts; Head, Torso and Legs. Each of these is are an animated sprite. I've mounted the animated Head and Legs sprites to the Torso animated sprite. Each of the mounted parts has its own component called "AnimatedCharacterComponent" and I set mounting options to "Owned by Mount" and "Track Rotation"
The idea is when the users moves north or south the Head, Torso and Legs will animate and all sprites move. Later if the character is "running" I would change the relevant body parts.for East, West, NW, etc I will just rotate the main torso sprite.
In the code I do the following attached below. I thought at first of animating all parts with a single public method "PlayMyAnimation", then tried doing the animations "individually" - PlayHeadAnimation,PlayTorsoAnimation,PlayLegsAnimation but that doesn't seem to work either. Tried the body parts on separate layers, with & without "AnimatedCharacterComponent" but just doesn't seem to want to work in harmony.The sprites just jitter and fail to move or animate. Individually without the mounts or other parts the code works fine. What am I Missing ? Would really appreciate any pointers or advice on this kind of thing, been scratching my head on it all day !
public class AnimatedCharacterComponent : TorqueComponent, ITickObject
{
//======================================================
#region Constructors
#endregion
//======================================================
#region Public properties, operators, constants, and enums
public T2DSceneObject playerObj
{
get { return Owner as T2DSceneObject; }
}
public T2DAnimatedSprite playerHead
{
get { return Owner as T2DAnimatedSprite; }
}
public T2DAnimatedSprite playerTorso
{
get { return Owner as T2DAnimatedSprite; }
}
public T2DAnimatedSprite playerLegs
{
get { return Owner as T2DAnimatedSprite; }
}
#endregion
//======================================================
#region Public Methods
//Mebbe play em all at once with public Methods ???
public void PlayMyAnimation(T2DAnimationData headAnimationData, T2DAnimationData bodyAnimationData, T2DAnimationData legsAnimationData)
{
playerHead.PlayAnimation(headAnimationData);
playerHead.PlayAnimation();
playerTorso.PlayAnimation(bodyAnimationData);
playerTorso.PlayAnimation();
playerLegs.PlayAnimation(legsAnimationData);
playerLegs.PlayAnimation();
}
//Or have a method for each body part ??
public void PlayTorsoAnimation(T2DAnimationData animationData)
{
if (playerTorso.AnimationData != animationData || !playerTorso.IsAnimationPlaying)
{
playerTorso.PlayAnimation(animationData);
playerTorso.PlayAnimation();
}
}
public void PlayHeadAnimation(T2DAnimationData animationData)
{
if (playerHead.AnimationData != animationData || !playerHead.IsAnimationPlaying)
{
playerHead.PlayAnimation(animationData);
playerHead.PlayAnimation();
}
}
public void PlayLegsAnimation(T2DAnimationData animationData)
{
if (playerLegs.AnimationData != animationData || !playerLegs.IsAnimationPlaying)
{
playerLegs.PlayAnimation(animationData);
playerLegs.PlayAnimation();
}
}
public void ProcessTick(Move move, float elapsed)
{
if (move != null)
{
if ((move.Sticks[0].X == 0) && (move.Sticks[0].Y == 0)) //NOTMOVING
{
_playerSpeed = 0;
_sceneObject.SetRotation(_playerDirection, false);
_playerSpeed = 0;
PlayHeadAnimation(_walkingHead);
playerTorso.SetAnimationFrame(5);
playerTorso.PauseAnimation();
PlayTorsoAnimation(_walkingTorso);
playerTorso.SetAnimationFrame(5);
playerTorso.PauseAnimation();
PlayLegsAnimation(_walkingLegs);
playerLegs.SetAnimationFrame(5);
playerLegs.PauseAnimation();
}
//WALKING--------------------------------------------------------------------------------------
else if ((move.Sticks[0].Y > 0)) // NORTH
{
_playerDirection = 180;
_sceneObject.SetRotation(_playerDirection, false);
if ((move.Buttons[0].Pushed == false) && (move.Buttons[4].Pushed == false))
{
_sceneObject.SetRotation(_playerDirection, false);
_playerSpeed = 200;
PlayHeadAnimation(_walkingHead);
PlayLegsAnimation(_walkingTorso);
PlayLegsAnimation(_walkingLegs);
//tried the "multiple" method but no joy !?
//PlayMyAnimation(_walkingHead, _walkingTorso, _walkingLegs);
}
}
else if ((move.Sticks[0].Y < 0)) // SOUTH
{
_playerDirection = 0;
_sceneObject.SetRotation(_playerDirection, false);
if ((move.Buttons[0].Pushed == false) && (move.Buttons[4].Pushed == false))
{
_sceneObject.SetRotation(_playerDirection, false);
_playerSpeed = 200;
PlayHeadAnimation(_walkingHead);
PlayLegsAnimation(_walkingTorso);
PlayLegsAnimation(_walkingLegs);
//tried the "multiple" method but no joy !?
//PlayMyAnimation(_walkingHead, _walkingTorso, _walkingLegs);
}
}
//StandStill--------------------------------------------------------------------------------------
else
{
_playerSpeed = 0;
PlayHeadAnimation(_walkingHead);
playerTorso.SetAnimationFrame(5);
playerTorso.PauseAnimation();
PlayTorsoAnimation(_walkingTorso);
playerTorso.SetAnimationFrame(5);
playerTorso.PauseAnimation();
PlayLegsAnimation(_walkingLegs);
playerLegs.SetAnimationFrame(5);
playerLegs.PauseAnimation();
}
//Movement Velocity--------------------------------------------------------------------------------------
// set our test object's Velocity based on stick/keyboard input
_sceneObject.Physics.VelocityX = move.Sticks[0].X * _playerSpeed;
_sceneObject.Physics.VelocityY = -move.Sticks[0].Y * _playerSpeed;
}
}
public virtual void InterpolateTick(float k)
{
// todo: interpolate between ticks as needed here
}
public override void CopyTo(TorqueComponent obj)
{
base.CopyTo(obj);
AnimatedCharacterComponent obj2 = obj as AnimatedCharacterComponent;
obj2._walkingHead = _walkingHead;
obj2._walkingTorso = _walkingTorso;
obj2._walkingLegs = _walkingLegs;
}
#endregion
//======================================================
#region Private, protected, internal methods
protected override bool _OnRegister(TorqueObject owner)
{
if (!base._OnRegister(owner) || !(Owner is T2DSceneObject))
return false;
// retain a reference to this component's owner object
_sceneObject = owner as T2DSceneObject;
_SetupInputMap(_sceneObject, 0, "gamepad" + 0, "keyboard");
// tell the process list to notifiy us with ProcessTick and InterpolateTick events
ProcessList.Instance.AddTickCallback(Owner, this);
return true;
}
void _OnBackButton(float val)
{
if (val > 0.0f)
Game.Instance.Exit();
}
private void _SetupInputMap(TorqueObject player, int playerIndex, String gamePad, String keyboard)
{
// Set player as the controllable object
PlayerManager.Instance.GetPlayer(playerIndex).ControlObject = player;
// Get input map for this player and configure it
InputMap inputMap = PlayerManager.Instance.GetPlayer(playerIndex).InputMap;
int gamepadId = InputManager.Instance.FindDevice(gamePad);
if (gamepadId >= 0)
{
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.X, MoveMapTypes.Button, 0);
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.A, MoveMapTypes.Button, 1);
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.B, MoveMapTypes.Button, 2);
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.Y, MoveMapTypes.Button, 3);
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.RightTriggerButton, MoveMapTypes.Button, 4);
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.LeftThumbX, MoveMapTypes.StickAnalogHorizontal, 0);
inputMap.BindMove(gamepadId, (int)XGamePadDevice.GamePadObjects.LeftThumbY, MoveMapTypes.StickAnalogVertical, 0);
inputMap.BindAction(gamepadId, (int)XGamePadDevice.GamePadObjects.Back, _OnBackButton);
}
// keyboard controls
int keyboardId = InputManager.Instance.FindDevice(keyboard);
if (keyboardId >= 0)
{
inputMap.BindMove(keyboardId, (int)Keys.Right, MoveMapTypes.StickDigitalRight, 0);
inputMap.BindMove(keyboardId, (int)Keys.Left, MoveMapTypes.StickDigitalLeft, 0);
inputMap.BindMove(keyboardId, (int)Keys.Up, MoveMapTypes.StickDigitalUp, 0);
inputMap.BindMove(keyboardId, (int)Keys.Down, MoveMapTypes.StickDigitalDown, 0);
inputMap.BindMove(keyboardId, (int)Keys.Space, MoveMapTypes.Button, 0);
}
}
protected override void _RegisterInterfaces(TorqueObject owner)
{
base._RegisterInterfaces(owner);
// todo: register interfaces to be accessed by other components
// E.g.,
// Owner.RegisterCachedInterface("float", "interface name", this, _ourInterface);
}
#endregion
//======================================================
#region Private, protected, internal fields
T2DSceneObject _sceneObject;
//int _playerNumber = 0;
int _playerSpeed = 0;
int _playerDirection =0;
T2DAnimationData _walkingHead = TorqueObjectDatabase.Instance.FindObject<T2DAnimationData>("JayWalkingHeadAnimation");
T2DAnimationData _walkingTorso = TorqueObjectDatabase.Instance.FindObject<T2DAnimationData>("JayWalkingTorsoAnimation");
T2DAnimationData _walkingLegs = TorqueObjectDatabase.Instance.FindObject<T2DAnimationData>("JayWalkingLegsAnimation");
#endregion
}
}
#2
16. public T2DAnimatedSprite playerHead
17. {
18. get { return Owner as T2DAnimatedSprite; }
19. }
20.
21. public T2DAnimatedSprite playerTorso
22. {
23. get { return Owner as T2DAnimatedSprite; }
24. }
25.
26. public T2DAnimatedSprite playerLegs
27. {
28. get { return Owner as T2DAnimatedSprite; }
29. }
30.
and thats the sceneObject that this component is attached to.
What I would do is break this out into 4 components as follows
AnimatedHeadComponent
AnimatedTorsoComponent
AnimatedLegsComponent
AnimatedCharacterComponent
Then Move each of the PlaYXAnimation methods into thier respective components
then In the AnimatedCharacterComponent (attached to 1 of the 3 sprites only) OnRegister method you can then find the other components and store references to them so you can call the methods on the proper sceneObjects and have them do what you want them to do.
06/02/2010 (2:20 pm)
I think part of your problem is that the following properties all return the same object.16. public T2DAnimatedSprite playerHead
17. {
18. get { return Owner as T2DAnimatedSprite; }
19. }
20.
21. public T2DAnimatedSprite playerTorso
22. {
23. get { return Owner as T2DAnimatedSprite; }
24. }
25.
26. public T2DAnimatedSprite playerLegs
27. {
28. get { return Owner as T2DAnimatedSprite; }
29. }
30.
and thats the sceneObject that this component is attached to.
What I would do is break this out into 4 components as follows
AnimatedHeadComponent
AnimatedTorsoComponent
AnimatedLegsComponent
AnimatedCharacterComponent
Then Move each of the PlaYXAnimation methods into thier respective components
then In the AnimatedCharacterComponent (attached to 1 of the 3 sprites only) OnRegister method you can then find the other components and store references to them so you can call the methods on the proper sceneObjects and have them do what you want them to do.
#3
I had considered making separate Components for each and then in the OnRegister method somehow "get a hold of" the other components, I gave this a shot
but that just gave me some CRAY-ZEE stuff going on with multiple copies of all the sprites layering on top of each other and the input reversing !? (up is down, down is up)
Another idea was for me to not bother setting the 3 body parts in the TXB editor at all and build the whole thing dynamically on the onRegister method.
This came pretty close to what I was wanting to achieve, but still a copy of the torso was remaining unanimated, while the onRegister invoked Torso was animating ok.
Hmmm, been at it 24hrs plus now so might sleep on it and try your suggestion later...how do you propose I :
Would this be using FindInstance ?
Anyway Thanks for you assistance, its real handy just to have someone to bounce the ideas / problem / questions off
06/02/2010 (4:20 pm)
Hey Thanks for the feedback its really appreciated, been hacking away all night to find a solution but to no success. I had considered making separate Components for each and then in the OnRegister method somehow "get a hold of" the other components, I gave this a shot
playerLegs = TorqueObjectDatabase.Instance.FindObject<T2DAnimatedSprite>("playerLegs");
playerTorso = TorqueObjectDatabase.Instance.FindObject<T2DAnimatedSprite>("playerTorso");
playerHead = TorqueObjectDatabase.Instance.FindObject<T2DAnimatedSprite>("playerHead");
playerTorso.CopyTo(playerLegs);
playerTorso.CopyTo(playerHead);but that just gave me some CRAY-ZEE stuff going on with multiple copies of all the sprites layering on top of each other and the input reversing !? (up is down, down is up)
Another idea was for me to not bother setting the 3 body parts in the TXB editor at all and build the whole thing dynamically on the onRegister method.
playerTorso = owner as T2DAnimatedSprite;
playerTorso.SortPoint = new Vector2(0.013f, -0.068f);
playerTorso.Layer = 0;
playerTorso.Visible = true;
playerLegs = new T2DAnimatedSprite();
playerTorso.CopyTo(playerLegs);
playerLegs.SortPoint = new Vector2(0.019f, -0.134f);
TorqueObjectDatabase.Instance.Register(playerLegs);
playerLegs.Mount(_sceneObject, "legsLink", true);
playerHead = new T2DAnimatedSprite();
playerTorso.CopyTo(playerHead);
playerHead.SortPoint = new Vector2(0.013f, -0.071f);
TorqueObjectDatabase.Instance.Register(playerHead);
playerHead.Mount(_sceneObject, "headLink", true);
T2DSceneObject _camera = TorqueObjectDatabase.Instance.FindObject<T2DSceneCamera>("Camera");
_camera.Mount(playerTorso, "", false);
_camera.TrackMountRotation = false;This came pretty close to what I was wanting to achieve, but still a copy of the torso was remaining unanimated, while the onRegister invoked Torso was animating ok.
Hmmm, been at it 24hrs plus now so might sleep on it and try your suggestion later...how do you propose I :
Quote: "OnRegister method you can then find the other components and store references to them so you can call the methods on the proper sceneObjects and have them do what you want them to do."
Would this be using FindInstance ?
Anyway Thanks for you assistance, its real handy just to have someone to bounce the ideas / problem / questions off
#4
So in the file I have you should be able to work it more to your needs and I can't say that what I have done will actully work as I haven't tested it but it should give you the gist of the idea.
you would attach the AnimatedCharacterComponent to the 1 SceneObject that all the others mount to.
and then attach the other components to the mounted objects.
here is a link http://cid-5effa3d2e9884b4e.skydrive.live.com/self.aspx/Public/AnimatedCharacterComponent.cs to my modified components for you.
now the only issue I can think of with this is that the torso is the main component and hence wouldn't be caught in the OnRegister method so you could in theory get rid of the torsocomponent and use the AnimatedCharacterComponent as both the torso and the manager.
hope this gives you something decent to work with.
06/02/2010 (11:05 pm)
it would be more like using the components.findcomponent methods but you first need to get the mounted objects.So in the file I have you should be able to work it more to your needs and I can't say that what I have done will actully work as I haven't tested it but it should give you the gist of the idea.
you would attach the AnimatedCharacterComponent to the 1 SceneObject that all the others mount to.
and then attach the other components to the mounted objects.
here is a link http://cid-5effa3d2e9884b4e.skydrive.live.com/self.aspx/Public/AnimatedCharacterComponent.cs to my modified components for you.
now the only issue I can think of with this is that the torso is the main component and hence wouldn't be caught in the OnRegister method so you could in theory get rid of the torsocomponent and use the AnimatedCharacterComponent as both the torso and the manager.
hope this gives you something decent to work with.
#5
Curiouser and curiouser I didn't need the separate components!!! Upon seeing your example with each animated sprite just as a plain get n set; I always thought I had to put "as owner" or at least return what obj I was expecting ie
so tinkering with your example and eventually piece by piece taking out what was needed or keeping what was needed et viola she's working like a beauty !
For anyone else who bangs their head against a wall like I did , here's how thanks to Scott !
www.sasqwach.com/torque/MovementComponent.cs
Here's a little screen cap too; all separate components moving beautifully as one !

Cheers Scott, I owe you a beer / beef jerky / first born son!
06/03/2010 (5:32 am)
Scott, you are a champion amongst men ! Curiouser and curiouser I didn't need the separate components!!! Upon seeing your example with each animated sprite just as a plain get n set; I always thought I had to put "as owner" or at least return what obj I was expecting ie
get { return playerTorso; }
set { playerTorso = value; })so tinkering with your example and eventually piece by piece taking out what was needed or keeping what was needed et viola she's working like a beauty !
For anyone else who bangs their head against a wall like I did , here's how thanks to Scott !
www.sasqwach.com/torque/MovementComponent.cs
Here's a little screen cap too; all separate components moving beautifully as one !

Cheers Scott, I owe you a beer / beef jerky / first born son!
#6
06/03/2010 (7:44 pm)
I'm glad it worked for you
#7
06/03/2010 (8:32 pm)
Thanks again !
Torque 3D Owner Bob Dobbs
13th Hour Studios