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.