Monogame - 切场景方法?

Monogame - Cut scene methods?

我想创造什么

所以我正在使用 Monogame 构建一个 2D 冒险游戏,在游戏开始时,我想使用 3 张图片 来显示之前的故事情节,有点像介绍过场动画,因此每张图片都显示了游戏中故事开始的不同快照。

我希望它如何工作

我希望它只显示一张图片,然后与第二张图片重叠,然后是第三张图片,每张图片之间暂停约 3 秒(根本不关心过渡)。

我对所有读者的提问

我想知道,什么是最好的实现方式,以便它在我的游戏开始时开始?我正在考虑在 Premier Pro 之类的东西上制作过场动画,它只是一个播放的视频,但我不确定是否有更好的选择来做到这一点。 我只想知道实现这样的目标的最简单方法是什么。

最好用一两句话准确定义您想要的内容,这样您就可以简单地对其进行编码。我个人将过场动画定义为:"List of images that fade in from black screen, stay there a bit, then fade to black. You can skip one image by pressing a single keyboard button (like spacebar)." 这意味着我将包括计时器和图像。这样我就知道每张图片以及整个幻灯片的完整播放时间。

我有一些旧代码可能会对您有所帮助。此代码获取要显示的图像列表、淡入时间、待机时间和淡出时间。通过将 fade-in/out-to-black 效果切换为淡入淡出重叠效果,可以轻松将其转换为您想要的效果。

首先这里是单屏class:

using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace F.U.N
{
    public class SplashScreen
    {
        public enum Status
        {
            Ready,
            FadingIn,
            Waiting,
            FadingOut,
            NotReady
        }
        private Status currentStatus;
        public Status CurrentStatus { get { return currentStatus; } }

        private Texture2D image;
        private Color color;
        private byte fade;
        private float timer,
            fadeInTime,
            waitTime,
            fadeOutTime;

        private float startToWaitTime { get { return fadeInTime; } }
        private float startToFadeOutTime { get { return fadeInTime + waitTime; } }
        private float startToEndTime { get { return fadeInTime + waitTime + fadeOutTime; } }

        public SplashScreen(Texture2D image, float fadeInTime, float waitTime, float fadeOutTime)
        {
            float min = 0.1f;
            this.image = image;
            this.fadeInTime = Math.Max(fadeInTime, min);
            this.waitTime = Math.Max(waitTime, min);
            this.fadeOutTime = Math.Max(fadeOutTime, min);
            Prepare();
        }

        public void Prepare()
        {
            fade = 0;
            timer = 0;
            color = new Color(fade, fade, fade);
            currentStatus = Status.Ready;
        }

        public void Update(GameTime gt)
        {
            //CALCULATE ALPHA & status
            if (timer < startToWaitTime)
            {
                fade = (byte)((byte.MaxValue * timer) / startToWaitTime);
                if (currentStatus != Status.FadingIn) currentStatus = Status.FadingIn;
            }
            else if (timer < startToFadeOutTime)
            {
                if (color.A < byte.MaxValue) color.A = byte.MaxValue;
                if (currentStatus != Status.Waiting) currentStatus = Status.Waiting;
            }
            else if (timer < startToEndTime)
            {
                fade = (byte)(byte.MaxValue - ((byte.MaxValue * (timer - startToFadeOutTime)) / fadeOutTime));
                if (currentStatus != Status.FadingOut) currentStatus = Status.FadingOut;
            }
            else
            {
                fade = byte.MinValue;
                if (currentStatus != Status.NotReady) currentStatus = Status.NotReady;
            }

            //UPDATE COLOR AND TIME
            color = new Color(fade, fade, fade);
            timer += (float)gt.ElapsedGameTime.TotalSeconds;
        }

        public void Draw(SpriteBatch sp)
        {
            sp.Draw(image, new Vector2(), color);
        }

        public void End()
        {
            currentStatus = Status.NotReady;
        }

    }
}

现在,这里是创建这些 SpalshScreens 序列的管理器或 "parent" class:

using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace F.U.N
{
    public class SplashScreenManager
    {
        private List<SplashScreen> screens;
        private Keys skipButton;
        public bool Running
        {
            get
            {
                foreach (SplashScreen s in screens)
                    if (s.CurrentStatus != SplashScreen.Status.NotReady)
                        return true;
                return false;
            }
        }

        public SplashScreenManager() : this(new List<SplashScreen>(), Keys.None) { }
        public SplashScreenManager(List<SplashScreen> screens, Keys skipButton)
        {
            this.screens = screens;
            this.skipButton = skipButton;
            Prepare();
        }
        public SplashScreenManager(string path, float fadeIn, float wait, float fadeOut, Keys skipButton)
        {
            List<Texture2D> images = GContent.Textures(path);
            screens = new List<SplashScreen>();
            foreach (Texture2D t in images)
                screens.Add(new SplashScreen(t, fadeIn, wait, fadeOut));
            this.skipButton = skipButton;
        }

        public void Prepare()
        {
            foreach (SplashScreen s in screens)
                s.Prepare();
        }

        public void Update(GameTime gt)
        {
            for (int i = 0; i < screens.Count(); i++)
            {
                if (screens[i].CurrentStatus != SplashScreen.Status.NotReady)
                {
                    screens[i].Update(gt);
                    if (KState.Clicked(skipButton)) screens[i].End();
                    break;
                }
            }
        }

        public void Draw(SpriteBatch sp)
        {
            for (int i = 0; i < screens.Count(); i++)
            {
                if (screens[i].CurrentStatus != SplashScreen.Status.NotReady)
                {
                    screens[i].Draw(sp);
                    break;
                }
            }
        }
    }
}

使用方法:

protected override void LoadContent()
        {
            //SPLASH SCREEN
            ssm = new SplashScreenManager(@"Splash\Pictures", 3, 1, 3, Keys.Space);
        }

protected override void Update(GameTime gameTime)
        {
            if (currentGameState == GameState.Initialize)
            {
                ssm.Update(gameTime);
                if (!ssm.Running)
                    currentGameState = GameState.Menu;
            }
        }

protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.DeepSkyBlue);

            switch (currentGameState)
            {
                case GameState.Initialize:
                    spriteBatch.Begin();
                    ssm.Draw(spriteBatch);
                    spriteBatch.End();
                    break;
            }
        }

SplashScreenManager 的构造函数注意事项:GContent.Textures(path); 的目标是尝试加载该文件夹中的所有图像。这个函数看起来像这样:

public static           List<Texture2D>     Textures(string folderPath)
{
    if (!folderPath.StartsWith(@"\")) folderPath = @"\" + folderPath;
    List<string> paths = Directory.GetFiles(Help.RootDir(Content.RootDirectory) + folderPath).ToList();
    List<Texture2D> images = new List<Texture2D>();
    foreach (string s in paths)
    {
        if (s.EndsWith(".xnb"))
            images.Add(Texture(s.Replace(Content.RootDirectory, "").Replace(".xnb", "")));
    }
    return images;
}

这里 Help.RootDir 只是 returns 根目录,像这样:

public static string RootDir(string s)
{
    return s.Substring(0, s.LastIndexOf(@"\"));
}

这样您就可以将所有启动图像放在一个文件夹中。 Offcorse,这取决于你想如何管理你的资源。这是一种非常简单的方法,具有一些强大的功能。尝试在后台使用旧的 sony play station 声音对其进行测试,并将这些参数用于 SSM:ssm = new SplashScreenManager(<em>your list of splash images</em>, 3, 1, 3 )。看起来和听起来都很棒。如果对您有帮助,请点个赞!

编辑:

我没有告诉你如何在游戏开始时玩这个。使用枚举来定义您的游戏状态,并使用单个变量来存储该信息,如下所示:

public enum GameState
{
    Initialize,
    Menu,
    Level,
    MapMaker
}
public GameState currentGameState = GameState.Initialize;

在游戏开始时,设置 currentGameState = GameState.Initialize;,加载 ssm,并在更新中检查 ssm(启动画面管理器)是否已结束播放。如果是,将currentGameState改为GameState.Menu,在Update方法中使用一个switch函数,这样你就知道你的游戏当前处于哪个游戏状态,所以你知道什么更新。你在上面的代码中有这样的例子,但我没有写那个,这就是这次编辑的原因。