Game Development Community

AI Animations only plays when losing window focus (Kinda fixed?)

by Kevin Mitchell · in Torque 3D Professional · 01/08/2012 (12:14 pm) · 35 replies

I'm making a Melee and Magic system but I'm finding that making the AI perform actions is not working unless I lose screen focus. The moment i click inside the game window they return to root animation. Is this a new issue?

I'm currently looking into the setActionThread function to see what is different about the flow when the window is in focus and when not in focus.

Any tips?

Thanks
Kevin
Page «Previous 1 2
#1
01/08/2012 (2:06 pm)
To have more insite i have added a
AI set trigger function to give the ability to send commands to the AI to control AI just like the player.

So instead of:


$mvTriggerCount2++;

i have:


%obj.setAITrigger($TRIGGERS::sCombo1Trigger,true);

things like jumping is working but when i play a animation for a hit it fails. The players melee works.... So why is the AI not applying the animation unless i alt tab to another window.


its not making sence...
#2
01/08/2012 (2:20 pm)

at the end i hit the windows key to lose focus...
#3
01/08/2012 (2:49 pm)
Don't you have to manually reset the root animation after manually calling a setActionThread? There was a thread on this ages ago ...

[edit]
%yourAi.setActionThread("look"); not root
#4
01/08/2012 (3:04 pm)
When focus is not set to the window they are returning to root/idle I have the states debugging: I think its something to do with:


convertActionToImagePrefix

I think this is truncating off the animation... Looking into it now. But to answer your question the animation is not even triggered once undil i hit windows key. But once they are sent it plays fine until focus is regained to the game window.
#5
01/08/2012 (4:14 pm)


ok so i found something:

if(!isControlObject())Con::errorf("2)seq:  %d", anim.sequence);
      if (sUseAnimationTransitions && (action != RPGPlayerData::LandAnim || !(mDataBlock->landSequenceTime > 0.0f && !mDataBlock->transitionToLand)) && (isGhost()/* || mActionAnimation.animateOnServer*/))
      {
         // The transition code needs the timeScale to be set in the
         // right direction to know which way to go.
         F32   transTime = sAnimationTransitionTime;
         if (mDataBlock && mDataBlock->isJumpAction(action))
            transTime = 0.15f;

         F32 timeScale = mActionAnimation.forward ? 1.0f : -1.0f;
         if (mDataBlock && mDataBlock->isJumpAction(action))
            timeScale *= 1.5f;

         mShapeInstance->setTimeScale(mActionAnimation.thread,timeScale);
		 
		if(!isControlObject())Con::errorf("3)seq:  %d", anim.sequence);
         S32 seq = anim.sequence;
		 
		if(!isControlObject())Con::errorf("3)__:  %d", seq);
         S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action);
         if (imageBasedSeq != -1){
			
			 seq = imageBasedSeq;
		 }
		if(!isControlObject())Con::errorf("3)>>:  %d", seq);

         // If we're transitioning into the same sequence (an action may use the
         // same sequence as a previous action) then we want to start at the same
         // position.
         F32 pos = mActionAnimation.forward ? 0.0f : 1.0f;
         RPGPlayerData::ActionAnimation &lastAnim = mDataBlock->actionList[lastAction];
         if (lastAnim.sequence == anim.sequence)
         {
            pos = mShapeInstance->getPos(mActionAnimation.thread);
         }
		 
		if(!isControlObject())Con::errorf("4)>>:  %d", seq);
		if(!isControlObject())Con::errorf("4)seq:  %d", anim.sequence);
         mShapeInstance->transitionToSequence(mActionAnimation.thread,seq,
            pos, transTime, true);
      }
      else
      {
         S32 seq = anim.sequence;
		if(!isControlObject())Con::errorf("5)>>:  %d", seq);
		if(!isControlObject())Con::errorf("5)seq:  %d", anim.sequence);
         S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action);
		 
         if (imageBasedSeq != -1)
            seq = imageBasedSeq;
		 
		if(!isControlObject())Con::errorf("6)>>:  %d", seq);
		if(!isControlObject())Con::errorf("6)seq:  %d", anim.sequence);
         mShapeInstance->setSequence(mActionAnimation.thread,seq,
            mActionAnimation.forward ? 0.0f : 1.0f);
      }

If the screen has focus its going through:

setSequence

if it does not have focus its doing:

transitionToSequence

it seems like transitionToSequence works and setSequence does not.


#6
01/08/2012 (4:25 pm)
what is isGhost() ?
#7
01/08/2012 (5:10 pm)
commented out that one isghost() call but i think its causing issues else where. Is there a more in-dept explanation of ghost?
#8
01/08/2012 (6:03 pm)
Search TDN for ghosting.
#9
01/08/2012 (6:07 pm)
Looking at it now but I can't figure out if I have to make the AI server objects or if they are server objects and I'm calling the class functions incorrectly. Maybe I have to make a server cmd to call operations on them. Looking through your tactical example to see if I'm making my AI incorrectly.
#10
01/09/2012 (4:28 am)
I have moved my NPC code to be created on the server side and they still get isGhost() false sometimes, sometimes its true and its still not working. I've even changed setActionThread to be like this:


void RPGPlayer::setActionThread(U32 action,bool forward,bool hold,bool wait,bool fsp, bool forceSet)
{
   if (!mDataBlock || (mActionAnimation.action == action && mActionAnimation.forward == forward && !forceSet)){
	  return;
   }

   if (action >= RPGPlayerData::NumActionAnims)
   {
      Con::errorf("RPGPlayer::setActionThread(%d): Player action out of range", action);
      return;
   }

   RPGPlayerData::ActionAnimation &anim = mDataBlock->actionList[action];
   
   if (anim.sequence != -1)
   {
      U32 lastAction = mActionAnimation.action;
	  
      mActionAnimation.action          = action;
      mActionAnimation.forward         = forward;
      mActionAnimation.firstPerson     = fsp;
      mActionAnimation.holdAtEnd       = hold;
      mActionAnimation.waitForEnd      = hold? true: wait;
      mActionAnimation.animateOnServer = fsp;
      mActionAnimation.atEnd           = false;
      mActionAnimation.delayTicks      = (S32)sNewAnimationTickTime;
      mActionAnimation.atEnd           = false;
	  

      if (sUseAnimationTransitions && (action != RPGPlayerData::LandAnim || !(mDataBlock->landSequenceTime > 0.0f && !mDataBlock->transitionToLand)) && (isGhost() /*|| mActionAnimation.animateOnServer*/))
      {
         // The transition code needs the timeScale to be set in the
         // right direction to know which way to go.
         F32   transTime = sAnimationTransitionTime;
         if (mDataBlock && mDataBlock->isJumpAction(action))
            transTime = 0.15f;

         F32 timeScale = mActionAnimation.forward ? 1.0f : -1.0f;
         if (mDataBlock && mDataBlock->isJumpAction(action))
            timeScale *= 1.5f;

         mShapeInstance->setTimeScale(mActionAnimation.thread,timeScale);
		 
         S32 seq = anim.sequence;
		 
         S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action);
         if (imageBasedSeq != -1){
			
			 seq = imageBasedSeq;
		 }

         // If we're transitioning into the same sequence (an action may use the
         // same sequence as a previous action) then we want to start at the same
         // position.
         F32 pos = mActionAnimation.forward ? 0.0f : 1.0f;
         RPGPlayerData::ActionAnimation &lastAnim = mDataBlock->actionList[lastAction];
         if (lastAnim.sequence == anim.sequence)
         {
            pos = mShapeInstance->getPos(mActionAnimation.thread);
         }

         mShapeInstance->transitionToSequence(mActionAnimation.thread,seq,
            pos, transTime, true);
      }
      else
      {
        
         // The transition code needs the timeScale to be set in the
         // right direction to know which way to go.
         F32   transTime = sAnimationTransitionTime;
         if (mDataBlock && mDataBlock->isJumpAction(action))
            transTime = 0.15f;

         F32 timeScale = mActionAnimation.forward ? 1.0f : -1.0f;
         if (mDataBlock && mDataBlock->isJumpAction(action))
            timeScale *= 1.5f;

         mShapeInstance->setTimeScale(mActionAnimation.thread,timeScale);
		 
         S32 seq = anim.sequence;
		 
         S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action);
         if (imageBasedSeq != -1){
			
			 seq = imageBasedSeq;
		 }

         // If we're transitioning into the same sequence (an action may use the
         // same sequence as a previous action) then we want to start at the same
         // position.
         F32 pos = mActionAnimation.forward ? 0.0f : 1.0f;
         RPGPlayerData::ActionAnimation &lastAnim = mDataBlock->actionList[lastAction];
         if (lastAnim.sequence == anim.sequence)
         {
            pos = mShapeInstance->getPos(mActionAnimation.thread);
         }

         mShapeInstance->transitionToSequence(mActionAnimation.thread,seq,
            pos, transTime, true);
      }
   }
}


And still the animation is not showing...

Unless i lose focus on the game.
#11
01/09/2012 (4:50 am)
Also more in site to the function i have setup for Melee Combos

void RPGPlayer::applyComboCheck(const Move* move){
   
	
	if (
		move->trigger[sCombo1Trigger] 
		&& isIncombo==false)
    {
		 isIncombo=true;
	     S32 seq;
		 seq=0;
	     switch(sComboGauge)
		 {
				case 0 : {
					seq = RPGPlayerData::Combo1_0;         
					sComboGauge=1;
					break;
				}
				case 1 : {
					seq = RPGPlayerData::Combo1_1;         
					sComboGauge=2;
					break;
				}
				case 2 : {
					seq = RPGPlayerData::Combo1_2;         
					sComboGauge=3;
					break;
				}
				case 3 : {
					seq = RPGPlayerData::Combo1_3;         
					sComboGauge=4;
					break;
				}
		 }
		 if(seq>0){
			 sComboTick=0;
			 setActionThread( seq,false,false,true);
		 }
   }
   else
   {
	   if(
		    !move->trigger[sCombo1Trigger]
	   )
	   {
		   isIncombo=false;
	   }
	   if(sComboGauge>0)sComboTick++;
	   if(sComboTick>60){
		   sComboGauge=0;
		   sComboTick=0;
	   }
   }

}

This works fine with the player the main player can do combos all day. And not have issues.

Its just when I trigger this for AI mobs it fails to play anything. Until i lose focus on the game.

#12
01/09/2012 (6:46 am)
In theory, isGhost() should return false if the NPC is not in scope for any client. If the player is fighting with the NPC in question it should always return true because the NPC should certainly be in scope.

I notice you're using a custom player class - is the current setActionThread() copied directly from the Player class?
#13
01/09/2012 (7:02 am)
The RPGplayer class is a direct copy of the new player class with the only exception is taking the move update function being changed to be modular. And the addition of that function. I've moved the function call around the same modular calls for checking jumps and jetting. And it works just not with the AI player. As for the core functionalities of the Player class it's all the same as player.cpp from 1.2 release.
#14
01/09/2012 (1:14 pm)
I take it your AI agents are RPGPlayer objects and not AIPlayer objects? Otherwise changes to your RPGPlayer class methods won't affect AIPlayers because AIPlayer is descended from Player.
#15
01/09/2012 (1:23 pm)
I have duplicated the AI class into RPGAIPlayer as well in a similar fashion.
#16
01/09/2012 (1:25 pm)
There are no cross over linkages at all.
#17
01/09/2012 (2:02 pm)
if(seq>0){  
             sComboTick=0;  
             setActionThread( seq,false,false,true);  
         }

For that method, it looks like 'seq' should be a sequence name. That method compares the name to the names set in the actionList[], then passes the index to ::setActionThread(U32 action, bool forward, bool hold, bool wait, bool fsp, bool forceSet). Not sure it makes a difference, just a thought.
#18
01/09/2012 (3:23 pm)
RPGPlayerData::Combo1_2

Is the action isn't it? The right? It's translating the Sequence correctly. I've debugged it and it was getting to the check for.

if (sUseAnimationTransitions && (action != RPGPlayerData::LandAnim || !(mDataBlock->landSequenceTime > 0.0f && !mDataBlock->transitionToLand)) && (isGhost() /*|| mActionAnimation.animateOnServer*/))

And all arguments were matching. except for isGhost and it did not allow the code to get to:


mShapeInstance->transitionToSequence(mActionAnimation.thread,seq,  
            pos, transTime, true);

and instead calling:

setSequence

in the else of the function. As soon as I alt time out of the game it goes through:



mShapeInstance->transitionToSequence(mActionAnimation.thread,seq,  
            pos, transTime, true);

and the animations play on the AI
#19
01/09/2012 (5:05 pm)
Yes, but you're using a switch on a number. The function takes a string. If RPGPlayerData::Combo1_2 is a string, then it is what you're looking for. If it's not, then I'm not sure how the function will react.

The function that the script calls:

bool Player::setActionThread(const char* sequence,bool hold,bool wait,bool fsp)
{
   for (U32 i = 1; i < mDataBlock->actionCount; i++)
   {
      PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
      if (!dStricmp(anim.name,sequence))
      {
         setActionThread(i,true,hold,wait,fsp);
         setMaskBits(ActionMask);
         return true;
      }
   }
   return false;
}

So unless the script that loads the sequence names it a number, chances are it won't match. I'm not sure why it works with a player and not the AI though. And I'm not sure why it will play them after the window loses focus, either. I'm just saying, from the looks of it you should be getting an error instead of it working at all....

Something is definitely amiss.
#20
01/09/2012 (6:29 pm)

The function with the char string is not called because setActionThread is Polymorphed.

so if i send in a U32 because of Polymorphism it will use the correct function for the call. Another use of this in the system is:

void RPGPlayer::pickActionAnimation()
{
   // Only select animations in our normal move state.
   if (mState != MoveState || mDamageState != Enabled)
      return;

   if (isMounted() || mMountPending)
   {
      // Go into root position unless something was set explicitly
      // from a script.
      if (mActionAnimation.action != RPGPlayerData::RootAnim &&
          mActionAnimation.action < RPGPlayerData::NumTableActionAnims)
         setActionThread(RPGPlayerData::RootAnim,true,false,false);
      return;
   }
Page «Previous 1 2