diff --git a/Renako.Game.Tests/Renako.Game.Tests.csproj b/Renako.Game.Tests/Renako.Game.Tests.csproj
index 19e0600..b28cb32 100644
--- a/Renako.Game.Tests/Renako.Game.Tests.csproj
+++ b/Renako.Game.Tests/Renako.Game.Tests.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/Renako.Game.Tests/Visual/Drawables/TestSceneGameplayProgressBar.cs b/Renako.Game.Tests/Visual/Drawables/TestSceneGameplayProgressBar.cs
new file mode 100644
index 0000000..5aa13eb
--- /dev/null
+++ b/Renako.Game.Tests/Visual/Drawables/TestSceneGameplayProgressBar.cs
@@ -0,0 +1,45 @@
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using Renako.Game.Beatmaps;
+using Renako.Game.Graphics.Drawables;
+
+namespace Renako.Game.Tests.Visual.Drawables;
+
+public partial class TestSceneGameplayProgressBar : GameDrawableTestScene
+{
+ [Cached]
+ private BeatmapsCollection beatmapsCollection = new BeatmapsCollection();
+
+ [Cached]
+ private WorkingBeatmap workingBeatmap = new WorkingBeatmap();
+
+ private GameplayProgressBar gameplayProgressBar;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ beatmapsCollection.GenerateTestCollection();
+ workingBeatmap.BeatmapSet = beatmapsCollection.BeatmapSets.Find(set => set.ID == 1);
+ workingBeatmap.Beatmap = beatmapsCollection.Beatmaps.Find(beatmap => beatmap.ID == 1);
+ }
+
+ [Test]
+ public void TestGameplayProgressBar()
+ {
+ AddStep("add gameplay progress bar", () => Add(gameplayProgressBar = new GameplayProgressBar()));
+ AddStep("set total time", () => gameplayProgressBar.SetTotalTime(workingBeatmap.BeatmapSet.TotalLength));
+ AddStep("start track", () => AudioManager.Track.Start());
+ AddStep("set current time", () => gameplayProgressBar.SetCurrentTime(AudioManager.Track.CurrentTime));
+ AddStep("seek to 100s", () => AudioManager.Track.Seek(100000));
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (AudioManager.Track.IsRunning)
+ {
+ gameplayProgressBar.SetCurrentTime(AudioManager.Track.CurrentTime);
+ }
+ }
+}
diff --git a/Renako.Game.Tests/Visual/GameDrawableTestScene.cs b/Renako.Game.Tests/Visual/GameDrawableTestScene.cs
index 3763106..2d1ee2d 100644
--- a/Renako.Game.Tests/Visual/GameDrawableTestScene.cs
+++ b/Renako.Game.Tests/Visual/GameDrawableTestScene.cs
@@ -1,4 +1,5 @@
using osu.Framework.Allocation;
+using Renako.Game.Audio;
using Renako.Game.Graphics.ScreenStacks;
namespace Renako.Game.Tests.Visual;
@@ -20,6 +21,9 @@ public partial class GameDrawableTestScene : RenakoTestScene
[Cached]
public readonly SettingsScreenStack SettingsScreenStack = new SettingsScreenStack();
+ [Cached]
+ public readonly RenakoAudioManager AudioManager = new RenakoAudioManager();
+
[BackgroundDependencyLoader]
private void load()
{
@@ -27,5 +31,6 @@ private void load()
Add(MainScreenStack);
Add(LogoScreenStack);
Add(SettingsScreenStack);
+ Add(AudioManager);
}
}
diff --git a/Renako.Game/Beatmaps/BeatmapsCollection.cs b/Renako.Game/Beatmaps/BeatmapsCollection.cs
index 6451c46..8c4205b 100644
--- a/Renako.Game/Beatmaps/BeatmapsCollection.cs
+++ b/Renako.Game/Beatmaps/BeatmapsCollection.cs
@@ -29,6 +29,14 @@ public void GenerateTestCollection()
Beatmaps = beatmapTestUtility.Beatmaps;
}
+ ///
+ /// Sort all by ID.
+ ///
+ public void SortBeatmapSetsByID()
+ {
+ BeatmapSets = BeatmapSets.OrderBy(e => e.ID).ToList();
+ }
+
private void addThemeSongBeatmapSet()
{
// TODO: Get proper Title, Artist and Source translated from source
diff --git a/Renako.Game/Configurations/RenakoConfigManager.cs b/Renako.Game/Configurations/RenakoConfigManager.cs
index 6796ccb..86ae9b8 100644
--- a/Renako.Game/Configurations/RenakoConfigManager.cs
+++ b/Renako.Game/Configurations/RenakoConfigManager.cs
@@ -17,6 +17,7 @@ protected override void InitialiseDefaults()
SetDefault(RenakoSetting.DisableVideoPreview, false);
SetDefault(RenakoSetting.ShowFPSCounter, false);
+ SetDefault(RenakoSetting.HardwareAcceleration, true);
// Game state
SetDefault(RenakoSetting.LatestBeatmapSetID, 0);
diff --git a/Renako.Game/Configurations/RenakoSetting.cs b/Renako.Game/Configurations/RenakoSetting.cs
index 03d280e..5c9af88 100644
--- a/Renako.Game/Configurations/RenakoSetting.cs
+++ b/Renako.Game/Configurations/RenakoSetting.cs
@@ -7,6 +7,7 @@ public enum RenakoSetting
DisableVideoPreview,
ShowFPSCounter,
+ HardwareAcceleration,
LatestBeatmapSetID,
LatestBeatmapID,
diff --git a/Renako.Game/Graphics/Containers/PlayfieldContainer.cs b/Renako.Game/Graphics/Containers/PlayfieldContainer.cs
index fb6175f..b832b80 100644
--- a/Renako.Game/Graphics/Containers/PlayfieldContainer.cs
+++ b/Renako.Game/Graphics/Containers/PlayfieldContainer.cs
@@ -29,6 +29,7 @@ public partial class PlayfieldContainer : Container
private RenakoSpriteText breakText;
private RenakoSpriteText hitText;
private RenakoSpriteText missText;
+ private RenakoSpriteText comboText;
private RenakoSpriteText clockText;
private const int fade_in_time = move_time / 2;
@@ -147,6 +148,12 @@ private void load(TextureStore textureStore, WorkingBeatmap workingBeatmap)
Origin = Anchor.TopRight,
Text = "Miss: 0"
},
+ comboText = new RenakoSpriteText()
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Text = "Combo: 0"
+ },
clockText = new RenakoSpriteText()
{
Anchor = Anchor.TopRight,
@@ -171,6 +178,7 @@ protected override void Update()
Logger.Log($"Missed note at {note.EndTime}", LoggingTarget.Runtime, LogLevel.Debug);
note.IsHit = true;
stats.Miss++;
+ stats.Combo = 0;
addHitResultAnimation(HitResult.Miss);
updateScoreText();
}
@@ -354,21 +362,25 @@ private void processHit(NoteLane lane)
if (diff < 50)
{
stats.Critical++;
+ stats.Combo++;
addHitResultAnimation(HitResult.Critical);
}
else if (diff < 100)
{
stats.Break++;
+ stats.Combo++;
addHitResultAnimation(HitResult.Break);
}
else if (diff < 200)
{
stats.Hit++;
+ stats.Combo++;
addHitResultAnimation(HitResult.Hit);
}
else
{
stats.Miss++;
+ stats.Combo = 0;
addHitResultAnimation(HitResult.Miss);
playHitAnimation = false;
}
@@ -391,6 +403,7 @@ private void updateScoreText()
breakText.Text = $"Break: {stats.Break}";
hitText.Text = $"Hit: {stats.Hit}";
missText.Text = $"Miss: {stats.Miss}";
+ comboText.Text = $"Combo: {stats.Combo}";
}
private enum HitResult
@@ -421,6 +434,7 @@ private class Statistics
public int Break { get; set; } // More than 50ms but less than 100ms
public int Hit { get; set; } // More than 100ms but less than 200ms
public int Miss { get; set; } // More than 200ms
+ public int Combo { get; set; } // Count of critical, break, and hit
}
private class PlayfieldNote
diff --git a/Renako.Game/Graphics/Containers/SettingsContainer.cs b/Renako.Game/Graphics/Containers/SettingsContainer.cs
index 1cbc723..8f3f9ff 100644
--- a/Renako.Game/Graphics/Containers/SettingsContainer.cs
+++ b/Renako.Game/Graphics/Containers/SettingsContainer.cs
@@ -216,6 +216,14 @@ private void load(RenakoConfigManager renakoConfigManager, FrameworkConfigManage
Current = renakoConfigManager.GetBindable(RenakoSetting.ShowFPSCounter)
},
new SpriteText()
+ {
+ Text = "Enable Hardware Acceleration"
+ },
+ new BasicCheckbox()
+ {
+ Current = renakoConfigManager.GetBindable(RenakoSetting.HardwareAcceleration)
+ },
+ new SpriteText()
{
Text = "FPS Limit"
},
diff --git a/Renako.Game/Graphics/Drawables/GameplayProgressBar.cs b/Renako.Game/Graphics/Drawables/GameplayProgressBar.cs
new file mode 100644
index 0000000..8f0a031
--- /dev/null
+++ b/Renako.Game/Graphics/Drawables/GameplayProgressBar.cs
@@ -0,0 +1,105 @@
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+
+namespace Renako.Game.Graphics.Drawables;
+
+public partial class GameplayProgressBar : CompositeDrawable
+{
+ private RenakoSpriteText currentTime;
+ private RenakoSpriteText totalTime;
+ private double totalTimeValue;
+ private Container progressBar;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Anchor = Anchor.BottomCentre;
+ Origin = Anchor.BottomCentre;
+ RelativeSizeAxes = Axes.X;
+ Width = 0.85f;
+ Height = 15;
+ Y = -20;
+ InternalChildren = new Drawable[]
+ {
+ new Container()
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ RelativeSizeAxes = Axes.Both,
+ Width = 0.9f,
+ Children = new Drawable[]
+ {
+ new Container()
+ {
+ Masking = true,
+ CornerRadius = 10,
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Colour4.Gray
+ }
+ }
+ },
+ progressBar = new Container()
+ {
+ Masking = true,
+ CornerRadius = 10,
+ RelativeSizeAxes = Axes.Both,
+ Width = 0f,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Colour4.White
+ }
+ }
+ },
+ }
+ },
+ currentTime = new RenakoSpriteText()
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Font = RenakoFont.GetFont(RenakoFont.Typeface.JosefinSans, 25),
+ Text = "0:00"
+ },
+ totalTime = new RenakoSpriteText()
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Font = RenakoFont.GetFont(RenakoFont.Typeface.JosefinSans, 25),
+ Text = "0:00"
+ }
+ };
+ }
+
+ ///
+ /// Set the total time of the progress bar in milliseconds.
+ ///
+ /// The total time in milliseconds.
+ public void SetTotalTime(double time)
+ {
+ totalTime.Text = TimeSpan.FromMilliseconds(time).ToString(@"m\:ss");
+ totalTimeValue = time;
+ }
+
+ ///
+ /// Set the current time of the progress bar in milliseconds.
+ ///
+ /// The current time in milliseconds.
+ public void SetCurrentTime(double time)
+ {
+ currentTime.Text = TimeSpan.FromMilliseconds(time).ToString(@"m\:ss");
+ if (totalTimeValue == 0)
+ return;
+
+ progressBar.ResizeWidthTo((float)(time / totalTimeValue), 500, Easing.OutQuint);
+ }
+}
diff --git a/Renako.Game/Graphics/Screens/PlayablePlayfieldScreen.cs b/Renako.Game/Graphics/Screens/PlayablePlayfieldScreen.cs
index 3ea5593..85c91e9 100644
--- a/Renako.Game/Graphics/Screens/PlayablePlayfieldScreen.cs
+++ b/Renako.Game/Graphics/Screens/PlayablePlayfieldScreen.cs
@@ -12,6 +12,7 @@
using Renako.Game.Beatmaps;
using Renako.Game.Configurations;
using Renako.Game.Graphics.Containers;
+using Renako.Game.Graphics.Drawables;
using Renako.Game.Graphics.ScreenStacks;
namespace Renako.Game.Graphics.Screens;
@@ -22,8 +23,9 @@ namespace Renako.Game.Graphics.Screens;
public partial class PlayablePlayfieldScreen : PlayfieldScreen
{
private PlayfieldContainer playfieldContainer;
+ private GameplayProgressBar progressBar;
- private readonly StopwatchClock stopwatchClock = new StopwatchClock();
+ private readonly StopwatchClock playfieldClock = new StopwatchClock();
[Resolved]
private RenakoAudioManager audioManager { get; set; }
@@ -31,10 +33,13 @@ public partial class PlayablePlayfieldScreen : PlayfieldScreen
[Resolved]
private RenakoBackgroundScreenStack backgroundScreenStack { get; set; }
+ [Resolved]
+ private WorkingBeatmap workingBeatmap { get; set; }
+
[BackgroundDependencyLoader]
private void load(RenakoConfigManager configManager, RenakoBackgroundScreenStack backgroundScreenStack)
{
- AddInternal(playfieldContainer = new PlayfieldContainer(stopwatchClock)
+ AddInternal(playfieldContainer = new PlayfieldContainer(playfieldClock)
{
RelativeSizeAxes = Axes.Both
});
@@ -106,6 +111,9 @@ private void load(RenakoConfigManager configManager, RenakoBackgroundScreenStack
Margin = new MarginPadding(20)
});
+ AddInternal(progressBar = new GameplayProgressBar());
+ progressBar.SetTotalTime(workingBeatmap.BeatmapSet.TotalLength);
+
backgroundScreenStack.AdjustMaskAlpha(configManager.Get(RenakoSetting.PlayfieldBackgroundDim) / 100f);
}
@@ -117,9 +125,13 @@ protected override void LoadComplete()
audioManager.Track?.Restart();
audioManager.Track?.Seek(0);
audioManager.Track?.Start();
- stopwatchClock.Start();
- backgroundScreenStack.SeekBackgroundVideo(0f);
- backgroundScreenStack.ShowBackgroundVideo();
+ playfieldClock.Start();
+
+ if (workingBeatmap.BeatmapSet.HasVideo)
+ {
+ backgroundScreenStack.SeekBackgroundVideo(0f);
+ backgroundScreenStack.ShowBackgroundVideo();
+ }
}, 2000);
if (audioManager.Track != null)
@@ -132,6 +144,12 @@ protected override void LoadComplete()
base.LoadComplete();
}
+ protected override void Update()
+ {
+ base.Update();
+ progressBar.SetCurrentTime(playfieldClock.CurrentTime);
+ }
+
protected override bool OnKeyDown(KeyDownEvent e)
{
switch (e.Key)
diff --git a/Renako.Game/Renako.Game.csproj b/Renako.Game/Renako.Game.csproj
index 5048a95..7059016 100644
--- a/Renako.Game/Renako.Game.csproj
+++ b/Renako.Game/Renako.Game.csproj
@@ -6,6 +6,6 @@
-
+
diff --git a/Renako.Game/RenakoGame.cs b/Renako.Game/RenakoGame.cs
index 3b9df07..e5e89aa 100644
--- a/Renako.Game/RenakoGame.cs
+++ b/Renako.Game/RenakoGame.cs
@@ -23,6 +23,7 @@ public partial class RenakoGame : RenakoGameBase
private RenakoBackgroundScreenStack backgroundScreenStack;
private LogoScreenStack logoScreenStack;
private InternalBeatmapImporter internalBeatmapImporter;
+ private BeatmapCollectionReader beatmapCollectionReader;
[BackgroundDependencyLoader]
private void load()
@@ -30,6 +31,7 @@ private void load()
dependencies.CacheAs(backgroundScreenStack = new RenakoBackgroundScreenStack());
dependencies.CacheAs(mainScreenStack = new RenakoScreenStack());
dependencies.CacheAs(logoScreenStack = new LogoScreenStack());
+ dependencies.CacheAs(beatmapCollectionReader = new BeatmapCollectionReader(Host.Storage, BeatmapsCollection));
dependencies.CacheAs(logoScreenStack.LogoScreenObject);
Add(backgroundScreenStack);
Add(mainScreenStack);
@@ -55,8 +57,8 @@ protected override void LoadComplete()
LocalConfig.SetValue(RenakoSetting.FirstImport, true);
}
- beatmapCollectionReader = new BeatmapCollectionReader(Host.Storage, BeatmapsCollection);
beatmapCollectionReader.Read();
+ BeatmapsCollection.SortBeatmapSetsByID();
mainScreenStack.Push(new WarningScreen());
}
diff --git a/Renako.Game/RenakoGameBase.cs b/Renako.Game/RenakoGameBase.cs
index 994e419..3efb7e1 100644
--- a/Renako.Game/RenakoGameBase.cs
+++ b/Renako.Game/RenakoGameBase.cs
@@ -1,10 +1,12 @@
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
+using osu.Framework.Configuration;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Performance;
+using osu.Framework.Graphics.Video;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osuTK;
@@ -25,6 +27,9 @@ public partial class RenakoGameBase : osu.Framework.Game
protected override Container Content { get; }
+ [Resolved]
+ private FrameworkConfigManager frameworkConfig { get; set; }
+
protected Storage Storage { get; set; }
protected RenakoConfigManager LocalConfig { get; private set; }
@@ -39,6 +44,8 @@ public partial class RenakoGameBase : osu.Framework.Game
private Bindable fpsDisplayVisible;
+ private Bindable hardwareAcceleration;
+
protected BeatmapCollectionReader beatmapCollectionReader;
protected BeatmapsCollection BeatmapsCollection;
@@ -109,6 +116,12 @@ protected override void LoadComplete()
fpsDisplayVisible = LocalConfig.GetBindable(RenakoSetting.ShowFPSCounter);
fpsDisplayVisible.ValueChanged += visible => { FrameStatistics.Value = visible.NewValue ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; };
fpsDisplayVisible.TriggerChange();
+
+ hardwareAcceleration = LocalConfig.GetBindable(RenakoSetting.HardwareAcceleration);
+ hardwareAcceleration.ValueChanged += enabled =>
+ {
+ frameworkConfig.SetValue(FrameworkSetting.HardwareVideoDecoder, enabled.NewValue ? HardwareVideoDecoder.Any : HardwareVideoDecoder.None);
+ };
}
public override void SetHost(GameHost host)
diff --git a/Renako.iOS/Renako.iOS.csproj b/Renako.iOS/Renako.iOS.csproj
index 011db88..2614241 100644
--- a/Renako.iOS/Renako.iOS.csproj
+++ b/Renako.iOS/Renako.iOS.csproj
@@ -18,6 +18,6 @@
-
+