Game Development Community

dev|Pro Game Development Curriculum

Shapebase Animation Triggered Sounds

by Jon Jorajuria · 08/17/2006 (12:10 pm) · 10 comments

Download Code File

This resouces allows sounds to be played by animated objects based upon animation triggers. This was originally created for an upcoming content pack which will demonstrate how to use animations to trigger sounds. Adding this functionality allows developers to control the timing of animation sounds attached to a DTS/Shapebase object.

In shapeBase.cc change void ShapeBase::advanceThreads(F32 dt) from:

void ShapeBase::advanceThreads(F32 dt)
{
   for (U32 i = 0; i < MaxScriptThreads; i++) {
      Thread& st = mScriptThread[i];
      if (st.thread) {
         if (!mShapeInstance->getShape()->sequences[st.sequence].isCyclic() && !st.atEnd &&
             (st.forward? mShapeInstance->getPos(st.thread) >= 1.0:
              mShapeInstance->getPos(st.thread) <= 0)) {
            st.atEnd = true;
            updateThread(st);
            if (!isGhost()) {
               char slot[16];
               dSprintf(slot,sizeof(slot),"%d",i);
               Con::executef(mDataBlock,3,"onEndSequence",scriptThis(),slot);
            }
         }
         mShapeInstance->advanceTime(dt,st.thread);
      }
   }
}

to

void ShapeBase::advanceThreads(F32 dt)
{
   for (U32 i = 0; i < MaxScriptThreads; i++) {
      Thread& st = mScriptThread[i];
      if (st.thread) {
         if (!mShapeInstance->getShape()->sequences[st.sequence].isCyclic() && !st.atEnd &&
             (st.forward? mShapeInstance->getPos(st.thread) >= 1.0:
              mShapeInstance->getPos(st.thread) <= 0)) {
            st.atEnd = true;
            updateThread(st);
            if (!isGhost()) {
               char slot[16];
               dSprintf(slot,sizeof(slot),"%d",i);
               Con::executef(mDataBlock,3,"onEndSequence",scriptThis(),slot);
            }
         }
         mShapeInstance->advanceTime(dt,st.thread);
      }
   }
   if (mShapeInstance && !isGhost()) {
		for (U32 i = 0; i < 5; i++) {
			if (mShapeInstance->getTriggerState(i)) {
				char slot[1];
				dSprintf(slot,sizeof(slot),"%d",i);
				Con::executef(mDataBlock,3,"onAnimationTrigger",scriptThis(),slot);
			}
		}
	}  
}

Now compile the source code.

To activate the code via script add the following to the object's *.cs file:

function YourDataBlockName::onAnimationTrigger(%this,%obj,%trigger)
{
    %obj.PlayAudio(1,YourSoundDataBlock);
}

**Note You will have to create your sound's audioprofile and add triggers to your animation. This resource does not affect the player/character triggered sounds.

Included with this is a demo which you can use to test.

Place the demoobject directory in ~/data/shapes
Place the testsnd.ogg file in ~/data/sound
Place the demoobject.cs file in ~/server/scripts

In game.cs under

exec("./aiPlayer.cs");

add

exec("./demoobject.cs");

Now go into the editor and add the DemoObject located in the Shapes/Misc tree.

#1
08/17/2006 (9:20 pm)
Hey, cool. Didn't know you were going to post this.

It may not be the most elegant solution, but it seems to work.
#2
09/18/2006 (1:34 pm)
running into a problem

when i compile all of a sudden i get
7>c:\torque1.4 freshb\sdk\engine\core\unicode.cc(17) : warning C4068: unknown pragma
7>c:\torque1.4 freshb\sdk\engine\core\unicode.cc(33) : warning C4068: unknown pragma
7>c:\torque1.4 freshb\sdk\engine\core\unicode.cc(44) : warning C4068: unknown pragma
7>c:\torque1.4 freshb\sdk\engine\core\unicode.cc(48) : warning C4068: unknown pragma
7>c:\torque1.4 freshb\sdk\engine\core\unicode.cc(51) : warning C4068: unknown pragma
7>c:\torque1.4 freshb\sdk\engine\core\unicode.cc(63) : warning C4068: unknown pragma
7>c:\torque1.4 freshb\sdk\engine\core\unicode.cc(198) : warning C4068: unknown pragma
7>c:\torque1.4 freshb\sdk\engine\core\unicode.cc(334) : warning C4068: unknown pragma

i know only warnings but now they show up once i change the shapebase.cc and compile

now i have all the demo items put into game, but when i try to load the mission i get

Fatal: (c:\torque1.4 freshb\sdk\engine\ts\tsanimate.cc @ 1019) TSShapeInstance::getTriggerState: state index out of range

and it crashes any help would be appreciated
#3
09/21/2006 (7:30 pm)
Updated the code...sorry about that.
#4
03/07/2007 (2:47 am)
Hi

I also get this 'TSShapeInstance::getTriggerState: state index out of range' error message.

What should I do to fix it?
#5
06/08/2007 (7:31 am)
Terrific, this is just what I wanted. Thanks Jon, Todd & Mike.

For Maya2DTS users -- be aware that your triggers will not fire unless you have keyframed the bounds object for your .dsq export. It's not that the bounds needs to move, and the keyframe can be anywhere, but it needs to be there. Learned this after much head-wringing via this thread:
http://www.garagegames.com/mg/forums/result.thread.php?qt=32780
#6
06/11/2007 (4:45 pm)
@Imm Dtu, if you're still around, I discovered I had the same prob when running Debug build (though not Release) of TGE1.5.2.

Here is a solution*. Made 2 tiny changes to the new code in shapebase::advancethreads:

if (mShapeInstance && !isGhost()) {
		for (U32 i = [b]1; i < 33;[/b] i++) {          // was i<5, upped to 32 to grab more events
			if (mShapeInstance->getTriggerState(i)) {
				char [b]slot[2][/b];		// hmm, was 1, caused crash in dSprintf func in winStrings.cc
				dSprintf(slot,sizeof(slot),"%d",i);
				Con::executef(mDataBlock,3,"onAnimationTrigger",scriptThis(),slot);
			}
		}
	}

The key change is that the for-loop index needs to start at 1, not 0, because the getTriggerState will decrement the index itself. That's our crash.

Also, the char slot declaration needs to be at least [2] instead of [1]. Otherwise, dSprintf will crash. Not really sure why.

Finally, I upped the max index on the for-loop to 32, so I could capture more animation events. This means more processing time I suppose but doesn't seem to slow anything down for me. You could leave it at 5 if that's all you need.

*I don't really know WTF I'm doing. I'm no programmer. Just hacking about. If someone else can illuminate/show a better way, please do.
-Joe
#7
02/27/2008 (5:19 pm)
Hello,
I can't seem to ever get the onAnimationTrigger function to be called. So I went into the engine and set a break point and noticed that this if statement => if (mShapeInstance->getTriggerState(i)) never becomes true. Hence the reason why the Con::executef is never calling the onAnimationTrigger. Does this work for other people? If so, what is essentially going on here?, what makes mShapeInstance->getTriggerState(i) change and become true? Does anyone know? Any help would be greatly appreciated.
Thx.
#8
04/05/2008 (6:38 pm)
Joe:

Excellent work for someone who's "no programmer". You are correct in that the slot[1] was not big enough and this is why it crashed. Good catch! A string with "1" is actually the 1 and the zero terminator (not seen) and so, it requires 2 bytes.

A minor improvement: Now that you allow the 1-32 possibilities, the it should be char slot[3], since we can now have two digits (example, "28").

Also, the other problem is actually an "assert" that checks the range of the state needs to be 1 to 32. I really don't understand this since the DTS Plus exporter says you can have negative values. According to its help file, the negative ones will shut off the trigger. Still, you're right, the legal values are 1 to 32 and then is changed (only inside) to 0-31 for "internal" use since most C/C++ code uses zero-based indexing.

DALO: I found that when using DTS Plus exporter, the beginning and ending frames seem to be ignored. So, I copied the keyframe from 1 and made an extra at 2. I don't know if I really need to make an extra key-frame, but when I used this frame number (2) I was able to get it to fire the trigger. Same deal for the last frame. So, I'd guess it's a bug in the DTS Plus exporter, but this should give you a work-around.

Authors (and Joe): Thanks for a great resource!!

All: I hope this gets incorporated in the trunk. Anyone know how to do that?

-Mike
#9
04/05/2008 (9:55 pm)
Mike - thanks! and thx for the char slot[3] insight, I was putting off figuring out why my triggers above 9 weren't working, that has got to be it.
Joe
#10
05/13/2008 (4:22 am)
I had stack corruption arround "slot" which cause the error in debug mode ""dSprintf wrote to more memory than the specified buffer size - Stack Corruption Possible""

so I changed it to:

if (mShapeInstance && !isGhost()) {
		for (U32 i = [b]1[/b]; i < 5; i++) {
			if (mShapeInstance->getTriggerState(i)) {
[b]
				// char slot[1];
				// dSprintf(slot,sizeof(slot),"%d",i);
				Con::executef(mDataBlock,3,"onAnimationTrigger",scriptThis(),Con::getIntArg(i));
[/b]
			}
		}
	}

Edit: Ups, i just saw Joe Pinney did also made a fix for this. So this is an alternativ fix ;)
Edit2: and yes it should start at 1 instead of 0