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,现在地图就可以放大缩小了。

你可能感兴趣的:(游戏开发)