Audio playing later than it should - c#

[Unity 5.5.1f1] [C#]
I just made a reloading script which is supposed to play a sound as soon as it starts reloading.
The script works perfectly, but the sound starts playing exactly when it's DONE reloading.
Moving the line up outside of the current if, to under the if (currentClip <= 0 || pressedR == true) doesn't work either.
Does someone know how to let the sound play as soon as the reloading starts? (preferably under if (totalAmmo > 1) so that it won't play when all reserve ammo is also depleted)
// Update is called once per frame
void FixedUpdate()
{
if (Input.GetKey(KeyCode.Mouse0) && counter > DelayTime && reloading == false)
{
Instantiate(Bullet, transform.position, transform.rotation); //Spawning the bullet
currentClip += -1;
counter = 0;
}
if (Input.GetKey(KeyCode.R)) //This will allow the player to use "R" to reload.
{
pressedR = true;
}
//Start of reloading
if (currentClip <= 0 || pressedR == true)
{
reloading = true;
if (totalAmmo > 1)
{
GetComponent<AudioSource>().Play(); // <-- AUDIO AUDIO AUDIO
reloadCounter += Time.deltaTime;
if (reloadCounter > reloadTime)
{
missingAmmo = clipSize - currentClip;
if (totalAmmo >= missingAmmo)
{
totalAmmo += currentClip;
totalAmmo += -clipSize;
currentClip = clipSize;
}
else
{
currentClip += totalAmmo;
totalAmmo = 0;
}
reloading = false;
pressedR = false;
reloadCounter = 0;
//End of reloading
}
}
}
counter += Time.deltaTime;
}

What's happening here is that the clip is being played every frame during your reloading process, which keeps restarting it until reloading is done. As a result, you can't heard the clip play out in its entirety until the very end, which is why it seems like the audio is only being played after the reloading process.
To solve this, you should call AudioSource.Play() outside of the reloading logic - preferably when you first trigger the reload to start. However, you currently have multiple entry points into the reloading process - either when the current clip is empty, or when R is pressed. I'd suggest moving both of those conditions into the same if condition, and setting a single flag to start the reloading if either is true. At that point, you can call AudioSource.Play(), so it will only be triggered once per reload:
void FixedUpdate()
{
if (Input.GetKey(KeyCode.Mouse0) && counter > DelayTime && reloading == false)
{
// ...
}
// Start reloading if not already reloading and either R is pressed or clip is empty
if (!reloading && (Input.GetKey(KeyCode.R) || currentClip <= 0))
{
reloading = true;
GetComponent<AudioSource>().Play();
}
//Start of reloading
if (reloading)
{
if (totalAmmo > 1)
{
reloadCounter += Time.deltaTime;
if (reloadCounter > reloadTime)
{
missingAmmo = clipSize - currentClip;
if (totalAmmo >= missingAmmo)
{
totalAmmo += currentClip;
totalAmmo += -clipSize;
currentClip = clipSize;
}
else
{
currentClip += totalAmmo;
totalAmmo = 0;
}
reloading = false;
reloadCounter = 0;
//End of reloading
}
}
}
counter += Time.deltaTime;
}
Note that I appropriated your reloading flag for my purposes, since prior to that it wasn't doing a whole lot. By doing this, I was able to eliminate pressedR from your code, saving on a bit of complexity.
Hope this helps! Let me know if you have any questions.

Related

How Can I Reset And Loop This Timer Forever Unity?

I have a button and a timer inside of it when we click it I was wondering how could I make it so I can set the timer back to 0 and if we click the exit button the timer loops again until it's at 80 and goes back to 0 forever?
I have tried several ways adding a ttimer2 = 0 at the end but it would just loop my timer without me clicking the button any help is appreciated thank you!.
if (CrossPlatformInputManager.GetButton("exit"))
{
if (ttimer2 < 80 && flag2)
{
ttimer2 += 1;
UpDown = 2;
Debug.Log(ttimer);
transform.Translate(Vector3.up * speed * Time.deltaTime);
darkin.SetBool("play", false);
darkengame.SetActive(true);
}
else
{
flag2 = true;
Debug.Log(ttimer);
darkengame.SetActive(false);
}
}
you need reset loops index
int index = 0;
int maxIndex = 80;
if (CrossPlatformInputManager.GetButton("exit"))
{
if (index >= maxIndex && flag2)
{
index = 0;
}
else
{
index++;
}
}

Fire bullets at an interval continuously while the fire button is held down

In C# Unity3D, I'm trying to get my bullets to fire at an interval of bulletTime. Single shots work perfectly fine at the moment, but when I hold down the fire button, just 1 bullet is created and shot and then nothing else happens.
// Left mouse button HELD down.
else if (fireAgain && Input.GetMouseButton(0))
{
StartCoroutine("LoopNShoot");
fireAgain = false;
}
else if (timerH < bulletTime)
{
timerH += Time.deltaTime;
if(timerH >= bulletTime)
{
fireAgain = true;
timerH = 0;
}
}
IEnumerator LoopNShoot()
{
pivot.transform.Rotate(triggerAngle,0,0);
GameObject bullet = ObjectPooler.SharedInstance.GetPooledObject();
if (bullet != null) {
bullet.transform.position = SSpawn.transform.position;
bullet.transform.rotation = SSpawn.transform.rotation;
bullet.SetActive(true);
}
yield return new WaitForSeconds(.1f);
pivot.transform.rotation = Quaternion.Slerp(transform.rotation, originalRotationValue, Time.deltaTime * rotationResetSpeed);
}
Im thinking I need to place all my if statements and timer inside the coroutine? But that doesnt seem to help either...
Please ignore the pivot and rotation, it works fine for some animation, the only thing taht doesnt work is shooting bullets continuosly at a set interval while the fire button is Held down.
You should remove these else and just do.
private void Update()
{
if (fireAgain && Input.GetMouseButton(0))
{
StartCoroutine("LoopNShoot");
fireAgain = false;
timerH = 0;
}
if (timerH < bulletTime)
{
timerH += Time.deltaTime;
if(timerH >= bulletTime)
{
fireAgain = true;
}
}
}
You could also re-arange this to make clearer how it works:
private void Update()
{
if(!fireAgain)
{
timerH += Time.deltaTime;
if (timerH >= bulletTime)
{
fireAgain = true;
}
}
else
{
if(Input.GetMouseButton(0))
{
StartCoroutine("LoopNShoot");
fireAgain = false;
timerH = 0;
}
}
}
I actually thought you anyway already chose the Invoke method I showed you here

How to know when Composition animation 'ScalarKeyFrameAnimation' (or similar) finished

I need to know when Composition animation like ScalarKeyFrameAnimation finished. How to do this? I've made a call like following:
public void Start()
{
if (!IsLoaded) return;
//SET TARGET PROPERTY - OPACITY
TargetScalarKeyFrameAnimation.Target = "Opacity";
//SET FROM OPACITY
if (From < 0) this.Opacity = 0;
else if (From > 1) this.Opacity = 1;
else this.Opacity = From;
//SET FINAL VALUE (OPACITY)
if (To < 0) TargetScalarKeyFrameAnimation.InsertKeyFrame(1f, 0);
else if (To > 1) TargetScalarKeyFrameAnimation.InsertKeyFrame(1f, 1);
else TargetScalarKeyFrameAnimation.InsertKeyFrame(1f, (float)To);
//SET DURATION
if (Duration.TotalMilliseconds > 0) TargetScalarKeyFrameAnimation.Duration = Duration;
else TargetScalarKeyFrameAnimation.Duration = TimeSpan.FromSeconds(1);
//SET DELAY
if (Delay.TotalMilliseconds > 0) TargetScalarKeyFrameAnimation.DelayTime = Delay;
else TargetScalarKeyFrameAnimation.DelayTime = TimeSpan.FromMilliseconds(0);
//START
this.StartAnimation(TargetScalarKeyFrameAnimation);
}
But how to get notification animation has been finished? It will helps lot to synchronise UI and other animation. StoryboardAnimation has an event for this.
How to know when Composition animation 'ScalarKeyFrameAnimation' (or similar) finished
Currently, there is no such event that could detect animation finished for composition Api. For your scenario, you could make a timer ans set Interval same as animation duration, and call StartAnimation and timer Start at same time. Then could detect animation finished in timer Tick event.

How can i make the checkboxes to effect also in editore mode and when the game is running?

I have 3 checkboxes
public bool stateForward = false, stateReverse = false, stateRandom = false;
I want that i will be able to chose each time only one checkbox. But also in editor mode and also when the game is running. And when the game is running i want to make it effect on the game.
In the top of the script i added:
[ExecuteInEditMode]
I tried to do in the Start function
void Start()
{
while (true)
{
if (stateForward == true)
{
stateRandom = false;
stateReverse = false;
}
else if (stateReverse == true)
{
stateRandom = false;
stateForward = false;
}
else if (stateRandom == true)
{
stateForward = false;
stateReverse = false;
}
}
anims = GetComponent<Animations>();
waypoints = GameObject.FindGameObjectsWithTag("ClonedObject");
objectsToMove = GameObject.FindGameObjectsWithTag("Robots");
originalPosition = objectsToMove[0].transform.position;
}
But i'm getting on anims: Unreachable code detected
And in the Update function:
void Update()
{
if (MyCommands.walkbetweenwaypoints == true)
{
DrawLinesInScene();
anims.PlayState(Animations.AnimatorStates.RUN);
WayPointsAI();
}
}
And in WayPointsAI
private void WayPointsAI()
{
if (stateForward == true)
{
if (targetIndex == waypoints.Length)
targetIndex = 0;
}
if (stateReverse == true)
{
if (targetIndex == 0)
targetIndex = waypoints.Length;
}
waypoint = waypoints[targetIndex].transform;
float distance = Vector3.Distance(objectsToMove[0].transform.position, waypoint.transform.position);
objectsToMove[0].transform.rotation = Quaternion.Slerp(objectsToMove[0].transform.rotation, Quaternion.LookRotation(waypoint.position - objectsToMove[0].transform.position), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
objectsToMove[0].transform.position += objectsToMove[0].transform.forward * slowDownSpeed * Time.deltaTime;
}
else
{
objectsToMove[0].transform.position += objectsToMove[0].transform.forward * moveSpeed * Time.deltaTime;
}
if (distance < 2)
{
if (stateForward == true)
targetIndex++;
if (stateReverse == true)
targetIndex--;
}
}
First off, you are never going to get out of your Start() method. You have a while(true) loop that you never break out of, so you will be stuck there.
Instead of multiple checkboxes that you can only choose one of, just use an enumeration. Unity will give you a drop down and you can choose one of the three available states.
enum MyStateEnum{ Forward, Reverse, Random }
public MyStateEnum State;
If you want them to be exclusive, you should probably use a single enum field instead of three bool fields. Your enum would have values Forward, Reverse, and Random, while your MonoBehavior would have a single field of your enum type. This ensures that only one will be chosen at any given time, and the editor should show a drop-down for selecting which value rather than a series of checkboxes. This not only fixes the editor UI issue, but will also lead to cleaner code with fewer potential bugs.

Unity timer issue

I'll explain what I'm trying to do first of all.
I have an object with a material attached to it. From my camera I have a ray being projected and when it collides with my object I'm wanting the material of that object to be placed onto another, second object. This is what I have working.
Now I am trying to expand this by having the user hold the ray over the object for a protracted period of time before the material is taking off object 1 and placed on object 2. And I'm having some issues.
When my timers gets close to the allotted time, my ray seems to stop hitting it and the highlight (which appears when the ray touches it object) disappears. Then when I look away and back at the object, instead of resetting the time, the material instantly appears on my other object. So I think I might not be resetting my timer properly.
Infact I'm positive this is to do with my timer as even when my ray isn't hitting anything, it seems to trigger my change material event.
Could someone take a look at my code and tell me?
method which checks what object has been hit with a ray and casts its material onto the correct object
void CastRay()
{
if(hit.collider.gameObject.tag == "Colour1" && change_material == true)
{
new_colour1.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
var colums = GameObject.FindGameObjectsWithTag("column");
foreach( GameObject c in colums)
c.GetComponent<MeshRenderer>().materials[1].color = new_colour1.orignalMaterial;
}
}
void ResetTimer()
{
start_time = Time.time;
running_time = 0f;
track_timer = false;
}
Code that cast the highlights the object my ray has hit:
void HighLight(GameObject nextHitObject)
{
// Case1: Last ray and new ray both hit objects
if(lastHitObject != null && nextHitObject != null)
{
//1a: same objects, do nothing
if(lastHitObject.tag == nextHitObject.tag)return;
{ //1b: different objects, swap highlight texture
lastHitObject.renderer.material = oldMat;
lastHitObject = nextHitObject;
oldMat = lastHitObject.renderer.material;
lastHitObject.renderer.material = highlight_material;
track_timer = true;
return;
}
}
// Case2: Last ray hit nothing, new ray hit object.
if(lastHitObject == null && nextHitObject != null)
{
ResetTimer();
lastHitObject = nextHitObject;
oldMat = lastHitObject.renderer.material;
lastHitObject.renderer.material = highlight_material;
track_timer = true;
if(spawned_amount == 0)
{
StatusBar();
}
return;
}
// Case3: Last ray hit something, new ray hit nothing
if(lastHitObject != null && nextHitObject == null)
{
lastHitObject.renderer.material = oldMat;
lastHitObject = null;
track_timer = false;
return;
}
else
{
spawned_amount = 0;
Destroy (GameObject.FindWithTag("StatusBar"));
change_material = false;
}
}
Timer code
void Check(bool updateTimer)
{
if(updateTimer)
{
start_time = Time.time - end_time;
running_time += Time.deltaTime;
if ( running_time >= end_time )
{
track_timer = false;
}
}
else
end_time = Time.time;
}
My last method that should be set my change material bool to true once start_time is over 4 seconds.
void StatusBar()
{
if(start_time >= 4)
{
spawned_amount = 0;
Debug.Log("status bar function");
change_material = true;
Debug.Log("chaging amterial");
}
else
{
change_material = false;
ResetTimer();
Debug.Log("resetting timer");
}
}
Try working with this timer functionality instead:
float? start_time;
void ResetTimer()
{
start_time = null;
}
bool IsTimerDone()
{
if (start_time == null)
start_time = Time.time;
else if ((Time.time - start_time) >= 4)
{
ResetTimer();
return true;
}
return false;
}
It stores your data in a single float? start_time. If the value is null, you aren't timing anything. If it has a value, the timer is active. To start it or check on it, you use IsTimerDone(): the first time it is called 4 or more seconds after it starts, it will return true and reset the timer.

Categories