2015-01-22 14:52:20

Well,
The problem is that each set of code that was typed out here is different and none of it seems to work for me. Granted I am not the best at BGT, so there could have been some new thing that was introduced above what I was looking at.
But I would really like to get this sound positioning thing right as it is the last huge piece missing in my library.

2015-01-22 15:02:41

Which parts Are working for you?  For example, can you measure the angle that your character is facing?  Can you measure the angle between you and the sound you are trying to hear?  Perhaps listing what parts are working will help us piece together what is missing.
(Sorry if you already did this in an earlier post.  I didn't go back and read all of the posts on this thread.)

- Aprone
Please try out my games and programs:
Aprone's software

2015-01-22 16:16:01

The following code is correct as far as I can tell.  I included the test cases.  The function pan_value returns two things: a value between -1 and 1 as above and a true/false value that tells you if the object is behind the player or not.  This is basically a 2d transformation matrix, but gameobjects is overkill so I didn't bring it in.
Possibly unlike the first post, the angle 0 is east, the angle 90 is north, the angle 180 is west, and the angle 270 is south.  If you need to make it so that 90 is south and 270 is north, you can negate the parameter as the first line of the function.  Tabs apparently break hard on this forum in the sense that NVDA at least doesn't like reading indentation indication when you use them (the forum appears to be doing some sanitizing).  Consequently, I used single-space indentation.
if you can find a test case where this is broken, post it and I'll fix the code.

from __future__ import division
import math

def dot(a, b):
 return sum(a[i]*b[i] for i in xrange(len(a)))

def magnitude(a):
 return math.sqrt(sum(i**2 for i in a))

def normalize(a):
 m = magnitude(a)
 return tuple(i/m for i in a)

def pan_value(player_pos, player_facing, sound_pos):
 player_facing=player_facing*(math.pi/180.0)
 front = normalize((math.cos(player_facing), math.sin(player_facing)))
 right = normalize((math.cos(player_facing-math.pi/2.0), math.sin(player_facing-math.pi/2.0)))
 #translate the sound's position to be relative to the player.
 translated_pos= sound_pos[0]-player_pos[0], sound_pos[1]-player_pos[1]
 #y is front because this gives an angle on the range 0 to pi instead of -pi/2 to pi/2.
 x= dot(right, translated_pos)
 y = dot(front, translated_pos)
 angle =math.atan2(y, x)
 is_behind = y < 0
 return (math.cos(angle), is_behind)

print pan_value((5, 0), 0, (2, 0))
print pan_value((5, 0), 90, (0, 0))
print pan_value((5, 0), 110, (0, 0))
print pan_value((5, 0), 270, (2, 0))
print pan_value((5, 0), 270, (2, 1))

And that's it.

My Blog
Twitter: @ajhicks1992

2015-01-22 17:02:02 (edited by frastlin 2015-01-22 17:05:52)

I'll go through camlorn's code and see what he's doing. It's just a little different than what Aprone did.
BTW, you can copy and paste the code into EdSharp and do a ctrl+r and type:
replace:
   
with
\t
and it replaces all 4 spaces with a tab.
But Aprone, All my posts say the problem, but post 43 and 49 show what is going on.
In a nutshell, the code is giving an error because you can't divide numbers by 0 and it is coming out with the sound as a -90 when the player is at 10,2 facing 270 and the sound is at 5,2. It is also coming out with a -90 when the player is at the same place 10,2 facing 270 and the sound is at 15,2.
One should give 270 and the other should give 90.
*question*Why does everyone have east at 0 when most people like to start out north?

2015-01-22 17:51:44

My east is at 0 because I was trying not to break the convention too badly, but Aprone's east is at zero most likely because this is how trigonometry works.  The standard convention in the literature is east is 0 radians and positive angles are counterclockwise from that.  If you really need a version with north at 0 and going clockwise, I can maybe work one out for you-but clockwise is just negating the function's second parameter on the first line and north is just adding 90 degrees.
Do not try to understand my code.  Just use my code and post if it's broken.  The only way you'll understand my code is if you know what sine and cosine do in terms of the unit circle and what the dot product means.  In fact, going over it now, the normalize for front and right is a no-op.  Ah well.
I know how to find/replace. I used one space instead of one tab because the forum here is doing something interesting with tabs, a fact which made your code above hard to read.  The entire situation with Firefox indentation is weird, and there's almost no way around it.  Of all of us, I am the only one who does not care about four spaces versus one tab; this is because I read and work with code from the sighted, and it's really not that big a deal anyway.

My Blog
Twitter: @ajhicks1992

2015-01-23 01:32:59 (edited by frastlin 2015-01-23 01:35:28)

Hm, You're right, I'm very lost on this code... I did however make it python 2 and 3 compliant I believe as well as sped it up to 1/3 it's original speed.


from math import cos, sin, sqrt, atan2, pi


def dot(a, b):
    return (a[0]*b[0]) + (a[1]*b[1])


def magnitude(a):
    """returns the sqrt of a squared + b squared"""
    return sqrt(a[0]**2 + a[1]**2)


def normalize(a):
    m = magnitude(a)
    return (a[0]/m, a[1]/m)


def pan_value(player_pos, player_facing, sound_pos):
    player_facing = player_facing*(pi/180.0)
    front = normalize((cos(player_facing), sin(player_facing)))
    right = normalize((cos(player_facing-pi/2.0), sin(player_facing-pi/2.0)))
    #translate the sound's position to be relative to the player
    translated_pos = sound_pos[0]-player_pos[0], sound_pos[1]-player_pos[1]
    #y is front because this gives an angle on the range 0 to pi instead of -pi/2 to pi/2
    x= dot(right, translated_pos)
    y = dot(front, translated_pos)
    angle = atan2(y, x)
    is_behind = y <0
    return (cos(angle), is_behind)


print(pan_value((5, 0), 0, (2, 0)))


I guess with this many equasions, it is bound to be somewhat slow, but I don't think it matters when using pygame though as I don't think you can have more than 16 sounds going at once...

2015-01-23 03:34:46 (edited by camlorn 2015-01-23 03:37:16)

Okay. So, here is an informative Python repl session.

>>> import timeit
>>> timeit.timeit(lambda: pan_value((5, 0), 90, (2, 0)), number=10000)/10000.0
1.0377488363072835e-05
>>> timeit.timeit(lambda: pan_value((5, 0), 90, (2, 0)), number=10000)
0.10387503569908141
>>>

So you can basically run 2 or 3 thousand calls to it a frame without it even being that big a deal.
Your version may be faster, but mine is extensible.  The magnitude, normalize  and dot functions I am utilizing will work with any number of dimensions, up to and including vectors that are so long they break your ram.  Unfortunately, the pan_value function only works in 2-the 3-dimensional version absolutely requires transformation matrices, because otherwise you have to do a projection onto a plane.  Obviously panning stops mattering once we get into 4, as do most games.
But seriously, don't optimize until and unless it proves to be too slow.  This can be microoptimized even further by not using tuples at all and getting rid of the dot and normalize functions and technically you can compute cosine with a division.  But it simply does not matter.  Code for clarity, not for speed.
Make your game.  When your game proves to be too slow, profile it.  Then fix whichever functions are at the top of the list of total time taken.  Any other optimization in Python is simply not worth it-if you are actually doing something that might actually have a performance issue of this sort at all then you're using C/C++.
I could care less about Python 3-a huge number of libraries remain unported, enough that most people who use the language still use 2.  This is changing and I'll start caring when Twisted and Gevent port as, if you're going to try networked games in Python, you really really want one or the other of those.  3 has some very nice features, but a lot of the useful stuff is available as backports and it's just not worth it without all the stuff you can get in 2 (hint: gameobjects, possibly the ODE bindings, not sure if lxml ported yet or not...).  3 is nice in theory, but it's going to stay theory until we actually see a concerted effort by all the major libraries-in practice, braking backward compatibility in an established programming language as they did means they're lucky that Python is still popular.
As for the explanation of what I'm doing, I need 2 or 3 lengthy articles to teach the preliminaries.  I'll probably write them over the summer when I have some time and mental energy; I'm basically certain they'd be valuable at this point, given how often this kind of thing comes up.  It's not that hard if you have pictures, but we don't have pictures. They are worth a whole lot more than a thousand words for this kind of thing.

My Blog
Twitter: @ajhicks1992

2015-01-23 12:19:23

So, This just pans a sound based on a location 1 coord away.
But it also says that the sound is behind when it is really to the side. See the following prints:

print pan_value((3, 2), 90, (5, 2))
print pan_value((4, 2), 90, (5, 2))
print pan_value((5, 2), 90, (5, 2))
print pan_value((6, 2), 90, (5, 2))
print pan_value((7, 2), 90, (5, 2))


(1.0, False)
(1.0, False)
(1.0, False)
(-1.0, True)
(-1.0, True)

2015-01-23 17:31:49

Yeah. You probably can't actually fix that, and in practice it's not noticeable.
See, the thing is that two floats are almost never equal.  The only time you can be sure that a==b is if you type out the values for a and b immediately before hand.
In the same way that we can't represent 1/3 as a decimal with complete accuracy, computers also can't represent certain decimals with complete accuracy, either.  Floating point math is completely weird and surprises a lot of people-for example, if you keep adding one to a float for long enough it'll stop going up because the next value is further than 1 away and negative zero actually exists.  When you add in the fact that pi is also an approximation (recall that pi is infinite), the chances of it deciding that 90 is consistently in front of you in all cases is basically nil.
that line is ambiguous anyway.  At what point do we say that it is "behind" you?  It's kind of both.  But in practice, your players aren't going to notice that 89.939 degrees to your right is in front but 90 degrees to your right is behind, etc.

My Blog
Twitter: @ajhicks1992

2017-04-30 04:53:49

I understud all of the consepts of the code, but i'm not able to think on how we could play a footstep sound exactly whem the player moves a distance of 1.0 from their previous spot. Is there a formula to solve it, or do i must get the average motion time?

2020-05-28 22:31:31

Hi all,
Afraid I've done that most terrible of things! I've copy and pasted Aprone's wonderful formula into my own code, without really understanding the maths behind it.

It's all working fine, except I'm not sure how to adjust my WebAudio listener's orientation to match.

Does anyone have the magic formula? Or a pointer where I can go learn, read, and work one out for myself?

Thanks so much for the maths Aprone, it's really useful stuff, and thanks all in advance.

-----
I have code on GitHub

2020-05-29 06:32:03

I know this question might be stupid, but I am genuinely interestedi n the process.
How does the cosine relate to the horizontal, and sine to the vertical line? DOes it have to do something with the function graphs? I actually understand the math, just don't understand that part.

If you want to contact me, do not use the forum PM. I respond once a year or two, when I need to write a PM myself. I apologize for the inconvenience.
Telegram: Nuno69a
E-Mail: nuno69a (at) gmail (dot) com

2020-05-29 15:59:11 (edited by Rastislav Kish 2020-05-29 16:11:50)

Hi there,
@62: you can imagine it easily from sine's and cosine's diagrams (warning, I mean diagrams, not function graphs).

Imagine a 2d graphing plane, with x axis going from left to right, and y axis from bottom to top. On this plane, imagine a cyrcle with radius 1 and center in coordinates 0;0.
Now, imagine a pointer, vector, which would be fixed in 0;0, and point to 1;0 i.e. to the right. This pointer has length 1, and angle 0 degrees with x axis.
Imagine this pointer would start rotating in the cyrcle, like clock pointer rotates in watches, just in opposite direction i.e. counterclockwise.
At every moment, distance between pointer's ending and x axis is sine of the current angle.

Why?
Because the pointer has length 1 and is hypotenuse, whereas the distance between pointer's ending and x axis is the opposite cathetus to our angle. Marking this distance as d, from definition of sine as oppositeCathetus/hypotenuse we get:
sin=d/1
and thus:
sin=d

The distance between pointer's ending and y axis is on the other hand cosine. Reason is similar like with sine, cosine is defined as:
cos=nearbyCathetus/hypotenuse
i.e. in this case:
cos=nearbyCathetus/1
cos=nearbyCathetus

I'm not sure if I'm using the right english term for nearby cathetus, I mean thatone directly near the angle.

Now, you should be able to tell just naturally, why sine for one and cosine for other dymension.

If your angle system has 0 degrees on the east and increases in counterclockwise movement, like in the case above, sine is logical choice for forward movement i.e. y axis, because the sine goes in this direction, whereas cosine is natural for sides, as it's the distance from y axis and thus modifies your x coordinate.
Infact, any angle you choose on these "clocks", you can see its sine and cosine, like you'd need to add in a game.

If your angle system works like in perhaps most games, i.e. with zero pointing to the north and angle increasing when turning in clockwise direction, the same diagram applies, you just need to turn it to right position.
Now zero is infront of you, the pointer rotates in clockwise movement and sine is the distance between its ending and y axis, whereas cosine the distance between its ending and x axis.
Thus it makes sense to increase x by sine and y by cosine, as those are their natural directions.

I hope this explanation made at least some sense. smile

Best regards

Rastislav

2020-05-29 16:15:31

@Aprone, thank you for these tutorials on coding and can someone please post the link for beginners since I’m still learning the language. This is advanced for me but very helpful.