695 lines
27 KiB
C#
695 lines
27 KiB
C#
using Dto;
|
||
using Enum;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Collections.ObjectModel;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Data;
|
||
using System.Windows.Documents;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using System.Windows.Media.Animation;
|
||
using System.Windows.Media.Imaging;
|
||
using System.Windows.Navigation;
|
||
using System.Windows.Shapes;
|
||
using System.Windows.Threading;
|
||
using Views.JumpRope;
|
||
using Wpf_AiSportsMicrospace.Common;
|
||
using Wpf_AiSportsMicrospace.Enum;
|
||
using Wpf_AiSportsMicrospace.MyUserControl;
|
||
using Wpf_AiSportsMicrospace.Views;
|
||
using Yztob.AiSports.Inferences.Things;
|
||
using Yztob.AiSports.Postures.Sports;
|
||
|
||
namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||
{
|
||
/// <summary>
|
||
/// MusicJumpRope.xaml 的交互逻辑
|
||
/// </summary>
|
||
public partial class MusicJumpRope : UserControl
|
||
{
|
||
private Main _mainWin => Application.Current.MainWindow as Main;
|
||
private MediaPlayer _mediaPlayer = new MediaPlayer();
|
||
private readonly object _updateLock = new object();
|
||
private readonly Dictionary<int, (string lastNumber, DateTime lastChangeTime, string currentState)> _jumpStatus = new Dictionary<int, (string, DateTime, string)>();
|
||
private MusicJumpRopeContext _musicJumpRopeContext;
|
||
public Dictionary<int, List<double>> _musicBeatsDic;
|
||
private GameState _currentGameState = GameState.NotStarted;
|
||
|
||
List<RankItem> RankingItemList = new();
|
||
private List<double> _musicBeats = new List<double>()
|
||
{
|
||
0.545,1.09,1.635,2.18,2.725,3.27,3.815,4.36,4.905,5.45,
|
||
5.995,6.54,7.085,7.63,8.175,8.72,9.265,9.81,10.355,10.9,
|
||
11.445,11.99,12.535,13.08,13.625,14.17,14.715,15.26,15.805,16.35,
|
||
16.895,17.44,17.985,18.53,19.075,19.62,20.165,20.71,21.255,21.8,
|
||
22.345,22.89,23.435,23.98,24.525,25.07,25.615,26.16,26.705,27.25,
|
||
27.795,28.34,28.885,29.43,29.975,30.52,31.065,31.61,32.155,32.7,
|
||
33.245,33.79,34.335,34.88,35.425,35.97,36.515,37.06,37.605,38.15,
|
||
38.695,39.24,39.785,40.33,40.875,41.42,41.965,42.51,43.055,43.6,
|
||
44.145,44.69,45.235,45.78,46.325,46.87,47.415,47.96,48.505,49.05,
|
||
49.595,50.14,50.685,51.23,51.775,52.32,52.865,53.41,53.955,54.5,
|
||
55.045,55.59,56.135,56.68,57.225,57.77,58.315,58.86,59.405,59.95,
|
||
60.495,61.04,61.585,62.13,62.675,63.22,63.765,64.31,64.855,65.4,
|
||
65.945,66.49,67.035,67.58,68.125,68.67,69.215,69.76,70.305,70.85,
|
||
71.395,71.94,72.485,73.03,73.575,74.12,74.665,75.21,75.755,76.3,
|
||
76.845,77.39,77.935,78.48,79.025,79.57,80.115,80.66,81.205,81.75,
|
||
82.295,82.84,83.385,83.93,84.475,85.02,85.565,86.11,86.655,87.2,
|
||
87.745,88.29,88.835,89.38,89.925,90.47,91.015,91.56,92.105,92.65,
|
||
93.195,93.74,94.285,94.83,95.375,95.92,96.465,97.01,97.555,98.1,
|
||
98.645,99.19,99.735,100.28,100.825,101.37,101.915,102.46,103.005,103.55,
|
||
104.095,104.64,105.185,105.73,106.275,106.82,107.365,107.91,108.455
|
||
|
||
};
|
||
|
||
private List<TextBlock> _musicBeatTextBlock = new List<TextBlock>();
|
||
|
||
// 容忍时间(节拍误差)
|
||
public double _beatTolerance = 0.15; // ±150ms
|
||
private int _totalDots = 0;
|
||
// 滚动显示的节拍点集合
|
||
public ObservableCollection<BeatItem> BeatDisplayLeft { get; set; } = new();
|
||
public ObservableCollection<BeatItem> BeatDisplayRight { get; set; } = new();
|
||
|
||
public MusicJumpRope()
|
||
{
|
||
InitializeComponent();
|
||
Loaded += UserControl_Loaded;
|
||
Unloaded += UserControl_Unloaded;
|
||
_musicJumpRopeContext = new MusicJumpRopeContext();
|
||
_musicBeatsDic = _musicJumpRopeContext.MusicBeatsDic;
|
||
|
||
_totalDots = _musicJumpRopeContext.MusicBeats["1"].Count();
|
||
|
||
// 左控件
|
||
LeftBeats.DotCount = _totalDots;
|
||
LeftBeats.MusicBeatsDic = _musicBeatsDic;
|
||
|
||
// 右控件
|
||
RightBeats.DotCount = _totalDots;
|
||
RightBeats.MusicBeatsDic = _musicBeatsDic;
|
||
}
|
||
|
||
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
DrawCirclesWithText();
|
||
// 播放音乐
|
||
PlayMusic("raisehand.mp3");
|
||
}
|
||
|
||
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
|
||
{
|
||
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
||
}
|
||
private void PlayMusic(string musicFileName)
|
||
{
|
||
// 获取项目根目录
|
||
string projectRoot = System.IO.Path.Combine(AppContext.BaseDirectory, @"..\..\..");
|
||
string musicPath = System.IO.Path.Combine(projectRoot, "Resources", "Music", musicFileName);
|
||
string imgPath = System.IO.Path.Combine(projectRoot, "Resources", "Img", "提示图.png");
|
||
|
||
if (!File.Exists(musicPath))
|
||
{
|
||
Console.WriteLine($"音乐文件不存在: {musicPath}");
|
||
return;
|
||
}
|
||
|
||
_mediaPlayer.Open(new Uri(musicPath, UriKind.Absolute));
|
||
|
||
ShowCenterTip(imgPath, TimeSpan.FromSeconds(3));
|
||
|
||
// 监听播放完成事件
|
||
_mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;
|
||
|
||
_mediaPlayer.Play();
|
||
}
|
||
private void ShowCenterTip(string imagePath, TimeSpan duration)
|
||
{
|
||
var tipImage = new Image
|
||
{
|
||
Source = new BitmapImage(new Uri(imagePath, UriKind.Absolute)),
|
||
HorizontalAlignment = HorizontalAlignment.Center,
|
||
VerticalAlignment = VerticalAlignment.Center,
|
||
Opacity = 0,
|
||
Margin = new Thickness(0, -100, 0, 0),
|
||
};
|
||
|
||
// 增加图片的大小,调整比例
|
||
tipImage.Width = 1920 * 0.9; // 宽度为 Canvas 宽度的 90%
|
||
tipImage.Height = 1080 * 0.6; // 高度为 Canvas 高度的 60%
|
||
|
||
// 将图片添加到 Overlay Canvas
|
||
userBox.Children.Add(tipImage);
|
||
|
||
// 渐变出现动画
|
||
var fadeInAnimation = new DoubleAnimation
|
||
{
|
||
From = 0,
|
||
To = 1,
|
||
Duration = TimeSpan.FromSeconds(1.5)
|
||
};
|
||
tipImage.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);
|
||
|
||
// 定时移除,并且渐变消失
|
||
Task.Delay(duration).ContinueWith(_ =>
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
// 渐变消失动画
|
||
var fadeOutAnimation = new DoubleAnimation
|
||
{
|
||
From = 1,
|
||
To = 0,
|
||
Duration = TimeSpan.FromSeconds(1.5)
|
||
};
|
||
tipImage.BeginAnimation(UIElement.OpacityProperty, fadeOutAnimation);
|
||
|
||
// 完成后移除图片
|
||
fadeOutAnimation.Completed += (s, e) =>
|
||
{
|
||
userBox.Children.Remove(tipImage);
|
||
};
|
||
});
|
||
});
|
||
}
|
||
private void MediaPlayer_MediaEnded(object sender, EventArgs e)
|
||
{
|
||
// 音乐播放完成后的逻辑
|
||
Console.WriteLine("音乐播放完成!");
|
||
|
||
// 可在这里绑定抽帧事件
|
||
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
|
||
}
|
||
private void OnHumanFrameUpdated(object sender, List<Human> humans)
|
||
{
|
||
try
|
||
{
|
||
if (humans == null || humans.Count == 0) return;
|
||
switch (_currentGameState)
|
||
{
|
||
case GameState.NotStarted: // 未开始
|
||
int rightWaving = DetectRightHandRaise(humans);
|
||
if (rightWaving >= 3)
|
||
{
|
||
switch (rightWaving)
|
||
{
|
||
case (int)WavingAction.FirstHand:
|
||
StartCountdown(3);
|
||
break;
|
||
case (int)WavingAction.Raising:
|
||
UpdateCountdown();
|
||
break;
|
||
case (int)WavingAction.RaiseHand:
|
||
FinishCountdown();
|
||
_currentGameState = GameState.Running; // 倒计时完成 → 游戏开始
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case GameState.Running: // 游戏进行中
|
||
UpdateCircleCounts(humans);
|
||
|
||
// 可选:判断游戏结束条件,将状态切换到 Finished
|
||
// if (CheckGameFinished()) _currentGameState = GameState.Finished;
|
||
break;
|
||
|
||
case GameState.Finished: // 游戏完成
|
||
int leftWaving = DetectLeftHandRaise(humans);
|
||
if (leftWaving == 5)
|
||
{
|
||
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
||
_mainWin.WebcamClient.StopExtract();
|
||
|
||
// 举左手逻辑,例如结束动画或退出
|
||
var newPage = new Home();
|
||
_mainWin?.SwitchPageWithMaskAnimation(newPage, true);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("OnFrameExtracted error: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
private int _currentCountdown = 3;
|
||
private DateTime _lastUpdateTime = DateTime.Now;
|
||
private void StartCountdown(int start = 3)
|
||
{
|
||
_currentCountdown = start;
|
||
countdownText.Text = _currentCountdown.ToString();
|
||
countdownGrid.Visibility = Visibility.Visible;
|
||
_lastUpdateTime = DateTime.Now;
|
||
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
|
||
}
|
||
|
||
private void UpdateCountdown()
|
||
{
|
||
if ((DateTime.Now - _lastUpdateTime).TotalSeconds >= 1)
|
||
{
|
||
_lastUpdateTime = DateTime.Now;
|
||
_currentCountdown--;
|
||
|
||
if (_currentCountdown > 0)
|
||
{
|
||
countdownText.Text = _currentCountdown.ToString();
|
||
}
|
||
else
|
||
{
|
||
FinishCountdown();
|
||
}
|
||
}
|
||
}
|
||
|
||
private async void FinishCountdown()
|
||
{
|
||
countdownText.Text = "GO!";
|
||
countdownGrid.Visibility = Visibility.Hidden;
|
||
// 启动60秒倒计时(独立任务)
|
||
StartGameCountdown(105);
|
||
}
|
||
|
||
private async void StartGameCountdown(int seconds)
|
||
{
|
||
//_musicCurrentTime = Utils.GetMusicTotalDuration();
|
||
|
||
countdownGrid.Visibility = Visibility.Visible;
|
||
|
||
// 播放背景音乐(循环)
|
||
Utils.PlayBackgroundMusic("1.MP3", true);
|
||
|
||
StartBeatScrollTimer();
|
||
|
||
for (int i = seconds; i >= 0; i--)
|
||
{
|
||
countdownText.Text = i.ToString();
|
||
await Task.Delay(1000);
|
||
}
|
||
|
||
// 停止音乐
|
||
Utils.StopBackgroundMusic();
|
||
// 停止实时检测和视频流
|
||
|
||
countdownGrid.Visibility = Visibility.Hidden;
|
||
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
||
_mainWin.WebcamClient.StopExtract();
|
||
|
||
// 计算排名
|
||
var rankList = _musicJumpRopeContext.UpdateRankList();
|
||
var scoreList = _musicJumpRopeContext.UpdateScoreRankList();
|
||
|
||
|
||
// 更新游戏状态
|
||
_currentGameState = GameState.Finished;
|
||
|
||
// 恢复视频流
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
|
||
_mainWin.WebcamClient.StartExtract();
|
||
|
||
// 更新 UI(排行榜或分数板)
|
||
// ShowRankingBoard(rankList);
|
||
});
|
||
}
|
||
|
||
private DateTime? _raiseStartTime;
|
||
private bool _firstHandTriggered;
|
||
private int _lastCountdownSecond = 3;
|
||
private DateTime? _countdownStartTime;
|
||
public int DetectRightHandRaise(List<Human> humans)
|
||
{
|
||
if (humans == null || humans.Count == 0)
|
||
return (int)WavingAction.None;
|
||
|
||
foreach (var human in humans)
|
||
{
|
||
if (human?.Keypoints == null)
|
||
continue;
|
||
|
||
// --- 筛选右脚踝坐标 ---
|
||
var rightAnkle = human.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
|
||
if (rightAnkle == null)
|
||
continue;
|
||
|
||
double xNorm = rightAnkle.X / 1920;
|
||
double yNorm = rightAnkle.Y / 1080;
|
||
|
||
// 仅检测中心区域内的人
|
||
if (!(xNorm >= 0.44 && xNorm <= 0.57 && yNorm >= 0.81))
|
||
continue;
|
||
|
||
// --- 获取右臂关键点 ---
|
||
var rightWrist = human.Keypoints.FirstOrDefault(k => k.Name == "right_wrist");
|
||
var rightElbow = human.Keypoints.FirstOrDefault(k => k.Name == "right_elbow");
|
||
|
||
const double raiseThreshold = 60; // 举手阈值
|
||
const double holdDuration = 1; // 持续时间(秒)
|
||
const int countdownSeconds = 3; // 倒计时长度
|
||
|
||
bool handRaised = false;
|
||
|
||
if (rightWrist != null && rightElbow != null)
|
||
{
|
||
double verticalRise = rightElbow.Y - rightWrist.Y;
|
||
handRaised = verticalRise >= raiseThreshold;
|
||
}
|
||
|
||
// ---------- 第一步:持续举手触发倒计时 ----------
|
||
if (!_firstHandTriggered)
|
||
{
|
||
if (handRaised)
|
||
{
|
||
_raiseStartTime ??= DateTime.Now;
|
||
var holdElapsed = (DateTime.Now - _raiseStartTime.Value).TotalSeconds;
|
||
if (holdElapsed >= holdDuration)
|
||
{
|
||
// 举手达到指定时间,启动倒计时
|
||
_firstHandTriggered = true;
|
||
_countdownStartTime = DateTime.Now;
|
||
_lastCountdownSecond = countdownSeconds;
|
||
return (int)WavingAction.FirstHand;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 手放下,重置举手开始时间
|
||
_raiseStartTime = DateTime.Now;
|
||
}
|
||
|
||
continue; // 本人未触发,检测下一个人
|
||
}
|
||
|
||
// ---------- 第二步:倒计时逻辑 ----------
|
||
var countdownElapsed = (DateTime.Now - _countdownStartTime.Value).TotalSeconds;
|
||
int currentSecond = countdownSeconds - (int)Math.Floor(countdownElapsed);
|
||
|
||
if (currentSecond > 0 && currentSecond != _lastCountdownSecond)
|
||
{
|
||
_lastCountdownSecond = currentSecond;
|
||
Console.WriteLine($"倒计时:{currentSecond}");
|
||
return (int)WavingAction.Raising; // 倒计时中
|
||
}
|
||
|
||
if (countdownElapsed >= countdownSeconds)
|
||
{
|
||
ResetRaiseState();
|
||
return (int)WavingAction.RaiseHand; // 举手完成
|
||
}
|
||
|
||
return (int)WavingAction.Raising;
|
||
}
|
||
|
||
// 没有任何中心区域内的人举手
|
||
return (int)WavingAction.None;
|
||
}
|
||
public int DetectLeftHandRaise(List<Human> humans)
|
||
{
|
||
if (humans == null || humans.Count == 0)
|
||
return (int)WavingAction.None;
|
||
|
||
|
||
foreach (var human in humans)
|
||
{
|
||
if (human?.Keypoints == null)
|
||
continue;
|
||
|
||
// --- 筛选右脚踝坐标 ---
|
||
var rightAnkle = human.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
|
||
if (rightAnkle == null)
|
||
continue;
|
||
double xNorm = rightAnkle.X / 1920;
|
||
double yNorm = rightAnkle.Y / 1080;
|
||
// 仅检测中心区域内的人
|
||
if (!(xNorm >= 0.44 && xNorm <= 0.57 && yNorm >= 0.81))
|
||
continue;
|
||
|
||
// 获取左手关键点
|
||
var leftWrist = human.Keypoints.FirstOrDefault(k => k.Name == "left_wrist");
|
||
var leftElbow = human.Keypoints.FirstOrDefault(k => k.Name == "left_elbow");
|
||
|
||
if (leftWrist == null || leftElbow == null)
|
||
continue;
|
||
|
||
const double raiseThreshold = 60; // 举手阈值
|
||
|
||
// 判断左手是否举起
|
||
double verticalRise = leftElbow.Y - leftWrist.Y;
|
||
if (verticalRise >= raiseThreshold)
|
||
{
|
||
return (int)WavingAction.RaiseHand; // 一旦检测到左手举起,立即返回
|
||
}
|
||
}
|
||
|
||
return (int)WavingAction.None; // 没有检测到举手
|
||
}
|
||
|
||
private void ResetRaiseState()
|
||
{
|
||
_raiseStartTime = null;
|
||
_countdownStartTime = null;
|
||
_firstHandTriggered = false;
|
||
_lastCountdownSecond = 3;
|
||
}
|
||
|
||
private void DrawCirclesWithText()
|
||
{
|
||
userBox.Children.Clear();
|
||
_musicJumpRopeContext.Sports.Clear();
|
||
_musicJumpRopeContext.UserList.Clear();
|
||
_musicJumpRopeContext.UserNumberList.Clear();
|
||
_musicJumpRopeContext.UserBeatSyncList.Clear();
|
||
_musicBeatTextBlock.Clear();
|
||
|
||
double imgWidth = userBox.ActualWidth;
|
||
double imgHeight = userBox.ActualHeight;
|
||
double radius = 100;
|
||
|
||
for (int i = 0; i < _musicJumpRopeContext.CirclePositions.Count; i++)
|
||
{
|
||
//var pos = _musicJumpRopeContext.CirclePositions[i == 0 ? 1 : 0];
|
||
|
||
var pos = _musicJumpRopeContext.CirclePositions[i];
|
||
|
||
double x = pos.XNorm * imgWidth;
|
||
double y = pos.YNorm * imgHeight;
|
||
|
||
// 绘制发光圆
|
||
var userItem = AddUserItem(x, y, i);
|
||
// 绑定运动对象
|
||
var sport = SportBase.Create("rope-skipping");
|
||
int indexCopy = i;
|
||
var currentItem = userItem;
|
||
|
||
// 订阅事件
|
||
sport.OnTicked += (count, times) =>
|
||
{
|
||
// 更新UI
|
||
userItem.NumberText = count.ToString();
|
||
// 更新数字源
|
||
_musicJumpRopeContext.UserNumberList[indexCopy] = count.ToString();
|
||
|
||
if (userItem.ImageState != "2")
|
||
userItem.ImageState = "2";
|
||
|
||
var currentTime = Utils.GetMusicCurrentTime();
|
||
int currentSecond = (int)Math.Floor(currentTime);
|
||
|
||
// 判断是否命中节拍
|
||
var beats = _musicJumpRopeContext.MusicBeats["1"];
|
||
bool hit = beats.Any(b => Math.Abs(b - currentTime) <= _beatTolerance);
|
||
if (hit)
|
||
{
|
||
_musicJumpRopeContext.UserBeatSyncList[indexCopy]++;
|
||
_musicBeatTextBlock[indexCopy].Text = $"卡点 x{_musicJumpRopeContext.UserBeatSyncList[indexCopy]}";
|
||
|
||
if (indexCopy == 0)
|
||
LeftBeats.SetSelected(currentSecond, true);
|
||
else
|
||
RightBeats.SetSelected(currentSecond, true);
|
||
|
||
//Application.Current.Dispatcher.BeginInvoke(() =>
|
||
//{
|
||
// if (indexCopy == 0)
|
||
// LeftBeats.SetSelected(currentSecond, true);
|
||
// else
|
||
// RightBeats.SetSelected(currentSecond, true);
|
||
//});
|
||
}
|
||
};
|
||
|
||
sport.Start();
|
||
_musicJumpRopeContext.Sports.Add(sport);
|
||
}
|
||
}
|
||
|
||
private void UpdateCircleCounts(List<Human> humans)
|
||
{
|
||
double radiusNormX = 0.07;
|
||
double radiusNormY = 0.14;
|
||
|
||
for (int i = 0; i < _musicJumpRopeContext.CirclePositions.Count; i++)
|
||
{
|
||
var circleX = _musicJumpRopeContext.CirclePositions[i].XNorm;
|
||
var circleY = _musicJumpRopeContext.CirclePositions[i].YNorm;
|
||
|
||
Human humanInCircle = null;
|
||
|
||
// 找圈内的人
|
||
foreach (var hu in humans)
|
||
{
|
||
var rightFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
|
||
var leftFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "left_ankle");
|
||
if (rightFoot == null || leftFoot == null)
|
||
continue;
|
||
|
||
double xRightNorm = rightFoot.X / userBox.ActualWidth;
|
||
double xLeftNorm = leftFoot.X / userBox.ActualWidth;
|
||
double yRightNorm = rightFoot.Y / userBox.ActualHeight;
|
||
double yLeftNorm = leftFoot.Y / userBox.ActualHeight;
|
||
|
||
bool outOfCircle =
|
||
xRightNorm < (circleX - radiusNormX) || xRightNorm > (circleX + radiusNormX) ||
|
||
xLeftNorm < (circleX - radiusNormX) || xLeftNorm > (circleX + radiusNormX) ||
|
||
yRightNorm < (circleY - radiusNormY) || yRightNorm > (circleY + radiusNormY) ||
|
||
yLeftNorm < (circleY - radiusNormY) || yLeftNorm > (circleY + radiusNormY);
|
||
|
||
if (!outOfCircle)
|
||
{
|
||
humanInCircle = hu;
|
||
break; // 每圈只处理一个人
|
||
}
|
||
}
|
||
|
||
// 根据是否有人和跳绳数字判断状态
|
||
bool hasHuman = humanInCircle != null;
|
||
lock (_updateLock)
|
||
{
|
||
_musicJumpRopeContext.UserList[i].ImageState = GetJumpState(i, hasHuman);
|
||
}
|
||
|
||
// 推送计数
|
||
//if (hasHuman)
|
||
_musicJumpRopeContext.Sports[i].Pushing(humanInCircle);
|
||
}
|
||
}
|
||
private string GetJumpState(int circleIndex, bool humanInCircle)
|
||
{
|
||
if (!humanInCircle)
|
||
{
|
||
// 无人 → 出圈
|
||
return "3";
|
||
}
|
||
|
||
string currentNumber = _musicJumpRopeContext.UserNumberList[circleIndex];
|
||
|
||
if (!_jumpStatus.ContainsKey(circleIndex))
|
||
{
|
||
_jumpStatus[circleIndex] = (currentNumber, DateTime.Now, "1"); // 初始状态先为停止
|
||
return "1";
|
||
}
|
||
var (lastNumber, lastChangeTime, lastState) = _jumpStatus[circleIndex];
|
||
|
||
if (currentNumber != lastNumber)
|
||
{
|
||
// 数字变化 → 跳绳中
|
||
_jumpStatus[circleIndex] = (currentNumber, DateTime.Now, "2");
|
||
return "2";
|
||
}
|
||
|
||
// 数字未变化,判断是否超过 2 秒
|
||
double elapsed = (DateTime.Now - lastChangeTime).TotalSeconds;
|
||
if (elapsed >= 0.8)
|
||
{
|
||
// 超过 2 秒未变化 → 停止
|
||
_jumpStatus[circleIndex] = (currentNumber, lastChangeTime, "1");
|
||
return "1";
|
||
}
|
||
// 维持上次状态
|
||
return lastState;
|
||
}
|
||
|
||
private SportUserItem AddUserItem(double centerX, double centerY, int index)
|
||
{
|
||
var userItem = new SportUserItem();
|
||
userItem.Width = 270;
|
||
userItem.Height = 560;
|
||
userItem.DisplayText = _musicJumpRopeContext.UseNameList[index];
|
||
userItem.VerticalAlignment = VerticalAlignment.Top;
|
||
userItem.HorizontalAlignment = HorizontalAlignment.Left;
|
||
userItem.ImageState = "1";
|
||
userItem.Margin = new Thickness(centerX + 120, centerY - 700, 0, 0);
|
||
|
||
// ----------- 创建上方 TextBlock ------------
|
||
var textBlock = new TextBlock
|
||
{
|
||
Text = "0", // 卡点个数
|
||
FontSize = 50,
|
||
FontWeight = FontWeights.Bold,
|
||
Foreground = Brushes.Red,
|
||
HorizontalAlignment = HorizontalAlignment.Left,
|
||
VerticalAlignment = VerticalAlignment.Top,
|
||
TextAlignment = TextAlignment.Center,
|
||
Width = userItem.Width // 文本宽度和 userItem 一样
|
||
};
|
||
|
||
// 设置 TextBlock 的 Margin,使其在 userItem 上方居中
|
||
double textLeft = userItem.Margin.Left;
|
||
double textTop = userItem.Margin.Top - 50; // 50 可以根据需要调整高度
|
||
textBlock.Margin = new Thickness(textLeft, textTop, 0, 0);
|
||
|
||
// 添加控件
|
||
userBox.Children.Add(userItem);
|
||
userBox.Children.Add(textBlock);
|
||
|
||
_musicJumpRopeContext.UserList.Add(userItem);
|
||
_musicJumpRopeContext.UserNumberList.Add("0");
|
||
_musicJumpRopeContext.UserBeatSyncList.Add(0);
|
||
_musicBeatTextBlock.Add(textBlock);
|
||
|
||
return userItem;
|
||
}
|
||
|
||
private DispatcherTimer _beatScrollTimer;
|
||
private double totalTime = 108.455; // 音乐总时长
|
||
private int _lastSecond = -1; // 上一次已经处理的秒数
|
||
private void StartBeatScrollTimer()
|
||
{
|
||
_beatScrollTimer = new DispatcherTimer();
|
||
_beatScrollTimer.Interval = TimeSpan.FromMilliseconds(500); // 每秒一次
|
||
|
||
int lastSecond = -1;
|
||
|
||
_beatScrollTimer.Tick += (s, e) =>
|
||
{
|
||
double currentTime = Utils.GetMusicCurrentTime();
|
||
int currentSecond = (int)Math.Floor(currentTime)+1;
|
||
|
||
if (currentSecond <= lastSecond) return;
|
||
lastSecond = currentSecond;
|
||
// 左控件
|
||
if (_musicBeatsDic.TryGetValue(currentSecond, out var leftBeats))
|
||
{
|
||
LeftBeats.SetSelected(currentSecond, true);
|
||
LeftBeats.ScrollToDotCenter(currentSecond, mirror: true);
|
||
}
|
||
|
||
// 右控件
|
||
if (_musicBeatsDic.TryGetValue(currentSecond, out var rightBeats))
|
||
{
|
||
RightBeats.SetSelected(currentSecond, true);
|
||
RightBeats.ScrollToDotCenter(currentSecond, mirror: false);
|
||
}
|
||
};
|
||
_beatScrollTimer.Start();
|
||
}
|
||
}
|
||
}
|