我对XNA来说相对较新且缺乏经验,我一直在跟随Jamie McMahon的指南,在C#中创建一个基本的突围游戏(http://xnagpa.net/xna4beginner.php)。最近,我偶然发现了微软的游戏状态管理代码示例(http://xbox.create.msdn.com/en-US/education/catalog/sample/game_state_management),并试图将我的突破代码移动到所以我可以利用代码示例提供给我的菜单。但是,每当我尝试启动游戏时,我都会在spriteBatch.Begin中获得一个NullReferenceException,代码如下:
#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using GameStateManagement;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Media;
#endregion
namespace Rebound
{
/// <summary>
/// This screen implements the actual game logic. It is just a
/// placeholder to get the idea across: you'll probably want to
/// put some more interesting gameplay in here!
/// </summary>
public class GameplayScreen : GameScreen
{
#region Fields
ContentManager content;
SpriteFont gameFont;
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Ball ball;
Paddle paddle;
Rectangle screenRectangle;
int bricksWide = 10;
int bricksHigh = 5;
Texture2D brickImage;
Brick[,] bricks;
float pauseAlpha;
InputAction pauseAction;
#endregion
#region Initialization
/// <summary>
/// Constructor.
/// </summary>
public GameplayScreen()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 480;
screenRectangle = new Rectangle(
0,
0,
graphics.PreferredBackBufferWidth,
graphics.PreferredBackBufferHeight);
TransitionOnTime = TimeSpan.FromSeconds(1.5);
TransitionOffTime = TimeSpan.FromSeconds(0.5);
pauseAction = new InputAction(
new Buttons[] { Buttons.Start, Buttons.Back },
new Keys[] { Keys.Escape },
true);
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// Load graphics content for the game.
/// </summary>
///
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
gameFont = Content.Load<SpriteFont>("gamefont");
Texture2D tempTexture = Content.Load<Texture2D>("paddle");
paddle = new Paddle(tempTexture, screenRectangle);
tempTexture = Content.Load<Texture2D>("ball");
ball = new Ball(tempTexture, screenRectangle);
brickImage = Content.Load<Texture2D>("brick");
StartGame();
// once the load has finished, we use ResetElapsedTime to tell the game's
// timing mechanism that we have just finished a very long frame, and that
// it should not try to catch up.
ScreenManager.Game.ResetElapsedTime();
base.LoadContent();
}
private void StartGame()
{
paddle.SetInStartPosition();
ball.SetInStartPosition(paddle.GetBounds());
bricks = new Brick[bricksWide, bricksHigh];
for (int y = 0; y < bricksHigh; y++)
{
Color tint = Color.White;
switch (y)
{
case 0:
tint = Color.Blue;
break;
case 1:
tint = Color.Red;
break;
case 2:
tint = Color.Green;
break;
case 3:
tint = Color.Yellow;
break;
case 4:
tint = Color.Purple;
break;
}
for (int x = 0; x < bricksWide; x++)
{
bricks[x, y] = new Brick(
brickImage,
new Rectangle(
x * brickImage.Width,
y * brickImage.Height,
brickImage.Width,
brickImage.Height),
tint);
}
}
}
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>
#endregion
#region Update and Draw
/// <summary>
/// Updates the state of the game. This method checks the GameScreen.IsActive
/// property, so the game will stop updating when the pause menu is active,
/// or if you tab away to a different application.
/// </summary>
public override void Update(GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, false);
// Gradually fade in or out depending on whether we are covered by the pause screen.
if (coveredByOtherScreen)
pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
else
pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
paddle.Update();
ball.Update();
foreach (Brick brick in bricks)
{
brick.CheckCollision(ball);
}
ball.PaddleCollision(paddle.GetBounds());
if (ball.OffBottom())
StartGame();
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
}
/// <summary>
/// Draws the gameplay screen.
/// </summary>
public override void Draw(GameTime gameTime)
{
// This game has a blue background. Why? Because!
ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
Color.CornflowerBlue, 0, 0);
spriteBatch.Begin();
foreach (Brick brick in bricks)
brick.Draw(spriteBatch);
paddle.Draw(spriteBatch);
ball.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
// If the game is transitioning on or off, fade it out to black.
if (TransitionPosition > 0 || pauseAlpha > 0)
{
float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);
ScreenManager.FadeBackBufferToBlack(alpha);
}
}
#endregion
}
}
那我在哪里错了?我对这个错误进行了一些研究,没有一个解决方案帮助过我。请随意指出我的代码中的任何其他冗余/错误,因为我对这类事情非常缺乏经验。谢谢!
编辑:从微软的GameStateManagement代码示例和我试图与之合并的Breakout游戏代码中添加原始GameplayScreen.cs。如果有人知道一个简单的方法,我很乐意听取他们的建议。
突破游戏代码:
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 Rebound
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Ball ball;
Paddle paddle;
Rectangle screenRectangle;
int bricksWide = 10;
int bricksHigh = 5;
Texture2D brickImage;
Brick[,] bricks;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 750;
graphics.PreferredBackBufferHeight = 600;
screenRectangle = new Rectangle(
0,
0,
graphics.PreferredBackBufferWidth,
graphics.PreferredBackBufferHeight);
}
/// <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);
Texture2D tempTexture = Content.Load<Texture2D>("paddle");
paddle = new Paddle(tempTexture, screenRectangle);
tempTexture = Content.Load<Texture2D>("ball");
ball = new Ball(tempTexture, screenRectangle);
brickImage = Content.Load<Texture2D>("brick");
StartGame();
}
private void StartGame()
{
paddle.SetInStartPosition();
ball.SetInStartPosition(paddle.GetBounds());
bricks = new Brick[bricksWide, bricksHigh];
for (int y = 0; y < bricksHigh; y++)
{
Color tint = Color.White;
switch (y)
{
case 0:
tint = Color.Blue;
break;
case 1:
tint = Color.Red;
break;
case 2:
tint = Color.Green;
break;
case 3:
tint = Color.Yellow;
break;
case 4:
tint = Color.Purple;
break;
}
for (int x = 0; x < bricksWide; x++)
{
bricks[x, y] = new Brick(
brickImage,
new Rectangle(
x * brickImage.Width,
y * brickImage.Height,
brickImage.Width,
brickImage.Height),
tint);
}
}
}
/// <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();
paddle.Update();
ball.Update();
foreach (Brick brick in bricks)
{
brick.CheckCollision(ball);
}
ball.PaddleCollision(paddle.GetBounds());
if (ball.OffBottom())
StartGame();
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();
foreach (Brick brick in bricks)
brick.Draw(spriteBatch);
paddle.Draw(spriteBatch);
ball.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
来自Microsoft代码示例的原始GameplayScreen.cs:
#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using GameStateManagement;
#endregion
namespace GameStateManagementSample
{
/// <summary>
/// This screen implements the actual game logic. It is just a
/// placeholder to get the idea across: you'll probably want to
/// put some more interesting gameplay in here!
/// </summary>
class GameplayScreen : GameScreen
{
#region Fields
ContentManager content;
SpriteFont gameFont;
Vector2 playerPosition = new Vector2(100, 100);
Vector2 enemyPosition = new Vector2(100, 100);
Random random = new Random();
float pauseAlpha;
InputAction pauseAction;
#endregion
#region Initialization
/// <summary>
/// Constructor.
/// </summary>
public GameplayScreen()
{
TransitionOnTime = TimeSpan.FromSeconds(1.5);
TransitionOffTime = TimeSpan.FromSeconds(0.5);
pauseAction = new InputAction(
new Buttons[] { Buttons.Start, Buttons.Back },
new Keys[] { Keys.Escape },
true);
}
/// <summary>
/// Load graphics content for the game.
/// </summary>
public override void Activate(bool instancePreserved)
{
if (!instancePreserved)
{
if (content == null)
content = new ContentManager(ScreenManager.Game.Services, "Content");
gameFont = content.Load<SpriteFont>("gamefont");
// A real game would probably have more content than this sample, so
// it would take longer to load. We simulate that by delaying for a
// while, giving you a chance to admire the beautiful loading screen.
Thread.Sleep(1000);
// once the load has finished, we use ResetElapsedTime to tell the game's
// timing mechanism that we have just finished a very long frame, and that
// it should not try to catch up.
ScreenManager.Game.ResetElapsedTime();
}
#if WINDOWS_PHONE
if (Microsoft.Phone.Shell.PhoneApplicationService.Current.State.ContainsKey("PlayerPosition"))
{
playerPosition = (Vector2)Microsoft.Phone.Shell.PhoneApplicationService.Current.State["PlayerPosition"];
enemyPosition = (Vector2)Microsoft.Phone.Shell.PhoneApplicationService.Current.State["EnemyPosition"];
}
#endif
}
public override void Deactivate()
{
#if WINDOWS_PHONE
Microsoft.Phone.Shell.PhoneApplicationService.Current.State["PlayerPosition"] = playerPosition;
Microsoft.Phone.Shell.PhoneApplicationService.Current.State["EnemyPosition"] = enemyPosition;
#endif
base.Deactivate();
}
/// <summary>
/// Unload graphics content used by the game.
/// </summary>
public override void Unload()
{
content.Unload();
#if WINDOWS_PHONE
Microsoft.Phone.Shell.PhoneApplicationService.Current.State.Remove("PlayerPosition");
Microsoft.Phone.Shell.PhoneApplicationService.Current.State.Remove("EnemyPosition");
#endif
}
#endregion
#region Update and Draw
/// <summary>
/// Updates the state of the game. This method checks the GameScreen.IsActive
/// property, so the game will stop updating when the pause menu is active,
/// or if you tab away to a different application.
/// </summary>
public override void Update(GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, false);
// Gradually fade in or out depending on whether we are covered by the pause screen.
if (coveredByOtherScreen)
pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
else
pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);
if (IsActive)
{
// Apply some random jitter to make the enemy move around.
const float randomization = 10;
enemyPosition.X += (float)(random.NextDouble() - 0.5) * randomization;
enemyPosition.Y += (float)(random.NextDouble() - 0.5) * randomization;
// Apply a stabilizing force to stop the enemy moving off the screen.
Vector2 targetPosition = new Vector2(
ScreenManager.GraphicsDevice.Viewport.Width / 2 - gameFont.MeasureString("Insert Gameplay Here").X / 2,
200);
enemyPosition = Vector2.Lerp(enemyPosition, targetPosition, 0.05f);
// TODO: this game isn't very fun! You could probably improve
// it by inserting something more interesting in this space :-)
}
}
/// <summary>
/// Lets the game respond to player input. Unlike the Update method,
/// this will only be called when the gameplay screen is active.
/// </summary>
public override void HandleInput(GameTime gameTime, InputState input)
{
if (input == null)
throw new ArgumentNullException("input");
// Look up inputs for the active player profile.
int playerIndex = (int)ControllingPlayer.Value;
KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];
// The game pauses either if the user presses the pause button, or if
// they unplug the active gamepad. This requires us to keep track of
// whether a gamepad was ever plugged in, because we don't want to pause
// on PC if they are playing with a keyboard and have no gamepad at all!
bool gamePadDisconnected = !gamePadState.IsConnected &&
input.GamePadWasConnected[playerIndex];
PlayerIndex player;
if (pauseAction.Evaluate(input, ControllingPlayer, out player) || gamePadDisconnected)
{
#if WINDOWS_PHONE
ScreenManager.AddScreen(new PhonePauseScreen(), ControllingPlayer);
#else
ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
#endif
}
else
{
// Otherwise move the player position.
Vector2 movement = Vector2.Zero;
if (keyboardState.IsKeyDown(Keys.Left))
movement.X--;
if (keyboardState.IsKeyDown(Keys.Right))
movement.X++;
if (keyboardState.IsKeyDown(Keys.Up))
movement.Y--;
if (keyboardState.IsKeyDown(Keys.Down))
movement.Y++;
Vector2 thumbstick = gamePadState.ThumbSticks.Left;
movement.X += thumbstick.X;
movement.Y -= thumbstick.Y;
if (input.TouchState.Count > 0)
{
Vector2 touchPosition = input.TouchState[0].Position;
Vector2 direction = touchPosition - playerPosition;
direction.Normalize();
movement += direction;
}
if (movement.Length() > 1)
movement.Normalize();
playerPosition += movement * 8f;
}
}
/// <summary>
/// Draws the gameplay screen.
/// </summary>
public override void Draw(GameTime gameTime)
{
// This game has a blue background. Why? Because!
ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
Color.CornflowerBlue, 0, 0);
// Our player and enemy are both actually just text strings.
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
spriteBatch.Begin();
spriteBatch.DrawString(gameFont, "// TODO", playerPosition, Color.Green);
spriteBatch.DrawString(gameFont, "Insert Gameplay Here",
enemyPosition, Color.DarkRed);
spriteBatch.End();
// If the game is transitioning on or off, fade it out to black.
if (TransitionPosition > 0 || pauseAlpha > 0)
{
float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);
ScreenManager.FadeBackBufferToBlack(alpha);
}
}
#endregion
}
}
答案 0 :(得分:0)
您正在spriteBatch
初始化LoadContent
。在你做任何事情之前,你确定你正在运行GamePlayScreen.Loadcontent()
吗?也许您可以向我们展示使用此GameScreen
对象的类。另一方面,您可以通过将它传递给SpriteBatch
的draw方法来使用主类中的GameScreen
对象。
public void Draw(GameTime gameTime, SpriteBatch spritebatch)
{
}
//In the main class (Game1) you can use draw like this:
gameScreen.Draw(gameTime, spritebatch);
另外,我从未在绘图方法中使用过GameTime。 GameTime afaik用于更新逻辑,在Update方法中。我觉得你创造了一些混乱。重新开始,也许是一个更简单的教程。 gameScreen需要基本的继承知识。如果您想了解有关游戏屏幕管理的更多信息,我建议您首先了解有关继承的更多信息。
答案 1 :(得分:0)
你的代码中似乎没有激活方法(除非我错过了它)。 从内存中,这是从屏幕管理器实际调用的方法。覆盖此方法,并从那里调用loadcontent方法。 此外 - 您不需要重新创建您的spritebatch。我相当肯定屏幕管理器有一个引用它 - 所以你应该有这样的东西.spritebatch = ScreenManager.Spritebatch;
再次 - 这是来自内存,我可以在我回家后再次更新,并且可以引用我自己的项目。
答案 2 :(得分:0)
您是否先添加纹理文件?因为如果没有那就是为什么它会给出一个null异常......因为它无法找到并加载纹理文件。