How to change footstep noises on different regions

Started by LimboJimbo84, Fri 22/12/2017 16:28:50

Previous topic - Next topic

LimboJimbo84

Hi everyone,

I'm deep into creating my first game and have reached the point where I want my character to produce different footstep noises as he walks onto different surfaces.

I was planning on using the CharacterRegionSounds module by Strazer but the code seems to relate to a much older version of AGS, so I'm reluctant to put it into my game. From a quick look around it seems like I'm right to be skeptical.

From having a look through the forums it seems like the best way to approach this is to use region Walk Onto and Walk Off to produce the different footstep noises. As far as I can tell the way to approach this is to create functions in the global script that I might call DirtStep or Woodstep for example and call these as needed.

The problem is that I am at a bit of a loss on how to code these. Am I right in saying that the Game.GetViewFrame and LinkedAudio property are central to this?

As you can probably tell, I am completely new to coding and AGS, so any and all help would be greatly appreciated!


Slasher

Hi,

I generally set footstep linked audio to frame(s) if you want different sounds..

Example:

Code: ags

function repeatedly_execute_always()
{
 if(cDan.x >=369 && (cDan.Frame==1 || cDan.Frame==3))  // Happens on these Frames of the animation.
{      
 aSwim.Play();// Plays footstep in water sound.
}
}


Of course you can link the sound in other ways...

LimboJimbo84

That looks like quite an elegant solution. Am I right in saying that youd put this into the room script rather than the global script? I guess you'd have to check the player room in the global script? Thanks for the suggestion :-)

eri0o

I think the room rep execute doesn't run on blocking actions, so if a player walk is blocking it won't trigger.

LimboJimbo84

Quote from: eri0o on Fri 22/12/2017 20:28:31
I think the room rep execute doesn't run on blocking actions, so if a player walk is blocking it won't trigger.
From what i can gather that's true of repeatedly_execute, but repeatedly_execute_always ignores the block. I will test this out tomorrow and report back.

Vincent

Quickly answer: I would start doing something like this but it's not tested, it might not work too.

Code: ags

ViewFrame *frame; // how many frames you need to play a sound, you could use an array if you need many frames.

function HandleFootstepsSound()
{
  Region*r = Region.GetAtRoomXY(player.x, player.y);
  if (player.View == WALK)
  {
    if (r == region[1])
    {
      frame = Game.GetViewFrame(WALK, player.Loop, 3);
      frame.LinkedAudio = aFootstep_wood;
    }
  }
}


function repeatedly_execute_always() 
{
  HandleFootstepsSound();
}

Snarky

Some other posts while I was writing. This advice overlaps a little with them, but deals with other issues too.

Room_RepExec() (or whatever it's called, I forget the exact syntax) doesn't run while the game is blocking, but I'm 90% certain that if you put a repeatedly_execute_always() function in a room, it will run like any other repeatedly_execute_always() function as long as you're in that room.

I think there's a problem in Slasher's script, though. It will play a sound every game loop while that frame is displayed, which means that with an animation speed of 3 or 4 you'll be playing 3 or 4 instances of the footstep sound each step. (I think this will create a hollow echo or clang-y effect, but that's just a guess.)

What you should do is make sure that the sound is only played once each frame, by only playing it if the frame has just changed:

Code: ags
int danLastFrame; // Make this a global variable, probably

function repeatedly_execute_always()
{
 if(cDan.x >=369 && ( (cDan.Frame==1 && danLastFrame != 1) || (cDan.Frame==3 && danLastFrame != 3) ))  // Happens on these Frames of the animation.
   aSwim.Play();// Plays footstep in water sound.
 danLastFrame = cDan.Frame;
}


There's also another issue: this will also play the footstep sound on the 1st and 3rd frame when Dan is talking or playing any other animation.  All in all, I think it would be cleaner to make a dedicated function in a separate script:

Code: ags
// Dan Footsteps Header
import AudioClip* danFootStepSound;


Code: ags
// Dan Footsteps Script
#define DAN_LEFT_FOOT_FRAME 1
#define DAN_RIGHT_FOOT_FRAME 3
AudioClip* danFootStepSound;
int danLastFrame;
export danFootStepSound;

bool newFrame(int frame)
{
  return (cDan.Frame == frame && danLastFrame != frame);
}

function repeatedly_execute_always()
{
  if(cDan.Moving && cDan.View == this.NormalView)
  {
    if( danFootStepSound != null && (newFrame(DAN_LEFT_FOOT_FRAME) || newFrame(DAN_RIGHT_FOOT_FRAME)) )
      danFootStepSound.Play();
    danLastFrame = cDan.Frame;
  }
  else
    danLastFrame = -1;
}


And then you can put something like this in your room script:

Code: ags
function region1_WalksOnto()
{
  danFootStepSound = aSwim;  // Footstep in water sound
}

function region1_WalksOff()
{
  danFootStepSound = aFootstep;  // Normal footstep sound
}


(Edit: Looking closer at Vincent's code, I like his basic approach better, since it actually sets the built-in link-sound-to-frame property. I see some room for improvement, but I guess we don't need to do a whole module for this.)

Vincent

I also think that Snarky's code will work as well. It might could look fancy to write a new module for it.:)
I have added more lines just to prevent hopefully some null errors while I was reading back. But I can't have a time to test this.
Code: ags

ViewFrame *frame;
 
function HandleFootstepsSound()
{
  Region*r = Region.GetAtRoomXY(player.x, player.y);
  if (player.View != WALK) return; // put your view
  else
  {
    if (r != region[1] && r != region[2]) return; //all the regions which contains sound
    else
    {
      if (r == region[1])
      {
        frame = Game.GetViewFrame(WALK, player.Loop, 3);
        frame.LinkedAudio = aFootstep_wood;
      } 
      else if (r == region[2])
      {
        frame = Game.GetViewFrame(WALK, player.Loop, 3);
        frame.LinkedAudio = aFootstep_wood_02;
      }
      else
      {
        frame.LinkedAudio = null;
      }
    }
  }
}
 
 
function repeatedly_execute_always() 
{
  HandleFootstepsSound();
}



Crimson Wizard

#8
Vincent, your code for setting new sound is right, but in the way you call it I see few wrong things.

First of all, it again will be called 40 times every second as long as player stands on a region.
You better remember which region player was before to see if regions has changed, and set new sounds only when they are changed.
If you do that, you could also remove "if (player.View != WALK)" check, because that becomes unnecessary, and set sounds to every walking loop too, not only the ones that are currently active.

Normally this should work like a state change. When region change, all the frames that have walking sounds should be updated, but only once.

Vincent

Quote from: Crimson Wizard on Fri 22/12/2017 22:02:19
You better remember which region player was before to see if regions has changed, and set new sounds only when they are changed.
If you do that, you could also remove "if (player.View != WALK)" check, because that becomes unnecessary, and set sounds to every walking loop too, not only the ones that are currently active.

This is right indeed. Also the "if (player.View != WALK)" check is unnecessary. It was enough to specific the view that need to be used only, like "player.View == WALK". This is called 40 times every second as long as player stands on a region but if the frame which as the linked sound is 3 which I suppose that doesn't happen every 40 times every second. But at the moment I am not sure about this. However I would check the region state change by using a boolean at all.

SMF spam blocked by CleanTalk