Control sound to only play once in XNA - xna

I use a sound each time a key is pressed to fire a missile. But it doesn't sound nice. I guess it's because the the sound is repeated so many times while the key is pressed down and that the code is within the Update method. I'm looking for a simple solution to just play the sound once when key is pressed? (I have tested to use a boolean variable to be true the first time and then false after det the sound has been played, but this didn't worked well because, when and where should I set it to true again) Help is preciated!
// Fire
if (keyboardState.IsKeyDown(Keys.Space))
{
missile.launchMissile(spaceship.spaceshipPosition, spaceship.spaceshipDirection);
soundExplosion.Play();
}
EDIT New code that isn't working!?
KeyboardState keyboardState = Keyboard.GetState();
KeyboardState prevKeyboardState;
prevKeyboardState = keyboardState;
keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.Space) && prevKeyboardState.IsKeyUp(Keys.Space))
{
missile.launchMissile(spaceship.spaceshipPosition, spaceship.spaceshipDirection);
soundExplosion.Play();
}

Store your previous keyboard state as well
KeyBoardState prevKeyboardState;
then in your update
prevKeyboardState = keyboardState;
keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.Space) && prevKeyboardState.IsKeyUp(Keys.Space))
{
//your code
}
You're basically just triggering the sound tons of times every second making it sound awful.

Related

Strange behaviour with setCurrentPlaybackTime

I use: MPMoviePlayerController to show video.
Below I put list of thumbs from the video.
When pressing a thumb I want to jump to a specific place in the video using: setCurrentPlaybackTime.
I also have a timer updating the selected thumb according to the location of the video using: currentPlaybackTime.
My problem: when calling: setCurrentPlaybackTime, the player keeps giving the seconds before seeking to the specific second. It take few seconds to the player to reflect the new seconds. In the mean time the experience of the user is bad: Pressing a thumb shows it selected for a show time, then the timer updates to the previous thumb, then it jumps back to the thumb I selected.
I tried using (in the timer):
if (moviePlayer.playbackState != MPMoviePlaybackStatePlaying && !(moviePlayer.loadState & MPMovieLoadStatePlaythroughOK)) return;
In order to prevent from the timer to update the selected thumb as long the player is in a transition phase between showing the previous thumb and the new thumb, but it doesn't seem to work. The "playbackState" and "loadState" seems to be totally inconstant and unpredictable.
For solving this issue, this how I have implemented this nasty state coverage in one of my projects. This is nasty and fragile but worked good enough for me.
I used two flags and two time intervals;
BOOL seekInProgress_;
BOOL seekRecoveryInProgress_;
NSTimeInterval seekingTowards_;
NSTimeInterval seekingRecoverySince_;
All of the above should be defaulted to NO and 0.0.
When initiating the seek:
//are we supposed to seek?
if (movieController_.currentPlaybackTime != seekToTime)
{ //yes->
movieController_.currentPlaybackTime = seekToTime;
seekingTowards_ = seekToTime;
seekInProgress_ = YES;
}
Within the timer callback:
//are we currently seeking?
if (seekInProgress_)
{ //yes->did the playback-time change since the seeking has been triggered?
if (seekingTowards_ != movieController_.currentPlaybackTime)
{ //yes->we are now in seek-recovery state
seekingRecoverySince_ = movieController_.currentPlaybackTime;
seekRecoveryInProgress_ = YES;
seekInProgress_ = NO;
seekingTowards_ = 0.0;
}
}
//are we currently recovering from seeking?
else if (seekRecoveryInProgress_)
{ //yes->did the playback-time change since the seeking-recovery has been triggered?
if (seekingRecoverySince_ != movieController_.currentPlaybackTime)
{ //yes->seek recovery done!
seekRecoveryInProgress_ = NO;
seekingRecoverySince_ = 0.0;
}
}
In the end, MPMoviePlayerController simply is not really meant for such "micro-management". I had to throw in at least half a dozen flags for state coverage in all kinds of situations and I would never recommend to repeat this within other projects. Once you reach this level, it might be a great idea to think about using AVPlayer instead.

XNA MediaPlayer Issue

I have the following code in Draw() in Game1. However, the Music only plays when I'm pressing the close button for the program. Where should I be putting MediaPlayer.Play() if not there? normS, fastS, slowS, and playing are all Song types. If you need me to clear anything up, just ask.
if (stateS == "normal")
{
if (!MediaPlayer.Equals(playing, normS))
{
playing = normS;
}
spriteBatch.Draw(norm, pos, Color.White);
}
else if (stateS == "fast")
{
if (!MediaPlayer.Equals(playing, fastS))
{
playing = fastS;
}
spriteBatch.Draw(fast, pos, Color.White);
}
else if (stateS == "slow")
{
if (!MediaPlayer.Equals(playing, slowS))
{
playing = slowS;
}
spriteBatch.Draw(slow, pos, Color.White);
}
MediaPlayer.Play(playing);
Is there a particular reason why you need to have the songs play out of the Draw method? Draw should be reserved for drawing things.
Like user1306322 said in the comment above, I would recommend moving the
MediaPlayer.Play(playing);
into the Update method, and wrap it in a conditional (so you dont just keep playing a new song every update, which I think is what is happening for you now since clicking and holding the Close Window x stops the updates. You could try this by dragging the window around as well):
if (!MediaPlayer.IsPlaying)
{
MediaPlayer.Play(playing);
}
If necessary, you can leave the rest of the code in Draw, but most likely this whole thing should go in Update.
edit I just realised you may not have a conditional on this because you want to be able to change the song at any time. In that case, you should set a variable to hold your "Last Song", so you can compare it in your conditional like this:
if (MediaPlayer.IsPlaying == false || playing != lastPlaying)
{
MediaPlayer.Play(playing);
}

XNA MediaPlayer loading music timing

I load music with the following code in my load content function:
song = Content.Load<Song>("music/game");
MediaPlayer.IsRepeating = false;
MediaPlayer.Play(song);
nothing strange there, but each round in my game is 2 minutes long and should sync up with the music (that is 2 minutes long) but the music ends betweem 2-4s early. This wouldn't be a problem if it was always the same time.
My guess is that it has something to do with load times? any advice?
One thing you could do is move the Content.Load<Song> to Load method and check if it is playing in the update, and if not, play. Eg,
public void LoadContent(ConentManager content)
{
song = content.Load<Song>("music/game");
gameSongStartedPlaying = false; // this variable to hold if you have starting playing this song already
MediaPlayer.IsRepeating = false;
}
public void Update(GameTime gameTime)
{
if(MediaPlayer.State == MediaState.Stopped && !gameSongStartedPlaying)
{
MediaPlayer.Play(song);
gameSongStartedPlaying = true;
}
}
This should start playing the song on the first pass of the Update method rather than in the Loading phase where the song is 'playing' while all resources after Content.Load<Song> are still loading (this would be the reason your song finishes early).

Process.Start opens too many browsers in XNA game

I'm creating a game in XNA that runs on a PC.
On the splash screen, the user has three options. If they press "Enter" the game will begin, if they press "M" they'll go to the Help menu and if they press "W" I want that to take them to my website.
I'm using Process.Start to open the browser to my website.
The problem is that when I press "W", sometimes it will open 1 browser with the website. However, most of the time it will open anywhere from 3 - 7 browsers simultaneously.
Why is it opening multiple browsers simultaneously?
How do I make it open only 1 browser when "W" is pressed?
Here is my code. I haven't built my website yet, so I'm using yahoo.com as the destination:
private void UpdateSplashScreen()
{
KeyboardState keyState = Keyboard.GetState();
if (gameState == GameState.StartScreen)
{
if (keyState.IsKeyDown(Keys.Enter))
{
gameState = GameState.Level1;
explosionTime = 0.0f;
}
if (keyState.IsKeyDown(Keys.M))
{
gameState = GameState.HelpScreen;
}
if (keyState.IsKeyDown(Keys.W))
{
Process.Start("IExplore.exe", "www.yahoo.com");
}
}
Thanks,
Mike
A common way to handle this is to always track the keyboard state from the previous frame. If a key wasn't down on the previous frame, but is down this frame then you know it was just pressed. If the key was down on the previous frame then you know it's being held down.
// somewhere in your initialization code
KeyboardState keyState = Keyboard.GetState();
KeyboardState previousKeyState = keyState;
...
private void UpdateSplashScreen()
{
previousKeyState = keyState; // remember the state from the previous frame
keyState = Keyboard.GetState(); // get the current state
if (gameState == GameState.StartScreen)
{
if (keyState.IsKeyDown(Keys.Enter) && !previousKeyState.IsKeyDown(Keys.Enter))
{
gameState = GameState.Level1;
explosionTime = 0.0f;
}
if (keyState.IsKeyDown(Keys.M) && !previousKeyState.IsKeyDown(Keys.M))
{
gameState = GameState.HelpScreen;
}
if (keyState.IsKeyDown(Keys.W) && !previousKeyState.IsKeyDown(Keys.W))
{
Process.Start("IExplore.exe", "www.yahoo.com");
}
}
I usually create a KeyPressed function which cleans things up a bit.
bool KeyPressed(Keys key)
{
return keyState.IsKeyDown(key) && !previousKeyState.IsKeyDown(key);
}
The code you are using runs about 60 times a second; you may only press your key down for 100ms or so but in that time it checks to see if W is pressed down about 7 times. As such, it opens a large number of browser windows.
Try recording a timestamp (using DateTime.Now) of when you open the browser and then check that a certain time has elapsed (~2 secs?) before allowing another window to be opened. Or, create a boolean flag that is set false by opening the browser, so the browser can be opened only once.
Thanks guys, that's what the problem was.
Callum Rogers solution was the easiest:
I declared a boolean:
bool launchFlag = false;
Then checked it and set it to true after the website launched.
private void UpdateSplashScreen()
{
KeyboardState keyState = Keyboard.GetState();
if (gameState == GameState.StartScreen)
{
if (keyState.IsKeyDown(Keys.Enter))
{
gameState = GameState.Level1;
explosionTime = 0.0f;
}
if (keyState.IsKeyDown(Keys.M))
{
gameState = GameState.HelpScreen;
}
if (keyState.IsKeyDown(Keys.W))
{
if (launchFlag == false)
{
Process.Start("IExplore.exe", "www.yahoo.com");
launchFlag = true;
}
}
}
I held the W key down for 30 seconds and it launched just 1 browser!
Thanks,
Mike

Notification when AVQueuePlayer rewinds to the beginning of an item

I have 5 AVPlayerItems in my AVQueuePlayer, which is set to AVPlayerActionAtItemEndAdvance. I hit play on my UI, and play the first, the second and then start playing the third. Then I hit my rewind button. What I want to happen is that the third video rewinds to its start, and I then get a notification that allows me to stop. What I'm seeing is that I get a status of ready to play for the 4th item, followed by a current item changed to the 4th item - then the 4th item plays.
Why does the 4th item become the current item after the 3rd item has rewound to its start
Is the only way I can stop this to set the player to not auto advance (AVPlayerActionAtItemEndPause), observe the end of each item, and hope I get an "end of play" notification for the rewinding of the 3rd item as well as when it plays to its end naturally. Then in my end observer code, I can check the rate of the player, and if rewinding, not advance to the next item.
The way I handled this was in the begin seeking code, set the actionAtItemEnd to AVPlayerActionAtItemEndNone, and then reset it back to AVPlayerActionAtItemEndAdvance when the seeking ends. On iOS6, it appears that one can seek past the start of the track. In the "end seeking" code, I reset the rate and current time before beginning normal playback.
The following method is called by a long press gesture recognizer.
- (IBAction)fastRewind:(id)sender
{
UIGestureRecognizer *recog = (UIGestureRecognizer*)sender;
if (recog.state == UIGestureRecognizerStateBegan) {
if (_player.rate == 1) {
NSLog(#"fastRewind begin\n%#", sender);
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
_player.rate = -2;
}
} else if (recog.state != UIGestureRecognizerStateChanged) {
// Ended/Canceled
NSLog(#"fastRewind end\n%#", sender);
if (_player.rate < 0) {
_player.rate = 0;
if (CMTimeGetSeconds(_player.currentTime) < 0) {
[_player seekToTime:CMTimeMake(0, 1)];
}
_player.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
_player.rate = 1;
}
}
}
One may wish to speed up the seek if it is held for a longer period of time.

Resources