修复mania皮肤,增加单列判定偏移器

This commit is contained in:
LA
2025-05-04 15:26:30 +08:00
parent 19747855df
commit c7720aad14
9 changed files with 286 additions and 139 deletions

View File

@@ -0,0 +1,233 @@
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Configuration;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning.Ez2.Ez2HUD
{
public partial class EzColumnHitErrorMeter : HitErrorMeter
{
[SettingSource("Icon Height", "Icon Height")]
public BindableNumber<float> IconHeight { get; } = new BindableNumber<float>(2)
{
MinValue = 1,
MaxValue = 20,
Precision = 1f,
};
[SettingSource("Move Height", "Move Height")]
public BindableNumber<float> MoveHeight { get; } = new BindableNumber<float>(10)
{
MinValue = 1,
MaxValue = 50,
Precision = 1f,
};
[SettingSource("Background Alpha", "Background Alpha")]
public BindableNumber<float> BackgroundAlpha { get; } = new BindableNumber<float>(0.2f)
{
MinValue = 0,
MaxValue = 1,
Precision = 0.1f,
};
private double[] floatingAverages = null!;
private Box[] judgementMarkers = null!;
private Container[] columns = null!;
private float keyCount;
private OsuConfigManager config = null!;
[Resolved]
private InputCountController controller { get; set; } = null!;
public EzColumnHitErrorMeter()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
this.config = config;
recreateComponents();
}
private void recreateComponents()
{
keyCount = controller.Triggers.Count;
floatingAverages = new double[(int)keyCount];
judgementMarkers = new Box[(int)keyCount];
InternalChild = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding(2),
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(0, 0),
Children = columns = Enumerable.Range(0, (int)keyCount).Select(index =>
{
var column = createColumn();
var marker = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Y,
Blending = BlendingParameters.Additive,
Width = (float)config.Get<double>(OsuSetting.ColumnWidth),
Height = IconHeight.Value,
Colour = Colour4.Gray,
Alpha = 0.8f
};
column.Add(marker);
judgementMarkers[index] = marker;
return column;
}).ToArray()
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
controller.Triggers.BindCollectionChanged((_, __) => recreateComponents(), true);
// 更新标识块高度
IconHeight.BindValueChanged(height =>
{
foreach (var marker in judgementMarkers)
marker.Height = height.NewValue;
}, true);
// 更新背景柱状列高度和标识块移动范围
MoveHeight.BindValueChanged(height =>
{
foreach (var column in columns)
{
var backgroundBox = column.Children.OfType<Box>().FirstOrDefault();
if (backgroundBox != null)
{
backgroundBox.Height = height.NewValue;
}
}
foreach (var marker in judgementMarkers)
{
marker.Y = Math.Clamp(marker.Y, -height.NewValue / 2, height.NewValue / 2);
// 重新计算标识块的相对位置
float currentAbsoluteY = marker.Y * height.OldValue;
float newRelativeY = currentAbsoluteY / height.NewValue;
marker.Y = newRelativeY;
// 更新标识块的移动范围
marker.MoveToY(newRelativeY, 800, Easing.OutQuint);
}
Invalidate(Invalidation.DrawSize);
}, true);
// 更新背景透明度
BackgroundAlpha.BindValueChanged(alpha =>
{
foreach (var column in columns)
{
var backgroundBox = column.Children.OfType<Box>().FirstOrDefault();
if (backgroundBox != null)
backgroundBox.Alpha = alpha.NewValue;
}
}, true);
}
private Container createColumn()
{
var backgroundBox = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Height = MoveHeight.Value,
Width = (float)config.Get<double>(OsuSetting.ColumnWidth),
Colour = Colour4.Gray,
Alpha = 0.2f
};
return new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
backgroundBox
}
};
}
protected override void OnNewJudgement(JudgementResult judgement)
{
if (!judgement.IsHit || !judgement.Type.IsScorable())
return;
int columnIndex = getColumnIndex(judgement);
if (columnIndex < 0 || columnIndex >= keyCount)
return;
floatingAverages[columnIndex] = floatingAverages[columnIndex] * 0.9 + judgement.TimeOffset * 0.1;
const int marker_move_duration = 800;
var marker = judgementMarkers[columnIndex];
float targetY = getRelativeJudgementPosition(floatingAverages[columnIndex]);
marker.Y = targetY;
marker.MoveToY(targetY, marker_move_duration, Easing.OutQuint);
marker.Colour = GetColourForHitResult(judgement.Type);
}
private float getRelativeJudgementPosition(double value)
{
float moveRange = MoveHeight.Value;
return (float)Math.Clamp((value / HitWindows.WindowFor(HitResult.Miss)) * moveRange, -moveRange, moveRange);
}
private int getColumnIndex(JudgementResult judgement)
{
if (judgement.HitObject is IHasColumn hasColumn)
return hasColumn.Column;
return -1;
}
public override void Clear()
{
foreach (var column in columns)
{
column.Clear();
}
}
}
}

View File

@@ -212,12 +212,23 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2.Ez2HUD
resetDisappearTask();
}
private bool hasTriggeredReset;
private bool shouldDisplayJudgement(AloneShowMenu aloneShowMenu, double timeOffset)
{
if (!hasTriggeredReset)
{
resetDisappearTask(); // 第一次判定时触发任务
hasTriggeredReset = true;
}
if (timeOffset == 0)
return true;
if (Math.Abs(timeOffset) < Threshold.Value)
{
return false;
}
return aloneShowMenu switch
{

View File

@@ -23,9 +23,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
{
private readonly ManiaBeatmap beatmap;
// private readonly Bindable<float> columnWidth = new Bindable<float>(50);
// private readonly Bindable<float> specialFactor = new Bindable<float>(1);
private readonly OsuConfigManager config;
// private readonly ManiaRulesetConfigManager config;
@@ -38,14 +35,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
this.config = config ?? throw new ArgumentNullException(nameof(config));
}
// [BackgroundDependencyLoader]
// private void load(ManiaRulesetConfigManager config)
// {
// // 将配置中的绑定值转换后绑定到我们自己的绑定变量上
// // 假设你的 ManiaRulesetConfigManager 提供了一个 GetBindable<double> 方法:
// columnWidth.BindTo(config.GetBindable<double>(ManiaRulesetSetting.ColumnWidth).ConvertToFloatBindable());
// }
// public ManiaEz2SkinTransformer(ISkin skin, IBeatmap beatmap)
// : base(skin)
// {
@@ -61,12 +50,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
// }
// }
// private void calculateColumnWidth(int columnIndex, StageDefinition stage)
// {
// bool isSpecialColumn = stage.IsSpecialColumn(columnIndex);
// width = (float)(46 * (isSpecialColumn ? 1.2 : 1));
// }
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
{
switch (lookup)
@@ -145,6 +128,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
keyCounter.Position = new Vector2(0, -Stage.HIT_TARGET_POSITION - stage_padding_bottom);
}
var columnHitErrorMeter = container.OfType<EzColumnHitErrorMeter>().FirstOrDefault();
if (columnHitErrorMeter != null)
{
columnHitErrorMeter.Anchor = Anchor.Centre;
columnHitErrorMeter.Origin = Anchor.Centre;
columnHitErrorMeter.Position = new Vector2(0, 20);
}
var hitErrorMeter = container.OfType<BarHitErrorMeter>().FirstOrDefault();
if (hitErrorMeter != null)
@@ -175,7 +167,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Ez2
new EzComComboCounter(),
new EzComComboCounter(),
new EzComKeyCounterDisplay(),
// new ArgonKeyCounterDisplay(),
new EzColumnHitErrorMeter(),
new BarHitErrorMeter(),
new EzComHitResultScore(),
new EzComHitTiming(),

View File

@@ -1277,12 +1277,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType<FilterControl.FilterControlTextBox>().First().Text = "nonono");
AddStep("select all", () => InputManager.Keys(PlatformAction.SelectAll));
AddStep("press ctrl-x", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.X);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddStep("press ctrl/cmd-x", () => InputManager.Keys(PlatformAction.Cut));
AddAssert("filter text cleared", () => songSelect!.FilterControl.ChildrenOfType<FilterControl.FilterControlTextBox>().First().Text, () => Is.Empty);
}

View File

@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using osu.Framework;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
@@ -190,7 +189,10 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f, 0.01f);
SetDefault(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f, 0.01f);
SetDefault(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
if (RuntimeInfo.IsMobile)
SetDefault(OsuSetting.UIScale, 1f, 0.8f, 1.1f, 0.01f);
else
SetDefault(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
SetDefault(OsuSetting.UIHoldActivationDelay, 200.0, 0.0, 500.0, 50.0);
@@ -273,10 +275,6 @@ namespace osu.Game.Configuration
public override TrackedSettings CreateTrackedSettings()
{
// these need to be assigned in normal game startup scenarios.
Debug.Assert(LookupKeyBindings != null);
Debug.Assert(LookupSkinName != null);
return new TrackedSettings
{
new TrackedSetting<bool>(OsuSetting.ShowFpsDisplay, state => new SettingDescription(
@@ -421,6 +419,7 @@ namespace osu.Game.Configuration
IncreaseFirstObjectVisibility,
ScoreDisplayMode,
SelectEzMode,
SelectManiaRulesetSubset,
ScalingGameMode,
ColumnWidth,
SpecialFactor,
@@ -469,6 +468,7 @@ namespace osu.Game.Configuration
/// The status for the current user to broadcast to other players.
/// </summary>
UserOnlineStatus,
MultiplayerRoomFilter,
HideCountryFlags,
EditorTimelineShowTimingChanges,

View File

@@ -1,76 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osuTK;
using System;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.Music
{
public partial class FilterControl : Container
{
public Action<FilterCriteria> FilterChanged;
public readonly FilterTextBox Search;
private readonly NowPlayingCollectionDropdown collectionDropdown;
public FilterControl()
{
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
Search = new FilterTextBox
{
RelativeSizeAxes = Axes.X,
Height = 40,
},
collectionDropdown = new NowPlayingCollectionDropdown { RelativeSizeAxes = Axes.X }
},
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Search.Current.BindValueChanged(_ => updateCriteria());
collectionDropdown.Current.BindValueChanged(_ => updateCriteria(), true);
}
private void updateCriteria() => FilterChanged?.Invoke(createCriteria());
private FilterCriteria createCriteria() => new FilterCriteria
{
SearchText = Search.Current.Value,
Collection = collectionDropdown.Current.Value?.Collection
};
public partial class FilterTextBox : BasicSearchTextBox
{
protected override bool AllowCommit => true;
[BackgroundDependencyLoader]
private void load()
{
Masking = true;
CornerRadius = 5;
BackgroundUnfocused = OsuColour.Gray(0.06f);
BackgroundFocused = OsuColour.Gray(0.12f);
}
}
}
}

View File

@@ -1,25 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using JetBrains.Annotations;
using osu.Game.Collections;
using osu.Game.Database;
namespace osu.Game.Overlays.Music
{
public class FilterCriteria
{
/// <summary>
/// The search text.
/// </summary>
public string SearchText;
/// <summary>
/// The collection to filter beatmaps from.
/// </summary>
[CanBeNull]
public Live<BeatmapCollection> Collection;
}
}

View File

@@ -22,7 +22,5 @@ namespace osu.Game.Rulesets.Mods
new DifficultyRange(HitResult.Meh, 151, 136, 121),
new DifficultyRange(HitResult.Miss, 188, 173, 158),
};
}
}

View File

@@ -66,17 +66,17 @@ namespace osu.Game.Storyboards.Drawables
Size = new Vector2(640, 480);
bool onlyHasVideoElements = Storyboard.Layers.SelectMany(l => l.Elements).All(e => e is StoryboardVideo);
// 动态计算视频的宽高比
bool hasVideoElement = Storyboard.Layers.SelectMany(l => l.Elements).All(e => e is StoryboardVideo);
// 如果只有视频元素,动态调整宽高
if (onlyHasVideoElements)
if (hasVideoElement)
{
Size = Vector2.One; // 填满窗口
FillMode = FillMode.Fit; // 保持比例
}
else
{
Height = Width * (storyboard.Beatmap.WidescreenStoryboard ? 9 / 16f : 3 / 4f);
Width = Height * (storyboard.Beatmap.WidescreenStoryboard ? 16 / 9f : 4 / 3f);
}
Anchor = Anchor.Centre;
@@ -117,14 +117,33 @@ namespace osu.Game.Storyboards.Drawables
{
base.LoadComplete();
// 动态计算视频的宽高比
if (Storyboard.Layers.SelectMany(l => l.Elements).FirstOrDefault() is StoryboardVideo videoElement)
{
if (videoElement.CreateDrawable() is DrawableStoryboardVideo drawableVideo)
{
Schedule(() =>
{
FillAspectRatio = drawableVideo.DrawSize.X / drawableVideo.DrawSize.Y;
if (Parent != null)
{
float windowAspectRatio = Parent.DrawWidth / Parent.DrawHeight;
float videoAspectRatio = drawableVideo.DrawSize.X / drawableVideo.DrawSize.Y;
if (windowAspectRatio > videoAspectRatio)
{
// 窗口更高,以宽度为基准调整高度
Width = Parent.DrawWidth;
Height = Width / videoAspectRatio;
}
else
{
// 窗口更宽,以高度为基准调整宽度
Height = Parent.DrawHeight;
Width = Height * videoAspectRatio;
}
}
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
});
}
}