Game Development Community

[SOLVED]4:32am and my cloned T2DAnimatedSprite has invalid animation data ><

by Bob Dobbs · in Torque X 2D · 09/11/2010 (7:48 pm) · 9 replies

Hey Folks, burning a bit of the midnight oil and come across a strange one and would appreciate anyone's advice / assistance / help.

I hate having to burden peeps with questions but it's 4:32 am and my head's cabbage T_T

I'm making a spawner based on Henry Shillings GameKit and the PlatformerKit

In my Spawner Component I use

T2DAnimatedSprite spawnedEnemy = _enemyTorso.Clone() as T2DAnimatedSprite;

The corresponding SceneObject (with the Spawner component attached) takes the following params

public T2DSceneObject SceneObject
        {
            get { return Owner as T2DSceneObject; }
        }

        public T2DAnimatedSprite enemyHead
        {
            set { _enemyHead = value; }
            get { return _enemyHead; }
        }

        public T2DAnimationData enemyHeadAnim
        {
            set { _enemyHeadAnim = value; }
            get { return _enemyHeadAnim; }
        }


        public T2DAnimatedSprite enemyTorso
        {
            set { _enemyTorso = value; }
            get { return _enemyTorso; }
        }

        public T2DAnimationData enemyTorsoAnim
        {
            set { _enemyTorsoAnim = value; }
            get { return _enemyTorsoAnim; }
        }


        public T2DAnimatedSprite enemyLegs
        {
            set { _enemyLegs = value; }
            get { return _enemyLegs; }
        }

        public T2DAnimationData enemyLegsAnim
        {
            set { _enemyLegsAnim = value; }
            get { return _enemyLegsAnim; }
        }

        [TorqueXmlSchemaType(DefaultValue = "50")]
        public int SpawnCount
        {
            get { return _spawnCount; }
            set { _spawnCount = value; }
        }

        [TorqueXmlSchemaType(DefaultValue = "2")]
        public float SpawnTime
        {
            get { return _spawnTime; }
            set { _spawnTime = value; }
        }

        [TorqueXmlSchemaType(DefaultValue = "1")]
        public float SpawnVariance
        {
            get { return _spawnVariance; }
            set { _spawnVariance = value; }
        }

        [TorqueXmlSchemaType(DefaultValue = "true")]
        public bool SpawnEnabled
        {
            get { return _spawnEnabled; }
            set { _spawnEnabled = value; }
        }

        public enum SpawnOrientation : int
        {
            Area, Edges, Center, Top, Left, Right, Bottom, LeftTopRight,
        }

        [TorqueXmlSchemaType(DefaultValue = "Area")]
        public SpawnOrientation SpawnLocation
        {
            get { return _spawnLocation; }
            set { _spawnLocation = value; }
        }

Now, the T2DAnimatedSprite I'm cloning / Spawning is actually 3 animated sprites mounted on top of each other (Head, Torso & Legs)

When I spawn just the torso, because it should inherit all Mount properties; the legs and head clone just fine, no problem there.

The issue is in my AIChaseComponent (which is for enemies chasing main player) I need to change which animation is being played for the cloned T2DAnimatedSprite like so
if (dist < 200.0f)
{
PlayEnemyAnimation(_enemyHeadAnim, _enemyTorsoAnimLunge, _enemyLegsAnim);
_sceneObject.Physics.Velocity = T2DVectorUtil.VelocityFromTarget(_sceneObject.Position, _TargetObject.Position, _speed + 75);
}
else
{

PlayEnemyAnimation(_enemyHeadAnim, _enemyTorsoAnim, _enemyLegsAnim);
_sceneObject.Physics.Velocity = T2DVectorUtil.VelocityFromTarget(_sceneObject.Position, _TargetObject.Position, _speed);
}

ie as the enemy gets closer his animation is changed into a "lunge"


At runtime, the the template begins to clone and spawn no problems at all but as soon as it hits the PlayEnemyAnimation I get the following Fatal Assert error msg

www.13hours.net/bugs/1invalidAnimData.jpg
So I insert a few breakpoints and see if there is Animation Data for the Cloned T2DAnimatedSprite
et viola just after spawning, seems there's AnimationData in there ok

www.13hours.net/bugs/2afterSpwan.jpg
So how about when I call the PlayEnemyAnimation ?

www.13hours.net/bugs/3onPlayAnim.jpg
hmmm....Seem there's animationData present

so how about within the PlayEnemyAnimation having a look at both the T2DAnimatedSprite and T2DAnimation Data

www.13hours.net/bugs/4withinPlayAnim.jpg
seems there's animation data and sprite but just not playing and causing that nasty Fatal assert error above.

I was thinking might be a problem in the
public override void CopyTo(TorqueComponent obj)
when I'm doing my obj2.Copyto etc but both the spawner and the AiChase Component has the 3 relevant animated sprites AND the animation data copied to the obj

like so
AiPlayerChaseComponent obj2 = (AiPlayerChaseComponent)obj;

            obj2.enemyHead = enemyHead;
            obj2.enemyTorso = enemyTorso;
            obj2.enemyLegs = enemyLegs;
            obj2._enemyHeadAnim = _enemyHeadAnim;
            obj2._enemyTorsoAnim = _enemyTorsoAnim;
            obj2._enemyTorsoAnim2 = _enemyTorsoAnimLunge;
            obj2._enemyLegsAnim = _enemyLegsAnim;

            obj2.TargetObject = TargetObject;
            obj2.AIType = AIType;
            obj2.Active = Active;
            obj2.Speed = Speed;

and also same thing in the Spawner component

I've tried just messing around with the built in Spawner Object in the Editor and non mounted sprites, just singles and no matter what each time I clone I cant directly change the sprites animation using TheT2DSprite.PlayAnimation(TheCorrespondingAnimationData);

what am I missing here ? I really need to find a way to change the animations of my cloned items, so would really appreciate any ideas. A 1000 dollars and a newborn calf to any man who can answer

Much obliged

#1
09/11/2010 (8:02 pm)
What does your CopyTo function look like.

I think you just forgot to copy it from the template with CopyTo(it gets called when you clone).
#2
09/11/2010 (8:12 pm)
wow that was fast ! Much appreciated

it looks like this in the playerChaseComponent
public override void CopyTo(TorqueComponent obj)
        {
            base.CopyTo(obj);

            AiPlayerChaseComponent obj2 = (AiPlayerChaseComponent)obj;

            obj2.enemyHead = enemyHead;
            obj2.enemyTorso = enemyTorso;
            obj2.enemyLegs = enemyLegs;
            obj2._enemyHeadAnim = _enemyHeadAnim;
            obj2._enemyTorsoAnim = _enemyTorsoAnim;
            obj2._enemyTorsoAnimLunge = _enemyTorsoAnimLunge;
            obj2._enemyLegsAnim = _enemyLegsAnim;

            obj2.TargetObject = TargetObject;
            obj2.AIType = AIType;
            obj2.Active = Active;
            obj2.Speed = Speed;
        }


and in the spawnerComponent like so
public override void CopyTo(TorqueComponent obj)
        {
            base.CopyTo(obj);
            Spawner obj2 = obj as Spawner;
            obj2.enemyHead = enemyHead;
            obj2.enemyTorso = enemyTorso;
            obj2.enemyLegs = enemyLegs;
            obj2._enemyHeadAnim = _enemyHeadAnim;
            obj2._enemyTorsoAnim = _enemyTorsoAnim;
            obj2._enemyTorsoAnimLunge = _enemyTorsoAnimLunge;
            obj2._enemyLegsAnim = _enemyLegsAnim;
            obj2.SpawnCount = SpawnCount;
            obj2.SpawnTime = SpawnTime;
            obj2.SpawnVariance = SpawnVariance;
            obj2.SpawnEnabled = SpawnEnabled;
            obj2.SpawnLocation = SpawnLocation;
        }

I'll be addign a whole bunch of other animations as well for each of the Head, Torso & Legs like "attack", "die", "wave arms", etc so I kinda need to find a way to get those clones animating (the chase component works fine as does the actual cloning) just no animation (!?)

P'raps Pool with components !? or cross copy the Spawner & Chase component in each copyto respectively ?

I'm at a loss
#3
09/11/2010 (8:49 pm)
Yeah, pool with components is almost necessary. I have crashes without that being ticked. Try that. If that doesn't work, can you tell me where in the update logic are you changing the animationdata. Is it outside of processtick?
#4
09/12/2010 (3:29 am)
Thanks for getting back & sorry for delay, had to sleep ! flipped the various options with pool, pool with components but to no success.

I'm Updating the animation right inside the ProcessTick as expected

public void ProcessTick(Move move, float elapsed)
        {
            if (!_active)
                return;

            switch (_aiType)
            {
                case EnemyAITypes.Chase:
                    _UpdateChase(elapsed);
                    break;

                case EnemyAITypes.Avoid:
                    _UpdateAvoid(elapsed);
                    break;
            }

            return;
        }

the called function UpdateChase is darned simple too

private void _UpdateChase(float elapsed)
        {
       
            float angle = T2DVectorUtil.AngleFromTarget(_sceneObject.Position, _TargetObject.Position);

          _sceneObject.Rotation = angle;

            float dist = Vector2.Distance(_TargetObject.Position, _sceneObject.Position);

            if (dist < 200.0f)
            {
                PlayEnemyAnimation(_enemyHeadAnim, _enemyTorsoAnimLunge, _enemyLegsAnim);
                _sceneObject.Physics.Velocity = T2DVectorUtil.VelocityFromTarget(_sceneObject.Position, _TargetObject.Position, _speed + 75);
            }
            else
            {
                              enemyTorso.PlayAnimation(_enemyTorsoAnim);
                //PlayEnemyAnimation(_enemyHeadAnim, _enemyTorsoAnim, _enemyLegsAnim);
                _sceneObject.Physics.Velocity = T2DVectorUtil.VelocityFromTarget(_sceneObject.Position, _TargetObject.Position, _speed);
            }

curiouser and curiouser
#6
09/12/2010 (4:27 am)
It's gotta be the fact that you are calling PlayAnimation twice in succession with it. I mean, there really isn't a point in it and it's the only thing I see thats related that seems off.

try:
public void PlayEnemyAnimation(T2DAnimationData enemyHeadAnimationData, T2DAnimationData enemyTorsoAnimationData, T2DAnimationData enemyLegsAnimationData)
        {
            if (enemyHead != null)
            {
                if (enemyHead.AnimationData != enemyHeadAnimationData || !enemyHead.IsAnimationPlaying)
                {
                    enemyHead.PlayAnimation(enemyHeadAnimationData);
                }
            }
            if (enemyTorso != null)
            {

                if (enemyTorso.AnimationData != enemyTorsoAnimationData || !enemyTorso.IsAnimationPlaying)
                {
                    enemyTorso.PlayAnimation(enemyTorsoAnimationData);
                }
            }
            if (enemyLegs != null)
            {
                if (enemyLegs.AnimationData != enemyLegsAnimationData || !enemyLegs.IsAnimationPlaying)
                {
                    enemyLegs.PlayAnimation(enemyLegsAnimationData);
                }
            }
        }
#7
09/12/2010 (11:59 am)
Thanks ! Hmm gave that a try but to no avail, still invalid animation data error...I will doodle around some more see if I can come up with something but in the meantime if you (or anyone else for that matter) notices whats up would be glad of any assistance.

Cheers M8
#8
09/12/2010 (4:47 pm)
Ahhh Hold on....I think the reason there animation data is invalid is because when I spawn the item in my Spawner.Cs I'm making a new T2DAnimatedSprite named spawnedEnemy

like so

T2DAnimatedSprite spawnedEnemy = _enemyTorso.Clone() as T2DAnimatedSprite;


This clones the item from the original template which has the
AiPlayerChaseComponent.cs attached to it.

my AiPlayerChaseComponent.cs updates the animation but it appears to be attempting to update the template's animation and not the cloned item.

Just to make sure; when I spawned the new enemy in the spawner.cs code I added

TorqueObjectDatabase.Instance.Register(spawnedEnemyHead);
                TorqueObjectDatabase.Instance.Register(spawnedEnemyTorso);
                TorqueObjectDatabase.Instance.Register(spawnedEnemyLegs);

                _enemyHeadAnim.Init();
                _enemyHeadAnim.AnimationCycle = true;

                _enemyTorsoAnim.Init();
                _enemyTorsoAnim.AnimationCycle = true;

                _enemyLegsAnim.Init();
                _enemyLegsAnim.AnimationCycle = true;

                spawnedEnemyHead.PlayAnimation(_enemyHeadAnim);

                spawnedEnemyTorso.PlayAnimation(_enemyTorsoAnim);

                spawnedEnemyLegs.PlayAnimation(_enemyLegsAnim);
                _currentCount++;

just after the enemy is spawned and et viola he animates OK

BUT the problem is I want to control animation from the
AiPlayerChaseComponent.cs not from the spawner so its not much use.

How on earth would I find the instance of each individual spawned item (spawnedEnemyHead, etc) in the AiPlayerChaseComponent and then animate it ?

hmm or should I remove the AiPlayerChaseComponent from my template and add it at runtime...

gaahhh my heads in knots ><
#9
09/17/2010 (10:51 am)
SOLVED - After Cloning a sprite with other mounted sprites, the animation data must be initialised; otherwise you get "invalid animation data" error.

spawnedEnemyHead.AnimationData.Init();
spawnedEnemyTorso.AnimationData.Init();
spawnedEnemyLegs.AnimationData.Init();
spawnedEnemyHead.AnimationData.AnimationCycle = true;
spawnedEnemyTorso.AnimationData.AnimationCycle = true;
spawnedEnemyLegs.AnimationData.AnimationCycle = true;


TorqueObjectDatabase.Instance.Register(spawnedEnemyHead);
TorqueObjectDatabase.Instance.Register(spawnedEnemyTorso);
TorqueObjectDatabase.Instance.Register(spawnedEnemyLegs);

spawnedEnemyHead.Mount(spawnedEnemyTorso, "Head", true);
spawnedEnemyLegs.Mount(spawnedEnemyTorso, "Legs", true);

_enemyHeadAnim.Init();
_enemyHeadAnim.AnimationCycle = true;

_enemyTorsoAnim.Init();
_enemyTorsoAnim.AnimationCycle = true;

_enemyLegsAnim.Init();
_enemyLegsAnim.AnimationCycle = true;

Then within other components getting the instance of each mounted object you have to use GetMountedObject, otherwise any child mounts of the parent object instance will be null.

T2DAnimatedSprite sceneObjectLegs = (T2DAnimatedSprite)_sceneObject.GetMountedObject("Legs");
T2DAnimatedSprite sceneObjectHead = (T2DAnimatedSprite)_sceneObject.GetMountedObject("Head");