Screen managment XNA C# - c#

I'm trying to implement screen management into my XNA game.
So far I've successfully implemented Example 3 from this link: http://www.xnadevelopment.com/tutorials/thestateofthings/thestateofthings.shtml
My current screen switch event is as follows:
public void GoToScreenOverview(object obj, EventArgs e)
{
screenStart = null;
screenOverview = new ScreenOverview(this, new EventHandler(GoToScreenOverview), globalVariables);
mCurrentScreen = screenOverview;
}
This moves from the (initial) screen "Start", and moves the game to the next screen "Overview". The equivalent reciprocal method GoToScreenStart does the opposite.
My question is this: How do I manage all the events?
Let's say I have 20 screens. Each screen Class will need 19 EventHandlers passed to it, which seems massively inefficient, and makes it a pain to add a new screen. Currently GoToScreenX type are events are in the Game1 class, but it seems to me that it would make more sense for all the events to be in the "BaseScreen" class, which each Screen inherits from.

If I were you, I would create a ScreenManager class. You could start with something like this:
public class ScreenManager
{
List<GameScreen> screens;
bool isinitialized = false;
public override void Initialize()
{
base.Initialize();
isinitialized = true;
}
protected override void LoadContent()
{
foreach (GameScreen screen in screens)
screen.LoadContent();
}
public void AddScreen(GameScreen screen)
{
screens.Add(screen);
if (isinitialized) screen.LoadContent();
}
// also create a removescreen
public void Draw(Gametime gametime)
{
foreach (GameScreen screen in screens)
{
screen.Draw(gametime);
}
}
// also create a similar method for Updating
}
You could make ScreenManager a DrawableGameComponent (recommended), or you could have the maingame call ScreenManager.Update() and ScreenManager.Draw() in it's respective methods. Finally, you'd have to create an abstract GameScreen class that has update and draw methods, and then have all your other Game Screens inherit from the GameScreen class. hopefully this will give you some ideas.

Related

C# Virtual Method Issue

I'm currently programming a 2D Game Engine in C# using GDI+ and at the moment I'm trying to implement a component based structure.
The idea is that my GameObject class has a list of components. Each component can be completely different to the next.
I represented components by creating a Component class, which looks like this:
public class Component : BaseObject
{
/// <summary>
/// The game object this component is attached too.
/// </summary>
private GameObject gameObject = null;
/*
* Below I called the protected virtual methods from the "BaseObject" class.
* The base object class only contains these 4 protected virtual methods. Nothing else.
*/
public void Load()
{
OnLoad(this, EventArgs.Empty);
}
public void Unload()
{
OnUnload(this, EventArgs.Empty);
}
public void Update(GameTime gameTime)
{
OnUpdate(this, new UpdateEventArgs(gameTime));
}
public void Render(GraphicsEngine graphicsEngine)
{
OnRender(this, new RenderEventArgs(graphicsEngine));
}
public GameObject GameObject
{
get { return gameObject; }
set { gameObject = value; }
}
}
In my game object class, every component that is within the components list gets updated and rendered like so:
internal void Update(GameTime gameTime)
{
OnUpdate(this, new UpdateEventArgs(gameTime));
// All components being updated
components.ForEach(c => c.Update(gameTime));
}
With all of that being said, I've created a SpriteComponent class, which is meant to render a simple 2D sprite, based on the game objects position, it's overrided render method looks like this:
protected override void OnRender(object sender, RenderEventArgs e)
{
e.GraphicsEngine.DrawBitmap(bitmap, GameObject.Transform.Position);
base.OnRender(sender, e);
}
However for some reason this happens (This is the render method of the GameObject class):
internal void Render(GraphicsEngine graphicsEngine)
{
if (Enabled)
{
// Rendering a sprite component like this works fine
// Here I've created created a SpriteComponent inside the game object class to test it
if(sprite != null)
graphicsEngine.DrawBitmap(sprite.Bitmap, transform.Position);
OnRender(this, new RenderEventArgs(graphicsEngine));
// But rendering it using it's own method doesn't work
//sprite.Render(graphicsEngine);
// and this doesn't work properly either
// components.ForEach(c => c.Render(graphicsEngine));
}
}
And here is a picture of the result when I either iterate through the components list and render it, or render the sprite component by it's self:
I would look into you calling the OnRender. It is not best practice to call these event handlers. The has to be some other method you need to call which will invoke the OnRender.
Similar to OnDraw (Event handler) and Update (Invoke method)
I think you did override wrong method, As I see you have override the OnRender
While probably you calling Render method.
Try to make the Render method virtual and override it in child classes

Nullreferenceexception after initializing an array [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 5 years ago.
I've been trying to make a test game in XNA (visual studio 2015), but i get a NullReferenceException every time i load this, even though i actually do initialize it. Here is a very short version of it...
namespace Overbox
{
public class Player
{
private Player[] characterList = new Player[14];
virtual public void Initialize()
{
//characterList = new Player[14];
characterList[0] = new Hero();
//for (int i = 0; i <= characterList.Length; i++)
// characterList[i].Initialize();
characterList[0].Initialize();
}
}
virtual public void Draw(SpriteBatch spriteBatch, Texture2D texture)
{
//for (int i = 0; i <= characterList.Length; i++)
//{
// if (characterList[i].Active)
// characterList[i].Draw(spriteBatch, texture);
//}
characterList[0].Draw(spriteBatch, texture); //Here the error occurs
}
}
}
If someone wants the whole class for any reason i could edit this, but this is all the related code i could find.
Edit: stack trace
Overbox.exe!Overbox.Player.Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Microsoft.Xna.Framework.Graphics.Texture2D texture) Line 53 C#
Overbox.exe!Overbox.MediaHandler.Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) Line 54 C#
Overbox.exe!Overbox.Program.Main(string[] args) Line 15 C#
It's all supposed to be there.
Hero class:
namespace Overbox.Characters
{
class Jacketed : Player
{
override public void Initialize()
{
//Unrelated setting of variables
}
public override void Draw(SpriteBatch spriteBatch, Texture2D texture)
{
spriteBatch.Draw(texture, PlayerPosition, Color.White);
}
}
}
Very short version of game loop class:
public class Game1 : Microsoft.XNA.Framework.Game
{
public static Player player;
//I do load the content which playerTexture requires before anyone asks about it
private Texture2D playerTexture;
protected override void Initialize()
{
player = new Player();
player.Initialize();
}
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
player.Draw(spriteBatch, playerTexture);
spriteBatch.End();
}
}
private Player[] characterList = new Player[14];
Makes an array of Player, but does not actually construct the objects. So what you end up with is basically an array full of null values.
You are missing something like this:
public Player()
{
foreach (var character in characterList)
{
character = new Player();
}
}
This is a wild guess, but I'm assuming Hero overrides one or both or Initialize and Draw. Let's say Hero looks like this:
class Hero : Player
{
public override void Initialize()
{
// Do some stuff. Don't call base.Initialize()
}
}
So we've overridden Initialize(), but haven't called base.Initialize(). We've also not overriden Draw(), so the Draw() from Player will be used when called on a Hero instance.
So let's consider when a Player player is constructed. Then player.characterList is also created, but its elements are null. Next, player.Initialize() is called, setting characterList[0] = new Hero(). Note that characterList[0] has its own characterList array as well.
The next thing to happen is characterList[0].Initialize(). This called the Initialize() override in Hero. This does not call Initialize() inside Player, so the characterList inside the Hero instance still has null elements.
Some time in the future, the Draw method is called in the Player. This, in turn, calls characterList[0].Draw(spriteBatch, texture). Since we didn't override the Draw method in Hero, this calls the same method, but on our Hero instance. But remember that the Hero's characterList still has null values? Well, the only thing to happen here is calling characterList[0].Draw(spriteBatch, texture), which as we now know, is calling Draw on a null value.
This is my guess as to what is happening. The details may vary, but I would imagine this sort of thing is the root cause. To be sure, check the stack trace of the exception. You'll notice two frames for the Draw method, letting you know that the exception occurred in a recursive call to Draw.
I've had this solution in mind for a while, but I didn't want to do it because of organizing reasons. All I had to do was to move characterList to the main game loop class and making it public, guaranteeing that it would be initialized before the draw method would be called. This did work.

Loading content in different levels

I am building an XNA game and I have used this method to create new levels.
First I make an abstract class called Level:
public abstract class Level {
public abstract void Update(GameTime gameTime);
public abstract void Draw(SpriteBatch spriteBatch);
}
In the Game1 object, I declare a public static Level currentLevel;
and after initialising it (Like : currentLevel = new GameLevel()), call it's update and draw methods in the Game1.Update and Game1.Draw. When I want to change the level, I do Game1.currentLevel = new RandomLevel()
I load my textures for all my levels in Game1.LoadContent by calling the static Load method in my levels.
My actual levels look like this:
class GameLevel : Level {
Background sky = new Background(new Vector2(0, 0), -0.2f);
Background land = new Background(new Vector2(0, 0), -0.2f);
public static void Load()
{
...
}
public override void Update(GameTime gameTime)
{
...
}
public override void Draw(SpriteBatch spriteBatch)
{
...
}
}
My problem:
If my textures are static in the levels, I can load them in my static Load method which I call in the Game1.LoadContent. It works fine only if there are static textures(In fact, It works like that in my other levels). But in this level, I have a normal background objects. So I can't call sky.Load and land.Load in a static method. I want to create a lot of backgrounds with different textures. So how do I load the texture for my each of my background objects at the start of the GameLevel?
I was thinking of using a DrawableGameComponent and inherit it in GameLevel but I can't. Because I already inherited Level. I also got to know that GameComponents are deprecated.
I made a static ContentManager reference in Game1and assigned Game1's Content to it. Then I used that content to load the texture for the Background object in the constructor of the GameLevel object. I didn't think it will work before because I thought the contents are only loaded once per game. (As it was written in the comments of the LoadContent method. It worked anyway.
If there is a better way to do it, please do tell me.

How to load sprite from another class and change it on input XNA

I am just learning XNA and i have some basic questions.
Let's say I have class Car and want to load/draw sprites. How should I do it? Xna created methods for that but they are in Game class.
After I deal with loading my sprite using custom class my class to have 3 states and depending on state. I want it to draw different sprite. I would like to have them all loaded in array of Texture2d.
As you said, these methods are in the Game class and unless you change radically how things work, you'll have to get used to that fact.
However, as soon as you inherit from DrawableGameComponent (which all your game objects should do if you want them to draw/update), you'll see that all your objects need to have that Game class passed to them in the constructor anyway because of their base class. You can then use it from there to load your textures :
public class Car : DrawableGameComponent
{
private Texture2D texture;
public Car(Game game) : base(game)
{
texture = game.Content.Load<Texture2D>("mytexture");
}
}
Or hold a reference to it and load them later. This also means somewhere in your Game1.cs you have something like
Car car = new Car(this);
or, if you're loading it from an other file, that file need to know your Game class in order to create your car.
To draw the sprites, simply use the
public override void Draw(GameTime gameTime)
method of DrawableGameComponent.
If you want to make it less painful, you could still create a static class that holds your Game class (and possibly your SpriteBatch, as it will be quite useful) :
public static class GameHolder
{
public static Game Game { get; set; }
}
So your game components can be created like this :
public class Car : DrawableGameComponent
{
private Texture2D texture;
public Car() : base(GameHolder.Game)
{
texture = GameHolder.Game.Content.Load<Texture2D>("mytexture");
}
}
And don't forget to feed the Game value from your Game1 class :
public class Game1 : Game
{
public Game1()
{
graphics = new GraphicsDeviceManager(this);
GameHolder.Game = this;
}
}
To handle states, you can indeed load them all in an array and print one of them. To remove the logic from the Draw method, you can also use an other Texture2D that will hold the current sprite you want to render.
public class Car : DrawableGameComponent
{
private List<Texture2D> states;
private Texture2D currentState;
private SpriteBatch spriteBatch;
public Car(Game game, SpriteBatch sb) : base(GameHolder.Game)
{
states.Add(game.Content.Load<Texture2D>("state1"));
states.Add(game.Content.Load<Texture2D>("state2"));
states.Add(game.Content.Load<Texture2D>("state3"));
currentState = states[0];
spriteBatch = sb;
}
public override void Update(GameTime gameTime)
{
if (someCondition)
{
currentState = states[0];
}
else if (someOtherCondition)
{
currentState = states[1];
}
else if (moreConditions)
{
currentState = states[2];
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
spriteBatch.Draw(currentState, new Vector2(0,0), Color.White);
base.Draw(gameTime);
}
}
As you can see, the Draw method don't really care you have multiple states to handle.
If you need a detailed tutorial, the Platformer Starter Kit is really awesome.

XNA Update methods from other classes

I want to create a simple game where my character can walk, jump, shoot. But I got stuck on how to call the update method from another class? At the moment all of my code is in Game.cs (the main class), but I want to create different classes, depending on my needs. How can I do this?
Your update code is called many times each second automaticly (Depending on your frame rate).
Unlike regular C# programming that uses events and must update on its own, in XNA your update code will loop every few milliseconds.
If you want to have other classes make their own updates you can use a GameComponent, or simply create a method in another class.
Here is an example of your main class:
public class Game1 : Game
{
Level level;
protected override void Initialize()
{
// TODO: Add your initialization logic here
level = new Level();
base.Initialize();
}
protected override void Update(GameTime gameTime)
{
// TODO: Add your update logic here
level.Update(gameTime);
base.Update(gameTime);
}
And your other Level class
public class Level
{
Vector2 CharacterPosition;
public Level()
{
}
public void Update(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
//Move the character
ChactacterPosition.X += elapsed * speed;
}
}
you could create custom classes with an update and a draw method to call on every update from game.cs.
this is the best place to start learning xna:
http://xbox.create.msdn.com/en-US/education/tutorial/2dgame/getting_started
if you go through the first parts of the tutorial, you'll get exactly the examples you need for your question.

Categories