zoukankan      html  css  js  c++  java
  • 如何在WP7上用XNA写2D游戏(三)

    第 2 章 制作XNA 2D游戏的常用组件

    2.1使用LoadContent加载2D图片资源

    在XNA项目中,如果制作2D游戏,那么都会涉及到图片资源。比如人物的行走动画可以由连续播放一系列帧图片完成。如何在XNA里加载图片资源就是我们首先要掌握的。

    在上一章节,我们建了一个XNA项目解决方案,里面就带有一个Content项目。我们游戏所用的图片,音效,字体等资源都是放在这个项目里的。当然这个项目你可以认为是储存游戏资源的文件夹,方便来管理游戏资源。

    如何在Content项目里把不同类型的文件放置呢?跟普通站点项目一样,同类型的文件一般都放在一个文件夹里。比如图片资源都放在UI文件夹,音效文件放在Sound文件夹。

    下面我们演示如何把UI文件里的一个.png图片添加进Content项目里。如图2-1,我们在UI文件夹上右键,选择“Add”,再选择“Existing Item”。


                                                                                    图2-1

    然后如图2-2选择我们预先放在UI文件夹里的loading.png的图片,点击确定。



                                                                                             图2-2

    添加完毕图片,效果如图2-3.右键点击图片我们可以看到属性。其中Asset Name 是我们在XNA里引用资源的唯一标识。“Copy to Output Directory”我们默认选择“Do not copy” 如果选择了其他两项选项,那么会在我们最终生产的XAP文件里包含进了原始的.png文件。一般来说图片文件会变成.xnb文件,这就是XNA游戏运行时的资源文件。如果选择了复制,那么还会把原始的.png文件也复制进去,这样就造成了XAP体积不必要的增大。要想把最终生成的XAP游戏包精简,就不要选择“Copy”。

    需要说明的是,在XNA里支持的图片纹理文件除了.png还有.dds,.jpg,.bmp,.tga.等类型的文件。

                                                                      

                                                                       图2-3

    2.2       使用SpriteBatch绘制2D纹理

    在我们添加了图片资源以后,我们就可以用Texture2D这个对象来在XNA里处理了。代码如下:

    Texture2D loadingTexture=Content.Load<Texture2D>(“UI/loading”);

              有了2D纹理对象loadingTexture 后,我们就可以把图片绘制出来了,如图2-4


                                                                          图2-4

         这样我们就加载了loading.png这个纹理到loadingTexture对象里,接下来,我们只需要如图2-5Draw函数里用SpriteBatch把纹理绘制出来:

         

    spriteBatch.Draw(loadingTexture,new Vector2(0,0),Color.White);



                                                           2-5

           然后我们按F5,就能在模拟器里看到效果,如图2-6:

     2-6

     

    如何把图片绘制出来,并且能缩放,旋转,透明等处理?XNA里的SpriteBatch类完全就可以满足我们的要求。

    比如,在图2-6里我们把上面加载好的loadingTexture对象绘制在屏幕左上角,也就是坐标 Vector2(0,0);

    spriteBatch.Begin();
    spriteBatch.Draw(loadingTexture,new Vector2(0,0),Color.White);
    spriteBatch.End();

     

    当然,我们做游戏的时候,一个场景里肯定不可能只使用一张图片,那么就需要我们同时绘制多个纹理到屏幕上。不过不用担心,一个SpriteBatch对象可以多次调用Draw方法来绘制不同的Texture2D对象。

    spriteBatch.Begin();
    spriteBatch.Draw(loadingTexture,new Vector2(0,0),Color.White);
    spriteBatch.Draw(playerTexture,new Vector2(260,100),Color.White);
    spriteBatch.End();

    这段代码绘制的结果如图2-7,这样我们就把一个弓箭手绘制在城堡上面:



     

                                                           2-7

    在绘制多个图片的时候可能出现纹理堆叠的情况,在XNA里最后绘制的默认在最上层,当然也可以指定绘制的层次。这些高级的Draw方法的应用,就需要深入我们了解SpriteBatch对象。

    SpriteBatch对象的Draw方法有不同的重载,其中最复杂的如下:

    public void Draw (Texture2D texture,Vector2 position,Nullable<Rectangle> sourceRectangle,Color color,float rotation,Vector2 origin,Vector2 scale,SpriteEffects effects,float layerDepth)

    这个Draw方法的第三个参数sourceRectangle为制定绘制纹理的那个矩形部分。这个参数非常有用,比如游戏编程里我们为了减少图片的数量和大小,我们总是把很多帧动画绘制在一张图片上如图2-8,如果我们要绘制单帧图片的话,我们就需要制定绘制这个图片的单帧大小的矩形范围。当然如果想要把图片完整的现实出来,只需要把这个参数设置为null就行了。

         

      2-8

    还有其他高级用法,比如:旋转某个纹理。

    旋转

    那么我们就需要使用rotation 参数旋转一个图像。你需要使用弧度值指定这个旋转角度,所以如果你想让图像顺时针旋转20度,你可以使用MathHelper.ToRadians(20)

    需要注意的是旋转一个图片,我们总是要指定旋转中心点的,origin这个参数就是旋转中心点。当然origin不光是在旋转时有用。origin可以指定你想让图像上的哪个点位于屏幕上你在position参数中指定的位置上。例如,如果你将position,origin两个参数都指定为(0,0),那么图像的左上角将位于屏幕的左上角,如图2-9

      2-9

    如果一张80 × 60大小的图像,你将屏幕位置指定为(0,0)origin指定为(40,30),那么图像的中心点将位于屏幕的左上角,如图2-10所示

      2-10

    如果两个参数都是(40,30),那么图像的中心点将位于屏幕的(40,30)位置。这时的结果与图2-9一样。

    在图片旋转的时候如果把(0,0)为图像的origin,这个图像会围绕这个点旋转,如图所示。如果你指定(32,32)为图像的origin,这个图像会围绕它的中心点旋转,如图2-11:

     

                                                        2-11

    缩放某个纹理也是我们常见的。

    缩放

    如果你想放大/缩小图像,可以设置参数scale。因为这个参数是一个Vector2,你可以对图像的水平方向和竖直方向施加不同的缩放值。例如,设置为(0.5f, 2.0f)将会使宽度变为原始宽度的一半,高度变为原始高度的2倍。如图2-12


     

                                                                    2-12

    其他还有

    镜像

    参数effects让你可以水平或竖直翻转图像。通过flags,你可以使用SpriteEffects.FlipHorizontally|SpriteEffects.FlipVertically同时进行这两个操作,这和将图像旋转180度的效果是相同的。

    层深度

    最后一个参数layerDepth让你可以指定图像位于哪个层,当你想将多个图像在有重叠时层的概念是很有用的。

    需要注意的是

    1.  layerDepth是一个介于0.0-1.0之间的float值,一般默认为0.0,如果是0.0那么就指定系统最后绘制,如果是1.0那么指定系统最先绘制。通俗的来讲,layerDepth值越小就越在其他纹理上面。

    2.   需要使用spriteBatch.Begin的一个重载函数:

              SpriteBatch.Begin(SpriteSortMode.BackToFront,BlendState.AlphaBlend);

        

    2.2        使用ScreenManager管理游戏场景

        在一个稍微复杂的游戏里,我们都需要用到场景来处理。什么是场景,简单的来讲,比如一个过关游戏,第一关就是一个场景,第二关就是另外一个场景。每个场景里有不过的背景图片,不同的精灵角色,有不同的关卡设计。场景里的资源如何展示,从一个场景到另一个场景如何切换,这些我们需要写一个专门的类ScreenManager来处理。

    微软WP7开发者官方网站http://create.msdn.com上就有XNA的小游戏示例代码,里面就有这样的ScreenManager类,下面我们来简要分析下这个类,为后面我们设计游戏菜单打下基础。我省掉了中间的一些代码,大家可以看函数来理解这个类的作用。

    namespace WPGame2D.Screens

    {

        /// <summary>

    ///画面管理类,负责管理多个画面

    /// </summary>

        public class ScreenManager : DrawableGameComponent

        {

            public Camera2D Camera;

            public ContentManager ContentManager;

            /// <summary>

            ///所有用到的文字资源

            /// </summary>

            public SpriteFonts SpriteFonts;

            ……..

            private List<GameScreen> screens = new List<GameScreen>();

            private List<GameScreen> screensToUpdate = new List<GameScreen>();

            private SpriteBatch spriteBatch;

            ……..

            /// <summary>

            ///构造函数

            /// </summary>

            public ScreenManager(Game game)

                : base(game)

            {

                TouchPanel.EnabledGestures = GestureType.None;

                ContentManager = game.Content;

                ContentManager.RootDirectory = "Content";

            }

     

            /// <summary>

            ///游戏通用默认SpriteBatch对象。每个场景都同享它,缓存了每个场景所用到的文件资源

            /// <summary>

            public SpriteBatch SpriteBatch

            {

                get { return spriteBatch; }

            }

            /// <summary>

            ///初始化ContentManager对象

            /// </summary>

            public override void Initialize()

            {

                SpriteFonts = new SpriteFonts(ContentManager);

                base.Initialize();

                isInitialized = true;

            }

            public void ResetTargets()

            {

                transitions.Clear();

            }

            /// <summary>

            ///加载游戏资源

            /// </summary>

            protected override void LoadContent()

            {

                spriteBatch = new SpriteBatch(GraphicsDevice);

                blankTexture = ContentManager.Load<Texture2D>("Common/blank");

                Camera = new Camera2D(GraphicsDevice);

                // 让每个场景加载所用的资源

                foreach (GameScreen screen in screens)

                {

                    screen.LoadContent();

                }

            }

            /// <summary>

            /// 卸载所用的游戏资源

            /// </summary>

            protected override void UnloadContent()

            {

                foreach (GameScreen screen in screens)

                {

                    screen.UnloadContent();

                }

            }

            /// <summary>

            /// 让每个场景执行update操作,执行游戏逻辑-

            /// </summary>

            public override void Update(GameTime gameTime)

            {

                input.Update();

                Camera.Update();

                screensToUpdate.Clear();

                foreach (GameScreen screen in screens)

                    screensToUpdate.Add(screen);

               ………..

            }

            /// <summary>

            ///开始渲染每个场景

            /// </summary>

            public override void Draw(GameTime gameTime)

            {

                int transitionCount = 0;

                foreach (GameScreen screen in screens)

                {

                   ……..

                  screen.Draw(gameTime);

                   …….

                }

               ……..

            }

            /// <summary>

            /// 添加一个新的场景到管理器中

            /// </summary>

            public void AddScreen(GameScreen screen, PlayerIndex? controllingPlayer)

            {

                …….

                if (isInitialized)

                {

                    screen.LoadContent();

                }

                screens.Add(screen);

               ……….

            }

            /// <summary>

            /// 移除指定的场景。不过通常调用GameScreen.ExitScreen来代替这个方法。这样比直接调用RemoveScreen好,中间有一个过渡过程。

            /// </summary>

            public void RemoveScreen(GameScreen screen)

            {

                if (isInitialized)

                {

                    screen.UnloadContent();

                }

                ……..

            }

        }

    }

       分析这个类,我们可以看到该类继承于DrawableGameComponent,   从字面意思我们就可以理解到这是一个在XNA里可绘制出来的组件的基类。我们把场景继承于这个类,是因为每一个Screen里的东西基本都是可绘制的。我们在这个类上按F12,可以看到类的结构如图2-13

     

       2-13

      比如我们从一个场景到另一个场景,我们就可以用到RemoveScreenAddScreen方法。

       需要注意的是,ScreenManager类里含有Draw方法是实现基类DrawableGameComponent里定义的方法。在这个方法里我们负责把场景里的元素绘制出来。

                 ScreenManager类里还涉及到GameScreen类,我放到XNAGameSample2里提供给大家下载阅读,在下一章里,我们会介绍菜单的制作,配合2.3章节的ScreenManager类和GameScreen类实现点击菜单项就完成了不同游戏场景的切换。

            

               XNAGameSample2下载地址:/Files/wangergo/XNAGameSample2.rar

  • 相关阅读:
    WF4.0 Beta1 自定义跟踪
    WF4.0 Beta1 流程设计器与Activity Designer
    新版本工作流平台的 (二) 权限算法(组织结构部分)
    WF4.0 Beta1 WorkflowInvoker
    WF4.0 基础篇 (十) Collection 集合操作
    WF4.0 基础篇 (十五) TransactionScope 事物容器
    WF4.0 基础篇 (六) 数据的传递 Arguments 参数
    WF4B1 的Procedural Activity 之InvokeMethod , InvokeMethod<T> 使用
    WF4.0 Beta1 异常处理
    WF4.0 Beta1 变量 Variables
  • 原文地址:https://www.cnblogs.com/wangergo/p/2228933.html
Copyright © 2011-2022 走看看