6.制作XNA乒乓1-游戏状态管理和中文支持

前一个教程已经完成了一个基本的2D游戏引擎,下面让我们使用这个引擎制作一个小游戏XNA乒乓,这个游戏来自于《ProfessionalXNA》中的第一个游戏,这也是我玩过的第一个游戏,好像还是初中时,邻居拿来一个盒子,将一个夹子夹在我家9寸黑白电视机的天线上,就可以用一个旋钮控制乒乓板的上下移动,现在看来很简陋,但当时玩得是如痴如醉,连饭也顾不得吃,在《机器人总动员》中又一次看到wall-E无聊地把电脑打了个7999比0,无限感慨。

Well-E01Wall-E02

好了,言归正传。首先,直接在引擎中编写代码不很恰当,好的做法是在解决方案管理器中新建一个项目,我起的名称叫Sample01,然后添加对引擎项目的引用,之后对Sample01项目中的Game1类从引擎类继承,代码如下:

namespace Sample01
{
    /// <summary>
    /// 游戏主类,从引擎继承
    /// </summary>
    public class Game1:StunXnaGE 
    {
        /// <summary>
        /// 用于变暗背景的图像
        /// </summary>
        Texture2D blankTexture; 
       
        /// <summary>
        /// 用于标题的字体
        /// </summary>
        SpriteFont fontTitle;

        /// <summary>
        /// 用于菜单的字体
        /// </summary>
        SpriteFont fontMenu;

        public Game1()
        {
            
        }

        /// <summary>
        /// 获取用于变暗背景的图像
        /// </summary>
        public Texture2D BlankTexture
        {
            get { return blankTexture; }
        }

        /// <summary>
        /// 获取用于标题的字体
        /// </summary>
        public SpriteFont FontTitle
        {
            get { return fontTitle; }
        }

        /// <summary>
        /// 获取用于菜单的字体
        /// </summary>
        public SpriteFont FontMenu
        {
            get { return fontMenu; }
        }

        protected override void Initialize()
        {
            base.Initialize(); 

            //加载游戏中用到的
            blankTexture = Content.Load<Texture2D>("Textures\\blank");
            fontTitle = Content.Load<SpriteFont>("Fonts\\fontTitle");
            fontMenu = Content.Load<SpriteFont>("Fonts\\fontMenu"); 

            //添加背景、主菜单屏幕
            SceneManager.AddScene(new BackgroundScreen(this));
            SceneManager.AddScene(new MainMenuScreen(this));              
        }

        protected override void LoadContent()
        {
            base.LoadContent();
        }

        
        protected override void UnloadContent()
        {
            base.UnloadContent();
        }
        
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            
            // 如果applyDeviceChanges为true则重新设置图形设备
            if (applyDeviceChanges)
            {
                GameSettings gameSettings = SettingsManager.Read();
                Graphics.PreferredBackBufferHeight = gameSettings.PreferredWindowHeight; 
                Graphics.PreferredBackBufferWidth = gameSettings.PreferredWindowWidth;
                Graphics.IsFullScreen = gameSettings.PreferredFullScreen;
                Graphics.ApplyChanges();
                applyDeviceChanges =false ;
            }
            
            //更新声音
            Sound.Update();  
        }

        protected override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);            
        }

        /// <summary>
        /// 绘制透明的黑色全屏图像,用于屏幕的淡入和淡出,将弹出屏幕后的背景变暗
        /// </summary>
        public void FadeBackBufferToBlack(int alpha)
        {
            Viewport viewport = GraphicsDevice.Viewport;

            spriteBatch.Begin();
            spriteBatch.Draw(BlankTexture, new Rectangle(0, 0, viewport.Width, viewport.Height), new Color(0, 0, 0, (byte)alpha));
            spriteBatch.End();
        }
    }
}

其中需要说明的就是SettingsManager类,这个类可以读取游戏根目录下的一个xml文件: GameSettings.xml,设置屏幕的分辨率和是否全屏,当然,你也可以拓展这个文件,保存游戏得分,敌人数量等几乎所有游戏中要用到的设置,先看一下GameSettings.xml的内容:

<?xml version="1.0" encoding="utf-8" ?> 
- <GameSettings>
  <PreferredFullScreen>false</PreferredFullScreen> 
  <PreferredWindowWidth>800</PreferredWindowWidth> 
  <PreferredWindowHeight>600</PreferredWindowHeight> 
  <EnableVsync>true</EnableVsync> 
- <KeyboardSettings>
- <KeyboardSettings>
  <A>LeftControl</A> 
  <B>S</B> 
  <X>LeftShift</X> 
  <Y>X</Y> 
  <LeftShoulder>Space</LeftShoulder> 
  <RightShoulder>None</RightShoulder> 
  <LeftTrigger>None</LeftTrigger> 
  <RightTrigger>None</RightTrigger> 
  <LeftStick>Z</LeftStick> 
  <RightStick>None</RightStick> 
  <Back>Escape</Back> 
  <Start>Enter</Start> 
  <DPadDown>None</DPadDown> 
  <DPadLeft>None</DPadLeft> 
  <DPadRight>None</DPadRight> 
  <DPadUp>None</DPadUp> 
  <LeftThumbstickDown>Down</LeftThumbstickDown> 
  <LeftThumbstickLeft>Left</LeftThumbstickLeft> 
  <LeftThumbstickRight>Right</LeftThumbstickRight> 
  <LeftThumbstickUp>Up</LeftThumbstickUp> 
  <RightThumbstickDown>None</RightThumbstickDown> 
  <RightThumbstickLeft>None</RightThumbstickLeft> 
  <RightThumbstickRight>None</RightThumbstickRight> 
  <RightThumbstickUp>None</RightThumbstickUp> 
  </KeyboardSettings>
  </KeyboardSettings>
  </GameSettings>

这个游戏只用到了前三个数据项:默认不全屏,分辨率800*600,后面的选项没有用,以后制作键盘映射时会用到。 具体管理这个xml文件的是SettingsManager类,代码如下:

namespace Sample01
{
    [Serializable]
    public struct GameSettings
    {
        public bool PreferredFullScreen;
        public int PreferredWindowWidth;
        public int PreferredWindowHeight;
        public bool EnableVsync;
    }

    public static class SettingsManager
    {
        const string filename= "GameSettings.xml";
        
        public static GameSettings Read()
        {
            // 打开文件
            FileStream stream = File.Open(filename, FileMode.Open,FileAccess.Read);

            // 从文件中读取数据
            XmlSerializer serializer = new XmlSerializer(typeof(GameSettings));
            GameSettings data = (GameSettings)serializer.Deserialize(stream);

            // 关闭文件
            stream.Close();
            
            //返回获取的数据
            return data; 
        }       

        public static void Save(GameSettings data)
        {
            // 打开文件           
            FileStream stream = File.Open(filename, FileMode.Create);

            // 将对象转换为XML数据并写到流中
            XmlSerializer serializer = new XmlSerializer(typeof(GameSettings));
            serializer.Serialize(stream, data);

            // 关闭文件
            stream.Close();            
        }
    }
}

这个类很简单,实现了保存和读取的功能,它来自于《BeginningXNA》第12章中的12.4.2 管理游戏设置,基本思路就是使用.NET自带的IO功能进行串行化和反串行化,缺点是只支持PC平台,要同时支持Xbox360,需要用到XNA自带的StorageContainer类,《ProfessionalXNA》中的赛车游戏就是这样做的,设置文件默认保存在“我的文档/SaveData”目录中。建议看看XNA帮助文档中Programming Guide→Storage中的所有文件,最后给出了一个异步调用的例子,而《ProfessionalXNA》中并没有实现异步调用功能,如果看一下《XNARecipes》中的1.9 将数据保存到文件,从文件读取数据,你会发现这个例子和帮助文件中的例子非常类似。

还有一个办法,就是编写自定义内容导入器,将xml文件导入到内容管道中,相关例子可见《XNARecipes》中的5.11 从XML文件加载数据

中文支持

XNA 1.0 refresh版本之后提供了绘制文字的函数SpriteBatch.DrawString,具体的文字是保存在内容管道的spriteFont文件中的。但默认创建的spriteFont文件只支持英文字母,要支持中文,如果文字量少,第一种方法就是手动添加,例如spriteFont文件中的CharacterRegions元素中添加如下代码:

<CharacterRegions>
    <CharacterRegion>
        <Start>&#32;</Start>
        <End>&#126;</End>
        <Start></Start>
        <End></End>
    </CharacterRegion>
</CharacterRegions>

就可以显示“好”这个汉字。

如果要添加的中文很多,可以使用自定义内容管道,XNA帮助文档中的Programming Guide→Content Pipeline→How To: Extend the Font Description Processor to Support Additional Characters给出了具体实现方法,翻译可见如何扩展Font Description Processor支持附加的字符。 在Sample01项目的Content中添加对这个Font Description Processor的引用,别忘了在属性面板中将这个游戏使用的两个fontTitle,fontMenu文件的Content Processor从默认的Sprite Font Description - XNA Framework改为FontProcessor。

使用自定义字体处理器


发布时间:2009/12/1 上午9:08:49  阅读次数:7147

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号