diff --git a/Behavior/DotRPG.Behavior.csproj b/Behavior/DotRPG.Behavior.csproj index 65a43cc..1c14586 100644 --- a/Behavior/DotRPG.Behavior.csproj +++ b/Behavior/DotRPG.Behavior.csproj @@ -8,6 +8,12 @@ 1.3.6.0 + + + + + + diff --git a/Behavior/Routines/DotRPG.Behavior.Routines.csproj b/Behavior/Routines/DotRPG.Behavior.Routines.csproj new file mode 100644 index 0000000..e6fe793 --- /dev/null +++ b/Behavior/Routines/DotRPG.Behavior.Routines.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.1 + DotRPG + red-the-random-dev + red-the-random-dev + 1.3.6.0 + 1.3.6.0 + + + diff --git a/Behavior/Routines/FrameLoader.cs b/Behavior/Routines/FrameLoader.cs new file mode 100644 index 0000000..9372e10 --- /dev/null +++ b/Behavior/Routines/FrameLoader.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace DotRPG.Behavior.Routines +{ + public class FrameLoader + { + public ILoadable Loaded; + + public Single LoadPercentage + { + get + { + if (Loaded.ContentTasks_Total + Loaded.ObjectTasks_Total == 0) + { + return Single.NaN; + } + return (Single)Math.Round((100.0m * Loaded.ContentTasks_Done + Loaded.ObjectTasks_Done) / (Loaded.ContentTasks_Total + Loaded.ObjectTasks_Total), 1); + } + } + + public void Update() + { + if (Loaded.Loaded) + { + return; + } + else if (!Loaded.ReadyForLoad) + { + Loaded.PreloadTask(); + } + else if (Loaded.ContentTasks_Done < Loaded.ContentTasks_Total) + { + Loaded.PerformContentTask(); + } + else if (Loaded.ObjectTasks_Done < Loaded.ObjectTasks_Total) + { + Loaded.PerformObjectTask(); + } + else + { + Loaded.PostLoadTask(); + } + } + public FrameLoader(ILoadable il) + { + Loaded = il; + } + } +} diff --git a/Behavior/Routines/ILoadable.cs b/Behavior/Routines/ILoadable.cs new file mode 100644 index 0000000..a0b4755 --- /dev/null +++ b/Behavior/Routines/ILoadable.cs @@ -0,0 +1,25 @@ +using System; + +namespace DotRPG.Behavior.Routines +{ + public interface ILoadable + { + public Boolean ReadyForLoad { get; } + public Boolean Loaded { get; } + public Int32 ContentTasks_Total { get; } + public Int32 ObjectTasks_Total { get; } + public Int32 ContentTasks_Done { get; } + public Int32 ObjectTasks_Done { get; } + // Do single task per call + public void PerformContentTask(); + public void PerformObjectTask(); + + public Boolean SupportsMultiLoading { get; } + // Do multiple tasks at once + public void PerformContentTasks(Int32 step); + public void PerformObjectTasks(Int32 step); + + public void PreloadTask(); + public void PostLoadTask(); + } +} diff --git a/DotRPG.sln b/DotRPG.sln index 42d2b9f..9b5d69f 100644 --- a/DotRPG.sln +++ b/DotRPG.sln @@ -15,7 +15,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotRPG.Scripting.Pipeline", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotRPG.Scripting", "Scripting\DotRPG.Scripting.csproj", "{610FDEAE-E1EB-475C-A095-2F337FBB5A2C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRPG.Behavior", "Behavior\DotRPG.Behavior.csproj", "{7D48C09E-EDCF-4D41-9687-F32BC4F8DFC6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotRPG.Behavior", "Behavior\DotRPG.Behavior.csproj", "{7D48C09E-EDCF-4D41-9687-F32BC4F8DFC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRPG.Behavior.Routines", "Behavior\Routines\DotRPG.Behavior.Routines.csproj", "{67A8F31C-34A8-4DEC-9AB6-57F5A41018C7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -51,6 +53,10 @@ Global {7D48C09E-EDCF-4D41-9687-F32BC4F8DFC6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D48C09E-EDCF-4D41-9687-F32BC4F8DFC6}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D48C09E-EDCF-4D41-9687-F32BC4F8DFC6}.Release|Any CPU.Build.0 = Release|Any CPU + {67A8F31C-34A8-4DEC-9AB6-57F5A41018C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67A8F31C-34A8-4DEC-9AB6-57F5A41018C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67A8F31C-34A8-4DEC-9AB6-57F5A41018C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67A8F31C-34A8-4DEC-9AB6-57F5A41018C7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/_Example/DotRPG.Example.csproj b/_Example/DotRPG.Example.csproj index bc88727..e29756e 100644 --- a/_Example/DotRPG.Example.csproj +++ b/_Example/DotRPG.Example.csproj @@ -41,6 +41,7 @@ + diff --git a/_Example/Game1.cs b/_Example/Game1.cs index 3f7c4c4..0265ed5 100644 --- a/_Example/Game1.cs +++ b/_Example/Game1.cs @@ -10,6 +10,7 @@ using System.Xml.Linq; using System.IO; using System.Reflection; +using DotRPG.Behavior.Routines; namespace DotRPG._Example { @@ -166,10 +167,29 @@ protected override void Update(GameTime gameTime) } if (ActiveFrame != null) { - if (ActiveFrame.FrameID == -128 && stageSelect.SelectedOption != -1) + if (ActiveFrame is LoadingScreen l) { - ActiveFrame = Frames[stageSelect.SelectedOption]; - ActiveFrame.LoadContent(); + if (l.LoadedFrame.Loaded) + { + ActiveFrame = l.LoadedFrame as Frame; + } + if (ActiveFrame == null) + { + ActiveFrame = stageSelect; + } + } + else if (ActiveFrame.FrameID == -128 && stageSelect.SelectedOption != -1) + { + Frame toLoad = Frames[stageSelect.SelectedOption]; + if (toLoad is ILoadable load) + { + ActiveFrame = new LoadingScreen(load, this, ResourceHGlobal, LogicEventSet); + } + else + { + ActiveFrame = toLoad; + ActiveFrame.LoadContent(); + } } else if (ActiveFrame.FrameID != -128 && Keyboard.GetState().IsKeyDown(Keys.F2)) { diff --git a/_Example/LoadingScreen.cs b/_Example/LoadingScreen.cs new file mode 100644 index 0000000..f43233d --- /dev/null +++ b/_Example/LoadingScreen.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Text; +using DotRPG.Behavior; +using DotRPG.Behavior.Routines; +using DotRPG.Behavior.Routines; +using DotRPG.Objects; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace DotRPG._Example +{ + class LoadingScreen : Frame + { + public FrameLoader Loader; + TextObject percentage; + public ILoadable LoadedFrame + { + get + { + return Loader.Loaded; + } + set + { + Loader = new FrameLoader(value); + } + } + public override int FrameID + { + get + { + return -129; + } + } + public LoadingScreen(ILoadable il, Game owner, ResourceHeap globalGameResources, HashSet globalEventSet) : base(owner, globalGameResources, globalEventSet) + { + Loader = new FrameLoader(il); + percentage = new TextObject(globalGameResources.Fonts["vcr_large"], "00.0%", 0.5f, 0.35f, Color.White, AlignMode.BottomCenter, 1080); + } + public override void SetPlayerPosition(object sender, EventArgs e, GameTime gameTime) + { + throw new NotImplementedException(); + } + public override void Update(GameTime gameTime, bool[] controls) + { + Loader.Update(); + percentage.Text = Loader.LoadPercentage.ToString() + "%"; + base.Update(gameTime, controls); + } + public override void Draw(GameTime gameTime, SpriteBatch spriteBatch, Rectangle drawZone) + { + Vector2 v = new Vector2(drawZone.X + (drawZone.Width / 8), drawZone.Y + (drawZone.Height / 2)); + Texture2D t1 = new Texture2D(spriteBatch.GraphicsDevice, drawZone.Width * 3 / 4, drawZone.Height / 20); + Texture2D t2 = new Texture2D(spriteBatch.GraphicsDevice, Math.Max(drawZone.Width * 3 / 4 * (Int32)Loader.LoadPercentage/100, 1), drawZone.Height / 20); + + Color[] d1 = new Color[drawZone.Width * 3 / 4 * drawZone.Height / 20]; + Color[] d2 = new Color[Math.Max(drawZone.Width * 3 / 4 * (Int32)Loader.LoadPercentage / 100, 1) * drawZone.Height / 20]; + + for (int i = 0; i < d1.Length; i++) d1[i] = Color.Gray; + for (int i = 0; i < d2.Length; i++) d2[i] = Color.White; + + t1.SetData(d1); + t2.SetData(d2); + + spriteBatch.Draw(t1, v, Color.White); + spriteBatch.Draw(t2, v, Color.White); + + percentage.Draw(spriteBatch, Owner.Window); + } + public override void Initialize() + { + + } + public override void LoadContent() + { + + } + public override void UnloadContent() + { + + } + } +} diff --git a/_Example/ScriptTest_Dynamic.cs b/_Example/ScriptTest_Dynamic.cs index 0c81950..1834467 100644 --- a/_Example/ScriptTest_Dynamic.cs +++ b/_Example/ScriptTest_Dynamic.cs @@ -11,11 +11,12 @@ using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Media; using DotRPG.Scripting; +using DotRPG.Behavior.Routines; namespace DotRPG.Example { [SceneBuilder("example/topdown", true)] - public class ScriptTest_Dynamic : Frame, IXMLSceneBuilder + public class ScriptTest_Dynamic : Frame, IXMLSceneBuilder, ILoadable { CameraFrameObject cam = new CameraFrameObject(); PlayerObject player; @@ -28,74 +29,175 @@ public class ScriptTest_Dynamic : Frame, IXMLSceneBuilder Dictionary props = new Dictionary(); Dictionary interactable = new Dictionary(); - public override int FrameID + + #region ILoadable implementation + Int32 content = 0; + Int32 objects = 0; + Boolean ready = false; + Boolean loaded = false; + public Int32 ContentTasks_Total { get { - return _id; + return resourceLoad.Count; + } + } + public Int32 ObjectTasks_Total + { + get + { + return objectPrototypes.Count; + } + } + public Int32 ContentTasks_Done + { + get + { + return content; + } + } + public Int32 ObjectTasks_Done + { + get + { + return objects; + } + } + public Boolean ReadyForLoad + { + get + { + return ready; + } + } + public Boolean Loaded + { + get + { + return loaded; } } - public override void SetPlayerPosition(object sender, EventArgs e, GameTime gameTime) + public void PerformContentTask() { - throw new NotImplementedException(); + PerformContentTasks(1); } - #region Loader - public override void LoadContent() + public void PerformObjectTask() + { + PerformObjectTasks(1); + } + public void PerformContentTasks(Int32 step) + { + Int32 x = content; + for (int i = content; i < Math.Min(x+step, resourceLoad.Count); i++) + { + ResourceLoadTask rlt = resourceLoad[i]; + LoadResource(rlt); + content = i+1; + } + } + public void PerformObjectTasks(Int32 step) + { + Int32 x = objects; + for (int i = objects; i < Math.Min(x + step, objectPrototypes.Count); i++) + { + XElement xe = objectPrototypes[i]; + LoadObject(xe); + objects = i+1; + } + } + public void PreloadTask() { cam.CameraVelocity = 300.0f; cam.DefaultHeight = 540; - foreach (ResourceLoadTask rlt in resourceLoad) + ready = true; + } + public void PostLoadTask() + { + cam.Focus = player.Location.ToPoint(); + foreach (LuaModule x in Scripts) + { + x.Runtime["obj"] = props; + } + loaded = true; + } + public Boolean SupportsMultiLoading + { + get { - switch (rlt.Resource) + return true; + } + } + #endregion + public override int FrameID + { + get + { + return _id; + } + } + public override void SetPlayerPosition(object sender, EventArgs e, GameTime gameTime) + { + if (e is FrameShiftEventArgs fs) + { + if (fs.IncludesPlayerLocation && player != null) { - case ResourceType.Texture2D: - foreach (String x in FrameResources.Textures.Keys) + player.Location = fs.PlayerLocation.ToVector2(); + } + } + } + #region Loader + void LoadResource(ResourceLoadTask rlt) + { + switch (rlt.Resource) + { + case ResourceType.Texture2D: + foreach (String x in FrameResources.Textures.Keys) + { + if (x == rlt.ResourceID) { - if (x == rlt.ResourceID) - { - goto end_t; - } + goto end_t; } - FrameResources.Textures.Add(rlt.ResourceID, Owner.Content.Load(rlt.ResourcePath)); - end_t: break; - case ResourceType.SoundEffect: - foreach (String x in FrameResources.Sounds.Keys) + } + FrameResources.Textures.Add(rlt.ResourceID, Owner.Content.Load(rlt.ResourcePath)); + end_t: break; + case ResourceType.SoundEffect: + foreach (String x in FrameResources.Sounds.Keys) + { + if (x == rlt.ResourceID) { - if (x == rlt.ResourceID) - { - goto end_s; - } + goto end_s; } - FrameResources.Sounds.Add(rlt.ResourceID, Owner.Content.Load(rlt.ResourcePath)); - end_s: break; - case ResourceType.SpriteFont: - foreach (String x in FrameResources.Textures.Keys) + } + FrameResources.Sounds.Add(rlt.ResourceID, Owner.Content.Load(rlt.ResourcePath)); + end_s: break; + case ResourceType.SpriteFont: + foreach (String x in FrameResources.Textures.Keys) + { + if (x == rlt.ResourceID) { - if (x == rlt.ResourceID) - { - goto end_f; - } + goto end_f; } - FrameResources.Fonts.Add(rlt.ResourceID, Owner.Content.Load(rlt.ResourcePath)); - end_f: break; - case ResourceType.Song: - foreach (String x in FrameResources.Textures.Keys) + } + FrameResources.Fonts.Add(rlt.ResourceID, Owner.Content.Load(rlt.ResourcePath)); + end_f: break; + case ResourceType.Song: + foreach (String x in FrameResources.Textures.Keys) + { + if (x == rlt.ResourceID) { - if (x == rlt.ResourceID) - { - goto end_m; - } + goto end_m; } - FrameResources.Music.Add(rlt.ResourceID, Owner.Content.Load(rlt.ResourcePath)); - end_m: break; - } + } + FrameResources.Music.Add(rlt.ResourceID, Owner.Content.Load(rlt.ResourcePath)); + end_m: break; } - foreach (XElement xe in objectPrototypes) + } + void LoadObject(XElement xe) + { + switch (xe.Name.LocalName.ToLower()) { - switch (xe.Name.LocalName.ToLower()) - { - case "player": + case "player": { #region Parameters definition Point startPos = XMLSceneLoader.ResolveVector2(xe.Attribute(XName.Get("startPos")).Value).ToPoint(); @@ -131,7 +233,7 @@ public override void LoadContent() } break; } - case "prop": + case "prop": { #region Parameters definition String ID = xe.Attribute(XName.Get("id")).Value; @@ -155,13 +257,13 @@ public override void LoadContent() } break; } - case "script": + case "script": { String scriptContent = File.ReadAllText(Path.Combine(Owner.Content.RootDirectory, xe.Attribute(XName.Get("location")).Value)); Scripts.Add(new LuaModule(scriptContent)); break; } - case "backdrop": + case "backdrop": { Texture2D t = null; Vector2 p = Vector2.Zero; @@ -186,13 +288,22 @@ public override void LoadContent() } break; } - } } - cam.Focus = player.Location.ToPoint(); - foreach (LuaModule x in Scripts) + } + public override void LoadContent() + { + PreloadTask(); + foreach (ResourceLoadTask rlt in resourceLoad) { - x.Runtime["obj"] = props; + LoadResource(rlt); + } + foreach (XElement xe in objectPrototypes) + { + LoadObject(xe); } + PostLoadTask(); + objects = objectPrototypes.Count; + content = resourceLoad.Count; } public Frame BuildFromXML(XDocument Document, Object[] parameters) @@ -328,6 +439,10 @@ public override void UnloadContent() player = null; Scripts.Clear(); backdrops.Clear(); + content = 0; + objects = 0; + ready = false; + loaded = false; } } }