I have a Player class that has a public float reviveTimer. So when a player is downed another player can go revive them which starts a coroutine for the downed player that increases the revive timer until it gets to 10.
There is a situation where if one player goes to revive a downed player, 5 seconds later a second player comes to revive that same downed player and 1 second after that the first player leaves, the second player should continue the revive process with 6 seconds left (5+1) instead of restarting the timer since the original starter left. So when the second player come's in to revive the downed player, they need to know the revive timer is at 5 seconds.
Therefore I do something as simple as timer = downedPlayer.reviveTimer;.
However this always returns 0, no matter what the actual timer is set to, as if I cant retrieve what the reviveTimer is being set to in the coroutine.
So how do I or can I retrieve the value of reviveTimer that is being set within a coroutine?
EDIT:
As requested, adding code. When a player is downed, this is the coroutine that the downed player runs.
IEnumerator ReviveMeTimer()
{
while (reviveTimer < 10)
{
reviveTimer += Time.deltaTime;
reviveIndicatorTimer.text = ((int)reviveTimer).ToString();
reviveIndicatorImage.fillAmount = reviveTimer / 10;
yield return null;
}
}
The downed player should store the revive timer. The reviving players should call a public revive() method that gets the downed player’s timer. The revive method should check how long is left on the timer and update it accordingly. The revive method should be called from the downed player’s code. This way the downed player is in charge of maintaining its own timer internally and the other player’s access it through public methods. If you use coroutines to handle the timing, just have the coroutines (in the downed player) access its timer.
Adam B is essentially correct. I just want to point out that the reason for that is because of this line:
timer = downedPlayer.reviveTimer;
Because you get the timer value and store it in a local variable you aren't actually updating the downed player's reviveTimer. You're manipulating a local variable called timer and that's why it doesn't appear to synchronize with a second helping player.
Nominally this could be fixed by simply removing the local variable all together, however that would let two players aid the downed player and they'd get up faster (10 seconds divided by number of helpers). So in your detailed scenario, the first player would leave and the second player would see 3 seconds remaining, not 4:
Note: question says "6 second", but it's counting up towards 10, but says this value is the value remaining to get to 10.
1 player for 5 seconds: +5
2 players for 1 second: +2
Total: +7
As both coroutines would be running.
The only way to fix this is as Adam B says: let the downedPlayer's code handle the timer (but you don't want the revive() method modifying it!).
Related
I am having a simple game with a player, a moving background and moving walls, kinda like flappy bird. My player is able to collect a powerUp. It is transforming after and put into a special level.
I want this level to run for like 10 seconds and then call a backtransformation.
Currently I am using a timer which just calls the backtransformation after 10 seconds. I am having trouble now when the player pauses the game, the timer is still running.
--> what I found on stackoverflow is that you can't resume a timer, you can just invalidate and restart it. This would miss my target, otherwise the player pauses the game every 9 seconds and can stay in the super Level forever.
Do you guys have any idea how I can solve my problem or like an alternative to use Timers in my code?
I appreciate any help
Edit: Here is how I simply used the timer
// transform and go into super level added here
self.transform()
timer = Timer.scheduledTimer(withTimeInterval:10, repeats: false) {
timer in
self.backtransform()
}
When you start the timer record the current time as a Double using
let start = Date().timeIntervalSinceReferenceDate
var remaining = 10.0
When the user pauses the timer, calculate the amount of time that has passed with:
let elapsed = Date().timeIntervalSinceReferenceDate - start
And the amount of remaining time for your timer:
remaining -= elapsed
If the user resumes the timer, set it to remaining, not 10 seconds.
I'm designing a typeracer game in unity where the player is in an athletics 100m sprint and the faster they type, the faster the character runs.
I'm just wondering should I be aiming to get an animation that completes between every correct letter entered or working out an algorithm that speeds up, slows down and pauses the animation depending on whether the letter is correct.
Has anyone had any experience with something like this before?
Also, being quite new to unity i'm just using the standard assets with Ethan as my model, is this the right thing to be doing here?
Original Thoughts
You could have it so that every correct character type speeds up the animation of the character and slowly ticks down per millisecond that passes (i.e slowing down if you aren't typing). Then when the user enters a wrong character the animation gets increasingly slower (1/10 of the previous time every time (?)).
Solution
In Unity, working with timing is a little difficult, my class and I had issues with it this year. The best solution we found is working within the FixedUpdate loop itself, as this is run on a more concise time frame than just Update.
Example
For my solution (and what we all ended up doing) was to update the time in FixedUpdate and use it in Update
void FixedUpdate() {
if (timer >= 12f) stopped = true;
if (!stopped) timerDT = updateTimer(Time.deltaTime);
}
If the timer variable is greater than 12 (seconds) then stop movement.
If not stopped, continue updating and adding to the timer, as well as giving the frame time back to timerDT
void Update() {
this.transform.translate(velocity * timerDT);
}
This will run and update the game object attached based on its velocity and the time frame given in FixedUpdate
For you, I would have the script save the animation controller as a variable in the script:
Controller animation = {animation controller};
Note: Don't remember what needs to go here, but I'm pretty sure it's the controller
Then you can change the animation like so:
void Update() {
update_animation(timerDT, anim_speed);
}
void FixedUpdate() {
timerDT = updateTimer(Time.deltaTime);
if (timerDT - oldDT > 0.1) {
oldDT = timerDT;
anim_speed = anim_speed / 0.1; // for decreasing speed
}
}
void update_animation(float deltatime, float speed) {
animation["run"].speed = anim_speed;
}
I am writing a game using CreateJS and using CocoonJS to distribute. Within CocoonJS API are a couple of listener functions that allow callbacks when pausing and resuming of the game. The game's timer and (time-based) animations are driven by the Ticker event's "delta" property. The issue that I am having at the moment is, on resuming the game following on from pausing it, the timer will pick up from the time at which it paused plus the time spent whilst paused. For example, I pause the game after 20 seconds for exactly 4 seconds, on resuming the game the timer will carry on from 24 seconds (not 20 seconds, which is intended). I've tried storing the ticker event's "runTime" property before pausing and attempting to then set the ticker event's "runTime" to this stored value on resume, but this doesn't work.
A snippet of my original code (before tinkering) is like the following:
createjs.Ticker.setFPS(60);
createjs.Ticker.on("tick", onTick, this);
Cocoon.App.on("activated", function() {
console.log("---[[[[[ APP RESUMING ]]]]]---");
createjs.Ticker.paused = false;
});
Cocoon.App.on("suspending", function() {
console.log("---[[[[[ APP PAUSING ]]]]]---");
createjs.Ticker.paused = true;
});
onTick = function (e) {
if (!e.paused) {
animateUsingTicker(e.deltaTime);
countDownTimerUsingTicker(e.deltaTime);
//etc...
stage.update();
}
};
Can someone please assist me on this?
Many thanks
One really easy way to deal with this is to use Timer.maxDelta. For example, if you are targeting 60fps, you could set this to something like 32ms (double the expected delta), to prevent getting huge values back when resuming an app/game.
I need to play 2 sounds with 2 AVAudioPlayer objects at the same exact time... so I found this example on Apple AVAudioPlayer Class Reference (https://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVAudioPlayerClassReference/Reference/Reference.html):
- (void) startSynchronizedPlayback {
NSTimeInterval shortStartDelay = 0.01; // seconds
NSTimeInterval now = player.deviceCurrentTime;
[player playAtTime: now + shortStartDelay];
[secondPlayer playAtTime: now + shortStartDelay];
// Here, update state and user interface for each player, as appropriate
}
What I don't understand is: why also the secondPlayer has the shorStartDelay?
Shouldn't it be without? I thought the first Player needed a 0.1 sec delay as it is called before the second Player... but in this code the 2 players have the delay...
Anyone can explain me if that is right and why?
Thanks a lot
Massy
If you only use the play method ([firstPlayer play];), firstPlayer will start before the second one as it will receive the call before.
If you set no delay ([firstPlayer playAtTime:now];), the firstPlayer will also start before de second one because firstPlayer will check the time at which it is supposed to start, and will see that it's already passed. Thus, it will have the same behaviour as when you use only the play method.
The delay is here to ensure that the two players start at the same time. It is supposed to be long enough to ensure that the two players receive the call before the 'now+delay' time has passed.
I don't know if I'm clear (English is not my native langage). I can try to be more clear if you have questions
Yeah what he said ^ The play at time will schedule both players to start at that time (sometime in the future).
To make it obvious, you can set "shortStartDelay" to 2 seconds and you will see there will be a two second pause before both items start playing.
Another tip to keep in mind here are that when you play/pause AVAudioPlayer they dont actually STOP at exactly the same time. So when you want to resume, you should also sync the audio tracks.
Swift example:
let currentDeviceTime = firstPlayer.deviceCurrentTime
let trackTime = firstPlayer.currentTime
players.forEach {
$0.currentTime = trackTime
$0.play(atTime: currentDeviceTime + 0.1)
}
Where players is a list of AVAudioPlayers and firstPlayer is the first item in the array.
Notice how I am also resetting the "currentTime" which is how many seconds into the audio track you want to keep playing. Otherwise every time the user plays/pauses the track they drift out of sync!
I have a program that uses a file player audio unit to play, pause and stop a audio file. The way I am accomplishing this is by initializing the file player audio unit to play the file at position zero and then when the user presses the pause button, I stop the AUGraph, capture the current position, and then use that position as the start position when the user presses the play button. Everything is working as it should, but every 3 or 4 times I hit pause and then play, the song starts playing a half to a full second BEFORE the point where I hit pause.
I can't figure out why this is happening, do any of you have any thoughts? here is a simplified version of my code.
//initialize AUGraph and File player Audio unit
...
...
...
//Start AUGraph
...
...
...
// pause playback
- (void) pauseAUGraph {
//first stop the AuGrpah
result = AUGraphStop (processingGraph);
// get current play head position
AudioTimeStamp ts;
UInt32 size = sizeof(ts);
result = AudioUnitGetProperty(filePlayerUnit,
kAudioUnitProperty_CurrentPlayTime, kAudioUnitScope_Global, 0, &ts,
&size);
//save our play head position for use later
//must add it to itself to take care of multiple presses of the pause button
sampleFrameSavedPosition = sampleFrameSavedPosition + ts.mSampleTime;
//this stops the file player unit from playing
AudioUnitReset(filePlayerUnit, kAudioUnitScope_Global, 0);
NSLog (#"AudioUnitReset - stopped file player from playing");
//all done
}
// Stop playback
- (void) stopAUGraph {
// lets set the play head to zero, so that when we restart, we restart at the beginning of the file.
sampleFrameSavedPosition = 0;
//ok now that we saved the current pleayhead position, lets stop the AUGraph
result = AUGraphStop (processingGraph);
}
May be you should use packet counts instead of timestamps, since you just want to pause and play the music, not display the time information.
See BufferedAudioPlayer for an example of using this method.
It may be due to rounding problems with your code:
For example, if every time you hit the pause button, your timer would record at a 0.5/4 seconds before your actual pause time, you would still see a desired result. But after repeating for four more times, the amount of space you have created is 0.5/4 times 4 which is the half of a second you seem to be experiencing.
Thus, I would pay careful attention to the object types you are using and make sure they don't round inappropriately. Try using a double float for your sample times to try to alleviate that problem!
Hope this is clear and helpful! :)