2019-12-28 21:41:06 +00:00
using OpenTK.Graphics ;
2019-12-28 20:35:01 +00:00
using RecrownedAthenaeum.Graphics.Render ;
2018-11-30 02:41:06 +00:00
using System ;
using System.Diagnostics ;
2019-12-28 21:41:06 +00:00
namespace RecrownedAthenaeum.Graphics.UI.ScreenSystem
2018-11-30 02:41:06 +00:00
{
2019-01-14 05:23:03 +00:00
/// <summary>
/// Called when the first screen is being shown.
/// </summary>
/// <param name="screen">The screen to show after the loading screen.</param>
2019-03-24 00:04:43 +00:00
public delegate void NeedNextScreen ( Screen screen ) ;
2018-11-30 02:41:06 +00:00
2019-01-14 07:26:46 +00:00
/// <summary>
/// A manager for screens. Helps with transitions and updating screens as well as resizes.
/// </summary>
2018-11-30 02:41:06 +00:00
public class ScreenManager : IDisposable
{
2018-12-29 06:23:29 +00:00
bool disposed = false ;
2019-01-14 05:23:03 +00:00
/// <summary>
/// Called when the first loading screen is done, and needs to show the landing screen.
/// </summary>
2019-03-24 00:04:43 +00:00
public event NeedNextScreen NeedNextScreen ;
2019-03-09 08:03:05 +00:00
/// <summary>
2019-03-10 06:50:03 +00:00
/// The settings this manager will use to begin a sprite batch.
2019-03-09 08:03:05 +00:00
/// </summary>
2019-01-30 13:46:58 +00:00
private GraphicsDeviceManager graphics ;
2018-11-30 02:41:06 +00:00
private Screen previousScreen ;
private RenderTarget2D previousScreenRenderTarget ;
private Screen currentScreen ;
private bool firstScreenChangeComplete ;
2019-01-14 05:23:03 +00:00
private bool resizing ;
/// <summary>
/// Currently displayed screen.
/// </summary>
2018-11-30 02:41:06 +00:00
public Screen Screen
{
get
{
return currentScreen ;
}
set
{
previousScreen = currentScreen ;
currentScreen = value ;
2019-03-24 00:04:43 +00:00
if ( Screen . width ! = graphics . PreferredBackBufferWidth | | Screen . height ! = graphics . PreferredBackBufferHeight )
2018-11-30 02:41:06 +00:00
{
2019-03-24 00:04:43 +00:00
Screen . ApplySize ( graphics . PreferredBackBufferWidth , graphics . PreferredBackBufferHeight ) ;
2018-11-30 02:41:06 +00:00
}
if ( previousScreen ! = null & & previousScreenRenderTarget = = null & & previousScreen . UseRenderTargetForExitTransition )
{
previousScreenRenderTarget = new RenderTarget2D ( graphics . GraphicsDevice , graphics . PreferredBackBufferWidth , graphics . PreferredBackBufferHeight ) ;
}
graphics . GraphicsDevice . SetRenderTarget ( previousScreenRenderTarget ) ;
2019-12-28 21:41:06 +00:00
graphics . GraphicsDevice . Clear ( Color4 . Black ) ;
2018-11-30 02:41:06 +00:00
Debug . WriteLine ( "Showing " + value . GetType ( ) . Name ) ;
Screen . Show ( ) ;
previousScreen ? . Hide ( ) ;
}
}
2019-01-14 05:23:03 +00:00
/// <summary>
2019-03-24 00:04:43 +00:00
/// Creates a screen manager that helps update, draw, and manage multiple screens and their transitions. Uses its own <see cref="SpriteBatch"/>.
2019-01-14 05:23:03 +00:00
/// </summary>
2019-03-24 00:04:43 +00:00
/// <param name="graphicsDeviceManager">The graphics device manager to be used.</param>
public ScreenManager ( GraphicsDeviceManager graphicsDeviceManager )
2018-11-30 02:41:06 +00:00
{
2019-03-24 00:04:43 +00:00
graphics = graphicsDeviceManager ? ? throw new ArgumentNullException ( "Graphics device manager argument cannot be null." ) ;
2018-11-30 02:41:06 +00:00
}
2019-01-14 05:23:03 +00:00
/// <summary>
/// Updates the screens. Should be called once every frame.
/// </summary>
/// <param name="gameTime">Contains the time that has passed from the last frame.</param>
/// <param name="waiting">Whether or not there is something a transition should be waiting for. Usually used to wait for assets to complete loading.</param>
public void UpdateCurrentScreen ( GameTime gameTime , bool waiting )
2018-11-30 02:41:06 +00:00
{
2019-03-24 00:04:43 +00:00
waiting = ! waiting ;
2018-11-30 02:41:06 +00:00
switch ( Screen . State )
{
case ScreenState . EnterTransition :
if ( previousScreen ! = null & & previousScreen . UseRenderTargetForExitTransition )
{
2019-01-14 05:23:03 +00:00
previousScreen . UpdateTransition ( gameTime . ElapsedGameTime . TotalSeconds , waiting ) ;
2018-11-30 02:41:06 +00:00
}
2019-01-14 05:23:03 +00:00
Screen . UpdateTransition ( gameTime . ElapsedGameTime . TotalSeconds , waiting ) ;
2018-11-30 02:41:06 +00:00
break ;
case ScreenState . ExitTransition :
2019-01-14 05:23:03 +00:00
if ( Screen . UseRenderTargetForExitTransition | | Screen . UpdateTransition ( gameTime . ElapsedGameTime . TotalSeconds , waiting ) )
2018-11-30 02:41:06 +00:00
{
if ( ! firstScreenChangeComplete )
{
firstScreenChangeComplete = true ;
2019-03-24 00:04:43 +00:00
OnNeedNextScreen ( Screen ) ;
2018-11-30 02:41:06 +00:00
}
if ( Screen . NextScreen ! = null )
{
Debug . WriteLine ( "Changing to the next given screen." ) ;
Screen = Screen . NextScreen ;
}
else if ( previousScreen ! = null )
{
Debug . WriteLine ( "Changing to previous screen." ) ;
Screen = previousScreen ;
}
else
{
throw new InvalidOperationException ( "No next screen provided and no previous screen to return to." ) ;
}
}
break ;
}
Screen . Update ( gameTime ) ;
}
2019-01-14 05:23:03 +00:00
/// <summary>
/// Renders screen into window.
2019-03-24 00:04:43 +00:00
/// Starts and ends the given <paramref name="consistentSpriteBatch"/>.
2019-01-14 05:23:03 +00:00
/// </summary>
2019-03-24 00:04:43 +00:00
/// <param name="consistentSpriteBatch">The consistent sprite batch to use. Needs to be consistent as changing render targets requires ending and beginning the sprite batch.</param>
public void DrawScreen ( ConsistentSpriteBatch consistentSpriteBatch )
2018-11-30 02:41:06 +00:00
{
2019-03-24 00:04:43 +00:00
if ( consistentSpriteBatch = = null )
{
throw new ArgumentNullException ( nameof ( consistentSpriteBatch ) ) ;
}
if ( Screen = = null ) return ;
2018-11-30 02:41:06 +00:00
graphics . GraphicsDevice . Clear ( Screen . BackgroundColor ) ;
if ( Screen . State = = ScreenState . EnterTransition & & previousScreen ! = null & & previousScreen . UseRenderTargetForExitTransition )
{
graphics . GraphicsDevice . SetRenderTarget ( previousScreenRenderTarget ) ;
graphics . GraphicsDevice . Clear ( previousScreen . BackgroundColor ) ;
2019-03-24 00:04:43 +00:00
consistentSpriteBatch . Begin ( ) ;
previousScreen . Draw ( consistentSpriteBatch ) ;
consistentSpriteBatch . End ( ) ;
2018-11-30 02:41:06 +00:00
graphics . GraphicsDevice . SetRenderTarget ( null ) ;
Screen . UpdatePreviousScreenFrame ( previousScreenRenderTarget ) ;
}
2019-03-24 00:04:43 +00:00
consistentSpriteBatch . Begin ( ) ;
Screen . Draw ( consistentSpriteBatch ) ;
consistentSpriteBatch . End ( ) ;
2018-11-30 02:41:06 +00:00
}
2019-01-14 05:23:03 +00:00
/// <summary>
/// Should be called when resize is occurring to change to a loading screen.
/// This will notify the screen of the status of the assets, change the screen to a loading screen, and dispose of the previous screen buffer.
/// </summary>
/// <param name="loadingScreen">The loading screen to change to.</param>
2018-11-30 02:41:06 +00:00
public void Resize ( LoadingScreen loadingScreen )
{
2019-01-14 05:23:03 +00:00
if ( resizing ) throw new InvalidOperationException ( "Already resizing." ) ;
resizing = true ;
2019-11-24 02:47:21 +00:00
if ( Screen ! = null ) {
Screen . AssetLoadStateChange ( false ) ;
Screen = loadingScreen ;
previousScreenRenderTarget . Dispose ( ) ;
previousScreenRenderTarget = null ;
}
2018-11-30 02:41:06 +00:00
}
2019-03-10 06:50:03 +00:00
2019-01-14 05:23:03 +00:00
/// <summary>
/// Notifies all screen that assets have completed being loaded after a resize.
/// </summary>
2018-11-30 02:41:06 +00:00
public void PostResize ( )
{
2019-01-14 05:23:03 +00:00
if ( ! resizing ) throw new InvalidOperationException ( "Was never resizing." ) ;
Screen . AssetLoadStateChange ( true ) ;
2018-11-30 02:41:06 +00:00
}
2019-03-24 00:04:43 +00:00
private void OnNeedNextScreen ( Screen screen )
2018-11-30 02:41:06 +00:00
{
2019-03-24 00:04:43 +00:00
NeedNextScreen ? . Invoke ( screen ) ;
2018-11-30 02:41:06 +00:00
}
2019-01-14 07:26:46 +00:00
/// <summary>
/// Disposes this.
/// </summary>
2018-11-30 02:41:06 +00:00
public void Dispose ( )
{
2018-12-29 06:23:29 +00:00
Dispose ( true ) ;
GC . SuppressFinalize ( this ) ;
}
2019-01-14 07:26:46 +00:00
/// <summary>
/// An overridable dispose.
/// </summary>
/// <param name="disposing">True of user invoked dispose called.</param>
2018-12-29 06:23:29 +00:00
public virtual void Dispose ( bool disposing )
{
2019-03-24 00:04:43 +00:00
if ( disposed ) throw new ObjectDisposedException ( GetType ( ) . Name ) ;
2018-12-29 06:23:29 +00:00
if ( disposing )
{
previousScreenRenderTarget ? . Dispose ( ) ;
}
2019-03-24 00:04:43 +00:00
disposed = true ;
2018-11-30 02:41:06 +00:00
}
2019-01-14 07:26:46 +00:00
/// <summary>
/// Destructor.
/// </summary>
~ ScreenManager ( )
{
Dispose ( false ) ;
}
2018-11-30 02:41:06 +00:00
}
}