您的位置:首页 > 其它

XNA学习(三) 一个简单的2D游戏

2008-08-20 09:02 204 查看
这是http://creators.xna.com上的一个示例。

新建一个XNA(Windowsgame)工程,取名为Windows2DGame。

右击解决方案资源管理器中的Content节点,添加两个文件夹,分别命名为Fonts和Sprites,然后向这两个文件夹里分别添加GameFont.spritefont和backgrount.tga、cannon.tga、cannonball.tga、enemy.tga资源(可在http://creators.xna.com网上下载,也可从我的源码里获得,下载地址在最下面)。

一、 绘制背景

1. 加入两个变量

Texture2D backgroundTexture; //定义背景纹理变量

Rectangle viewportRect; //定义矩形,为视图区域

2. 在LoadContent()函数中加入如下代码载入背景纹理

//载入背景

backgroundTexture = Content.Load<Texture2D>(

"Sprites//background");

//设置视图区域为全窗口

viewportRect = new Rectangle(0, 0,

graphics.GraphicsDevice.Viewport.Width,

graphics.GraphicsDevice.Viewport.Height);

3. 在Draw(GameTime gameTime)函数中添加如下代码绘背景

spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

spriteBatch.Draw(backgroundTexture, viewportRect,

Color.White);

spriteBatch.End();

SpriteBatch.Begin()通过接受参数,来控制如何渲染sprite。参数BlendMode表示进行哪种模式的混合。End()与Begin()对应,表示绘制结束。
4. 运行后,效果如下图



二、添加一门大炮
1. 添加一个类GameObject.cs,方便操作,因为待会还要添加炮弹,敌机等对象。代码如下:

using System;

using System.Collections.Generic;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Audio;

using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Input;

using Microsoft.Xna.Framework.Storage;

namespace Windows2DGame

{

class GameObject

{

public Texture2D sprite; //定义2D对象

public Vector2 position; //位置

public float rotation; //旋转参数

public Vector2 center; //对象中心

//构造函数

public GameObject(Texture2D loadedTexture)

{

rotation = 0.0f;

position = Vector2.Zero; //默认在原点,屏幕左上角

sprite = loadedTexture; //精灵对象为载入的纹理

center = new Vector2(sprite.Width / 2, sprite.Height / 2);

}

}

}

2. 加入一个变量

GameObject cannon; //对象大炮

3. 在LoadContent()函数中添加如下代码

//载入大炮

cannon = new GameObject(Content.Load<Texture2D>(

"Sprites//cannon"));

//大炮位置

cannon.position = new Vector2(120,

graphics.GraphicsDevice.Viewport.Height - 80);

4. 在Update(GameTime gameTime)函数中加入如下代码

//获取键盘状态

KeyboardState keyboardState = Keyboard.GetState();

//如果按下左键

if (keyboardState.IsKeyDown(Keys.Left))

{

cannon.rotation -= 0.1f;

}

//如果按下右键

if (keyboardState.IsKeyDown(Keys.Right))

{

cannon.rotation += 0.1f;

}

//旋转参数

cannon.rotation = MathHelper.Clamp(cannon.rotation,

-MathHelper.PiOver2, 0);

KeyboardState类,用来检测键盘的状态,GetState()将返回一个KeyboardState对象,该对象保存了当前的键盘状态。

MathHelper 是一个辅助数学计算的类,MathHelper.Clamp()函数是将cannon.rotation的值限制在-PI/2到0之间。因为大炮放在左下角,炮口方向只能调整90度。

5. 在Draw(GameTime gameTime)函数中添加如下代码载入大炮,加在spriteBatch.Begin和spriteBatch.End之间

//绘大炮

spriteBatch.Draw(cannon.sprite,

cannon.position,

null,

Color.White,

cannon.rotation,

cannon.center, 1.0f,

SpriteEffects.None, 0);

SpriteBatch.Draw (Texture2D, Vector2, Nullable<Rectangle>, Color, Single, Vector2, Single, SpriteEffects, Single),第一个参数表示精灵的纹理,即所要绘制在屏幕上的图像;第二个参数表示精灵(图像)的左上角位置;第三个参数表示以图像的左上角为(0,0),在屏幕上只显示图像中sourceRectangle指定的矩形中的部分,null就是全部显示;第四个参数调制通道的颜色,采用白色时,则保持图片原色;第五个参数表示以精灵(图像)的左上角位置为中心(此处即cannon.position),顺时针旋转的弧度;第六个参数为原点,此处为精灵中心位置;第七个参数表示显示图像时放缩倍数;第八个参数表示显示特效,有SpriteEffects.FlipHorizontally(水平翻转)、SpriteEffects.FlipVertically(垂直翻转)、SpriteEffects.None(不翻转)三种;第九个参数表示精灵的显示图层深度。
6. 运行后,即可见在上图基础上左下方多了一门大炮,如下图:



三、添加炮弹
1. 在GameObject.cs类中添加两个变量

public Vector2 velocity; //移动速度

public bool alive; //是否显示(生或死)

在其构造函数中初始化变量值

velocity = Vector2.Zero;

alive = false;

2. 在Game.cs中添加几个变量

GameObject[] cannonBalls; //炮弹数组

const int maxCannonBalls = 3; //屏幕允许显示的炮弹个数

KeyboardState previousKeyboardState = Keyboard.GetState();

KeyboardState用来检测键盘的状态。Keyboard.GetState()返回一个KeyboardState对象,该对象保存了当前的键盘状态。

3. 添加两个类

//开火,发射炮弹

public void FireCannonBall()

{

foreach (GameObject ball in cannonBalls)

{

if (!ball.alive)

{

ball.alive = true;

ball.position = cannon.position - ball.center;

ball.velocity = new Vector2(

(float)Math.Cos(cannon.rotation),

(float)Math.Sin(cannon.rotation)) * 5.0f;

return;

}

}

}

//炮弹刷新

public void UpdateCannonBalls()

{

foreach (GameObject ball in cannonBalls)

{

if (ball.alive)

{

ball.position += ball.velocity;

//如果炮弹跑出视野,则自动消失

if (!viewportRect.Contains(new Point(

(int)ball.position.X,

(int)ball.position.Y)))

{

ball.alive = false;

continue;

}

}

}

}

4. 在LoadContent()函数中加入以下代码

//炮弹

cannonBalls = new GameObject[maxCannonBalls];

for (int i = 0; i < maxCannonBalls; i++)

{

cannonBalls[i] = new GameObject(Content.Load<Texture2D>(

"Sprites//cannonball"));

}

5. 在Update(GameTime gameTime)函数中添加如下代码

//如果按下空格,大炮开火。

if (keyboardState.IsKeyDown(Keys.Space)& //一次只发射一枚炮弹

previousKeyboardState.IsKeyUp(Keys.Space))

{

FireCannonBall(); //开火

}

UpdateCannonBalls(); //刷新炮弹

previousKeyboardState = keyboardState; //当前键盘状态为下一初始状态

6. 在Draw(GameTime gameTime)函数中添加代码绘炮弹

//绘炮弹

foreach (GameObject ball in cannonBalls)

{

if (ball.alive)

{

spriteBatch.Draw(ball.sprite,

ball.position, Color.White);

}

}

7. 运行,按下空格大炮便会发射出红色的炮弹来。效果如下图:



四、添加敌机

1. 在Game.cs中添加如下变量

GameObject[] enemies; //敌机数组

Random random = new Random();

const int maxEnemies = 3;

const float maxEnemyHeight = 0.1f;

const float minEnemyHeight = 0.5f;

const float maxEnemyVelocity = 5.0f;

const float minEnemyVelocity = 1.0f;

敌机随机出现,可大可小,可快可慢。

2. 在LoadContent()函数中添加如下代码

//敌机

enemies = new GameObject[maxEnemies];

for (int i = 0; i < maxEnemies; i++)

{

enemies[i] = new GameObject(

Content.Load<Texture2D>("Sprites//enemy"));

}

3. 添加函数UpdateEnemies()刷新敌机

//敌机刷新

public void UpdateEnemies()

{

foreach (GameObject enemy in enemies)

{

if (enemy.alive)

{

enemy.position += enemy.velocity;

if (!viewportRect.Contains(new Point( //跑出视野,消失

(int)enemy.position.X,

(int)enemy.position.Y)))

{

enemy.alive = false;

}

}

else

{

enemy.alive = true;

enemy.position = new Vector2(

viewportRect.Right,

MathHelper.Lerp(

(float)viewportRect.Height * minEnemyHeight,

(float)viewportRect.Height * maxEnemyHeight,

(float)random.NextDouble()));

enemy.velocity = new Vector2(

MathHelper.Lerp(

-minEnemyVelocity,

-maxEnemyVelocity,

(float)random.NextDouble()), 0);

}

}

}

MathHelper.Lerp(float value1,float value2,float amount)函数线性插入一个限定在两个值之间的值。插值=value1 + (value2 - value1) * amount,amount在0和1之间。random.NextDouble()就是返回一个介于0.0与1.0之间的随机数。
4. 在Update(GameTime gameTime)中调用刚才新建的类适时刷新敌机

UpdateEnemies();

5. 在Draw(GameTime gameTime)函数中添加代码绘敌机

//绘敌机

foreach (GameObject enemy in enemies)

{

if (enemy.alive)

{

spriteBatch.Draw(enemy.sprite,

enemy.position, Color.White);

}

}

6. 运行后即可在游戏窗口中出现从右到左飞行的敌机,如下图



五、击毁敌机。在UpdateCannonBalls()函数中if (ball.alive){}内添加如下代码:

//炮弹区域

Rectangle cannonBallRect = new Rectangle(

(int)ball.position.X,

(int)ball.position.Y,

ball.sprite.Width,

ball.sprite.Height);

foreach (GameObject enemy in enemies)

{

//敌机区域

Rectangle enemyRect = new Rectangle(

(int)enemy.position.X,

(int)enemy.position.Y,

enemy.sprite.Width,

enemy.sprite.Height);

//如果击中敌机,炮弹和敌机同时消失

if (cannonBallRect.Intersects(enemyRect))

{

ball.alive = false;

enemy.alive = false;

break;

}

}

六、 添加记分牌

1. 在Game.cs中添加几个变量

int score; //分值

SpriteFont font; //字体

Vector2 scoreDrawPoint = new Vector2( //记分牌位置

0.05f * viewportRect.Width, 0.05f * viewportRect.Height);

SpriteFont用来绘制字体,像加载纹理一样,先声明再载入所需字体即可。
2. 在LoadContent()函数中载入字体

//载入字体

font = Content.Load<SpriteFont>("Fonts//GameFont");

这里的GameFont字体其实为系统中的Arial字体,也可以载入其它系统字体(以便输入中文啊什么的),不过要转换后才行。
参考:http://www.xnadev.cn/Article/ShowArticle.asp?ArticleID=82
3. 在UpdateCannonBalls()函数中if (cannonBallRect.Intersects(enemyRect)){}(击中敌机)内加如下代码增加分数值

score += 1;

4. 在Draw(GameTime gameTime)函数中添加代码绘记分牌

//绘记分牌

spriteBatch.DrawString(font,

"Score " + score.ToString(),

new Vector2( //记分牌确切位置,scoreDrawPoint可看作比例因子

scoreDrawPoint.X * viewportRect.Width,

scoreDrawPoint.Y * viewportRect.Height),

Color.Yellow);

七、 至此,这个粗糙简陋的2D游戏算是完成了,虽说没有音效、没有爆炸效果、没有关卡……但还是值得高兴的。最终效果如下:



我的程序源码下载地址:http://download.csdn.net/source/582288
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: