C# XNA - Background music logic - c#

So I'm working with the XNA game states sample. I have 2 different songs that play and loop in the game states. That means:
mainMenu.mp3 goes to the mainMenu game state
backgroundMusic.mp3 goes to the Playing game state
So this works. But I also have the Escape key logic, which returns you from any state to the mainMenu state (when Esc is pressed). The song from Playing stops and the song from mainMenu begins playing. But if I click the Start Game button which moves me to Playing the background music is not being played. Where could be the problem?
Here's my code:
// Main Menu game state
if (Game1.g_CurrentGameState == Game1.g_GameStates.g_MainMenu && !m_SongStart)
{
MediaPlayer.Play(m_MainMenuTheme);
m_SongStart = true; // bool
}
if (Game1.g_CurrentGameState != Game1.g_GameStates.g_MainMenu)
{
MediaPlayer.Stop();
m_SongStart = false; // bool
}
// Playing game state
if (Game1.g_CurrentGameState == Game1.g_GameStates.g_GamePlaying && !m_SongStart)
{
MediaPlayer.Play(m_GamePlayingMainTheme);
m_SongStart = true;
}
if (Game1.g_CurrentGameState != Game1.g_GameStates.g_GamePlaying)
{
MediaPlayer.Stop();
m_SongStart = false;
}

The following code will play a song according current game state:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace WindowsGame1
{
public class Game1 : Game
{
private GameState _gameState;
private GraphicsDeviceManager _graphicsDeviceManager;
private KeyboardState _keyboardState;
private Song _song1;
private Song _song2;
public Game1() {
_graphicsDeviceManager = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void LoadContent() {
_song1 = Content.Load<Song>("song1");
_song2 = Content.Load<Song>("song2");
}
protected override void UnloadContent() {
if (_song1 != null) _song1.Dispose();
if (_song2 != null) _song2.Dispose();
}
protected override void Update(GameTime gameTime) {
GameState gameState = _gameState;
var keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.A) && _keyboardState.IsKeyUp(Keys.A)) {
gameState = GameState.Menu;
}
else if (keyboardState.IsKeyDown(Keys.B) && _keyboardState.IsKeyUp(Keys.B)) {
gameState = GameState.InGame;
}
else if (keyboardState.IsKeyDown(Keys.C) && _keyboardState.IsKeyUp(Keys.C)) {
gameState = GameState.Undefined;
}
_keyboardState = keyboardState;
if (gameState != _gameState) {
switch (gameState) {
case GameState.Undefined:
MediaPlayer.Stop();
break;
case GameState.Menu:
MediaPlayer.Stop();
MediaPlayer.Play(_song1);
break;
case GameState.InGame:
MediaPlayer.Stop();
MediaPlayer.Play(_song2);
break;
default:
throw new ArgumentOutOfRangeException();
}
_gameState = gameState;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime) {
GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
}
}
internal enum GameState
{
Undefined,
Menu,
InGame
}
}

Why not just write a wrapper class to wrap both a song and game state member in it and play the song when the current game state is equal to that one? An oop version of #Aybe's answer. Credit to him!
Here is what I've come up with. Should be easy to use and manage. Pretty simple.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Collections.Generic;
namespace WindowsGame1
{
public class GameStateManager
{
private static List<GameState> GameStates = new List<GameState>();
private static GameState CurrentGameState = null;
public static void AddState(GameState gameState)
{
GameStates.Add(gameState);
}
public static GameState GetState(string name)
{
return GameStates.Find(gameState => gameState.Name == name);
}
public static void SwitchStates(GameState gameState)
{
if (CurrentGameState != null)
{
if (MediaPlayer.State == MediaState.Playing)
MediaPlayer.Stop();
}
CurrentGameState = gameState;
if (CurrentGameState != null)
MediaPlayer.Play(CurrentGameState.Song);
}
public static void SwitchStates(string gameState)
{
SwitchStates(GetState(gameState));
}
public static void Dispose()
{
foreach (var gameState in GameStates)
{
gameState.Song.Dispose();
}
GameStates.Clear();
}
}
public class GameState
{
public Song Song;
public string Name;
public GameState(string name, Song song)
{
Name = name;
Song = song;
}
}
public class Game1 : Game
{
private GraphicsDeviceManager _graphicsDeviceManager;
private KeyboardState _keyboardState;
public Game1()
{
_graphicsDeviceManager = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void LoadContent()
{
GameStateManager.AddState(new GameState("InGame", Content.Load<Song>("song1")));
GameStateManager.AddState(new GameState("Menu", Content.Load<Song>("song2")));
}
protected override void UnloadContent()
{
GameStateManager.Dispose();
}
protected override void Update(GameTime gameTime)
{
var keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.A) && _keyboardState.IsKeyUp(Keys.A))
{
GameStateManager.SwitchStates("Menu");
}
else if (keyboardState.IsKeyDown(Keys.B) && _keyboardState.IsKeyUp(Keys.B))
{
GameStateManager.SwitchStates("InGame");
}
else if (keyboardState.IsKeyDown(Keys.C) && _keyboardState.IsKeyUp(Keys.C))
{
GameStateManager.SwitchStates("Undefined");
}
_keyboardState = keyboardState;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
}
}
}

Related

Unity change player PhotonNetwork Room with two object with collider

I'm looking for a solution of the following issue:
I have a player instantiated in lobby room at initial scene. There are two platform objects with colliders, I need to make one platform to join to one room with a scene and other platform to another room with another scene. I have attached the same script for both of platform with a boolean value which determines to which room to join. Instead of determine to which room to join it triggers both scripts. I need to work it in this way:
Platform A:
private bool teleportToShop = true;
If (teleportToShop) -> join shop room
Platform B:
private bool teleportToShop = false;
If (!teleportToShop) -> join event room
Here's my code attached to both game objects:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Portal : MonoBehaviourPunCallbacks
{
[SerializeField] private bool teleportToShop;
private float portalTime = 3f;
private string EVENT_HALL_TAG = "Cinema Hall";
private string LOBBY_TAG = "Lobby";
private string SHOP_TAG = "Shop";
private int currentRoom = 0;
private string nextRoom;
private PhotonView portalView;
private void Awake()
{
portalView = GetComponent<PhotonView>();
}
private void OnTriggerStay(Collider other)
{
if (portalView.IsMine)
{
if (other.GetComponent<PhotonView>().IsMine)
{
if (other.gameObject.tag == "Player")
{
if (portalTime > 0)
{
portalTime -= Time.deltaTime;
}
else
{
portalTime = 3f;
if (SceneManager.GetActiveScene().name == EVENT_HALL_TAG || SceneManager.GetActiveScene().name == SHOP_TAG)
{
currentRoom = 1;
PhotonNetwork.LeaveRoom();
}
else
{
currentRoom = 0;
PhotonNetwork.LeaveRoom();
}
}
}
}
}
}
private void OnTriggerExit(Collider other)
{
portalTime = 3f;
}
public override void OnConnectedToMaster()
{
PhotonNetwork.AutomaticallySyncScene = false;
PhotonNetwork.JoinLobby();
}
public override void OnJoinedLobby()
{
if (currentRoom == 0)
{
if (!teleportToShop)
{
PhotonNetwork.JoinRoom("EventHallRoom");
}
else if (teleportToShop)
{
PhotonNetwork.JoinRoom("ShopRoom");
}
}
else PhotonNetwork.JoinRoom("LobbyRoom");
}
public override void OnJoinRoomFailed(short returnCode, string message)
{
base.OnJoinRandomFailed(returnCode, message);
if (currentRoom == 0)
{
if (!teleportToShop)
{
PhotonNetwork.CreateRoom("EventHallRoom");
}
else if (teleportToShop)
{
PhotonNetwork.CreateRoom("ShopRoom");
}
}
else PhotonNetwork.CreateRoom("LobbyRoom");
}
public override void OnJoinedRoom()
{
if (currentRoom == 0)
{
if (!teleportToShop)
{
PhotonNetwork.LoadLevel(EVENT_HALL_TAG);
}
else if (teleportToShop)
{
PhotonNetwork.LoadLevel(SHOP_TAG);
}
}
else PhotonNetwork.LoadLevel(LOBBY_TAG);
}
public override void OnLeftRoom()
{
GameManager.currentPlayer = null;
}
}

State Design pattern and infinite loops with MonoGame

I am trying to use state design pattern with my game(MonoGame game).I want to detect collision , so I give cell type and upon it gives me a specific action.But unfortunately this implementation gives infinite loop "Exception.StackOverflow" in player instances.
abstract class PlayerState
{
abstract public void doAction(Player player );
}
Classes implements PlayerState
class DeadState:PlayerState
{
public override void doAction(Player player)
{
player.dead();
player.setState(this);
}
}
class IncreaseGold: PlayerState
{
public override void doAction(Player player)
{
player.increaseGold();
player.setState(this);
}
}
Player.cs
public void setState(PlayerState state) { this.state = state; }
public PlayerState getState() { return state; }
ActionController.cs
Player player = new Player();
public void passCell(int cellType)
{
if (cellType == 1)
{
new DeadState().doAction(player);
}
if (cellType == 2)
{
new IncreaseGold().doAction(player);
}
}
finally in my Update method on Game1.cs
protected override void Update(GameTime gameTime)
{
player.passCell(cellType);
base.Update(gameTime);
}

C# XNA Add a GameComponent from within a GameComponent

public class Menu : DrawableGameComponent
{
ContentManager Content;
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Audio MenuMusic;
public Menu(Game game) : base(game)
{
spriteBatch = Game.Services.GetService(typeof(SpriteBatch)) as SpriteBatch;
graphics = Game.Services.GetService(typeof(GraphicsDeviceManager)) as GraphicsDeviceManager;
Content = game.Content;
Content.RootDirectory = #"Content\Menu\";
*MenuMusic = new Audio(game);* // Instantiate the new DrawableGameComponent
Game.Components.Add(this);
MenuMusic.PauseTune = false;
}
public override void Initialize()
{
Menustate = MenuState.LoadContent;
base.Initialize();
}
protected override void LoadContent()
{
base.LoadContent();
}
protected override void UnloadContent()
{
base.UnloadContent();
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
}
}
public class Audio : DrawableGameComponent
{
public bool PauseTune
{
get { return PauseTune; }
set { PauseTune = value; }
}
SoundEffect Tune = null;
SoundEffectInstance SFXInstance;
public Audio(Game game) : base(game)
{
*game.Components.Add(this)*;// This is the problem. It adds an entirely new Game object. :(
}
public override void Initialize()
{
PauseTune = true;
base.Initialize();
}
protected override void LoadContent()
{
switch (Game1.Gamestate)
{
case GameState.Menu:
string AudioPath = #"Audio\";
Tune = Game.Content.Load<SoundEffect>(AudioPath + "Tune");
break;
case GameState.InitialiseGame:
break;
case GameState.PlayGame:
break;
default:
break;
}
if (Tune != null) SFXInstance = Tune.CreateInstance();
base.LoadContent();
}
protected override void UnloadContent()
{
base.UnloadContent();
}
public override void Update(GameTime gameTime)
{
if (Tune != null)
{
if (PauseTune)
{
if (SFXInstance.State == SoundState.Playing)
SFXInstance.Pause();
}
else
{
if (SFXInstance.State != SoundState.Playing)
SFXInstance.Play();
}
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
}
}
The probelem comes when I use the Menu to add a new Audio class instance.
The Audio constructor attempts to add the new GameCompent but fails.
What it actually does is create an entirely new Game instance, which then goes on to instantiate a new Menu that attempts to add a new Audio class instance..... Until I eventually get a - would you beleive? - stackoverflow error.
What am I doing wrong? / How can I add one component from within another?
DOH! Some days I should never turn on my computer :)
Or better yet - after a long, tiring day do NOT mess with mycode.
A self-referencing PauseTune property.

Update a string based on key presses

I hope this isn't a stupid question but I cannot figure this out.
I have a barebones game class:
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
String inputHolder;
public Game1()
: base()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
inputHolder = "Enter your keys: ";
}
protected override void Initialize()
{
base.Initialize();
InputManager.stringToUpdate = inputHolder;
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
{
Exit();
}
base.Update(gameTime);
InputManager.update(Keyboard.GetState());
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
Console.WriteLine(inputHolder);
}
}
I also have an InputManager class:
static class InputManager
{
public static string stringToUpdate;
public static void update(KeyboardState kbState)
{
if (stringToUpdate != null)
{
foreach (Keys k in kbState.GetPressedKeys())
{
stringToUpdate += k.ToString();
}
}
}
}
However no matter what keys I press the original string inputHolder is not affected even though strings are treated as reference types in C#.
I have tried changing
InputManager.stringToUpdate = inputHolder;
to
InputManager.stringToUpdate = ref inputHolder;
and nothing happens.
What am I missing here?
As has already been said, strings are immutable. Wrapping the inputHolder string in a class will give you the desired effect:
public class InputHolder
{
public string Input { get; set; }
}
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
InputHolder inputHolder = new InputHolder();
public Game1()
: base()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
inputHolder.Input = "Enter your keys: ";
}
protected override void Initialize()
{
base.Initialize();
InputManager.inputHolderToUpdate = inputHolder;
}
// etc
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
Console.WriteLine(inputHolder.Input);
}
}
static class InputManager
{
public static InputHolder inputHolderToUpdate;
public static void update(KeyboardState kbState)
{
if (inputHolderToUpdate != null)
{
foreach (Keys k in kbState.GetPressedKeys())
{
inputHolderToUpdate.Input = inputHolderToUpdate.Input + k.ToString();
}
}
}
}

xna Loading content from a non GameComponent class

I am a relative beginner in XNA and am trying to create a screenManager for an C# XNA game. Everything in my code seems to work fine until I reach the LoadContent method in my MainMenu class which derives from Screen which is a custom standalone class. This is where I get my GraphicsDevice component not found ContentLoad exception.
This is on the the statement: texture = c.Load<Texture2D>("Textures/Start");
where c is a ContentManager.
This statement and the program works okay if I put it in the Update method of my class. However, I only want to load this texture once, not with each call to Update(GameTime). I think I must be calling load before some Content info is being initialized but I'm not sure what. There are some commented out statements in MainMenu.LoadContent that do pretty much the same thing and all produce the same error. I'm posting my entire code here. Hope it isn't flooding the forum.
Here's Game1.cs:
namespace RoomGame
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
public static GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
GameController gameController;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
gameController = new GameController(this,this.Content);
this.Components.Add(gameController);
}
}
}
GameController.cs:
namespace RoomGame
{
public class GameController : DrawableGameComponent
{
public Game game;
public ContentManager content;
public ScreenController screenController;
public MainMenu menu;
public GameController(Game game,ContentManager c)
: base(game)
{
this.game = game;
content = c;
screenController = new ScreenController(this);
menu = new MainMenu(this,this.content);
screenController.AddScreen(menu);
}
public override void Initialize()
{
base.Initialize();
screenController.Initialize();
}
protected override void LoadContent()
{
base.LoadContent();
}
protected override void UnloadContent()
{
base.UnloadContent();
}
public override void Update(GameTime gameTime)
{
screenController.Update(gameTime);
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
screenController.Draw(gameTime);
}
}
}
screenController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace RoomGame
{
public class ScreenController
{
public List<Screen> screens = new List<Screen>();
GameController gameController;
SpriteBatch spriteBatch;
public ScreenController(GameController gc)
{
this.gameController = gc;
}
public void Initialize()
{
spriteBatch = new SpriteBatch(this.gameController.GraphicsDevice);
}
public void Update(GameTime gameTime)
{
foreach (Screen screen in screens)
{
if (screen.isUpdating)
screen.Update(gameTime);
}
}
public void Draw(GameTime gameTime)
{
foreach (Screen screen in screens)
{
if (screen.isDrawn)
{
spriteBatch.Begin();
screen.Draw(gameTime,spriteBatch);
spriteBatch.End();
}
}
}
public void AddScreen(Screen screen)
{
if (!screens.Contains(screen))
{
screen.LoadContent(this.gameController.game.Content);
screens.Add(screen);
//screen.Initialize();
}
}
}
}
Screen.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
namespace RoomGame
{
public class Screen
{
public ScreenController screenController;
public bool isUpdating=true;
public bool isDrawn=true;
public GraphicsDeviceManager graphics;
/*public Screen(ScreenController sc)
{
this.screenController = sc;
this.isActive = true;
}*/
public virtual void Initialize()
{ }
public virtual void LoadContent(ContentManager c)
{ }
public virtual void Update(GameTime gameTime)
{ }
public virtual void Draw(GameTime gameTime,SpriteBatch sb)
{ }
}
}
MainMenu.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
namespace RoomGame
{
public class MainMenu : Screen
{
GameController gameController;
Texture2D texture;
Vector2 position;
ContentManager content;
//public GraphicsDeviceManager graphics;
public MainMenu(GameController gc,ContentManager c)
{
this.gameController = gc;
this.content = c;
this.isUpdating = true;
this.isDrawn = true;
//this.graphics = this.gameController.game.graphics;
}
public override void LoadContent(ContentManager c)
{
texture = c.Load<Texture2D>("Textures/Start");
//texture = this.gameController.game.Content.Load<Texture2D>("Textures/Start");
//texture=this.gameController.content.Load<Texture2D>("Textures/Start");
//texture = this.gameController.Game.Content.Load<Texture2D>("Textures/Start");
}
public override void Update(GameTime gameTime)
{
//texture = this.gameController.content.Load<Texture2D>("Textures/Start");
position = new Vector2(100, 100);
}
public override void Draw(GameTime gameTime,SpriteBatch sb)
{
sb.Draw(texture, position, Color.White);
}
}
}
Thanks in advance....
EDIT
I found my error. I was calling ScreenController.Add before the LoadContent in GameController. I put it after the base.LoadContent in gameController and it works now. I hope this can help somebody but if anyone know a better way of approaching this please let me know.
You call MainMenu.LoadContent from the constructor of the ScreenController which in turn is called by the Game's constructor. LoadContent should be called at the appropriate time. So move the call to AddScreen to the LoadContent method of the GameController.

Categories