1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 07:32:55 +08:00

Add scrolling teams container.

This commit is contained in:
smoogipooo 2017-02-27 15:02:38 +09:00
parent 25a1c7a8ad
commit 5f3e484353
7 changed files with 690 additions and 75 deletions

@ -1 +1 @@
Subproject commit 29dda31250aedb71f76d1c6b98f0bf0eebc798b2 Subproject commit 0e258afd11a68cb81773fabd49da7c8701dc3d34

View File

@ -0,0 +1,206 @@
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shaders;
using System;
using osu.Framework.Graphics.OpenGL;
using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Timing;
using osu.Framework.Graphics.Transforms;
namespace osu.Game.Screens.Tournament.Components
{
class VisualiserLine : Drawable
{
private float strokeWidth = 1;
public float StrokeWidth
{
get { return strokeWidth; }
set
{
if (strokeWidth == value)
return;
strokeWidth = value;
Invalidate(Invalidation.DrawNode, shallPropagate: false);
}
}
private float strokeHeight = 1;
public float StrokeHeight
{
get { return strokeHeight; }
set
{
if (strokeHeight == value)
return;
strokeHeight = value;
Invalidate(Invalidation.DrawNode, shallPropagate: false);
}
}
private float separation = 1;
public float Separation
{
get { return separation; }
set
{
if (separation == value)
return;
separation = value;
Invalidate(Invalidation.DrawNode, shallPropagate: false);
}
}
private Shader shader;
private VisualiserLineDrawNodeSharedData visualiserLineDrawNodeSharedData => new VisualiserLineDrawNodeSharedData();
/// <summary>
/// The period of this visualiser line, in radians.
/// </summary>
private float period;
/// <summary>
/// The period offset this line was constructed with, in radians.
/// </summary>
private float initialPeriodOffset;
/// <summary>
/// The rolling period offset (by transformation), in radians.
/// </summary>
private float internalPeriodOffset;
/// <summary>
/// The final period offset, in radians.
/// </summary>
private float periodOffset
{
get { return initialPeriodOffset + internalPeriodOffset; }
set
{
if (internalPeriodOffset == value)
return;
internalPeriodOffset = value;
Invalidate(Invalidation.DrawNode, shallPropagate: false);
}
}
/// <summary>
/// Constructs a new Visualiser Line.
/// </summary>
/// <param name="period">The period of the line, in radians.</param>
/// <param name="periodOffset">The offset to the period of the line, in radians.</param>
/// <param name="cycleTime">The time to cycle the line.</param>
public VisualiserLine(float period, float periodOffset = 0, int cycleTime = 0)
{
this.period = period;
this.initialPeriodOffset = periodOffset;
Clock = new FramedClock();
if (cycleTime > 0)
TransformFloatTo(0, period, cycleTime, EasingTypes.None, new TransformVisualiserOffset());
Loop();
}
protected override DrawNode CreateDrawNode() => new VisualiserLineDrawNode();
[BackgroundDependencyLoader]
private void load(ShaderManager shaders)
{
shader = shaders?.Load(VertexShaderDescriptor.Colour, @"DottedLine");
}
protected override void ApplyDrawNode(DrawNode node)
{
base.ApplyDrawNode(node);
VisualiserLineDrawNode vNode = node as VisualiserLineDrawNode;
vNode.Shader = shader;
vNode.Shared = visualiserLineDrawNodeSharedData;
vNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad;
vNode.Period = period;
vNode.PeriodOffset = periodOffset;
vNode.StrokeWidth = StrokeWidth;
vNode.StrokeHeight = StrokeHeight;
vNode.Separation = Separation;
}
class VisualiserLineDrawNodeSharedData
{
public QuadBatch<Vertex2D> QuadBatch = new QuadBatch<Vertex2D>(1, 1);
}
class VisualiserLineDrawNode : DrawNode
{
public Shader Shader;
public VisualiserLineDrawNodeSharedData Shared;
public Quad ScreenSpaceDrawQuad;
public float Period;
public float PeriodOffset;
public float StrokeWidth;
public float StrokeHeight;
public float Separation;
public override void Draw(Action<TexturedVertex2D> vertexAction)
{
base.Draw(vertexAction);
Shader.Bind();
Shader.GetUniform<Vector2>(@"g_Position").Value = ScreenSpaceDrawQuad.TopLeft;
Shader.GetUniform<Vector2>(@"g_Size").Value = ScreenSpaceDrawQuad.Size;
Shader.GetUniform<float>(@"g_Period").Value = Period;
Shader.GetUniform<float>(@"g_PeriodOffset").Value = PeriodOffset;
Shader.GetUniform<float>(@"g_StrokeWidth").Value = StrokeWidth;
Shader.GetUniform<float>(@"g_StrokeHeight").Value = StrokeHeight;
Shader.GetUniform<float>(@"g_Separation").Value = Separation;
Shared.QuadBatch.Add(new Vertex2D()
{
Position = ScreenSpaceDrawQuad.BottomLeft,
Colour = DrawInfo.Colour.BottomLeft.Linear
});
Shared.QuadBatch.Add(new Vertex2D()
{
Position = ScreenSpaceDrawQuad.BottomRight,
Colour = DrawInfo.Colour.BottomRight.Linear
});
Shared.QuadBatch.Add(new Vertex2D()
{
Position = ScreenSpaceDrawQuad.TopRight,
Colour = DrawInfo.Colour.TopRight.Linear
});
Shared.QuadBatch.Add(new Vertex2D()
{
Position = ScreenSpaceDrawQuad.TopLeft,
Colour = DrawInfo.Colour.TopLeft.Linear
});
Shader.Unbind();
}
}
class TransformVisualiserOffset : TransformFloat
{
public override void Apply(Drawable d)
{
base.Apply(d);
(d as VisualiserLine).periodOffset = CurrentValue;
}
}
}
}

View File

@ -1,8 +1,13 @@
using OpenTK; using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Tournament.Components;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -17,11 +22,25 @@ namespace osu.Game.Screens.Tournament
public Drawings() public Drawings()
{ {
GroupContainer gc; GroupsContainer gc;
ScrollingTeamContainer stc;
Container visualiserContainer;
Children = new[] Children = new Drawable[]
{ {
gc = new GroupContainer(8) // Visualiser
visualiserContainer = new Container()
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Size = new Vector2(1, 10),
Colour = new Color4(255, 204, 34, 255)
},
// Groups
gc = new GroupsContainer(8)
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
@ -34,9 +53,29 @@ namespace osu.Game.Screens.Tournament
Top = 35f, Top = 35f,
Bottom = 35f Bottom = 35f
} }
} },
stc = new ScrollingTeamContainer()
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Width = 0.75f
},
}; };
float offset = 0;
for (int i = 0; i < 6; i++)
{
visualiserContainer.Add(new VisualiserLine(2 * (float)Math.PI, offset, RNG.Next(10000, 12000))
{
RelativeSizeAxes = Axes.Both,
});
offset += (float)Math.PI / 6f;
}
Team t = new Team() Team t = new Team()
{ {
FullName = "Australia", FullName = "Australia",
@ -44,80 +83,18 @@ namespace osu.Game.Screens.Tournament
FlagName = "AU" FlagName = "AU"
}; };
gc.AddTeam(t); List<Team> teams = new List<Team>();
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
gc.AddTeam(t);
}
class GroupContainer : Container for (int i = 0; i < 17; i++)
{ {
private FlowContainer<Group> topGroups; gc.AddTeam(t);
private FlowContainer<Group> bottomGroups; teams.Add(t);
private List<Group> allGroups = new List<Group>();
public GroupContainer(int numGroups)
{
char nextGroupName = 'A';
Children = new[]
{
topGroups = new FlowContainer<Group>()
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(7f, 0)
},
bottomGroups = new FlowContainer<Group>()
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(7f, 0)
}
};
for (int i = 0; i < numGroups; i++)
{
Group g = new Group(nextGroupName.ToString());
allGroups.Add(g);
nextGroupName++;
if (i < (int)Math.Ceiling(numGroups / 2f))
topGroups.Add(g);
else
bottomGroups.Add(g);
}
} }
public void AddTeam(Team team) stc.AvailableTeams = teams;
{
for (int i = 0; i < allGroups.Count; i++)
{
if (allGroups[i].TeamsCount == 8)
continue;
allGroups[i].AddTeam(team); stc.StartScrolling();
break; Delay(3000).Schedule(() => stc.StopScrolling());
}
}
} }
} }
} }

View File

@ -107,12 +107,29 @@ namespace osu.Game.Screens.Tournament
} }
} }
public void RemoveTeam(Team team) public bool RemoveTeam(Team team)
{ {
if (topTeams.RemoveAll(gt => gt.Team == team) > 0) if (topTeams.RemoveAll(gt => gt.Team == team) > 0)
{
topTeamsCount--; topTeamsCount--;
return true;
}
else if (bottomTeams.RemoveAll(gt => gt.Team == team) > 0) else if (bottomTeams.RemoveAll(gt => gt.Team == team) > 0)
{
bottomTeamsCount--; bottomTeamsCount--;
return true;
}
return false;
}
public void ClearTeams()
{
topTeams.Clear();
bottomTeams.Clear();
topTeamsCount = 0;
bottomTeamsCount = 0;
} }
class GroupTeam : FlowContainer class GroupTeam : FlowContainer

View File

@ -0,0 +1,90 @@
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace osu.Game.Screens.Tournament
{
public class GroupsContainer : Container
{
private FlowContainer<Group> topGroups;
private FlowContainer<Group> bottomGroups;
private List<Group> allGroups = new List<Group>();
public GroupsContainer(int numGroups)
{
char nextGroupName = 'A';
Children = new[]
{
topGroups = new FlowContainer<Group>()
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(7f, 0)
},
bottomGroups = new FlowContainer<Group>()
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(7f, 0)
}
};
for (int i = 0; i < numGroups; i++)
{
Group g = new Group(nextGroupName.ToString());
allGroups.Add(g);
nextGroupName++;
if (i < (int)Math.Ceiling(numGroups / 2f))
topGroups.Add(g);
else
bottomGroups.Add(g);
}
}
public void AddTeam(Team team)
{
for (int i = 0; i < allGroups.Count; i++)
{
if (allGroups[i].TeamsCount == 8)
continue;
allGroups[i].AddTeam(team);
break;
}
}
public bool RemoveTeam(Team team)
{
for (int i = 0; i < allGroups.Count; i++)
{
if (allGroups[i].RemoveTeam(team))
return true;
}
return false;
}
public void ClearTeams()
{
for (int i = 0; i < allGroups.Count; i++)
{
allGroups[i].ClearTeams();
}
}
}
}

View File

@ -0,0 +1,322 @@
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using osu.Framework.Input;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Timing;
using osu.Framework.Threading;
using OpenTK.Graphics;
using osu.Framework.Graphics.Colour;
namespace osu.Game.Screens.Tournament
{
public class ScrollingTeamContainer : Container
{
public Action<ScrollingTeam> OnSelected;
public List<Team> AvailableTeams;
private Container tracker;
private float speed = 0f;
private int expiredCount = 0;
private float offset;
private float timeOffset;
private float leftPos => offset + timeOffset + expiredCount * ScrollingTeam.WIDTH;
private double lastTime;
private ScrollState _scrollState;
private ScrollState scrollState
{
get { return _scrollState; }
set
{
if (_scrollState == value)
return;
_scrollState = value;
switch (value)
{
case ScrollState.Scrolling:
idleDelegate?.Cancel();
resetSelected();
speedTo(600f, 200);
tracker.FadeOut(100);
break;
case ScrollState.Stopping:
speedTo(0f, 2000);
tracker.FadeIn(200);
Delay(2300).Schedule(() => scrollState = ScrollState.Stopped);
DelayReset();
break;
case ScrollState.Stopped:
// Find closest to center
Drawable closest = null;
foreach (var c in Children)
{
if (!(c is ScrollingTeam))
continue;
if (closest == null)
{
closest = c;
continue;
}
float offset = Math.Abs(c.Position.X + c.DrawWidth / 2f - DrawWidth / 2f);
float lastOffset = Math.Abs(closest.Position.X + closest.DrawWidth / 2f - DrawWidth / 2f);
if (offset < lastOffset)
closest = c;
}
offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f);
(closest as ScrollingTeam).Selected = true;
OnSelected?.Invoke(closest as ScrollingTeam);
idleDelegate = Delay(10000).Schedule(() => scrollState = ScrollState.Idle);
break;
case ScrollState.Idle:
resetSelected();
speedTo(40f, 200);
tracker.FadeOut(100);
break;
}
}
}
private ScheduledDelegate idleDelegate;
public ScrollingTeamContainer()
{
Masking = true;
Clock = new FramedClock();
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
tracker = new Container()
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
CornerRadius = 10f,
AutoSizeAxes = Axes.Both,
Children = new[]
{
new Box()
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
Size = new Vector2(2, 100),
ColourInfo = ColourInfo.GradientVertical(Color4.Transparent, Color4.White)
},
new Box()
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Size = new Vector2(2, 100),
ColourInfo = ColourInfo.GradientVertical(Color4.White, Color4.Transparent)
}
}
}
};
scrollState = ScrollState.Idle;
}
protected override void UpdateAfterChildren()
{
base.Update();
if ((AvailableTeams?.Count ?? 0) == 0)
return;
timeOffset -= (float)(Time.Current - lastTime) / 1000 * speed;
lastTime = Time.Current;
// Fill more than required to account for transformation + scrolling speed
while (Children.Count(c => c is ScrollingTeam) < DrawWidth * 2 / ScrollingTeam.WIDTH)
addFlags();
float pos = leftPos;
foreach (var c in Children)
{
if (!(c is ScrollingTeam))
continue;
if (c.Position.X + c.DrawWidth < 0)
{
c.ClearTransforms();
c.Expire();
expiredCount++;
}
else
c.MoveToX(pos, 100);
pos += ScrollingTeam.WIDTH;
}
}
private void addFlags()
{
for (int i = 0; i < AvailableTeams.Count; i++)
{
Add(new ScrollingTeam(AvailableTeams[i])
{
X = leftPos + DrawWidth
});
}
}
private void resetSelected()
{
foreach (var c in Children)
{
ScrollingTeam st = c as ScrollingTeam;
if (st == null)
continue;
if (st.Selected)
st.Selected = false;
RemoveTeam(st.Team);
AvailableTeams.Remove(st.Team);
}
}
public void RemoveTeam(Team team)
{
foreach (var c in Children)
{
ScrollingTeam st = c as ScrollingTeam;
if (st == null)
continue;
if (st.Team == team)
{
st.FadeOut(200);
st.Expire();
}
}
}
public void StartScrolling()
{
scrollState = ScrollState.Scrolling;
}
public void StopScrolling()
{
scrollState = ScrollState.Stopping;
}
private void speedTo(float value, double duration = 0, EasingTypes easing = EasingTypes.None)
{
DelayReset();
UpdateTransformsOfType(typeof(TransformScrollSpeed));
TransformFloatTo(speed, value, duration, easing, new TransformScrollSpeed());
}
enum ScrollState
{
Idle,
Stopping,
Stopped,
Scrolling
}
public class TransformScrollSpeed : TransformFloat
{
public override void Apply(Drawable d)
{
base.Apply(d);
(d as ScrollingTeamContainer).speed = CurrentValue;
}
}
public class ScrollingTeam : Container
{
public const float WIDTH = 58;
public const float HEIGHT = 41;
public Team Team;
private Sprite flagSprite;
private Box outline;
private bool selected;
public bool Selected
{
get { return selected; }
set
{
selected = value;
if (selected)
outline.FadeIn(100);
else
outline.FadeOut(100);
}
}
public ScrollingTeam(Team team)
{
Team = team;
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
Size = new Vector2(WIDTH, HEIGHT);
Masking = true;
CornerRadius = 8f;
Children = new Drawable[]
{
outline = new Box()
{
RelativeSizeAxes = Axes.Both,
Alpha = 0
},
flagSprite = new Sprite()
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(WIDTH, HEIGHT) - new Vector2(8)
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
flagSprite.Texture = textures.Get($@"Flags/{Team.FlagName}");
}
}
}
}

View File

@ -209,8 +209,11 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Screens\Select\BeatmapInfoWedge.cs" /> <Compile Include="Screens\Select\BeatmapInfoWedge.cs" />
<Compile Include="Screens\Select\WedgeBackground.cs" /> <Compile Include="Screens\Select\WedgeBackground.cs" />
<Compile Include="Screens\Tournament\Components\VisualiserLine.cs" />
<Compile Include="Screens\Tournament\Drawings.cs" /> <Compile Include="Screens\Tournament\Drawings.cs" />
<Compile Include="Screens\Tournament\Group.cs" /> <Compile Include="Screens\Tournament\Group.cs" />
<Compile Include="Screens\Tournament\GroupsContainer.cs" />
<Compile Include="Screens\Tournament\ScrollingTeamContainer.cs" />
<Compile Include="Screens\Tournament\Team.cs" /> <Compile Include="Screens\Tournament\Team.cs" />
<Compile Include="Screens\Tournament\Tournament.cs" /> <Compile Include="Screens\Tournament\Tournament.cs" />
<Compile Include="Users\User.cs" /> <Compile Include="Users\User.cs" />