zoukankan      html  css  js  c++  java
  • 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,现在地图就可以放大缩小了。

  • 相关阅读:
    全网最详细的一个超级好用的命令行工具【Cmder】的安装之后的一些配置(图文详解)
    全网最详细的一个超级好用的命令行工具【Cmder】的下载与安装(图文详解)
    全网最详细的一个超级好用的命令行工具【Cmder】的优点有哪些?
    全网最详细的一个超级好用的命令行工具【Cmder】是什么?
    全网最详细的IDEA、Eclipse和MyEclipse之间于Java web项目发布到Tomcat上运行成功的对比事宜【博主强烈推荐】【适合普通的还是Maven方式创建的】(图文详解)
    【适合公司业务】全网最详细的IDEA里如何正确新建【普通或者Maven】的Java web项目并发布到Tomcat上运行成功【博主强烈推荐】(类似eclipse里同一个workspace下【多个子项目】并存)(图文详解)
    全网最详细的Eclipse和MyEclipse里对于Java web项目发布到Tomcat上运行成功的对比事宜【博主强烈推荐】【适合普通的还是Maven方式创建的】(图文详解)
    全网最详细的IDEA里如何正确新建普通的Java web项目并发布到Tomcat上运行成功【博主强烈推荐】(类似eclipse里同一个workspace下【一个子项目】并存)(图文详解)
    [转]BEC Vantage
    [转]VB.NET DataTable Select Function
  • 原文地址:https://www.cnblogs.com/zcftech/p/3002929.html
Copyright © 2011-2022 走看看