当前位置: 移动技术网 > IT编程>移动开发>WP > 如何在WP7上用XNA写2D游戏(三)

如何在WP7上用XNA写2D游戏(三)

2018年10月13日  | 移动技术网IT编程  | 我要评论

站长资源网,山西柳林首富,消防犬国语

 

前一篇 /kf/201110/108037.html

 

第 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-5在draw函数里用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

 

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

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

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

 

   本文版权属于williams所有

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网