XNA之RPG游戏开发教程之八

在前一节中,我们创建了一个新的控件LeftRightSelector,主要是用来对角色进行选取,但是该控件并没有发挥其作用。本节中将添加PictureBox控件,在筛选角色信息同时,角色图片也相应改变,并且根据选取的角色来在GamePlayScreen页面上呈现;

首先在CharacterGeneratorScreen类中添加PictureBox控件和TextTure数组对象,并响应LeftRightSelector的SelctionChanged事件,实现代码如下

PictureBox characterImage;//添加的类级变量,PictureBox对象,用来呈现图片对象
Texture2D[,] characterImages;//图片对象数组
protected override void LoadContent()
{
 base.LoadContent();
 LoadImages();//加载图片信息
 CreateControls();
}
private void CreateControls()
{
 Texture2D leftTexture = Game.Content.Load<Texture2D>(@"GUI\leftarrowUp");
 Texture2D rightTexture = Game.Content.Load<Texture2D>(@"GUI\rightarrowUp");
 Texture2D stopTexture = Game.Content.Load<Texture2D>(@"GUI\StopBar");
 backgroundImage = new PictureBox(
 Game.Content.Load<Texture2D>(@"Backgrounds\titlescreen"),
 GameRef.ScreenRectangle);
 ControlManager.Add(backgroundImage);
 Label label1 = new Label();
 label1.Text = "Who will search for the Eyes of the Dragon?";
 label1.Size = label1.SpriteFont.MeasureString(label1.Text);
 label1.Position = new Vector2((GameRef.Window.ClientBounds.Width - label1.Size.X) / 2, 150);
 ControlManager.Add(label1);
 genderSelector = new LeftRightSelector(leftTexture, rightTexture, stopTexture);
 genderSelector.SetItems(genderItems, 125);
 genderSelector.Position = new Vector2(label1.Position.X, 200);
 genderSelector.SelectionChanged += new EventHandler(selectionChanged);
 ControlManager.Add(genderSelector);
 classSelector = new LeftRightSelector(leftTexture, rightTexture, stopTexture);
 classSelector.SetItems(classItems, 125);
 classSelector.Position = new Vector2(label1.Position.X, 250);
 classSelector.SelectionChanged += selectionChanged;
 ControlManager.Add(classSelector);
 LinkLabel linkLabel1 = new LinkLabel();
 linkLabel1.Text = "Accept this character.";
 linkLabel1.Position = new Vector2(label1.Position.X, 300);
 linkLabel1.Selected += new EventHandler(linkLabel1_Selected);
 ControlManager.Add(linkLabel1);
//以下是添加的代码 characterImage
= new PictureBox( characterImages[0, 0], new Rectangle(500, 200, 96, 96), new Rectangle(0, 0, 32, 32)); ControlManager.Add(characterImage);//初始化角色图片框并加入到管理类中 ControlManager.NextControl(); } private void LoadImages() { characterImages = new Texture2D[genderItems.Length, classItems.Length];//角色信息分为性别和类型,根据这两个因素将图片存储为一个图片组 for (int i = 0; i < genderItems.Length; i++) { for (int j = 0; j < classItems.Length; j++) { characterImages[i, j] = Game.Content.Load<Texture2D>(@"PlayerSprites\" + genderItems[i] + classItems[j]);//根据选择来加载图片 } } }
//selectionChanged事件的响应函数
void selectionChanged(object sender, EventArgs e) { characterImage.Image = characterImages[genderSelector.SelectedIndex, classSelector.SelectedIndex]; }

接下来就是将玩家选择的角色呈现在GamePlayScreen游戏页面上,这就需要CharacterGeneratorScreen类中提供角色选择信息的接口,代码如下

public string SelectedGender//返回角色的性别信息
{
get { return genderSelector.SelectedItem; }
}
public string SelectedClass//返回角色的类型信息
{
get { return classSelector.SelectedItem; }
}

在GamePlayScreen类中根据获得的角色信息来加载相应的角色到游戏界面,其LoadContent方法如下

protected override void LoadContent()
{
Texture2D spriteSheet = Game.Content.Load<Texture2D>(
@"PlayerSprites\" + 
 GameRef.CharacterGeneratorScreen.SelectedGender + 
 GameRef.CharacterGeneratorScreen.SelectedClass);//根据角色加载相应的图片
Dictionary
<AnimationKey, Animation> animations = new Dictionary<AnimationKey, Animation>(); Animation animation = new Animation(3, 32, 32, 0, 0); animations.Add(AnimationKey.Down, animation); animation = new Animation(3, 32, 32, 0, 32); animations.Add(AnimationKey.Left, animation); animation = new Animation(3, 32, 32, 0, 64); animations.Add(AnimationKey.Right, animation); animation = new Animation(3, 32, 32, 0, 96); animations.Add(AnimationKey.Up, animation);//根据角色的运动状态加载相应的动画 sprite = new AnimatedSprite(spriteSheet, animations);//初始化动态精灵对象
//以上为更新的代码
base.LoadContent(); Texture2D tilesetTexture = Game.Content.Load<Texture2D>(@"Tilesets\tileset1"); Tileset tileset1 = new Tileset(tilesetTexture, 8, 8, 32, 32); tilesetTexture = Game.Content.Load<Texture2D>(@"Tilesets\tileset2"); Tileset tileset2 = new Tileset(tilesetTexture, 8, 8, 32, 32); List<Tileset> tilesets = new List<Tileset>(); tilesets.Add(tileset1); tilesets.Add(tileset2); MapLayer layer = new MapLayer(40, 40); for (int y = 0; y < layer.Height; y++) { for (int x = 0; x < layer.Width; x++) { Tile tile = new Tile(0, 0); layer.SetTile(x, y, tile); } } MapLayer splatter = new MapLayer(40, 40); Random random = new Random(); for (int i = 0; i < 80; i++) { int x = random.Next(0, 40); int y = random.Next(0, 40); int index = random.Next(2, 14); Tile tile = new Tile(index, 0); splatter.SetTile(x, y, tile); } splatter.SetTile(1, 0, new Tile(0, 1)); splatter.SetTile(2, 0, new Tile(2, 1)); splatter.SetTile(3, 0, new Tile(0, 1)); List<MapLayer> mapLayers = new List<MapLayer>(); mapLayers.Add(layer); mapLayers.Add(splatter); map = new TileMap(tilesets, mapLayers); }

所以现在的游戏页面跳转逻辑是:startMenuScreen到characterGneratorScreen,根据选择的角色再在GamePlayScreen上绘制相应的角色图片。

下一步我们想实现Camera类的Zoom in 和Zoom out方法,实现对地图的放大和缩小,打开Camera类,更改update方法如下

public void Update(GameTime gameTime)
{
if (InputHandler.KeyReleased(Keys.PageUp) || 
InputHandler.ButtonReleased(Buttons.LeftShoulder, PlayerIndex.One))
 ZoomIn();//地图缩小
else if (InputHandler.KeyReleased(Keys.PageDown) || 
InputHandler.ButtonReleased(Buttons.RightShoulder, PlayerIndex.One))
 ZoomOut();//地图放大
//以上是更新代码
if (mode == CameraMode.Follow) return; Vector2 motion = Vector2.Zero; if (InputHandler.KeyDown(Keys.Left) || InputHandler.ButtonDown(Buttons.RightThumbstickLeft, PlayerIndex.One)) motion.X = -speed; else if (InputHandler.KeyDown(Keys.Right) || InputHandler.ButtonDown(Buttons.RightThumbstickRight, PlayerIndex.One)) motion.X = speed; if (InputHandler.KeyDown(Keys.Up) || InputHandler.ButtonDown(Buttons.RightThumbstickUp, PlayerIndex.One)) motion.Y = -speed; else if (InputHandler.KeyDown(Keys.Down) || InputHandler.ButtonDown(Buttons.RightThumbstickDown, PlayerIndex.One)) motion.Y = speed; if (motion != Vector2.Zero) { motion.Normalize(); position += motion * speed; LockCamera(); } } private void ZoomIn() { zoom += .25f; if (zoom > 2.5f) zoom = 2.5f; } private void ZoomOut() { zoom -= .25f; if (zoom < .5f) zoom = .5f; }

到目前为止我们只是在Camera类中加入了控制放大缩小的代码以及放大缩小梯度的设置,真正实现地图的放大和缩小是在GameplayScreen类中

可以将整个地图看做是一个像素矩阵,要对该矩阵做放大,缩小变化,要按照一定的矩阵变化顺序执行:等比-》按比例放大,缩小-》旋转-》平移。这些操作Matrix类都给我们提供了现成的方法,代码如下

public Matrix Transformation//转移矩阵
{
get { return Matrix.CreateScale(zoom) * 
Matrix.CreateTranslation(new Vector3(-Position, 0f)); }//先按照倍数放大矩阵,再朝Camera的负方向平移
}
public Rectangle ViewportRectangle//给出Camera视场矩阵的接口
{
get { return new Rectangle(
 viewportRectangle.X,
 viewportRectangle.Y, 
 viewportRectangle.Width, 
 viewportRectangle.Height); }
}

上述代码是Camera类给出的对外接口,向调用它进行放大缩小的对象提供变换矩阵transformation和变化对象矩阵ViewportRectangle。回到GameplayScreen类中,将其Draw方法中的spritebatch.begin()中参数重新设置,以便实现地图的放缩

public override void Draw(GameTime gameTime)
{
 GameRef.SpriteBatch.Begin(
SpriteSortMode.Deferred,
BlendState.AlphaBlend,
SamplerState.PointClamp,
null,
null,
null,
 player.Camera.Transformation);//Begin方法的7个参数重载,其中最关键的是最后一个参数Matrix,主要用于对Begin和End方法之间的所有绘制对象进行放缩变化的矩阵
 map.Draw(GameRef.SpriteBatch, player.Camera);
 sprite.Draw(gameTime, GameRef.SpriteBatch, player.Camera);
base.Draw(gameTime);
 GameRef.SpriteBatch.End();
}

已经设置好了放缩矩阵,那么现在就要对Begin和end方法之间要绘制的对象的Draw方法进行重写,实现其的放缩变化

先重写Tilemap类的Draw方法如下

public void Draw(SpriteBatch spriteBatch, Camera camera)
{
Rectangle destination = new Rectangle(0, 0, Engine.TileWidth, Engine.TileHeight);
Tile tile;
foreach (MapLayer layer in mapLayers)
 {
for (int y = 0; y < layer.Height; y++)
 {
 destination.Y = y * Engine.TileHeight;//改变的地方,实际上就是将以前画图时减去Camera对象的坐标给删除了,为什么呢?因为在我们转移矩阵中已经减过了...
for (int x = 0; x < layer.Width; x++)
 {
 tile = layer.GetTile(x, y);
if (tile.TileIndex == -1 || tile.Tileset == -1)
continue;
 destination.X = x * Engine.TileWidth;//
 spriteBatch.Draw(
 tilesets[tile.Tileset].Texture,
 destination,
 tilesets[tile.Tileset].SourceRectangles[tile.TileIndex],
Color.White);
 }
 }
 }
}

同样在AnimatedSprtie类的Draw方法中

public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Camera camera)
{
 spriteBatch.Draw(
 texture,
 position,
 animations[currentAnimation].CurrentFrameRect,
Color.White);
}

现在我们就可以对地图进行放大和缩小了,但是会有一个现象发生,当zoom缩小到一定值后地图边缘会被蓝色屏幕覆盖,这就需要我们对Camera位置进行重新锁定,使得地图的大小面积根据zoom的值进行变化,这样就不会出现地图不能覆盖整个窗体的现象,Camera类中修改代码如下

private void LockCamera()//对Camera锁定区域的修改
{
 position.X = MathHelper.Clamp(position.X,
 0,
 TileMap.WidthInPixels * zoom - viewportRectangle.Width);
 position.Y = MathHelper.Clamp(position.Y, 0,
 TileMap.HeightInPixels * zoom - viewportRectangle.Height);
}
public void LockToSprite(AnimatedSprite sprite)//对Follow状态下Camera区域的修改
{
 position.X = (sprite.Position.X + sprite.Width / 2) * zoom 
 - (viewportRectangle.Width / 2);
 position.Y = (sprite.Position.Y + sprite.Height / 2) * zoom
 - (viewportRectangle.Height / 2);
 LockCamera();
}

接着就是要将Camera和地图进行对齐

public void ZoomIn()
{
 zoom += .25f;
if (zoom > 2.5f)
 zoom = 2.5f;
Vector2 newPosition = Position * zoom;
 SnapToPosition(newPosition);
}
public void ZoomOut()
{
 zoom -= .25f;
if (zoom < .5f)
 zoom = .5f;
Vector2 newPosition = Position * zoom;
 SnapToPosition(newPosition);
}
private void SnapToPosition(Vector2 newPosition)
{
 position.X = newPosition.X - viewportRectangle.Width / 2;
 position.Y = newPosition.Y - viewportRectangle.Height / 2;
 LockCamera();
}

最后就是在PlayGameScreen类中修改Update方法,实现图像的放缩

public override void Update(GameTime gameTime)
{
 player.Update(gameTime);
 sprite.Update(gameTime);
//代码修改区域
//按pageup键,地图缩小
if (InputHandler.KeyReleased(Keys.PageUp) || InputHandler.ButtonReleased(Buttons.LeftShoulder, PlayerIndex.One)) { player.Camera.ZoomIn(); if (player.Camera.CameraMode == CameraMode.Follow) player.Camera.LockToSprite(sprite); } else if (InputHandler.KeyReleased(Keys.PageDown) || InputHandler.ButtonReleased(Buttons.RightShoulder, PlayerIndex.One)) { player.Camera.ZoomOut(); if (player.Camera.CameraMode == CameraMode.Follow) player.Camera.LockToSprite(sprite); }
//代码修改区域 Vector2 motion
= new Vector2(); if (InputHandler.KeyDown(Keys.W) || InputHandler.ButtonDown(Buttons.LeftThumbstickUp, PlayerIndex.One)) { sprite.CurrentAnimation = AnimationKey.Up; motion.Y = -1; } else if (InputHandler.KeyDown(Keys.S) || InputHandler.ButtonDown(Buttons.LeftThumbstickDown, PlayerIndex.One)) { sprite.CurrentAnimation = AnimationKey.Down; motion.Y = 1; } if (InputHandler.KeyDown(Keys.A) || InputHandler.ButtonDown(Buttons.LeftThumbstickLeft, PlayerIndex.One)) { sprite.CurrentAnimation = AnimationKey.Left; motion.X = -1; } else if (InputHandler.KeyDown(Keys.D) || InputHandler.ButtonDown(Buttons.LeftThumbstickRight, PlayerIndex.One)) { sprite.CurrentAnimation = AnimationKey.Right; motion.X = 1; } if (motion != Vector2.Zero) { sprite.IsAnimating = true; motion.Normalize(); sprite.Position += motion * sprite.Speed; sprite.LockToMap(); if (player.Camera.CameraMode == CameraMode.Follow) player.Camera.LockToSprite(sprite); } else { sprite.IsAnimating = false; } if (InputHandler.KeyReleased(Keys.F) || InputHandler.ButtonReleased(Buttons.RightStick, PlayerIndex.One)) { player.Camera.ToggleCameraMode(); if (player.Camera.CameraMode == CameraMode.Follow) player.Camera.LockToSprite(sprite); } if (player.Camera.CameraMode != CameraMode.Follow) { if (InputHandler.KeyReleased(Keys.C) || InputHandler.ButtonReleased(Buttons.LeftStick, PlayerIndex.One)) { player.Camera.LockToSprite(sprite); } } base.Update(gameTime); }

Ok,现在地图就可以放大缩小了。

原文地址:https://www.cnblogs.com/zcftech/p/3002929.html