Hello there. Here is the post I saved that Aprone posted a while back with his suggestions. It also contains a technique for movement code for an FPS.
Previously I wrote a thread to help people who were trying to learn the basic concepts of programming. It was aimed at the absolute beginner. For this next thread I will be changing my focus to the somewhat more advanced programmers, and I write this here so that people who benefited from the last thread won't misunderstand and think that this is the next step in their programming journey. smile
In this post I want to cover some free 2D movement and sound panning methods like those used in Swamp. I'm asked about this every so often and I would usually just respond with "It's complicated". Recently I decided to run Ghorthalon through the process to be helpful, but also to see if it would be as hard to explain as I feared it might be. As it turns out, the process went pretty smoothly and it seemed like a good idea to write up the steps here for the benefit of everyone.
This will cover 1 way that this could be done and it is intended to give you the concepts behind panning sound to mimic objects all around you. I will try to keep the code generic so that it can be used in any language which supports the ability to pan sounds and adjust their volume. Because this is intended for programmers with a bit more experience, it is very possible that some people will not understand how to follow along. If that happens there isn't much I can do to help you, it will just mean that you still need more work understanding things like arrays, loops, and so forth.
First we will cover free movement in a smooth landscape which is not locked to a grid. We will assume that your player's X and Y coordinates are stored in the variables mya and myb. The variable theta will hold the angle, in degrees, that your person is facing. I thought the use of radians might be a bit over some people's heads, so to keep things comfortable this code will do everything in degrees. Theta value of 0 represents East, 90 is South, 180 is West, and 270 is North. Of course a value such as 30 would be aiming the player a little bit South but mostly East. We will start with these 2 lines of code.
tempa = mya + (0.1 * Cos((theta * 3.14) / 180))
tempb = myb + (0.1 * Sin((theta * 3.14) / 180))
The 0.1 is the distance the player will travel, and this code is not meant to jump you directly to some destination but is rather meant to be placed in your main game loop so that it is repeated multiple times as you inch your way along the map. At the end of those 2 lines of code, the variables tempa and tempb will be holding the new X and Y coordinates that your player should be at after moving the 0.1 distance and facing the angle theta.
You might be wondering why the new coordinates are being stored in tempa and tempb, instead of being put back into mya and myb. It's true that your player won't actually be moving until you say mya = tempa and myb = tempb, but I used those temporary variables to help you with colision detection. Before you actually move your player, this is a good time to make sure your player won't be running inside of a wall or other obstacle. Just check using tempa and tempb, and if the area is actually clear, then you can update mya and myb to be the values stored in tempa and tempb.
Within your game loop you could continue to add or subtract from theta as the left or right arrow keys are held down. Moving forward with the up arrow would repeatedly run your movement code, and moving backward can be accomplished by just using -0.1 instead of 0.1 as the distance part.
Moving smoothly around a map is only a small part of the battle. The more difficult task is to have sounds pan and adjust volume to match. I am going to generalize all sounds that you might be using, even though in your game every decorative sound, enemy, or gun shot will all be their own sources of sound. Arrays are a very important part of this, so I recommend keeping your sound sources grouped so that you can easily loop through them.
As you move around or as sound sources move around you, the first step is to loop through all of the sounds and ignore any that are out of range. The range will be some maximum distance you decide, which marks the distance that any sound will be impossible to hear. For this example I will use the number 75. To ignore sounds that are too far away, you must first calculate the distance between you and each sound.
d = Int(Sqr(((mya - sounda) ^ 2) + ((myb - soundb)) ^ 2))
I am using mya and myb again to be the X and Y coordinates of your person. Sounda and Soundb are the coordinates for the sound we are checking. When this line of code is finished, the distance between you and the sound will be stored in the variable D. Now that we know the distance we can only proceede to the next steps if D is less than 75. Remember that 75 is our maximum distance we want players to be able to hear.
Now that we are dealing with a sound that is within range, we need to find out the absolute angle between you and the sound. Not everyone is going to understand the difference between absolute angles and relative angles so I will see if I can explain it. When dealing with absolute angles, this looks at the entire game world and compairs your coordinates to the sound's coordinates. When dealing with relative angles, the world is viewed from your player's perspective so your player's coordinates won't come in to play. I hate to possibly confuse people right here, but it will make far more sense later if I explain this here.
So now on to calculating that global angle! It is a total of 6 lines.
temp1 = sounda - mya
temp2 = myb - soundb
If temp1 = 0 And temp2 = 0 Then absolutetheta = -1
If temp2 > 0 Then absolutetheta = Atn(temp1 / temp2)
If temp2 <= 0 Then absolutetheta = Atn(temp1 / temp2) + 3.14
absolutetheta = (absolutetheta * (180 / 3.14))
Once again I am using a few temporary variables just to make things a little easier to separate. When we are finished, the variable absolutetheta holds the absolute angle between the player and the sound. Now we need to use this absolute angle to find out the relative angle, and thankfully this is as easy as just subtracting the angle the player is facing from the absolute angle.
relativetheta = absolutetheta - theta
To put things into perspective I will try to explain a little bit about what this relative angle means. If relativetheta was equal to zero, then the sound is exactly in front of us. If relativetheta equals 45 then the sound is 45 degrees to our right, putting it halfway between being in front of us and being exactly to our right. 180 would be behind us and 270 would be directly off to our left. Clearly we are getting closer to our goal, but we still have more to do.
We need to figure out our horizontal panning so we will use a line of code that is similar to one we used before.
temph = Cos(relativetheta * 3.14 / 180)
This will return a value between -1 and +1 but we need it to actually match the maximum panning values of the language you are using. For this example we will assume that your language uses -10000 for panning all the way to the left, and +10000 for panning all the way to the right. So to get our -1 to +1 to match the -10000 to +10000, we can just multiply temph by 10000.
temph = temph * 10000
Later on if you find that your sounds are going the exact opposite to how they should be, just make this -10000 instead. Basically putting the negative sign flips the left and right.
Use your language's sound commands to set the panning to be temph.
We aren't done yet though! Volume still needs to be figured out. For that we will use another line of code which will break down the vertical component.
tempv = Sin(relativetheta * 3.14 / 180)
The variable tempv now holds a value between -1 and +1. When tempv is less than zero it means that the sound is somewhere in front of you. A value greater than 1 means it is coming from behind you. This lets you use some kind of muffling feature or volume reduction if you want to help players know that it is behind them, but of course your language needs to support those features to use them.
To adjust our volume we will use the distance between us and the sound. The good news is that we already had the distance calculated and stored in the variable D from earlier. We will also be using the number 75 which was the maximum distance we can hear sounds from. Check your language and find out what value represents a sound being completely muted. For this example we will assume that a value of 0 will play the sound at full volume, and a value of -5000 is complete silence. Use the value for mute in the equation.
tempvolume = (d*-5000)/75
Now just use your language to set this sound's volume to be equal to tempvolume. And you're done! Horray!
I'm not really sure how to end this little tutorial, so I guess I'll end it by asking people to help each other out with this. If you understand what is going on and are able to make it work, consider lending a hand to help others who are still trying to figure it out. I've heard that many programming techniques get treated as secrets among developers. While I can understand the desire to hold on to knowledge that gives you an advantage with your games, but at the same time it works to hold back the developer community in general. I'd like to see everyone advancing their understanding of these types of concepts so that future games will be better as a result! smile
Go to Heaven for the climate, Hell for the company. - Mark Twain