OpenAL 3d Positioning and Panning Center - ios

Using OpenAL, one can set the distance model:
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
And the position of a sound effect:
float globalRefDistance = 125.0f;
float globalMaxDistance = 1250.0f;
ALfloat alPos[] = {pos.x, pos.y, 0.0f};
alSourcefv(soundId, AL_POSITION, alPos);
alSourcef(soundId, AL_REFERENCE_DISTANCE, globalRefDistance);
alSourcef(soundId, AL_MAX_DISTANCE, globalMaxDistance);
This attenuates and pans the sound nicely, except when the listener is close to the source and steps back and forth to the left and right of the sound. In this case, the panning quickly goes from left to right. There is not really a spot where it plays the sound panned in the center.
How can I define a range/window/cone where OpenAL puts a 3d sound right in the center, without panning?
I want to be able to walk up to the sound from the left, hearing it gradually fade in from the left channel. Then be in both channels for awhile. Then gradually fade out in the right channel.
I've tried messing with setting the sound to be directional, but it doesn't seem to do the trick:
alPos[0] = 0.0f; alPos[1] = 0.0f; alPos[2] = 1.0f;
alSourcefv(soundId, AL_DIRECTION, alPos);
alSourcef(soundId, AL_CONE_INNER_ANGLE, 180.0f);
alSourcef(soundId, AL_CONE_OUTER_ANGLE, 240.0f);

Instead of using OpenAL's AL_POSITION, I ended up tracking the listener position and all sound positions by hand, then manually applying volume rolloff and panning each tick.
This allows a certain window/width/cone of space where an effect is panned fully center. It sounds much better.
Also note that I switched from AL_LINEAR_DISTANCE_CLAMPED back to the default inverse clamped mode. For some reason the linear clamped mode was causing any effect that was positioned with a negative X value to pan much too quickly, regardless of reference or maximum distance. This only happened on Mac builds, so I think the OpenAL Mac implementation has a panning bug when using linear clamped.

Related

How to temporarily freeze a node in front of the camera using ARKit, SceneKit in Swift

I built a complete structure as a node (with its child nodes) and the user will walk through it using ARKit.
At some point, if the user cannot continue because of some real obstacle in the real world, I added a "pause" button which should freeze whatever the user currently sees in front of the camera, the user could then move freely to some other open space and when the user will release the pause button he/she will be able to resume where they left off (only someplace else in the real world).
A while ago I asked about it in the Apple Developer forum and an Apple Frameworks Engineer gave the following reply:
For "freezing" the scene, you could transform the anchor's position (in world coordinates) to camera coordinates, and then anchor your content to the camera. This will give you the effect that the scene is "frozen", i.e., does not move relative to the camera.
I'm currently not using an anchor because I don't necessarily need to find a flat surface. Rather, my node is placed at a certain position relative to where we start at (0,0,0).
My question is how do I exactly do what the Apple engineer told me to do?
I have the following code which I'm still stuck with. When I add the node to the camera (pointOfView, last line of the code below), it does freeze in place, but I can't get it to freeze in the same position and orientation as it was before it was frozen.
#IBAction func pauseButtonClicked(_ sender: UIButton) {
let currentPosition = sceneView.pointOfView?.position
let currentEulerAngles = sceneView.pointOfView?.eulerAngles
var internalNodeTraversal = lastNodeRootPosition - currentPosition! // for now, lastNodeRootPosition is (0,0,0)
internalNodeTraversal.y = lastNodeRootPosition.y + 20 // just so it’s positioned a little higher in front of the camera
myNode?.removeFromParentNode() // remove the node from the Real World view. Looks like this line has no effect and just adding the node as a child to the camera (pointOfView) is enough, but it feels more right to do this anyway.
myNode?.position = internalNodeTraversal // the whole node is moved respectively in the opposite direction from the root to where I’m standing to reposition the camera in my current position inside the node
// myNode?.eulerAngles = (currentEulerAngles! * -1) — this code put the whole node in weird positions so I removed it
myNode?.eulerAngles.y = currentEulerAngles!.y * -1 // opposite orientation of the node so the camera will be oriented in the same direction
myNode?.eulerAngles.x = 0.3 // just tilting it up a little bit to have a better view, more similar to the view as before it was locked to the camera
// I don’t think I need to change the eulerAngles.z
myNode!.convertPosition(internalNodeTraversal, to: sceneView.pointOfView) // I’m not sure I wrote this correctly. Also, this line doesn’t seem tp change anything
sceneView.pointOfView?.addChildNode(myNode!) // attaching the node to the camera so it will remain stuck while the user moves around until the button is released
}
So I first calculate where in the node I'm currently standing and then I change the position of the node in the opposite direction so that the camera will now be in that position. That seems to be correct.
Now I need to change the orientation of the node so that it will point in the right direction and here things get funky. I've been trying so many things for days now.
I use the eulerAngles for the orientation. If I set the whole vector multiplied by -1, it would show weird orientations. I ended up only using the eulerAngles.y which is the left/right orientation and I hardcoded the x orientation (up/down).
Ultimately what I have in the code above is the closest that I was able to get. If I'm pointing straight, the freeze will be correct. If I turn just a little bit, the freeze will be pretty close as well. Almost the same as what the user saw before the freeze. But the more I turn, the more the frozen image is off and more slanted. At some point (say I turn 50 or 60 degrees to the side) the whole node is off the camera and cannot be seen.
Somehow I have a feeling that there must be an easier and more correct way to achieve the above.
The Apple engineer wrote to "transform the anchor's position (in world coordinates) to camera coordinates". For that reason I added the "convertPosition" function in my code, but a) I'm not sure I used it correctly and b) it doesn't seem to change anything in my code if I have that line or not.
What am I doing wrong?
Any help would be very much appreciated.
Thanks!
I found the solution!
Actually, the problem I had was not even described as I didn't think it was relevant. I built the AR nodes 2 meters in front of the origin (-2 for the z-coordinate) while the center of my node was still at the origin. So when I changed the rotation or eulerAngles, it rotated around the origin so my nodes moved in a large curve and in fact also changed their position as a result.
The solution was to use a simdPivot. Instead of changing the position and rotation of the node itself, I created a translation matrix and a rotation matrix which was at the point of the camera (where the user is standing) and I then multiplied both matrices. Now when I added the node as a child of the camera (pointOfView) this would freeze the image and in effect show exactly what the user was seeing before it was frozen as the position is the same and the rotation is exactly around the user's standing position.

SpriteKit Jumping and moving issues

I'm fairly new to swift, and have been working on a game for fun, and i'm running into something I can't quite get my head around.
When the run button is pressed, the character moves forward with the following function
func move(dt: CGFloat) {
position.x += moveRate * dt
}
And when the jump button is pressed, the character jumps with the following function
func jump() {
physicsBody?.applyImpulse(CGVector(dx: 0, dy: jumpRate))
run(jumpAnimation!)
}
both work fine, but consider this senario. The player is running, and then they jump while still moving. While in the air, the player releases the move button and the player's x position stops dead. This obviously feels very unnatural, and i would like the player's x position to ease out.
Now i have also played with moving the character with physicsBody?.applyForce(CGVector(dx: 1000, dy: 0)) which would give that effect, but he seems to just gain more and more speed and you don't get a constant rate or "max speed" so to speak.
Could anybody share some insight with me? I'd love to learn anything I can about spritekit and game development in general. Thanks!
You should try to set the velocity instead of setting the X position. When setting the position you bypass all the physics behaviors.
You should also try to set it only when you actually press a button.
func move(dt: CGFloat) {
if Math.abs(moveRate) > 0.1 { // If player initiates movement. You can increase the value 0.1 if you want to filter move values
velocity = CGVector(dx: moveRate, dy: veloxity.dy)
}
}
It your character moves indefinitely like in space, linearDamping will be useful. it's used to simulate air friction, so values closer to 1 means more friction and values closer to 0 means less friction.
linearDamping = 0.85
Also, this way, moveRate isn't dt dependent but it should be lowered.
Try it, I haven't tested it yet, but that's basically how I would do it.
There are two schools of thought on platformer game "physics".
Don't use physics, do everything with positional incrementation.
Do everything with physics, since positional changes mess up physics
Since you're using physics for jumping, and physics jumping is fun:
There are three ways to create movement in a physics system:
Set the velocity as and when required. This is what Crazyrems is suggesting
Apply impulses as needed to increase and decrease rates of movement
Apply forces over time that increase or decrease rates of movement
Use fields to induce motion (too complex for this, and messy, but fun)
What you're attempting, with your physicsBody?.applyForce(CGVector(dx: 1000, dy: 0)) is the application of force over time. Number 3 in the list above. The longer you continue applying this force the faster the character moves.
Each of these techniques can be made to work, but they all need compensation for their various limitations and methodologies of simulation.
In the case of your approach, you need monitor speed and to set a maximum speed. Having reached maximum speed, if the player is still holding the button, only provide enough force to maintain speed (assuming you're using some form of constant resistance to slow the character).
Monitoring speed combined with force-over-time creates interesting acceleration trait possibilities - like very strong initial acceleration and then taper off acceleration as the player nears their maximum speed.
Slowing down when the player releases the button is the next focus. In all these approaches you need something that slows the player. This can be the application of opposing forces, or the use of friction or damping, as provided by the physics engine.

How to detect Panorama movement in iOS

I have been trying to achieve the animation in this link: http://www.visa.com/rio2016/na-en/#!gallery on an iPad. Try opening this link on and iPad and you'll see.
Basically, its a wrapped(360 degree/Infinite) horizontal list. If the user moves in circle the list on the device should follow the movement.
I know that JS uses webkitCompassHeading to calculate the compass readings.
I tried using the accelerometer but the issue is the deceleration factor is quite small. Lets say I am am moving in right circular motion, list scrolls backwards or from right to left, everything is fine. I stop and start moving in reverse, the deceleration is there but for a very short time. So for the reverse movement, only 1 or 2 carousel items gets scrolled forward or left to right and again since the accelerometer picked the +ve acceleration, the list starts moving in backwards or from right to left direction.
Tried it with magnetometer, by storing the last angle of rotation and seeing if the new one has changed by 6 or 7 degrees but the values are too shaky.

SpriteKit and combining multiple drags for a single rotation

What I'm attempting to recreate is the draggable arrow that's used in the popular iOS game called "Fragger", complete with both adjusting the rotation AND the strength of the pull all at the same time based on a finger drag but using SpriteKit - I believe they did it in Cocos2D.
I'll start by saying that I've honestly spent many weeks relearning trigonometry (http://www.mathsisfun.com/ is a great resource) and combing through Ray Wenderlich's tutorials (http://www.raywenderlich.com/35866/trigonometry-for-game-programming-part-1) but I can't find a solution for executing a "collective" drag rotation AND strength on a SpriteKit object.
Yes, it's relatively easy to set an anchor point and rotate that object (I'm using an arrow) so that it's pointing at your finger and thanks to Ray, I've got a really smooth action utilizing the following:
float angleRadians = atan2f(firstFingerTouchY - _arrow.position.y, firstFingerTouchX - _arrow.position.x);
float angleDegrees = RADIANS_TO_DEGREES(angleRadians);
float rotateDegreesPerSecond = 180 / 0.5; // Would take 0.5 seconds to rotate 180 degrees, or half a circle
float degreesDiff = (angleCurrent - angleDegrees) * 1.0;
float rotateDuration = fabs(degreesDiff / rotateDegreesPerSecond);
however, that's only part of the equation ...
Again, the first drag is fine as the angle of rotation is derived from the fulcrum of the arrow (it's anchor point) and where ever my finger ends up. However, let's say due to the limit of the screen size I can't get the angle I desire in a single drag, so I lift up my finger and then place it back down to continue the drag? Well I can't use the above mentioned code because as soon as I place my finger down for the second time, the arrow's rotation jumps over to where my finger now starts - again ending the same way.
So I thought the solution would be to not use the anchor point of the arrow at all, but instead use the initial finger touchpoint (as the new anchor) and calculate the arrow's angle based on that touchpoint and where ever my finger moves to? That works in theory, however when you then try to factor in the "strength draw" aspect that's represented in "Fragger" (where pulling the finger closer or farther away from the arrow adjusts the strength of the grenade throw), then you're not moving your finger towards the arrow to weaken the pull, but instead towards where ever you initially touched - which is not only visually difficult but as a bonus creates a lot of rotational jerky-ness as you get your finger closer to the origin.
I've been working on it for about a month now (did I mention that I hate math) trying to create a homogenized method (SIN ... COS ... TAN ... I hate you all!) to do the following:
First pull will rotate the arrow based on the direction of the pull but NOT automatically point to your finger (initial angle)
The ability to lift my finger up, place it back down and drag to ADD TO or SUBTRACT FROM the current rotation of the arrow. (delta angle)
The drag of my finger towards or away from the anchor of the arrow (relative to the current angle of the arrow) will adjust the strength of the "pull" accordingly. For example, if the arrow is already pointing straight up, then continuing the drag straight up would increase the strength and down would decrease the strength respectively. (hypotenuse derived from arrow anchor point).
I also need to be able to use another finger (2 finger touch) to assist in the rotation/strength calculation, so that adds even more chaos into my cluster ...
If you've ever played the 'Fragger' game, you'll recognize the complexity of this single finger action which is also it's functional beauty. I can get pieces of it working - I can rotate it, I can adjust a color mask to indicate strength - but not all of it working together. Perhaps I'm going about it completely wrong, however every example I find online stops at simply rotating an object to point to your finger with every new touch drag ending with the same results.

CCParticleSystem and iPhone "bug" or limitation?

I seemed to have run into a strange problem with the CCParticleSystem and the iPhone.
I have a laser that is being shot across the screen from left to right. I added a particle effect to give the laser more of a railgun look to it. I used the "emmas sharing" particle effect from Particle Designer.
Here is the code to send the laser across the screen:
-(void)fireLaserCannonAddon
{
if( _ship == nil || _ship.dead ) return;
CGSize winSize = [CCDirector sharedDirector].winSize;
shipLaserCannon = [_laserCannonArray nextSprite];
[shipLaserCannon stopAllActions];
shipLaserCannon.position = ccpAdd(_ship.position, ccp(shipLaserCannon.contentSize.width / 2, -shipLaserCannon.contentSize.height));
[shipLaserCannon revive];
CCParticleSystemQuad *laserEffect = [_laserEffect nextParticleSystem];
[laserEffect resetSystem];
[shipLaserCannon runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:0.5 position:ccp(winSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:#selector(invisNode:)],
[CCCallFunc actionWithTarget:self selector:#selector(endLaserEffects)], nil]];
}
And the code to set the particle system effect to the laser's position:
-(void)updateLaserEffects:(ccTime)dt
{
for( CCParticleSystemQuad *laserEffect in _laserEffect.array )
{
laserEffect.position = shipLaserCannon.position;
}
}
-(void)endLaserEffects
{
for( CCParticleSystemQuad *laserEffect in _laserEffect.array )
{
[laserEffect stopSystem];
}
}
If you open up the "emmas sharing" effect in Particle Designer, the effect is the same as when you click and drag across the screen. This works perfectly on the iPad and iPad simulator, however on my iPhone 3GS / iPhone (SD and retina) simulator, the emitted particles seem to be "carried" with the laser. It's not as identical as setting the PositionType to kCCPositionTypeGrouped (the emitted particles stay in that circle shape), but kind of a mix between it being kCCPositionTypeGrouped and kCCPositionTypeFree. The particles are emitting off the laser, but also being a dragged a bit behind the laser instead of staying where it was emitted like on the Particle Designer simulator and regular iPad. It looks as if the laser is creating its own layer with the particle effect on it with the "layer" lagging behind it.
I thought that maybe the laser was moving too fast, but even when slowed down, it had the same effect.
This "bug" also creates another small problem, since it's being "carried" with the laser, when the laser is off the screen and then taken out, the remnants of the last emitting particles are visible on the bottom left of the screen, since I'm sure its because the emitted particles are still following the position.x of the laser (which it shouldn't be doing, only the base of it is supposed to) and since the laser is gone, it defaults to it's default set position. However, I do not have this problem on the iPad / iPad simulator.
BTW, this wasn't just limited to only the "emma sharing" particle effect, it seems to do the same for all the other effects.
Has anyone else ever had similar issues with using CCParticleSystems on a moving object for the iPhone?
Any helpful input is greatly appreciated!
OK, so after some messing around, I found out what was causing all this.
I originally had the CCParticleSystem set to 1.0 (original scale) for the iPad and 0.5 for the iphone. I changed the scale for the iPhone to 1.0 and everything worked like it should..just a lot bigger, but it worked. I really didn't want to have two different particle effects for the same effect just because of screen size, so I figured I'll just scale up to 2.0 for the iPad while leaving 1.0 on the iPhone. Low and behold, now the iPad suffered the same weird looking effect as I did on the iPhone, but much more extreme.
Looks like I don't have much of a choice now, but to have two different files for the same effect, but I'm relieved I found out what was causing this and can save a few hairs from leaving prematurely.
I think scaling an effect is fine as long as it's not following an object dynamically, like in my case.
I don't know if this would be considered as a bug or not, since I'm sure it's a math thing that cocos2d is using and scaling it affects it.
TLDR::Scaling up/down a particle effect will cause this weird effect when it is following an object's position. Don't re-scale particle effects that are following an object's position dynamically. If it's just in one spot, then it's fine.

Resources