I am trying to create a simple menu, the string that is selected changes colur as it is instructed to do, I just can't seem to space out each item in the string list on the Y axis, currently they are all positioned in the middle but at the top of the screen overlapping each other. I know this is probably a very simple fix but I am a novice at XNA. Any help would be very appreciated. Thanks in advance.
MenuManagement Class
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Game2
{
class MenuManagement
{
KeyboardState keyboard;
KeyboardState prevKey;
MouseState mouse;
Vector2 position;
List<String> buttonList = new List<string>();
SpriteFont spriteFont;
int selected = 0;
public MenuManagement ()
{
buttonList.Add("Play");
buttonList.Add("Options");
buttonList.Add("instructions");
buttonList.Add("Exit");
}
private void MeasureMenu()
{
height = 0;
width = 0;
foreach (string item in buttonList)
{
Vector2 size = spriteFont.MeasureString(item);
height += spriteFont.LineSpacing + 5;
}
}
public void LoadContent (ContentManager Content )
{
spriteFont = Content.Load<SpriteFont>("spriteFont");
}
public void Update (GameTime theGameTime)
{
keyboard = Keyboard.GetState();
if (checkKeyBoard(Keys.Up))
{
if (selected > 0)
{
selected--;
}
}
if (checkKeyBoard(Keys.Down))
{
if (selected < buttonList.Count - 1)
{
selected++;
}
}
prevKey = keyboard;
}
public bool checkKeyBoard (Keys key)
{
return (keyboard.IsKeyDown(key) && prevKey.IsKeyDown(key));
}
public void Draw (SpriteBatch theSpriteBatch)
{
theSpriteBatch.Begin();
Color color;
for (int i = 0; i < buttonList.Count; i++)
{
Vector2 location = position;
if (i == selected)
{
color = Color.Yellow;
}
else
{
color = Color.Blue;
}
location.Y += spriteFont.LineSpacing + 5;
}
theSpriteBatch.DrawString(spriteFont, buttonList[0], new Vector2 (300, 100), color);
theSpriteBatch.DrawString(spriteFont, buttonList[1], new Vector2(300, 150), color);
theSpriteBatch.DrawString(spriteFont, buttonList[2], new Vector2(300, 200), color);
theSpriteBatch.DrawString(spriteFont, buttonList[3], new Vector2(300, 250), color);
theSpriteBatch.End();
}
}
}
Game1
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Game2
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch theSpriteBatch;
public static Rectangle screen;
public static string GameState = "Menu";
MenuManagement menuManagement;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
menuManagement = new MenuManagement();
screen = new Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight);
base.Initialize();
}
protected override void LoadContent()
{
theSpriteBatch = new SpriteBatch(GraphicsDevice);
menuManagement.LoadContent(Content);
// TODO: use this.Content to load your game content here
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
switch (GameState)
{
case "Menu":
menuManagement.Update(gameTime);
break;
}
base.Update(gameTime);
}
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
switch (GameState)
{
case "Menu":
menuManagement.Draw(theSpriteBatch);
break;
}
base.Draw(gameTime);
}
}
}
You can calculate the length of the strings and place the next item where you want. You should look to SpriteFont.MeasureString().
https://msdn.microsoft.com/en-us/library/bb464128(v=xnagamestudio.30).aspx
To be clear, the reason they are overlapping is because you are putting them all in the same position:
Vector2 location = position;
and then:
new Vector2 (300, location.Y)
is where you are drawing the entire batch. You need to change the position of each of the menu items. The simplest fix is to increment the Y value of the location for each item. That will move the next menu item toward the bottom. Increment X to move right.
Related
So I have a problem that I cant figure out, and that is drawing objects into my main Game1 class from a class of drawablegamecomponent.
I'v been playing around with it for a bit and looking at other examples, but can't figure out the issue. There isn't much code as it is, so I'll just post the two classes I have, the main class Game1 and the class I want to draw, Balloon.
The error is in the draw method I get this...
An unhandled exception of type 'System.NullReferenceException' occurred in Burst.exe
Additional information: Object reference not set to an instance of an object.
Main Class.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Burst
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Rectangle bounds;
Balloon ball;
Diamond dia;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferWidth = 1152;
graphics.PreferredBackBufferHeight = 648;
Content.RootDirectory = "Content";
bounds = new Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight);
ball = new Balloon(bounds, 1, this, spriteBatch);
dia = new Diamond(bounds, new Vector2(200, 200), this, spriteBatch);
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
ball.Update();
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
// TODO: Add your drawing code here
ball.Draw(gameTime);
dia.Draw(gameTime);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Balloon Class:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Burst
{
public class Balloon : Microsoft.Xna.Framework.DrawableGameComponent
{
Vector2 position;
Vector2 motion;
Texture2D texture;
Rectangle bounds;
Rectangle screenBounds;
Game game;
SpriteBatch spriteBatch;
float balloonSpeed = 4;
int colour = 0;
public Balloon(Rectangle screenBounds, int colour, Game game, SpriteBatch spriteBatch) : base (game)
{
this.colour = colour;
this.game = game;
this.spriteBatch = spriteBatch;
this.screenBounds = screenBounds;
this.position = new Vector2(200,200);
}
protected override void LoadContent()
{
switch (colour)
{
case 1: texture = Game.Content.Load<Texture2D>("Images/BlueBalloon");
break;
case 2: texture = Game.Content.Load<Texture2D>("Images/GreenBalloon");
break;
case 3: texture = Game.Content.Load<Texture2D>("Images/RedBalloon");
break;
case 4: texture = Game.Content.Load<Texture2D>("Images/YellowBalloon");
break;
case 5: texture = Game.Content.Load<Texture2D>("Images/PurpleBalloon");
break;
}
}
public Rectangle Bounds
{
get
{
bounds.X = (int)position.X;
bounds.Y = (int)position.Y;
return bounds;
}
}
public void Update()
{
position += motion * balloonSpeed;
}
private void CheckWallColision()
{
if (position.X < 0)
{
position.X = 0;
motion.X *= -1;
}
if (position.X + texture.Width > screenBounds.Width)
{
position.X = screenBounds.Width - texture.Width;
motion.X *= -1;
}
if (position.Y < 0)
{
position.Y = 0;
motion.Y *= -1;
}
if (position.Y + texture.Height > screenBounds.Height)
{
position.Y = screenBounds.Height - texture.Height;
motion.Y *= -1;
}
}
public void SetStartPosition()
{
Random rand = new Random();
motion = new Vector2(rand.Next(2, 6), -rand.Next(2, 6));
motion.Normalize();
position = new Vector2(200, 300);
}
public void Draw()
{
spriteBatch.Draw(texture, position, Color.White);
}
}
}
"spriteBatch" == null when you create objects "ball" and "dia".
You need move this code:
bounds = new Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight);
ball = new Balloon(bounds, 1, this, spriteBatch);
dia = new Diamond(bounds, new Vector2(200, 200), this, spriteBatch);
to method "LoadContent". After creating "spriteBatch".
I'm trying to code a very basic top down 2D game with XNA, and I've gotten to the part where I'm trying to add grass textures to the game and then draw those textures to the bottom portion of the screen.
However, I'm getting a "NullReferenceException was Unhandled" error on Line 32 of my Level.cs class. As I'm just new to XNA and fairly new (in comparison) to C#, I can't figure this out for the life of me.
UPDATE 1: Thanks mcmonkey4eva, that error is resolved. However, my code now stops during the Draw() method of my TestLevel class (Level.cs). The error is still a "NullReferenceException was Unhandled" error, and the methods are a bit different from what they were.
Anyone know this what I'm doing wrong here?
Here's my updated Level.cs class:
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;
using First.Tiles;
using First;
namespace First.Level
{
class TestLevel
{
//An array of groundtiles that will be set to grass, lava, etc
public GroundTile[,] groundlevel;
//Width/Height of the Level
int width, height;
Grass grass;
public TestLevel(int width, int height, ContentManager myContent)
{
content = myContent;
//I input my screen dimensions as my level size
this.width = width;
this.height = height;
groundlevel = new GroundTile[width, height];
grass = new Grass(content.Load<Texture2D>(#"Images\GroundTiles"));
}
//Drawing the grass near the bottom of the screen
public void generateGround()
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x += 32)
{
if (y == (height - 100))
{
if (groundlevel[x, y] == null)
{
groundlevel[x, y] = grass;
}
}
}
}
}
public void draw(GameTime gameTime, SpriteBatch spriteBatch)
{
foreach(GroundTile ground in groundlevel)
{
ground.Draw(gameTime, spriteBatch); //Here's where the error is
}
}
public static ContentManager Content
{
get { return content; }
}
static ContentManager content;
}
}
Here's my updated Game1.cs class:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using First.Entity;
using First.Tiles;
using First.Level;
namespace First
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
const int SCREEN_WIDTH = 600;
const int SCREEN_HEIGHT = 400;
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TestLevel level;
UserControlledSprite Lancer;
Texture2D lancerTexture;
Vector2 position = new Vector2(200, 200);
Point frameSize = new Point(32, 48);
int collisionOffset = 0;
Point currentFrame = new Point(0, 0);
Point sheetSize = new Point(4, 4);
Point spriteToUse = new Point(0, 0);
Vector2 speed = new Vector2(2, 2);
int millisecondsPerFrame = 500;
Rectangle clientBounds;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
clientBounds = graphics.GraphicsDevice.Viewport.Bounds;
clientBounds.Width = SCREEN_WIDTH;
clientBounds.Height = SCREEN_HEIGHT;
this.IsMouseVisible = true;
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
Content.RootDirectory = "Content";
level = new TestLevel(SCREEN_WIDTH, SCREEN_HEIGHT, Content);
level.generateGround();
lancerTexture = Content.Load<Texture2D>(#"Images\Lancer");
Lancer = new UserControlledSprite(lancerTexture, position, frameSize, collisionOffset,
currentFrame, sheetSize, spriteToUse, speed, millisecondsPerFrame);
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
Lancer.Update(gameTime, clientBounds);
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
Lancer.Draw(gameTime, spriteBatch);
level.draw(gameTime, spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Here's my Grass.cs class:
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;
using First.Tiles;
using First;
namespace First.Level
{
class TestLevel
{
//An array of groundtiles that will be set to grass, lava, etc
public GroundTile[,] groundlevel;
//Width/Height of the Level
int width, height;
Grass grass;
public TestLevel(int width, int height, ContentManager myContent)
{
content = myContent;
//I input my screen dimensions as my level size
this.width = width;
this.height = height;
groundlevel = new GroundTile[width, height];
grass = new Grass(content.Load<Texture2D>(#"Images\GroundTiles"));
}
//Drawing the grass near the bottom of the screen
public void generateGround()
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x += 32)
{
if (y == (height - 100))
{
if (groundlevel[x, y] == null)
{
groundlevel[x, y] = grass;
}
}
}
}
}
public void draw(GameTime gameTime, SpriteBatch spriteBatch)
{
foreach(GroundTile ground in groundlevel)
{
ground.Draw(gameTime, spriteBatch);
}
}
public static ContentManager Content
{
get { return content; }
}
static ContentManager content;
}
}
Just in case, here's my GroundTile class too:
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;
using First.Entity;
namespace First.Tiles
{
class GroundTile
{
//public GroundTile grass = new Grass(content.Load<Texture2D>(#"Images\GroundTiles"));
// frameSize needs to be modular, for the objects above walking-ground level
public Texture2D texture;
protected Point frameSize;
public Point frame;
public Vector2 position;
int collisionOffset;
protected Point sheetSize = new Point(9, 19);
public GroundTile(Texture2D tiles, Point frame, Point frameSize, int collisionOffset)
{
this.frame = frame;
this.frameSize = frameSize;
this.collisionOffset = collisionOffset;
this.texture = tiles;
}
//Collision Detection, incase of water or something
public Rectangle collisionRect
{
get
{
return new Rectangle(
(int)position.X + collisionOffset,
(int)position.Y + collisionOffset,
frameSize.X - (collisionOffset * 2),
frameSize.Y - (collisionOffset * 2));
}
}
//Not really used, but just in case
public bool collidesWith(GroundTile tile, Sprite sprite)
{
if (sprite.collisionRect.Intersects(collisionRect))
{
sprite.position -= sprite.direction;
}
return false;
}
public static ContentManager Content
{
get { return content; }
}
static ContentManager content;
public GraphicsDevice Graphicsdevice
{
get { return graphicsDevice; }
}
GraphicsDevice graphicsDevice;
public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture,
position,
new Rectangle(frame.X * frameSize.X,
frame.Y * frameSize.Y,
frameSize.X, frameSize.Y),
Color.White, 0, Vector2.Zero,
1f, SpriteEffects.None, 0);
}
}
}
You're accessing Content before it's set - or, rather, you're never setting it at all.
Change
public TestLevel(int width, int height)
{
//I input my screen dimensions as my level size
to
public TestLevel(int width, int height, ContentManager myContent)
{
content = myContent;
//I input my screen dimensions as my level size
and then add the Content argument when you create your TestLevel object (I assume from Game1.cs) (Note: Be sure to create TestLevel after you create the Content object [in the LoadContent method]!)
EDIT:
For the new problem:
You're not defining the contents of the array, except a single layer of grass...
The line
ground.Draw(gameTime, spriteBatch);
Change to
if (ground != null)
{
ground.Draw(gameTime, spriteBatch);
}
But you should also make sure that ground is actually getting filled with content... specifically, new tile objects for each and every point in the array, not just a single line, and not just the same 'grass' instance for every location.
I don't mean to offend, but you're dealing with pretty basic errors here. It might be beneficial for you to look up and follow some basic C# tutorials.
I don't know about XNA, but clearly you Content. returns null.
Is this a class name or a property of your level class? If it's a property, then make sure it is initialized. Otherwize, I don't understand how you would received a nullreferenceexception if you call a static property of a class
The problem is that it just shows a black screen and the loading mouse. It doesn't show the loading screen or the menu.
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteBatch mBatch;
Texture2D mTheQuantumBros2;
MenuComponent menuComponent;
public static Rectangle screen;
public static string GameState = "Menu";
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferMultiSampling = false;
graphics.IsFullScreen = true;
graphics.PreferredBackBufferWidth = 1366;
graphics.PreferredBackBufferHeight = 768;
graphics.ApplyChanges();
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
mBatch = new SpriteBatch(this.graphics.GraphicsDevice);
ContentManager aLoader = new ContentManager(this.Services);
aLoader.RootDirectory = "Content";
mTheQuantumBros2 = aLoader.Load<Texture2D>("TheQuantumBros2") as Texture2D;
menuComponent.LoadContent(Content);
}
protected override void UnloadContent()
{
}
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
switch (GameState)
{
case "Menu":
menuComponent.Update(gameTime);
break;
}
base.Update(gameTime);
}
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
switch (GameState)
{
case "Menu":
menuComponent.Draw(spriteBatch);
break;
}
base.Draw(gameTime);
}
}
Other class:
class MenuComponent
{
KeyboardState keyboard;
KeyboardState prevKeyboard;
SpriteBatch mBatch;
Texture2D mTheQuantumBros2;
GameTime gameTime;
MouseState mouse;
MouseState prevMouse;
SpriteFont spriteFont;
List<string> buttonList = new List<string>();
int selected = 0;
public MenuComponent()
{
buttonList.Add("Campaign");
buttonList.Add("Multiplayer");
buttonList.Add("Zombies");
buttonList.Add("Quit Game");
}
public void LoadContent(ContentManager Content)
{
spriteFont = Content.Load<SpriteFont>("Font");
}
public void Update(GameTime gameTime)
{
keyboard = Keyboard.GetState();
mouse = Mouse.GetState();
if (CheckKeyboard(Keys.Up))
{
if (selected > 0) selected--;
}
if (CheckKeyboard(Keys.Down))
{
if (selected < buttonList.Count - 1) selected++;
}
prevMouse = mouse;
prevKeyboard = keyboard;
}
public bool CheckMouse()
{
return (mouse.LeftButton == ButtonState.Pressed && prevMouse.LeftButton == ButtonState.Released);
}
public bool CheckKeyboard(Keys key)
{
return (keyboard.IsKeyDown(key) && prevKeyboard.IsKeyDown(key));
}
public void Draw(SpriteBatch spriteBatch)
{
Color color;
int linePadding = 3;
spriteBatch.Begin();
mBatch.Begin();
mBatch.Draw(mTheQuantumBros2, new Rectangle(300, 150, mTheQuantumBros2.Width, mTheQuantumBros2.Height), Color.White);
if (gameTime.TotalGameTime.TotalSeconds <= 3)
{
mBatch.End();
for (int i = 0; i < buttonList.Count; i++)
{
color = (i == selected) ? Color.LawnGreen : Color.Gold;
spriteBatch.DrawString(spriteFont, buttonList[i], new Vector2((Game1.screen.Width / 2) - (spriteFont.MeasureString(buttonList[i]).X / 2), (Game1.screen.Height / 2) - (spriteFont.LineSpacing * (buttonList.Count) / 2) + ((spriteFont.LineSpacing + linePadding) * i)), color);
}
spriteBatch.End();
}
}
}
What is the purpose of this statement: (gameTime.TotalGameTime.TotalSeconds <= 3)?
Also, you are never updating the game time in the menu component. So, the aforementioned code would behave unexpectedly.
You are also calling spriteBatch.Begin() twice before end is called... So that should be giving you an error. Check those for issues.
EDIT: Since you are only calling spriteBatch.End() IF less than 3 seconds have passed, it will never be called again after that? I think.
As a potential fix... I would try only calling spriteBatch.Begin() and spriteBatch.End() inside of game1.cs. So:
//In your menu class
public void Draw(SpriteBatch spriteBatch)
{
Color color;
int linePadding = 3;
if (gameTime.TotalGameTime.TotalSeconds <= 3)
{
mBatch.Begin();
mBatch.Draw(mTheQuantumBros2, new Rectangle(300, 150, mTheQuantumBros2.Width, mTheQuantumBros2.Height), Color.White);
mBatch.End();
}
else
{
for (int i = 0; i < buttonList.Count; i++)
{
color = (i == selected) ? Color.LawnGreen : Color.Gold;
spriteBatch.DrawString(spriteFont, buttonList[i], new Vector2((Game1.screen.Width / 2) - (spriteFont.MeasureString(buttonList[i]).X / 2), (Game1.screen.Height / 2) - (spriteFont.LineSpacing * (buttonList.Count) / 2) + ((spriteFont.LineSpacing + linePadding) * i)), color);
}
}
}
And back in game1.cs, call spriteBatch.End() right before base.Draw() is called.
In general, it is best to only use one spriteBatch, I believe it is simply faster than beginning and ending two different batches.
EDIT 2:
Just uhm... copy and paste I guess. It works fine after the adjustments I made (you can read about them at the bottom).
You're refactored code:
game1.cs:
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
MenuComponent menuComponent;
public static Rectangle screen;
public static string GameState = "Menu";
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferMultiSampling = false;
//graphics.IsFullScreen = true;
graphics.PreferredBackBufferWidth = 1366;
graphics.PreferredBackBufferHeight = 768;
graphics.ApplyChanges();
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
menuComponent = new MenuComponent();
menuComponent.LoadContent(Content, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height);
}
protected override void UnloadContent()
{
}
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
switch (GameState)
{
case "Menu":
menuComponent.Update(gameTime);
break;
}
base.Update(gameTime);
}
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
switch (GameState)
{
case "Menu":
menuComponent.Draw(spriteBatch, gameTime);
break;
}
spriteBatch.End();
base.Draw(gameTime);
}
MenuComponent:
KeyboardState keyboard;
KeyboardState prevKeyboard;
MouseState mouse;
MouseState prevMouse;
GraphicsDevice graphicsDevice;
Texture2D mTheQuantumBros2;
SpriteFont spriteFont;
List<string> buttonList = new List<string>();
int selected = 0;
int screenWidth;
int screenHeight;
public MenuComponent()
{
buttonList.Add("Campaign");
buttonList.Add("Multiplayer");
buttonList.Add("Zombies");
buttonList.Add("Quit Game");
}
public void LoadContent(ContentManager Content, int _screenWidth, int _screenHeight)
{
spriteFont = Content.Load<SpriteFont>("Font");
mTheQuantumBros2 = Content.Load<Texture2D>("TheQuantumBros2");
screenHeight = _screenHeight;
screenWidth = _screenWidth;
}
public void Update(GameTime gameTime)
{
keyboard = Keyboard.GetState();
mouse = Mouse.GetState();
if (CheckKeyboard(Keys.Up))
{
if (selected > 0) selected--;
}
if (CheckKeyboard(Keys.Down))
{
if (selected < buttonList.Count - 1) selected++;
}
prevMouse = mouse;
prevKeyboard = keyboard;
}
public bool CheckMouse()
{
return (mouse.LeftButton == ButtonState.Pressed && prevMouse.LeftButton == ButtonState.Released);
}
public bool CheckKeyboard(Keys key)
{
return (keyboard.IsKeyDown(key) && prevKeyboard.IsKeyDown(key));
}
public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
{
Color color;
int linePadding = 3;
if (gameTime.TotalGameTime.TotalSeconds <= 3)
{
spriteBatch.Draw(mTheQuantumBros2, new Rectangle(300, 150, mTheQuantumBros2.Width, mTheQuantumBros2.Height), Color.White);
}
else
{
for (int i = 0; i < buttonList.Count; i++)
{
color = (i == selected) ? Color.LawnGreen : Color.Gold;
spriteBatch.DrawString(spriteFont, buttonList[i], new Vector2((screenWidth / 2) - (spriteFont.MeasureString(buttonList[i]).X / 2), (screenHeight / 2) - (spriteFont.LineSpacing * (buttonList.Count) / 2) + ((spriteFont.LineSpacing + linePadding) * i)), color);
}
}
}
There were a bunch of little issues. For example, you were setting the menuComp = to a new menu component on every update call, so all the old variables in there would be lost, hence there was no images loading. SpriteBatch.Begin() was being called twice in a row, and then end was being called twice in a row. One spriteBatch (from game1.cs preferrably) should be used to draw. By sending that spritebatch through the method, you do not need to call begin again, and it is simply better not to for performance reasons. Last thing I noticed (that I can think of) was that the graphics device was not being set to anything because you were constantly creating a new version of it. Oh also, you were calling graphicsDevice.Clear() way too early, this only should be called once per draw (at the beginning, so old graphical info is removed from the screen).
I debugged your program/code and there are a few problems:
There is a declaration of MenuComponent menuComponent, but you didn't create an instance in the Game1() constructor. So add: menuComponent = new MenuComponent()
There is a declaration of SpriteBatch mBatch, but you didn't create an instance in the MenuComponent() constructor. So create one. The same for GameTime gameTime variable.
At the moment I don't have SpriteFont and TheQuantumBros2 texture. Is it possible that you upload your project and share it?
I am writing a simple game based on grid movements, something like Sokoban game. I need to use several different text files to store levels. I wrote some code, and now I get error:
System.ArgumentNullException "This method does not accept null for
this parameter. Nazwa parametru: texture"
Whole code is in several files. What is wrong with my code?
Game1.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Sokoban
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Texture2D tOtoczenie;
public Rectangle kOtoczenie;
Sceny scena;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferHeight = 660; // 11
graphics.PreferredBackBufferWidth = 900; // 15
Content.RootDirectory = "Content";
}
// Sterowanie po gridzie (en - grid movement with stops)
KeyboardState stanKlawiatury, poprzedniStanKlawiatury;
public bool WcisnietyKlawisz(Keys klawisz)
{
return stanKlawiatury.IsKeyDown(klawisz) && poprzedniStanKlawiatury.IsKeyUp(klawisz);
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
///
protected override void Initialize()
{
// TODO: Add your initialization logic here
scena = new Sceny();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
tOtoczenie = Content.Load<Texture2D>("Otoczenie");
kOtoczenie = new Rectangle(0, 0, 900, 660);
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
poprzedniStanKlawiatury = stanKlawiatury;
stanKlawiatury = Keyboard.GetState();
if (WcisnietyKlawisz(Keys.Right))
scena.wspX += 60;
else if (WcisnietyKlawisz(Keys.Left))
scena.wspX -= 60;
else if (WcisnietyKlawisz(Keys.Up))
scena.wspY -= 60;
else if (WcisnietyKlawisz(Keys.Down))
scena.wspY += 60;
if (Keyboard.GetState().IsKeyDown(Keys.NumPad1))
scena.LoadScene("level1");
scena.Update();
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
spriteBatch.Draw(tOtoczenie, kOtoczenie, Color.White);
spriteBatch.End();
scena.Draw(spriteBatch);
base.Draw(gameTime);
}
}
}
Sceny.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
using Microsoft.Xna.Framework.Content;
namespace Sokoban
{
public class Sceny
{
public List<ObiektyGry> sciany;
public List<ObiektyGry> sloty;
public List<ObiektyGry> diamenty;
public List<ObiektyGry> graczL;
public Texture2D tDiament;
public Texture2D tSlot;
public Texture2D tSciana;
public Rectangle kDiament;
public Rectangle kSlot;
public Rectangle kSciana;
public Texture2D tGracz;
public Rectangle kGracz;
public int wspX;
public int wspY;
public Sceny()
{
sciany = new List<ObiektyGry>();
sloty = new List<ObiektyGry>();
diamenty = new List<ObiektyGry>();
graczL = new List<ObiektyGry>();
}
public void LoadContent(ContentManager theContentManager, string theAssetName)
{
tGracz = theContentManager.Load<Texture2D>("Gracz");
tDiament = theContentManager.Load<Texture2D>("Diament");
tSlot = theContentManager.Load<Texture2D>("Slot");
tSciana = theContentManager.Load<Texture2D>("Ściana");
}
public void Update()
{
foreach (ObiektyGry obiekt in sciany)
obiekt.Update();
foreach (ObiektyGry obiekt in sloty)
obiekt.Update();
foreach (ObiektyGry obiekt in diamenty)
obiekt.Update();
foreach (ObiektyGry obiekt in graczL)
obiekt.Update();
}
public void Draw(SpriteBatch spriteBatch)
{
foreach (ObiektyGry obiekt in sciany)
obiekt.Draw(spriteBatch, obiekt.Tekstura, obiekt.Kształt);
foreach (ObiektyGry obiekt in sloty)
obiekt.Draw(spriteBatch, obiekt.Tekstura, obiekt.Kształt);
foreach (ObiektyGry obiekt in diamenty)
obiekt.Draw(spriteBatch, obiekt.Tekstura, obiekt.Kształt);
foreach (ObiektyGry obiekt in graczL)
obiekt.Draw(spriteBatch, obiekt.Tekstura, obiekt.Kształt);
}
public void LoadScene(string name)
{
sciany.Clear();
graczL.Clear();
diamenty.Clear();
sloty.Clear();
StreamReader reader = new StreamReader(name+".txt");
int x = 0;
int y = 0;
string file = reader.ReadToEnd();
for (int i = 0; i<file.Length; i++)
{
if (file[i] == 13)
{
y++;
x = 0;
}
else if (file[i] != 10)
x++;
if (file[i] == '1' ) // sciana
{
ObiektyGry sciana = new ObiektyGry();
sciana.Kształt = new Rectangle(x * 60, y * 60,60,60);
sciana.Tekstura = tSciana;
sciany.Add(sciana);
}
else if (file[i] == '2') // slot
{
ObiektyGry slot = new ObiektyGry();
slot.Kształt = new Rectangle(x * 60, y * 60, 60, 60);
slot.Tekstura = tSlot;
sloty.Add(slot);
}
else if (file[i] == '3') // diament
{
ObiektyGry diament = new ObiektyGry();
diament.Kształt = new Rectangle(x * 60, y * 60, 60, 60);
diament.Tekstura = tDiament;
diamenty.Add(diament);
}
else if (file[i] == '4') //gracz
{
ObiektyGry gracz = new ObiektyGry();
wspX = x * 60;
wspY = y * 60;
gracz.Kształt = new Rectangle(wspX, wspY, 60, 60);
gracz.Tekstura = tGracz;
graczL.Add(gracz);
}
}
}
}
}
ObiektyGry.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 Sokoban
{
public class ObiektyGry
{
public Rectangle Kształt { get; internal set; }
public Texture2D Tekstura { get; internal set; }
public static ContentManager contentManager;
public ObiektyGry()
{
}
public void Update()
{
}
public void Draw(SpriteBatch spriteBatch, Texture2D Tekstura, Rectangle Ksztalt)
{
//Tekstura = obiekt.Tekstura;
//Kształt = obiekt.Kształt;
//spriteBatch.Begin();
spriteBatch.Draw(Tekstura, Ksztalt, Color.White);
//spriteBatch.End();
}
}
}
level1.txt (for example)
111111111111111
100000000000001
100000000000001
100000000000001
100020000000001
100000034000001
100000000000001
100000000000001
100000000000001
100000000000001
111111111111111
Yea, as already mentioned in the comments, you haven't loaded your textures for your sceny. I see that you have loaded your level, but not your textures...
So, I deleted all Sceny class and I implemented level changing in Game1 class. After that I hadn't any problem with textures at all!
I am trying to make a game and I want "enemies" or meteors to spawn from the right side and going to the left side. I am working on some code and it seems to be working pretty good but the respawning fails. They end up spawning one and one REALLY slow and then after a while they don't spawn at all. All help would be really appreciated.
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace WindowsGame2
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
List<Enemies> enemies = new List<Enemies>();
Random random = new Random();
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void UnloadContent()
{
}
float spawn = 0;
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
spawn += (float)gameTime.ElapsedGameTime.TotalSeconds;
foreach (Enemies enemy in enemies)
{
enemy.Update(graphics.GraphicsDevice);
}
LoadEnemies();
base.Update(gameTime);
}
public void LoadEnemies()
{
int randY = random.Next(100, 400);
if (spawn > 1)
{
spawn = 0;
if (enemies.Count() < 10)
enemies.Add(new Enemies(Content.Load<Texture2D>("meteor"), new Vector2(1110, randY)));
}
for (int i = 0; i < enemies.Count; i++)
{
if (!enemies[i].isVisible)
{
enemies.RemoveAt(i);
i--;
}
}
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
foreach (Enemies enemy in enemies)
{
enemy.Draw(spriteBatch);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
}
and here's my class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace WindowsGame2
{
class Enemies
{
public Texture2D texture;
public Vector2 position;
public Vector2 velocity;
public bool isVisible = true;
Random random = new Random();
int randX, randY;
public Enemies(Texture2D newTexture, Vector2 newPosition)
{
texture = newTexture;
position = newPosition;
randX = random.Next(-4, 4);
randY = random.Next(-4, -1);
velocity = new Vector2(randX, randY);
}
public void Update(GraphicsDevice graphics)
{
position += velocity;
if (position.Y <= 0 || position.Y >= graphics.Viewport.Height - texture.Height)
{
velocity.Y = -velocity.Y;
}
if (position.X < 0 - texture.Width)
{
isVisible = false;
}
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, Color.White);
}
}
}
Your problem lies here. Some of your meteors are moving right and some are not moving at all.
randX = random.Next(-4, 4);
Try using
randX = random.Next(-4, -1);
Your enemies/meteors sometimes had a velocity on the X that was between 0 and 4, so they moved right.
I can see you changed the Y to this, maybe you got them mixed up?