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.
Related
[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.
I want to make a picture appear on my form, a certain number of seconds after it loads, and then have that picture move in a controlled manner within the form boundaries. I'd appreciate a code example that will get me started with timed events.
public partial class Form1 : Form
{
int horiz, vert, step;
public Form1()
{
InitializeComponent();
}
private void timer1_Tick_1(object sender, EventArgs e)
{
//image is moved at each interval of the timer
goblin.Left = goblin.Left + (horiz * step);
goblin.Top = goblin.Top + (vert * step);
// if goblin has hit the RHS edge, if so change direction left
if ((goblin.Left + goblin.Width) >= (Form1.ActiveForm.Width - step))
horiz = -1;
// if goblin has hit the LHS edge, if so change direction right
if (goblin.Left <= step)
horiz = 1;
// if goblin has hit the bottom edge, if so change direction upwards
if ((goblin.Top + goblin.Height) >= (Form1.ActiveForm.Height - step))
vert = -1;
// if goblin has hit the top edge, if so change direction downwards
if (goblin.Top < step)
vert = 1;
}
private void Form1_Load_1(object sender, EventArgs e)
{
//Soon as the forms loads activate the goblin to start moving
//set the intial direction
horiz = 1; //start going right
vert = 1; //start going down
step = 5; //moves goblin 5 pixels
timer1.Enabled = true;
}
}
}
The easiest solution based on what you've shown us so far is to use the same timer you are using already and essentially skip a few ticks. Let's assume that your current timer is happening at 100ms which is 10 timers per second (10hz)
If you want to delay this activity by 5 seconds, you need to skip 5 * 10 (50) of the first ticks. Create a new integer member variable to store how many ticks you've processed:
private int ticks = 0;
Each time the timer expires/ticks do this first:
ticks++;
if (ticks < 50) {
// Don't do anything just skip
return;
}
You can provide second 'temporary' timer (timer2)
private void Form1_Load_1(object sender, EventArgs e)
{
//Soon as the forms loads activate the goblin to start moving
//set the intial direction
horiz = 1; //start going right
vert = 1; //start going down
step = 5; //moves goblin 5 pixels
timer1.Tick += timer1_Tick_1;
// temporary timer
Timer timer2 = new Timer();
timer2.Interval = 5000;
timer2.Tick += delegate
{
// activate goblin timer
timer1.Enabled = true;
// deactivate 5s temp timer
timer2.Enabled = false;
timer2.Dispose();
};
timer2.Enabled = true;
}
I want a DispatcherTimer to restart everytime the conditions are not met. Only when the if-condition is met for 5 seconds, the method can continue.
How should I stop the Dispatchertimer? The timeToWait variable is set to 3000, that works as intended.
Below is the code in C#. It is not responding as I want. It only starts, but never stops or restarts. I am making a WPF application.
dispatcherTimerStart = new DispatcherTimer();
if (average >= centerOfPlayingField - marginOfDetection && average <= centerOfPlayingField + marginOfDetection)
{
dispatcherTimerStart.Interval = TimeSpan.FromMilliseconds(timeToWait);
dispatcherTimerStart.Tick += new EventHandler(tick_TimerStart);
startTime = DateTime.Now;
dispatcherTimerStart.Start();
} else
{
dispatcherTimerStart.Stop();
dispatcherTimerStart.Interval = TimeSpan.FromMilliseconds(timeToWait);
}
private void tick_TimerStart(object sender, EventArgs args)
{
DispatcherTimer thisTimer = (DispatcherTimer) sender;
thisTimer.Stop();
}
you need to preserve the dispatcherTimer that enter your if block because in your else block you are stopping the new instance of DispatcherTimer not the one that entered the if block.
take a class level field
DispatcherTimer preservedDispatcherTimer=null;
var dispatcherTimerStart = new DispatcherTimer();
if (average >= centerOfPlayingField - marginOfDetection && average <= centerOfPlayingField + marginOfDetection)
{
**preservedDispatcherTimer = dispatcherTimerStart;**
dispatcherTimerStart.Interval = TimeSpan.FromMilliseconds(timeToWait);
dispatcherTimerStart.Tick += new EventHandler(tick_TimerStart);
startTime = DateTime.Now;
dispatcherTimerStart.Start();
}
//use preservedDispatcherTimer in else
else if(preservedDispatcherTimer!=null)
{
preservedDispatcherTimer.Stop();
preservedDispatcherTimer.Interval = TimeSpan.FromMilliseconds(timeToWait);
}
I have a problem with my game. It is divided in a two states: GameState.MainMenu and GameState.Playing.
I want to draw on screen the timer when I playing. I use gameTime.TotalGameTime.Minutesand
gameTime.TotalGameTime.Seconds variable. But when I clicked on button Play Now, and my game switched state from GameState.MainMenu to GameState.Playing., the timer dosent start from 0. It starts with the time, which elapsed when I spend in MainMenu. I try create next variable to count time, which I spent in MainMenu, and I try to subtract from the first variable, but the displaying time is not properly.
My substracting time:
minutesPlaying = gameTime.TotalGameTime.Minutes; ;
secondsPlaying = gameTime.TotalGameTime.Seconds;
switch (currentGameState)
{
case GameState.MainMenu:
minutesMenu = gameTime.TotalGameTime.Minutes;
secondsMenu = gameTime.TotalGameTime.Seconds;
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
break;
case GameState.Playing:
minutesTotal = minutesPlaying - minutesMenu;
secondsTotal = secondsPlaying - secondsMenu;
break;
}
Invoke my method:
timer.Update(minutesTotal, secondsTotal);
Update method:
public void Update(int min, int sec)
{
string seconds, minutes;
seconds = sec.ToString();
minutes = min.ToString();
if (sec <= 9) seconds = "0" + seconds;
if (min <= 9) minutes = "0" + minutes;
nowString = minutes + ":" + seconds;
}
Thanks for answer :)
I would use a 'StopWatch' to 'Start' and 'Pause' the tracking of time spent in your playing state. When the playing state is entered start the 'StopWatch' and then pause it when leaving that state.
Your on the right track, but I think you should have a time passed variable instead of having to subtract it from the original gameTime, this way if you go back to the menu, then back into the game, your method wont be broken. Basicly it just takes a TimeSpan at 0 and adds gameTime to it as time passes, giving you the amount of time spent in the level.
First off add a new TimeSpan
public TimeSpan TimePassed;
Now in your Update() method inside case GameState.Playing: you will need to incriment the timer.
TimePassed -= gameTime.ElapsedGameTime;
You can also reset the timer to Zero if you need to make a new level (If that applies, Ex: Replaying a level will need a timer reset)
Now to render the time in your Draw() method.
string timeString = TimePassed.Minutes.ToString("00") + ":" + TimePassed.Seconds.ToString("00");
spriteBatch.DrawString(FONT,timeString,POSITION,Color.White);
And there you go!
You should modify time only in Playing state, I have modified the code to achieve it, whithout too much changes... PlayTime keeps the total time elapsed in the playing state.
TimeSpan Elapsed ;
switch (currentGameState)
{
case GameState.MainMenu:
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
Elapsed = TimeSpan.Zero;
break;
case GameState.Playing:
Elapsed = gametime.ElapsedGameTime;
break;
}
Invoke my method:
timer.Update(Elapsed);
Update method:
TimeSpan PlayTime = TimeSpan.Zero;
public void Update(TimeSpan Elapsed)
{
PlayTime.Add(Elapsed);
string seconds, minutes;
seconds = PlayTime.Seconds.ToString();
minutes = PlayTime.Minutes.ToString();
if (sec <= 9) seconds = "0" + seconds;
if (min <= 9) minutes = "0" + minutes;
nowString = minutes + ":" + seconds;
}
Try this:
Before the game starts you don't need to keep track of the time, so I have set minutesPlaying and secondsPlaying to 0 in GameState.MainMenu. Don't initialize minutesPlaying and secondsPlaying within the main Update() function.
minutesPlaying = 0;
secondsPlaying = 0;
switch (currentGameState)
{
case GameState.MainMenu:
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
break;
case GameState.Playing:
secondsPlaying += gameTime.ElapsedGameTime.Seconds;
if (secondsPlaying >= 60)
{
minutesPlaying++;
secondsPlaying = 0;
}
break;
}
When you display the time, just display minutesPlaying and secondsPlaying. You don't need any extra variables.
If that doesn't work, you can always keep track of the milliseconds (secondsPlaying += gameTime.ElapsedGameTime.Milliseconds) that pass when the game starts and divide the value by 1000 to display the seconds.
I have the following Play method for playing musical notes stored in an arraylist:
public void Play(Duration d){
int duration = 1;
if (d == Duration.SemiBreve)
{
duration = 16;
}
else if (d == Duration.DotMin)
{
duration = 10;
}
else if (d == Duration.minim)
{
duration = 8;
}
else if (d == Duration.Crotchet)
{
duration = 4;
}
else if (d == Duration.Quaver)
{
duration = 2;
}
else if (d == Duration.SemiQuaver)
{
duration = 1;
}
player = new SoundPlayer();
player.SoundLocation = "pathToLocation";
//set timer
time = new Timer();
time.Tick += new EventHandler(clockTick);
time.Interval = duration * 150;
player.Play();
time.Start();
}
When I call the method with a button:
private void PlayButton_Click(object sender, EventArgs e)
{
//Loops through the collection and plays each note one after the other
foreach (MusicNote music in this.staff.Notes)
{
music.Play(music.Dur)
}
}
Only the last note gets played. With PlaySync() all the notes get played but the duration isn't recognized. I also tried using threads like so:
foreach (MusicNote music in this.staff.Notes)
{
Thread t = new Thread(playMethod=>music.Play(music.Dur));
t.Start();
t.Join();
}
But it doesn't work either. Any suggestions as to how I can get the files to play consecutively like with PlaySync but using their set duration?
You don't wait for the timer anywhere. So in practice all notes get played almost simultaneously, causing just the last one to be effectively audible.
UPDATE: Using the System.Timers.Timer class the way to go is registering a handler for the Elapsed event and playing the notes one-by-one in the event handler. This way you avoid freezing the UI.
Another option is playing in a background thread and the using Thread.Sleep to wait. Here you will have to make sure the thread is stopped according to the state of the UI (i.e. the use closes the current dialog etc.).
In either case to avoid race conditions you will have to solve concurrent access to staff.Notes or make a copy of it for the sake of playing it.