37.StunEngine0.4的一些改变

在实际的使用引擎的情况中,通常都不会把引擎项目包括在游戏项目中,而只是在游戏项目中添加引擎动态链接库文件的引用,为了实现这个目的,引擎需要做一些调整:

使单元测试代码独立于引擎

首先创建一个叫做TestGame新解决方案,这一步比较简单,在StunEngine0.3中,几乎每个类的最后都添加了单元测试的代码,现在需要将这些代码剪切到这个项目中的TestGame.cs中,当然还要创建一个默认屏幕类DefaultScreen.cs,在其中添加被每个单元测试共享的光源,用作参考的地面等,代码请看源文件。在这个解决方案中还包括引擎项目、蒙皮动画处理器项目、字体处理器项目,要编辑它们的源代码也是在这个解决方案中进行的,截图如下:

TestGame解决方案

将资源嵌入引擎动态链接库文件中

要使用的图像等资源应尽量放在你自己的游戏项目中,但有些资源必须放在引擎项目中,这些资源包括Effect文件,UI控件默认使用的背景图像和UI控件使用的声音文件,必须要将这些资源嵌入到dll文件中。方法来自于帮助文件中的How To: Load Content(http://msdn.microsoft.com/en-us/library/bb203875.aspx)中的Loading Content from a Game Library部分,步骤如下:

1.右击StunEngine项目,在弹出菜单中选择“添加→新建项…”,在弹出的界面中选择“资源文件”,如下图所示:

添加资源文件

2.在项目中双击添加的资源文件(我起的名称为Resource.resx)在弹出的资源设计器界面中选择“添加资源→添加现有文件”:

添加xnb文件

3.导航到引擎项目的bin\x86\Debug\Content文件夹选择要添加到dll文件的xnb文件即可,注意需要选择“显示所有文件”才能看到xnb文件。

然后需要在引擎中加载嵌入的资源,首先需要在引擎类中添加一个新的内容管理器并在构造函数中进行初始化:

ContentManager resourceContent; 
public StunXnaGE()
{
    …
     
    resourceContent = new ResourceContentManager(this.Services,Resource.ResourceManager); 
    
    … 
}

要在引擎中调用嵌入的资源只需使用如下代码即可:

texture = engine.ResourceContent.Load(textureName); 

但是,Shawn Hargreaves 在他的博客中的这篇文章http://blogs.msdn.com/b/shawnhar/archive/2007/06/12/embedding-content-as-resources.aspx的留言中提到,想嵌入AudioEngine和.xgs文件是无法实现的。

不过XNA3.0提供了一个播放简单声音的方法SoundEffect.Play(),具体可参见7.1 播放并控制简单的.wav声音文件,它不使用.xgs文件,而是简单地将wave文件用Sound Effect内容处理器处理,这样经过处理后我的引擎中的Click.wav和Highlight.wav又可以编译为xnb文件,这样就可以嵌入到资源中去了。Click.wav和Highlight.wav分别表示鼠标点击控件和滑入控件发出的声音,如果你嫌烦也可以将引擎中的EnablePlayUISound设为false关闭这个声音,但是想要用自己的声音文件替换这两个文件的功能目前没有实现,但这是个小问题,解决起来不难,以后再说吧。

你需要在引擎类中定义这两个声音:

internal SoundEffect soundEffectClick; 
internal SoundEffect soundEffectHighlight; 

在播放声音时调用以下方法即可:

engine.soundEffectClick .Play(); 

重新编写处理声音的类

在StunEngine0.3中管理xgs声音播放的是一个Sound静态类,使用起来还不够灵活,所以直接参考了XNA Unleashed 3.0(其实这是一本好书,只是源代码不包含素材,如果谁有此书的素材,请也提供给我一份,先行谢过),将它的声音处理类SoundManager复制了过来,它可以指定使用的xact项目,使用起来更加灵活。代码如下:

namespace StunEngine.Sounds
{
    /// 
    /// 进行声音处理的管理类
    /// 
    public partial class SoundManager : Microsoft.Xna.Framework.GameComponent
    {
        public bool RepeatPlayList = true;
        private AudioEngine audioEngine;
        private WaveBank waveBank;
        private SoundBank soundBank;

        /// 
        /// 声音文件的集合
        /// 
        Dictionary cues = new Dictionary();

        private string[] playList;
        private int currentSong;
        private Cue currentlyPlaying;
        private Dictionary categories = new Dictionary();

        /// 
        /// 
        /// 
        /// 引擎
        /// xact项目名称
        public SoundManager(StunXnaGE game, string xactProjectName)
            : this(game, xactProjectName, xactProjectName)
        {
        }

        /// 
        /// 
        /// 
        /// 引擎
        /// xact项目名称
        /// xact文件名称
        public SoundManager(StunXnaGE game, string xactProjectName, string xactFileName)
            : this(game, xactProjectName, xactFileName, @"Content\Sounds\")
        {
        }

        /// 
        /// 
        /// 
        /// 引擎
        /// xact项目名称
        /// xact文件名称
        /// content路径
        public SoundManager(StunXnaGE game, string xactProjectName, string xactFileName, string contentPath)
            : base(game)
        {
            xactFileName = xactFileName.Replace(".xap", "");

            audioEngine = new AudioEngine(contentPath + xactFileName + ".xgs");
            waveBank = new WaveBank(audioEngine, contentPath + "Wave Bank.xwb");
            soundBank = new SoundBank(audioEngine, contentPath + "Sound Bank.xsb");
        }


        public override void Update(GameTime gameTime)
        {
            if (audioEngine != null)
            {
                audioEngine.Update();

                if (currentlyPlaying != null) //are we playing a list?
                {
                    //check current cue to see if it is playing
                    //if not, go to next cue in list
                    if (!currentlyPlaying.IsPlaying)
                    {
                        currentSong++;

                        if (currentSong == playList.Length)
                        {
                            if (RepeatPlayList)
                                currentSong = 0;
                            else
                                StopPlayList();
                        }

                        if (currentlyPlaying != null) //may have been set to null, if we finished our list
                        {
                            currentlyPlaying = soundBank.GetCue(playList[currentSong]);
                            currentlyPlaying.Play();
                        }
                    }
                }
            }

            base.Update(gameTime);
        }

        /// 
        /// 移除资源
        /// 
        /// 
        protected override void Dispose(bool disposing)
        {
            soundBank.Dispose();
            waveBank.Dispose();
            audioEngine.Dispose();

            playList = null;
            currentlyPlaying = null;
            cues = null;
            soundBank = null;
            waveBank = null;
            audioEngine = null;
            
            base.Dispose(disposing);
        }

        /// 
        /// 
        /// 
        /// 
        /// 
        public void SetGlobalVariable(string name, float amount)
        {
            audioEngine.SetGlobalVariable(name, amount);
        }

        #region Category方法
        
        private void CheckCategory(string categoryName)
        {
            if (!categories.ContainsKey(categoryName))
                categories.Add(categoryName, audioEngine.GetCategory(categoryName));
        }

        /// 
        /// 
        /// 
        /// 
        /// 
        public void SetVolume(string categoryName, float volumeAmount)
        {
            CheckCategory(categoryName);

            categories[categoryName].SetVolume(volumeAmount);
        }

        /// 
        /// 
        /// 
        /// 
        public void PauseCategory(string categoryName)
        {
            CheckCategory(categoryName);
            
            categories[categoryName].Pause();
        }

        /// 
        /// 
        /// 
        /// 
        public void ResumeCategory(string categoryName)
        {
            CheckCategory(categoryName);

            categories[categoryName].Resume();
        }
        #endregion


        public bool IsPlaying(string cueName)
        {
            if (cues.ContainsKey(cueName))
                return (cues[cueName].IsPlaying);

            return (false);
        }

        public void Play(string cueName)
        {
            Cue prevCue = null;

            if (!cues.ContainsKey(cueName))
                cues.Add(cueName, soundBank.GetCue(cueName));
            else
            {
                //store our cue if we were playing
                if (cues[cueName].IsPlaying)
                    prevCue = cues[cueName];

                cues[cueName] = soundBank.GetCue(cueName);
            }

            //if we weren't playing, then set previous to our current cue name
            if (prevCue == null)
                prevCue = cues[cueName];

            try
            {
                cues[cueName].Play();
            }
            catch (Exception)
            {
                //hit limit exception, set our cue to the previous and let's stop it
                //and then start it up again ...
                cues[cueName] = prevCue;
                //could just ignore this error, for now we will have our library stop and restart
                //regardless of what XACT says to do ... just an example
                if (cues[cueName].IsPlaying)
                    cues[cueName].Stop(AudioStopOptions.AsAuthored);

                Toggle(cueName);
            }
        }

        /// 
        /// 暂停声音文件的播放
        /// 
        /// 
        public void Pause(string cueName)
        {
            if (cues.ContainsKey(cueName))
                cues[cueName].Pause();
        }

        /// 
        /// 继续播放声音文件
        /// 
        /// 
        public void Resume(string cueName)
        {
            if (cues.ContainsKey(cueName))
                cues[cueName].Resume();
        }

        /// 
        /// 切换声音文件
        /// 
        /// 
        public void Toggle(string cueName)
        {
            if (cues.ContainsKey(cueName))
            {
                Cue cue = cues[cueName];

                if (cue.IsPaused)
                {
                    cue.Resume();
                }
                else if (cue.IsPlaying)
                {
                    cue.Pause();
                }
                else //played but stopped 
                {
                    //need to re-get Cue if stopped
                    Play(cueName);
                }
            }
            else //never played, need to re-get cue
                Play(cueName);

        }

        /// 
        /// 停止所有声音文件的播放
        /// 
        public void StopAll()
        {
            foreach (Cue cue in cues.Values)
                cue.Stop(AudioStopOptions.Immediate);
        }

        /// 
        /// 停止播放指定的声音文件
        /// 
        /// 
        public void Stop(string cueName)
        {
            if (cues.ContainsKey(cueName))
                cues[cueName].Stop(AudioStopOptions.Immediate);
            cues.Remove(cueName);
        }

        public void StartPlayList(string[] playList)
        {
            StartPlayList(playList, 0);
        }

        public void StartPlayList(string[] playList, int startIndex)
        {
            if (playList.Length == 0)
                return;

            this.playList = playList;

            if (startIndex > playList.Length)
                startIndex = 0;

            StartPlayList(startIndex);
        }

        public void StartPlayList(int startIndex)
        {
            if (playList.Length == 0)
                return;

            currentSong = startIndex;
            currentlyPlaying = soundBank.GetCue(playList[currentSong]);
            currentlyPlaying.Play();
        }


        public void StopPlayList()
        {
            if (currentlyPlaying != null)
            {
                currentlyPlaying.Stop(AudioStopOptions.Immediate);
                currentlyPlaying = null;
            }
        }

    }
}

将示例项目放置在一个解决方案中

目前拥有6个示例项目,为了使用起来更简单,创建一个新的解决方案Samples,将这六个示例放在这个解决方案中,只需右击项目选择“设为启动项目”即可使用相应的示例,如下图所示:

Samples解决方案

其中每个示例引用的都是引擎类库的StunEngine.dll文件,要编辑引擎的源代码,需要在TestGame解决方案中进行。

文件下载(已下载 1173 次)

发布时间:2010/6/11 10:16:26  阅读次数:5728

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

沪ICP备18037240号-1

沪公网安备 31011002002865号