在前一节中,我们创建了一个新的控件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(@"GUI\leftarrowUp"); Texture2D rightTexture = Game.Content.Load (@"GUI\rightarrowUp"); Texture2D stopTexture = Game.Content.Load (@"GUI\StopBar"); backgroundImage = new PictureBox( Game.Content.Load (@"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 (@"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(@"PlayerSprites\" + GameRef.CharacterGeneratorScreen.SelectedGender + GameRef.CharacterGeneratorScreen.SelectedClass);//根据角色加载相应的图片 Dictionary animations = new Dictionary (); 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 (@"Tilesets\tileset1");Tileset tileset1 = new Tileset(tilesetTexture, 8, 8, 32, 32); tilesetTexture = Game.Content.Load (@"Tilesets\tileset2");Tileset tileset2 = new Tileset(tilesetTexture, 8, 8, 32, 32);List tilesets = new List (); 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 mapLayers = new List (); 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,现在地图就可以放大缩小了。