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。
新建一个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。
相关文章推荐
- XNA学习(四) 一个简单的3D游戏
- J2EE学习笔记二:配置一个简单的J2EE测试环境
- [3]Selenium学习系列---- 一个简单的Selenium Java 工程
- XML的简单学习(一个优秀的mini库…
- 【性能跟踪】btrace学习二--btrace一个简单例子
- Android 多媒体开发学习之制作一个简单的画板
- ThinkPHP 学习(2)---一个简单的起步的例子
- Zigbee学习-添加一个简单应用到OSAL中
- NAnt学习笔记(1) -- NAnt的配置文件结构和一个简单的NAnt例子
- LeJOS学习(9):一个整合传感器与马达的简单例子
- android学习:Activity简单操作---打开、关闭一个新的Activity
- 数据结构学习三(一个简单的队列实现)
- iOS学习重要知识点整理02-进程和线程的一个简单解释
- Java线程(学习整理)--4---一个简单的生产者、消费者模型
- android初步学习时所有简单例子整合到一个ListView上
- SQL 数据库 学习 007 通过一个示例简单介绍什么是字段、属性、列、元组、记录、表、主键、外键 (上)
- Python web入门:Django学习与实践二(简单页面实现和创建一个模板)
- 一个简单的demo学习Vue.js
- 学习solidity的一个简单智能合约(2)——对冲
- Linux内核分析第二周学习博客——完成一个简单的时间片轮转多道程序内核代码