0

What I'd like to create

So I'm building a 2D adventure game using Monogame and at the begining of the game, I want to use 3 images that show the previous storyline, kinda like an introduction cut-scene, so each image shows different snapshots of what has lead up to where the story begins in-game.

How I'd like it to work

I'd like it to simply show 1 image, then be overlapped by the second one, and then the third one, with a pause of around 3 seconds between each image (not bothered about transitions at all).

My question to all readers

I'd like to know, what would be the best way to implement this so that it starts at the beginning of my game? I was thinking of making the cut-scene on something like Premier Pro, where it'll just be a video that plays, however I'm not sure if there are better options for doing this. I'd just like to know what would be the most simple way of achieving something like this.

Ottavio Campana
  • 4,088
  • 5
  • 31
  • 58
toadflax
  • 375
  • 4
  • 17

1 Answers1

1

It's good to define in a sentence or two exactly what you want so you can code it with that simplicity. I personaly defined cutscene as: "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)." This means I'll include timers and images. This way I know the full play time for each image and also for the whole slideshow.

I have some old code that might help you. This code takes list of images to show, fade in time, stand time and fade out time. This could be easily be translated to what you want by switching the fade-in/out-to-black effect to fade-with-overlapping effect.

First, here is the single screen 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;
        }

    }
}

Now, here is the manager or "parent" class that creates a sequence of these SpalshScreens:

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;
                }
            }
        }
    }
}

The how to use:

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;
            }
        }

A note for SplashScreenManager's constructor: GContent.Textures(path);'s goal is to try to load all images from that folder. This function looks like this:

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;
}

Where, again, the Help.RootDir just returns the root directory, like this:

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

This way you can have all splash images in one folder. Offcorse, it's up to you how you want to manage your resources. This is a realy simple way with some great functionality. Try testing it with old sony play station sound in the background and use these parameters for SSM: ssm = new SplashScreenManager(your list of splash images, 3, 1, 3). Looks and sounds great. Thumbs up if this helped you!

EDIT:

I didn't tell you how to play this at start of your game. Use an enum to define your game state, and single variable to store that information, something like this:

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

At the start of your game, set currentGameState = GameState.Initialize;, load ssm, and in update check if the ssm (splash screen manager) has ended playing. If it did, change currentGameState to GameState.Menu, and use a switch function in Update method, so you know which game state is your game currently at, so you know what to update. You have example of this in the code above, but I didn't write about that, and this is the reason for this edit.

Monset
  • 648
  • 5
  • 25