mirror of
https://github.com/ppy/osu.git
synced 2026-05-13 20:33:35 +08:00
Refactor card hand layout to be stateless
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using MessagePack;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Online.RankedPlay
|
||||
{
|
||||
@@ -21,5 +22,22 @@ namespace osu.Game.Online.RankedPlay
|
||||
|
||||
[Key(3)]
|
||||
public required bool Dragged { get; init; }
|
||||
|
||||
[Key(4)]
|
||||
public float DragX { get; init; }
|
||||
|
||||
[Key(5)]
|
||||
public float DragY { get; init; }
|
||||
|
||||
[IgnoreMember]
|
||||
public Vector2 DragPosition
|
||||
{
|
||||
get => new Vector2(DragX, DragY);
|
||||
init
|
||||
{
|
||||
DragX = value.X;
|
||||
DragY = value.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
{
|
||||
public partial class HandCard : CompositeDrawable
|
||||
{
|
||||
public float LayoutScale => CardHoveredOrDragged ? HOVER_SCALE : 1;
|
||||
|
||||
public float LayoutWidth => RankedPlayCard.SIZE.X * LayoutScale;
|
||||
|
||||
private readonly Bindable<RankedPlayCardState> state = new Bindable<RankedPlayCardState>();
|
||||
|
||||
public RankedPlayCardState State
|
||||
@@ -54,6 +50,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
|
||||
public bool CardHoveredOrDragged => CardHovered || CardDragged;
|
||||
|
||||
public Vector2 DragPosition
|
||||
{
|
||||
get => State.DragPosition;
|
||||
set => State = State with { DragPosition = value };
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private HandOfCards handOfCards { get; set; } = null!;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -25,6 +24,8 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
{
|
||||
protected const float HOVER_SCALE = 1.2f;
|
||||
|
||||
private const float card_spacing = -20;
|
||||
|
||||
public IEnumerable<HandCard> Cards => cardContainer.Children;
|
||||
|
||||
/// <summary>
|
||||
@@ -176,91 +177,132 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
if (Contracted)
|
||||
return;
|
||||
|
||||
const float spacing = -20;
|
||||
|
||||
float totalWidth = cardContainer.Sum(it => it.LayoutWidth + spacing) - spacing;
|
||||
|
||||
float x = -totalWidth / 2;
|
||||
|
||||
const int no_card_hovered = -1;
|
||||
int hoverIndex = no_card_hovered;
|
||||
|
||||
for (int i = 0; i < cardContainer.Count; i++)
|
||||
{
|
||||
// the mouse can temporarily leave the currently dragged card and hover a different card.
|
||||
// in that case the hovered card should take precedence here
|
||||
if (cardContainer[i].CardDragged)
|
||||
{
|
||||
hoverIndex = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cardContainer[i].CardHovered)
|
||||
{
|
||||
hoverIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
double delay = 0;
|
||||
|
||||
int activeCardIndex = getActiveCardIndex();
|
||||
|
||||
for (int i = 0; i < cardContainer.Count; i++)
|
||||
{
|
||||
var child = cardContainer[i];
|
||||
|
||||
x += child.LayoutWidth / 2;
|
||||
Vector2 position;
|
||||
float rotation;
|
||||
float scale;
|
||||
|
||||
float yOffset = 0;
|
||||
if (child.CardDragged)
|
||||
CalculateDraggedCardLayout(child.DragPosition, out position, out rotation, out scale);
|
||||
else
|
||||
CalculateCardLayout(i, activeCardIndex, out position, out rotation, out scale);
|
||||
|
||||
var position = new Vector2(x, MathF.Pow(MathF.Abs(x / 250), 2) * 20 - 10);
|
||||
if (Flipped)
|
||||
position *= -1;
|
||||
|
||||
if (hoverIndex != no_card_hovered && cardContainer.Children.Count > 1)
|
||||
{
|
||||
int distance = Math.Abs(i - hoverIndex);
|
||||
int direction = Math.Sign(i - hoverIndex);
|
||||
|
||||
position.X += direction switch
|
||||
{
|
||||
0 => 0,
|
||||
|
||||
// special case for the left card when there's only 2 cards
|
||||
// too much offset looks kinda odd here so it's reduced
|
||||
< 0 when cardContainer.Count == 2 => -3,
|
||||
|
||||
< 0 => -10 / MathF.Pow(distance, 3),
|
||||
|
||||
// cards right to the hovered card have a higher offset because they are partially
|
||||
// covering the cards to their left
|
||||
> 0 => 20 / MathF.Pow(distance, 2),
|
||||
};
|
||||
}
|
||||
|
||||
if (child.CardHovered)
|
||||
yOffset = -HoverYOffset;
|
||||
|
||||
float rotation = x * 0.03f;
|
||||
|
||||
float angle = MathHelper.DegreesToRadians(rotation + 90);
|
||||
|
||||
position += new Vector2(MathF.Cos(angle), MathF.Sin(angle)) * yOffset;
|
||||
|
||||
position *= Flipped ? -1 : 1;
|
||||
|
||||
float scale = child.LayoutScale;
|
||||
|
||||
ApplyLayoutToCard(child, position, rotation, scale, delay);
|
||||
|
||||
x += child.LayoutWidth / 2 + spacing;
|
||||
child.Delay(delay)
|
||||
.MoveTo(position, 300, Easing.OutExpo)
|
||||
.RotateTo(rotation, 300, Easing.OutExpo)
|
||||
.ScaleTo(scale, 400, Easing.OutElasticQuarter);
|
||||
|
||||
delay += stagger;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ApplyLayoutToCard(HandCard card, Vector2 position, float rotation, float scale, double delay)
|
||||
private int getActiveCardIndex()
|
||||
{
|
||||
card.Delay(delay)
|
||||
.MoveTo(position, 300, Easing.OutExpo)
|
||||
.RotateTo(rotation, 300, Easing.OutExpo)
|
||||
.ScaleTo(scale, 400, Easing.OutElasticQuarter);
|
||||
// the mouse can temporarily leave the dragged card, so dragged card should take precedence
|
||||
for (int i = 0; i < cardContainer.Count; i++)
|
||||
{
|
||||
if (cardContainer[i].CardDragged)
|
||||
return i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cardContainer.Count; i++)
|
||||
{
|
||||
if (cardContainer[i].CardHovered)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected void CalculateCardLayout(
|
||||
int index,
|
||||
int activeIndex,
|
||||
out Vector2 position,
|
||||
out float rotation,
|
||||
out float scale)
|
||||
{
|
||||
float x = GetCardX(index, activeIndex);
|
||||
|
||||
position = GetArcPosition(x);
|
||||
rotation = GetArcRotation(x);
|
||||
scale = index == activeIndex ? HOVER_SCALE : 1;
|
||||
|
||||
if (index == activeIndex)
|
||||
position += GetCardUpwardsDirection(rotation) * HoverYOffset;
|
||||
}
|
||||
|
||||
protected virtual void CalculateDraggedCardLayout(Vector2 dragPosition, out Vector2 position, out float rotation, out float scale)
|
||||
{
|
||||
position = dragPosition;
|
||||
rotation = 0;
|
||||
scale = HOVER_SCALE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the total width of the layout for all cards in the hand.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does not account for extra space needed for spreading the cards adjacent to the active card apart.
|
||||
/// </remarks>
|
||||
protected float TotalLayoutWidth => cardContainer.Count * (RankedPlayCard.SIZE.X + card_spacing) - card_spacing;
|
||||
|
||||
protected float GetCardX(int index, int activeIndex)
|
||||
{
|
||||
float x = -TotalLayoutWidth / 2
|
||||
+ index * (RankedPlayCard.SIZE.X + card_spacing)
|
||||
+ RankedPlayCard.SIZE.X / 2;
|
||||
|
||||
if (activeIndex < 0 || cardContainer.Count <= 1)
|
||||
return x;
|
||||
|
||||
// if a card is hovered or dragged, the adjacent cards should get spread apart
|
||||
int distance = Math.Abs(index - activeIndex);
|
||||
int direction = Math.Sign(index - activeIndex);
|
||||
|
||||
float baseOffset = RankedPlayCard.SIZE.X * 0.1f;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
// special case for the left card when there's only 2 cards
|
||||
// too much offset looks kinda odd here so it's reduced
|
||||
case -1 when cardContainer.Count == 2:
|
||||
x -= baseOffset + 3;
|
||||
break;
|
||||
|
||||
case -1:
|
||||
x -= baseOffset + 10 / MathF.Pow(distance, 2);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// cards right to the active card have a higher offset because they are partially
|
||||
// covering the cards to their left
|
||||
x += baseOffset + 20 / MathF.Pow(distance, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
protected static Vector2 GetArcPosition(float x) =>
|
||||
new Vector2(x, MathF.Pow(MathF.Abs(x / 250), 2) * 20 - 10);
|
||||
|
||||
protected static float GetArcRotation(float x) => x * 0.03f;
|
||||
|
||||
protected static Vector2 GetCardUpwardsDirection(float rotation)
|
||||
{
|
||||
float angle = MathHelper.DegreesToRadians(rotation - 90);
|
||||
|
||||
return new Vector2(MathF.Cos(angle), MathF.Sin(angle));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Online.RankedPlay;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
{
|
||||
@@ -24,5 +25,16 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
card.State = cardState;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CalculateDraggedCardLayout(Vector2 dragPosition, out Vector2 position, out float rotation, out float scale)
|
||||
{
|
||||
float maxExtent = TotalLayoutWidth / 2;
|
||||
|
||||
float x = float.Clamp(dragPosition.X, -maxExtent, maxExtent);
|
||||
|
||||
scale = HOVER_SCALE;
|
||||
rotation = GetArcRotation(x);
|
||||
position = GetArcPosition(x) + GetCardUpwardsDirection(rotation) * 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-15
@@ -88,14 +88,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
AddInternal(new HoverSounds());
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (IsDragged)
|
||||
updateDragMovement();
|
||||
}
|
||||
|
||||
protected override void OnStateChanged(ValueChangedEvent<RankedPlayCardState> state)
|
||||
{
|
||||
base.OnStateChanged(state);
|
||||
@@ -177,7 +169,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
#region Drag/Drop
|
||||
|
||||
private Vector2 dragOffset;
|
||||
private Vector2 dragPosition;
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
@@ -194,7 +185,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
dragPosition = e.MousePosition - AnchorPosition + dragOffset;
|
||||
DragPosition = e.MousePosition - AnchorPosition + dragOffset;
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
@@ -204,11 +195,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
CardDragged = false;
|
||||
}
|
||||
|
||||
private void updateDragMovement()
|
||||
{
|
||||
Position = Vector2.Lerp(dragPosition, Position, MathF.Exp(-0.03f * (float)Time.Elapsed));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Online.RankedPlay;
|
||||
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
@@ -216,13 +215,5 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand
|
||||
if (SelectionMode == HandSelectionMode.Single && !card.Selected)
|
||||
card.TriggerClick();
|
||||
}
|
||||
|
||||
protected override void ApplyLayoutToCard(HandCard card, Vector2 position, float rotation, float scale, double delay)
|
||||
{
|
||||
if (card.IsDragged)
|
||||
return;
|
||||
|
||||
base.ApplyLayoutToCard(card, position, rotation, scale, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user