战斗机飞船游戏






4.10/5 (8投票s)
正如名称所示,您将在银河系中驾驶一艘宇宙飞船,并保护自己免受小行星的侵害。现在,为了展示超极本的游戏功能,我将使用触摸和加速计。
引言
我感到很无聊,想做些创新。我喜欢玩游戏,并且一直想知道
- 游戏将如何开发?
- 角色如何移动等?
这个周末我尝试了 XNA 框架,并且为了应用程序创新竞赛,我提议创建一个战斗机太空游戏。这是我第一次尝试游戏编程,
正如名称所示,您将在银河系中驾驶一艘宇宙飞船,并保护自己免受小行星的侵害。通过这款游戏,我们还希望展示超极本的功能。
我们的游戏将展示超极本的两个主要功能 -
触摸感知 - 游戏将支持触摸,将有两个按钮,一个用于加速您的宇宙飞船,另一个用于制动您的宇宙飞船。这将为您在驾驶宇宙飞船时提供出色的控制。
加速计 - 游戏将支持加速计。玩家只需举起他们的超极本即可感受驾驶的乐趣。
这样,当游戏移植到其他设备(如 Windows Phone、平板电脑等)时,就不需要更改游戏了。
背景
为了理解这一点,您应该具备 C# 编程的基础知识。您对 OOPs 的概念应该很清晰。因为在游戏中,每一个事物都将以对象的形式向您展示。
开始吧
我们将在这里开发一款 2D 游戏。
我开始学习 XNA,创建了一些 2D 游戏。对于 2D 游戏,首先要了解游戏模板、事物如何执行、游戏生命周期、如何在屏幕上显示图像、如何移动事物、碰撞检测等。一旦您掌握了这些基础知识,创建 2D 游戏就只取决于您的创造力了。
我将从头开始,所以即使您正在开发您的第一个游戏,本教程也会对您有所帮助。
1) 先决条件
要开始开发 XNA 游戏,您首先应该在您的机器上安装 XNA Game Studio。XNA Game Studio 有不同的版本,当前运行的版本是 4.0。即使您安装了 Visual Studio 2008,您的机器上默认也会安装 XNA Game Studio 3.0,并且使用 Visual Studio 2010 会安装 Game Studio 4.0。
如果您没有在您的机器上安装 XNA Game Studio,只需在 Google 上搜索,下载并安装在您的机器上。
显然,我们需要 Visual Studio。
2) 理解游戏模板
2.1) 打开 XNA 项目
我将快速解释这一部分,否则这篇文章会太长。
打开 Visual Studio -> 选择新项目 -> 选择 XNA Game Studio -> 选择 Windows Game。
将其命名为 FirstGame。在这里,我们有不同类型的模板,用于 Windows Phone 游戏、Xbox 和 Windows 游戏等。
XNA 是一个非常容易开发游戏的框架,它将隐藏所有内部细节和复杂性,我们只需要想象和开发游戏。在这里,我们不仅仅是拖放事物,所有你需要做的就是通过编码来完成。
2.2) 理解基础知识
这将为我们打开游戏项目。请注意,您将有两个子项目:FirstGame 和 FirstGameContent。
FirstGame 项目将包含您的所有游戏逻辑,而 FirstGameContent 项目将包含您的所有游戏内容。游戏内容可以是您的图像、音频、字体和动画等。
您的 FirstGame 项目将包含 program.cs,这是您游戏的起点,以及项目中 game1.cs 文件,这几乎就是您的游戏本身。这是调用屏幕上其他对象的起点。让我们看看它的方法。
在继续之前,让我们先构思一个游戏,它应该是什么样的,它应该如何以无尽的方式运行。让我们看看下面的伪代码。
Game1() – General initialization (Game1.cs) Initialize() – Game initialization (Game1.cs) LoadContent() – Load Graphics resources (Game1.cs) Run() - Start game loop (Program.cs). In every step: Update() - Read user input, do calculations, and test for game ending (Game1.cs) Draw() – Renderization code (Game1.cs) UnloadContent() – Free graphics resources (Game1.cs)
如上所示的伪代码向我们解释了一切。从 program.cs 开始,我们将创建 game1 对象并调用它。
game1 的 Draw 方法将只调用一次 Initialize,然后它将加载内容并开始在屏幕上绘制对象。在一秒钟内,在 Xbox 或 Windows 上,update 方法将调用 60 次,所以这是我们读取输入、更新游戏对象等的地方。最后,根据用户输入,我们将停止游戏,然后调用其 unload content 方法。
现在看看下面的代码片段,它们是完全相同的。
/// <summary>
/// This is the main type for your game
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Frame rate is 30 fps by default for Windows Phone.
TargetElapsedTime = TimeSpan.FromTicks(333333);
// Extend battery life under lock.
InactiveSleepTime = TimeSpan.FromSeconds(1);
}
/// 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.
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// LoadContent will be called once per game and is the place to load
/// all of your content.
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
}
/// UnloadContent will be called once per game and is the place to unload
/// all content.
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// <param name="gameTime" />Provides a snapshot of timing values.
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
base.Update(gameTime);
}
/// This is called when the game should draw itself.
/// <param name="gameTime" />Provides a snapshot of timing values.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
因为它的结构会自动解释一切。到目前为止,您应该对游戏如何工作有了基本了解。所以我们将开始构建游戏,同时我会继续解释对象的创建、移动和其他动画概念。
开始游戏开发
在开始开发战斗机太空游戏之前,我们需要为游戏进行规划,将包含哪些功能,游戏将如何进行,游戏状态管理。
这款游戏会做什么?您将拥有一艘由用户输入控制的宇宙飞船。银河系中将有小行星移动并试图阻碍您。我们需要躲避它们并到达目的地。
从上面的陈述中,我们可以将某些项目视为游戏的组件。
- 您的宇宙飞船将是一个由用户控制的对象。
- 小行星将是一个对象。
- 您的 game1.cs,主对象将控制一切。银河系不需要成为一个对象,因为我们可以在 game1.cs 中直接绘制银河系的图像。
下面的飞船代码将用作组件。
class SpaceShip : DrawableGameComponent
{
Texture2D shipTexture;
Vector2 shipPosition;
const int ShipHeight = 84;
const int ShipWidth = 84;
public SpaceShip(Game game)
: base(game)
{
}
public override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
shipTexture = Game.Content.Load<texture2d>("FighterSpaceShip");
shipPosition = new Vector2(Artifacts.GamePreferedBufferWidth / 2 - ShipWidth/2, Artifacts.GamePreferedBufferHeight - ShipHeight);
base.LoadContent();
}
protected override void UnloadContent()
{
base.UnloadContent();
}
public override void Update(GameTime gameTime)
{
//keyboard demo
KeyboardState keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.Up))
shipPosition += new Vector2(0, -3);
if (keyboardState.IsKeyDown(Keys.Down))
shipPosition += new Vector2(0, 3);
if (keyboardState.IsKeyDown(Keys.Left))
shipPosition += new Vector2(-3, 0);
if (keyboardState.IsKeyDown(Keys.Right))
shipPosition += new Vector2(3, 0);
//keep space ship in bounds
if (!GraphicsDevice.Viewport.Bounds.Contains(GetBounds()))
{
Rectangle screenBounds = GraphicsDevice.Viewport.Bounds;
if (shipPosition.X < screenBounds.Left)
{
shipPosition.X = screenBounds.Left;
}
if (shipPosition.X > screenBounds.Width - ShipWidth)
{
shipPosition.X = screenBounds.Width - ShipWidth;
}
if (shipPosition.Y < screenBounds.Top)
{
shipPosition.Y = screenBounds.Top;
}
if (shipPosition.Y > screenBounds.Height - ShipHeight)
{
shipPosition.Y = screenBounds.Height - ShipHeight;
}
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
SpriteBatch spriteBatch = Game.Services.GetService(typeof(SpriteBatch)) as SpriteBatch;
spriteBatch.Begin();
spriteBatch.Draw(shipTexture, shipPosition, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
public Rectangle GetBounds()
{
return new Rectangle((int)shipPosition.X, (int)shipPosition.Y,
ShipWidth, ShipHeight);
}
public void RestSpaceShip()
{
shipPosition = new Vector2(Artifacts.GamePreferedBufferWidth / 2 - ShipWidth / 2, Artifacts.GamePreferedBufferHeight - ShipHeight);
}
}
正如您所看到的,类的 update 和 draw 方法,update 正在接受来自键盘的用户输入,这将帮助飞船移动。还要注意,update 方法不会让飞船超出屏幕。Draw 方法正在屏幕上绘制飞船。
同样,我们可以创建小行星组件。请查找下面的代码。
public class Meteor : Microsoft.Xna.Framework.DrawableGameComponent
{
Vector2 meteorPosition = Vector2.Zero;
Vector2 meteorVelocity = Vector2.Zero;
Texture2D meterorTexture;
Random random;
const int MeterorHeight = 56;
const int MeterorWidth = 64;
public Meteor(Game game)
: base(game)
{
// TODO: Construct any child components here
}
public override void Initialize()
{
// TODO: Add your initialization code here
base.Initialize();
}
protected override void LoadContent()
{
meterorTexture = Game.Content.Load<texture2d>("meteorImg64");
random = new Random(this.GetHashCode());
base.LoadContent();
}
protected override void UnloadContent()
{
base.UnloadContent();
}
protected void UpdateMeteorPosition()
{
meteorPosition.X = random.Next(Game.Window.ClientBounds.Width - MeterorWidth);
meteorPosition.Y = 0;
meteorVelocity.Y = 1 + random.Next(7);
meteorVelocity.X = random.Next(7) - 1;
}
public override void Update(GameTime gameTime)
{
if (!GraphicsDevice.Viewport.Bounds.Contains(new Rectangle((int)meteorPosition.X, (int)meteorPosition.Y, meterorTexture.Width, meterorTexture.Height)))
{
UpdateMeteorPosition();
}
meteorPosition += meteorVelocity;
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
SpriteBatch spriteBatch = Game.Services.GetService(typeof(SpriteBatch)) as SpriteBatch;
if (meteorPosition == Vector2.Zero && meteorVelocity == Vector2.Zero)
UpdateMeteorPosition();
spriteBatch.Begin();
spriteBatch.Draw(meterorTexture, meteorPosition, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
public bool CheckCollision(Rectangle rect)
{
Rectangle spriterect = new Rectangle((int)meteorPosition.X, (int)meteorPosition.Y, MeterorWidth, MeterorHeight);
return spriterect.Intersects(rect);
}
}
正如您所注意到的,我们正在随机创建一个小行星,并让它以随机确定的速度在屏幕上移动。
最后,我们可以创建我们的 game1.cs 类,它将连接这些组件并不断更新游戏。
public class SpaceGame : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D galaxyBackImage;
SpaceShip ship=null;
const int MeteorCount = 5;
const int MeteorUpdateTime = 10000;
private int lastTickCount;
int rockCount = 0;
public SpaceGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//graphics.IsFullScreen = true;
graphics.PreferredBackBufferWidth = Artifacts.GamePreferedBufferWidth;
graphics.PreferredBackBufferHeight = Artifacts.GamePreferedBufferHeight;
//graphics.ApplyChanges();
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
//if(ship==null)
// Components.Add(new SpaceShip(this));
//Components.Add(new Meteor(this));
//GameLevel = 1;
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
galaxyBackImage = Content.Load<texture2d>("BGI_galaxy");
// TODO: use this.Content to load your game content here
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
KeyboardState keyboard = Keyboard.GetState();
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || (keyboard.IsKeyDown(Keys.Escape)))
this.Exit();
if (ship == null)
{
StartGame();
}
UpdateGame();
base.Update(gameTime);
}
protected void UpdateGame()
{
// Check collisions
bool hasColision = false;
Rectangle shipRectangle =ship.GetBounds();
foreach (GameComponent gc in Components)
{
if (gc is Meteor)
{
hasColision = ((Meteor)gc).CheckCollision(shipRectangle);
if (hasColision)
{
// BOOM!
//explosion.Play();
// Remove all previous meteors
RemoveAllMeteors();
// Let's start again
StartGame();
break;
}
}
}
CheckforNewMeteor();
}
private void CheckforNewMeteor()
{
if ((System.Environment.TickCount - lastTickCount) > MeteorUpdateTime)
{
lastTickCount = System.Environment.TickCount;
Components.Add(new Meteor(this));
//newMeteor.Play();
rockCount++;
}
}
private void RemoveAllMeteors()
{
for (int i = 0; i < Components.Count; i++)
{
if (Components[i] is Meteor)
{
Components.RemoveAt(i);
i--;
}
}
}
protected void StartGame()
{
for (int i = 0; i < MeteorCount; i++)
Components.Add(new Meteor(this));
if (ship == null)
{
ship = new SpaceShip(this);
Components.Add(ship);
}
ship.RestSpaceShip();
lastTickCount = System.Environment.TickCount;
rockCount = MeteorCount;
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(galaxyBackImage, new Rectangle(0, 0, graphics.GraphicsDevice.DisplayMode.Width, graphics.GraphicsDevice.DisplayMode.Height), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
public class Artifacts
{
public const int GamePreferedBufferHeight=600;
public const int GamePreferedBufferWidth=800;
}
正如您所看到的,我们在游戏中添加了组件。
让我们来了解一下这些类的基本知识。
GraphicsDeviceManager 负责在您的屏幕上绘制对象。它在我们的 game1.cs 类中创建并添加。
SpriteBatch 将负责在屏幕上绘制图像。它将接受一个图形设备对象。SpriteBatch 将有一个 Draw 方法用于绘制图像,以及 DrawString 方法用于绘制文本。
正如您在代码中看到的,SpriteBatch 接受一个 Texture2D 对象,它就是我们的图像,以及一个图像在屏幕上绘制的位置。
这是非常简单的代码,您可以直接复制粘贴并在您的项目中使用。小行星类中也有一些用于与飞船进行碰撞检测的方法。
您可以深入复制粘贴代码并进行探索。
祝您编码愉快。
关注点
开发游戏非常有趣,我喜欢探索每一个细节。
我从 Alexandre Santos Lobão、Bruno Evangelista、José Antonio Leal de Farias 和 Riemer Grootjans 的《Begining XNA 3.0 Game Programing》一书中学习了所有这些内容。
我就是从这本书中获得的游戏创意。
历史
2012 年 9 月 19 日:添加了战斗机太空游戏的基本结构。