How to get an accurate method of time passed using audioSettings.dspTime? - c#

Thanks for looking and for any help / advice you might have.
I'm trying to sync audio with gameplay.
Is this an accurate method of finding out how much time has passed using audioSettings.dspTime?
I take a float representing time and multiply by song.clip.frequency.
float myChosenAmountOfTimeInSamples = myChosenAmountOfTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
And this onto audioSettings.dspTime * song.clip.frequency
float Audio = (float)AudioSettings.dspTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
I'm using this code to see if it works. As far as I can tell it happens 1 second later
In Update
if (Input.GetKeyDown (KeyCode.A)){
StartCoroutine(test ());
}
And the coroutine
IEnumerator test ()
{
float Audio = (float)AudioSettings.dspTime * song.clip.frequency;
float myChosenAmountOfTime = 1f;
float myChosenAmountOfTimeInSamples = myChosenAmountOfTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
print ("Audio seconds later: " + audioSecondsLater);
while (true)
{
Audio = (float)AudioSettings.dspTime * song.clip.frequency;
if (Audio >= audioSecondsLater)
{
print ("Audio Now: " + Audio); // Theoretically this is 1 second later
break;
}
yield return new WaitForSeconds(1 / 1000f);
}
}

I'm not entirely sure, but I think you're on the right track. I've done sync before against a single AudioClip, but I keep all my timing in samples. To check the current sample I examine AudioSource.timeSamples on the clip that is playing. I use REAPER to examine the audio file and figure out at which sample in the file the events happen, and compare that to timeSamples to see if my sync event has happened yet.
AudioSource also has a time property which is the playback position in seconds, though see note in the documentation about inaccuracy when used with compressed audio.

Related

Saving score at checkpoint

I'm working on a 3D BMX sidescroller with a trick system and points. I've all ready got check points in for when you crash and they work well. The problem I'm having is when you crash your score doesn't go back to what it was when you first passed the checkpoint, meaning people can just get a high score by doing a really big trick and crashing over and over again. Every tutorial, and I mean literally every one, has been about saving a high score at the end of a level. Here's the relevant script
private float flipScore;
private float spinScore;
void FixedUpdate()
if (!IsGrounded())
flipScore += (transform.localEulerAngles.x) * Time.deltaTime * 0.5f;
spinScore += transform.localEulerAngles.y * Time.deltaTime * 1f;
An example for anyone who needs extra clarification.
You reach the first checkpoint with 12345 points. You do a few tricks to get up to 15000 but crash before the next checkpoint then you start back at the first checkpoint with 12345 points.
I've tried pretty much everything I've seen in the tutorials but to no avail.
Store the points when you reach a checkpoint.
When resetting to the checkpoint, set the score to what was stored.
private float checkpointSpinScore;
private float checkpointFlipScore;
private void OnCheckpointReached()
{
checkpointSpinScore = spinScore;
checkpointFlipScore = flipScore;
}
private void OnResetToCheckpoint()
{
spinScore = checkpointSpinScore;
flipScore = checkpointFlipScore;
}

Execute if-statement after fixed amount of time

I don't know if this is an easy question or not.
I'm trying to write a single function that slows down the player and returns them to normal speed after a certain amount of time. I founds some answers online but they all use a second function to set a timer, but I'm trying to contain the whole thing in one function.
I've already tried this:
public void slowDown(float slowAmount, float durationSlowed)
{
var playerInfo = player.GetComponent<FPSMovement>();
playerInfo.speed *= slowAmount;
float lengthSlowed = Time.time + durationSlowed;
if(Time.time > lengthSlowed)
{
playerInfo.speed /= slowAmount;
}
}
and calling it with:
slowDown (0.5f, 2f)
It manages to slow down the player, but doesn't return their speed to normal.
What am I missing here?
I assume this is unity.
You probably want to do it with IEnumerators and Coroutines. They distribute code to be executed over multiple frames. Something like this:
IEnumerator SpeedUp() {
speed *= 2;
yield return new WaitForSeconds(2.0f);
speed /= 2;
}
void Update (){
if(Input.GetKeyDown(***YOUR KEY HERE***)) {
StartCoroutine(SpeedUp());
}
}
This will double the speed of your character, wait 2 seconds, and halve it. If you want to add a cooldown to the amount of times you can add speed, just drop a boolean check.

Frequency and pitch relation for AudioClip - Unity3D

I am trying to recreate the full range of a guitar only using 6 audio clips.
I was thinking there would be a way to set frequency of an audio clip but audio.frequency only returns the frequency of the audio based on compression format and not the actual tone.
I know I can read GetSpectrumData, but that solution is fairly complex and would require some Fourier Transform analysis or something of the kind.
Affecting the pitch, it is easy to alter the tone so I can go up and down but is there a way to figure out what are the steps to use.
void Update ()
{
CheckAudio(KeyCode.Q, 1.0f);
CheckAudio(KeyCode.W, 1.1f);
CheckAudio(KeyCode.E, 1.2f);
CheckAudio(KeyCode.R, 1.3f);
CheckAudio(KeyCode.T, 1.4f);
}
void CheckAudio(KeyCode key, float pitch)
{
if (Input.GetKeyDown (key))
{
audio.pitch = pitch;
audio.Play ();
}
}
I can hear it does not sound right.
Knowing the initial tone E4 329.63Hz with pitch at 1 is there any equation that affecting the pitch, I would get the next key F4 349.23Hz (or close enough)?
It has to be considered also that Unity AudioSource limits the pitch within -3/3 range (which I think is more than needed).
EDIT: Adding some personal research. It seems pitch 1 is initial note and setting to 2 give the same key one octave higher.
Since a chromatic scale (all black and white notes on the piano) is 12 keys, I assume that using 1/12 for each step should do it.
It sounds close but I fell it is not quite right. Here is the new code:
[SerializeField] private AudioSource audio;
float step = 1f/12f;
KeyCode[]keys = new KeyCode[]{
KeyCode.Q, KeyCode.W,KeyCode.E,KeyCode.R,KeyCode.T,
KeyCode.Y, KeyCode.U, KeyCode.I, KeyCode.O, KeyCode.P,
KeyCode.A, KeyCode.S, KeyCode.D
};
void Update ()
{
float f = 0.0f;
foreach (KeyCode key in keys)
{
CheckAudio(key, f);
f += 1f;
}
}
void CheckAudio(KeyCode key, float pitch)
{
if (Input.GetKeyDown (key))
{
audio.pitch = 1f + pitch * step;
audio.Play ();
}
}
What you are trying to do will not work well by simply changing the pitch of the audio. By changing the pitch, you will run into other problems such as sound finishing too fast or taking more time to finish and the sound will not be good either.
The first solution is to make a plugin(Synthesizer) in C++ that reads the audio file from Unity and change the frequency. It should also perform other actions to fix speed issues. This is very complicated unless you are an audio engineer with some great math skills. And trying this on a mobile device is whole different story. OnAudioFilterRead is a function you should use if you decide to go with this method.
The second and the recommended solution is to make an audio file for each guitar key then put them into array of audioClip. This solves every other problems.The down side is that you will have more files.
EDIT:
If you don't care about it being perfect, you can use something below from this nice guy on the internet.
void playSound(){
float transpose = -4;
float note = -1;
if (Input.GetKeyDown("a")) note = 0; // C
if (Input.GetKeyDown("s")) note = 2; // D
if (Input.GetKeyDown("d")) note = 4; // E
if (Input.GetKeyDown("f")) note = 5; // F
if (Input.GetKeyDown("g")) note = 7; // G
if (Input.GetKeyDown("h")) note = 9; // A
if (Input.GetKeyDown("j")) note = 11; // B
if (Input.GetKeyDown("k")) note = 12; // C
if (Input.GetKeyDown("l")) note = 14; // D
if (note>=0){ // if some key pressed...
audio.pitch = Mathf.Pow(2, (note+transpose)/12.0);
audio.Play();
}
EDIT: For those of you interested in why the Mathf.Pow equation is used and working, read the following: https://en.wikipedia.org/wiki/Twelfth_root_of_two

My FPS Counter is in-accurate any ideas why?

When I start up my game it stays around 95-101 rapidly changing, in between all of those numbers.. but when I open up the stats bar I'm getting upper 200's low 300's
so wondering why that is still new to c# so be easy on me lol. heres the code
thanks in advance as always ^_^.
float deltaTime = 0.0f;
void Update()
{
deltaTime += (Time.deltaTime - deltaTime) * 0.1f;
}
void OnGUI()
{
int w = Screen.width, h = Screen.height;
GUIStyle style = new GUIStyle ();
Rect rect = new Rect (0, 0, w, h * 2 / 100);
style.alignment = TextAnchor.UpperRight;
style.fontSize = h * 2 / 100;
style.normal.textColor = new Color (255.0f, 255.0f, 255.0f, 1.0f);
float msec = deltaTime * 1000.0f;
float fps = 1f / deltaTime;
string text = string.Format ("({1:0.} fps)", msec, fps);
GUI.Label (rect, text, style);
}
}
In order to display a meaningful FPS rate you need to measure how many frames were rendered over a constant period of time, for example one second. Then only after that period do you display the calculated value on screen. This will provide for an average frames per second as opposed to an instantaneous frames per second, the latter of which is not particularly useful in most cases as it leads to widely fluctuating values.
Code
First define some fields:
DateTime _lastTime; // marks the beginning the measurement began
int _framesRendered; // an increasing count
int _fps; // the FPS calculated from the last measurement
Then in your render method you increment the _framesRendered. You also check to see if one second has elapsed since the start of the period:
void Update()
{
_framesRendered++;
if ((DateTime.Now - _lastTime).TotalSeconds >= 1)
{
// one second has elapsed
_fps = _framesRendered;
_framesRendered = 0;
_lastTime = DateTime.Now;
}
// draw FPS on screen here using current value of _fps
}
Cross-technology
It should be pointed out that the above code makes no particular use of Unity whilst still being reasonably accurate and is compatible with many frameworks and APIs such as DirectX; OpenGL; XNA; WPF or even WinForms.
When I start up my game it stays around 95-101 rapidly changing, in between all of those numbers.. but when I open up the stats bar I'm getting upper 200's low 300's
The ASUS VG248QE is 1ms and the max it can do is 144Hz so it is unlikely you are getting "upper 200's low 300's". FPS is meaningless when VSYNC is turned off on a non-GSYNC monitor. Is your VSYNC turned on?
In Unity, FPS is equivalent to number of Updates that occur in 1 second. This is because Update() is called every Time.deltaTime seconds.
InvokeRepeating method
You can also use InvokeRepeating to implement your own FPS counter while using only integers, like this:
private int FrameCounter = 0;
private int Fps = 0;
void Start()
{
InvokeRepeating("CountFps", 0f, 1f);
}
void Update()
{
FrameCounter++;
}
private void CountFps()
{
Fps = FrameCounter;
FrameCounter = 0;
}
Then just display the Fps variable in the OnGUI() method. Using this method, your Fps value will get updated every second; if you want more frequent updates, change the last argument of InvokeRepeating call and then adjust the Fps calculation accordingly.
Note, however, that InvokeRepeating takes Time.timeScale into account, so e.g. if you pause the game with Time.timeScale = 0f; the counter will stop updating until you unpause the game.
FixedUpdate method
Another approach is to count the FPS in FixedUpdate() method instead of OnGUI() or Update(). This gets called every Time.fixedDeltaTime seconds which is always the same, no matter what. The value of Time.fixedDeltaTime can be set globally for the project via menu Edit->Project Settings->Time, item Fixed Timestep.
In this case, you would count frames the same way (in Update), but update your FPS counter in FixedUpdate - which is basically the same as calling you own method with InvokeRepeating("CountFps", 0f, 0.02f) (0.02f being a typical Time.fixedDeltaTime value, but this depends on your project settings as per above).
Conclusion
Most of the time, you won't need to update the displayed FPS that often, so I personally like to use the InvokeRepeating method and 1 second intervals.
OnGUI function is called at las twice per frame (sometimes more). You are calculating your "FPS" inside OnGUI so it will almost never be accurate.
Defining :
deltaTime += (Time.deltaTime - deltaTime) * 0.05f;
will bring your FPS values closest to real but it will not be accurate if you calc it on OnGUI method.
I guess (not sure) that you should use FixedUpdate() instead of OnGUI() to calc your FPS. (also you don't need to change your deltaTime to multiply by 0.05f if you use FixedUpdate)

Move object from one point to another based on duration

Been wrapping my head around this problem for a while and looking for solutions online to no effect.
Basically if I have a sprite for example located at (10,10) I want to move it to say (50,100) and the whole process to take 2 seconds or whatever duration I specify. What is the exact math behind this? I was using a distance based solution to determine speed but was just using a random modifier to control the process. I need something more precise to execute exactly over a set duration.
Any help on this issue would be greatly appreciated!
Assuming linear interpolation (i.e. moving in a straight line from start position to end position at a constant rate):
The vector from start to destination is destination - start, i.e. for your example (40,90).
If you want this to happen over two seconds you need to divide it by two to get the distance travelled per second, so (20,45) per second for your example.
To get the position at any given time, first record the start time and calculate the current time minus the start time in seconds. So if the animation started at 12:01:30.000 and it is now 12:01:31.500 then 1.5 seconds have past since the start of the animation.
To get the current location you add the start location to the movement per second vector * the time elapsed, so in my example:
(10,10) + (20,45) * 1.5 = (10,10) + (30, 67.5) = (40, 77.5)
It's just a thing of interpolation and time.
There is linear, sinus, quadratic, ...
Here is some more info and examples in actionscript: link
Take a closer look to jQuery's animation algorithms... maybe you can use some of the code.
http://code.jquery.com/jquery-1.6.1.js (search for "custom:" as a starting point).
You need a couple of pieces of information to do this, start location, end location, duration and elapsed time.
Here's an example in actionscript:
package {
import flash.utils.getTimer;
import flash.events.Event;
import flash.display.Shape;
import flash.geom.Point;
import flash.display.Sprite;
public class Mover extends Sprite {
private var circle :Shape;
private var start :Point;
private var end :Point;
private var duration :int;
public function Mover() {
// first we create something to move, like, a circle
circle = new Shape();
circle.graphics.beginFill(0xff00ff);
circle.graphics.drawCircle(0, 0, 20);
addChild(circle);
// start and end positions
start = new Point(0, 0);
end = new Point(100, 100);
// and finally, the duration, i'm using milliseconds
duration = 2000;
// this event handler will run each frame
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
}
private function handleEnterFrame(event:Event):void {
// we figure out how much of the animation has elapsed by using getTimer
// should you want to use a start time, add it here
var progress:Number = getTimer() / duration;
// we need to clamp our progress so we don't under- or overshoot the target
if(progress < 0) progress = 0;
if(progress > 1) progress = 1;
// then it's a matter of setting the position
// we use the start position as a base and then gradually add in
// the difference between the start and the end
circle.x = start.x + (end.x - start.x) * progress;
circle.y = start.y + (end.y - start.y) * progress;
}
}
}
If you're not all that interested in the how and just want the results, I wholeheartedly recommend a tweening engine like TweenLite or any of the other myriad of them. Just stay clear of the one that comes with flash, it's a bit crap.

Categories

Resources