From 0c8107299429e72333e327a8de9ae5d81d897e2b Mon Sep 17 00:00:00 2001 From: tanglong <842690096@qq.com> Date: Sun, 12 Oct 2025 16:13:44 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=87=BA=E5=9C=88=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Wpf_AiSportsMicrospace/Common/SportOperate.cs | 99 ++-- Wpf_AiSportsMicrospace/Views/CenterHome.xaml | 1 + .../Views/CenterHome.xaml.cs | 116 +---- Wpf_AiSportsMicrospace/Views/Home.xaml.cs | 151 +----- .../Views/JumpRope/GroupJumpRope.xaml | 11 + .../Views/JumpRope/GroupJumpRope.xaml.cs | 445 ++++++++++-------- Wpf_AiSportsMicrospace/Views/Main.xaml.cs | 30 +- 7 files changed, 360 insertions(+), 493 deletions(-) diff --git a/Wpf_AiSportsMicrospace/Common/SportOperate.cs b/Wpf_AiSportsMicrospace/Common/SportOperate.cs index d340b89..3c75f9b 100644 --- a/Wpf_AiSportsMicrospace/Common/SportOperate.cs +++ b/Wpf_AiSportsMicrospace/Common/SportOperate.cs @@ -65,14 +65,12 @@ namespace Wpf_AiSportsMicrospace.Common return _webcamClient; } - public int VerifyWavingAction(Human human) { var nose = human.Keypoints.FirstOrDefault(k => k.Name == "right_ankle"); if (nose == null) return (int)WavingAction.None; - // 使用 Canvas 宽度归一化 double xNorm = nose.X / 1920; double yNorm = nose.Y / 1080; @@ -93,10 +91,16 @@ namespace Wpf_AiSportsMicrospace.Common ref _lastLeftWrist, ref _leftWristDeltaX, true); - if (leftResult != (int)WavingAction.None) return leftResult; + if (leftResult != (int)WavingAction.None) + return leftResult; } - // --- 右手挥手或举手 --- + // --- 右手举手优先判断 --- + int rightHandAction = DetectRightHandRaise(human); + if (rightHandAction != (int)WavingAction.None) + return rightHandAction; + + // --- 右手挥手,仅当未举手时判断 --- if (rightWrist != null && rightElbow != null) { int rightWaveResult = DetectHorizontalWave( @@ -105,51 +109,64 @@ namespace Wpf_AiSportsMicrospace.Common ref _lastRightWrist, ref _rightWristDeltaX, false); - if (rightWaveResult != (int)WavingAction.None) return rightWaveResult; + if (rightWaveResult != (int)WavingAction.None) + return rightWaveResult; + } - // --- 举手逻辑 --- - double verticalRise = rightElbow.Y - rightWrist.Y; // 手腕在肘上方 → 正值 - if (verticalRise >= 60) // 举手阈值 + return (int)WavingAction.None; + } + + /// + /// 检测右手举手状态 + /// + /// 人体关键点信息 + /// 返回 WavingAction:FirstHand / Raising / RaiseHand / None + public int DetectRightHandRaise(Human human) + { + var rightWrist = human.Keypoints.FirstOrDefault(k => k.Name == "right_wrist"); + var rightElbow = human.Keypoints.FirstOrDefault(k => k.Name == "right_elbow"); + + if (rightWrist == null || rightElbow == null) + return (int)WavingAction.None; + + double verticalRise = rightElbow.Y - rightWrist.Y; // 手腕在肘上方 → 正值 + const double raiseThreshold = 60; // 举手阈值,可根据实际调整 + + if (verticalRise >= raiseThreshold) + { + // 初始化计时 + if (_raiseStartTime == null) _raiseStartTime = DateTime.Now; + if (_wristStartTime == null) _wristStartTime = DateTime.Now; + + var wristDuration = DateTime.Now - _wristStartTime.Value; + + if (!_firstHandTriggered && wristDuration.TotalSeconds >= 1) { - // 初始化计时 - if (_raiseStartTime == null) - _raiseStartTime = DateTime.Now; + _firstHandTriggered = true; + return (int)WavingAction.FirstHand; // 举手开始 + } - if (_wristStartTime == null) - _wristStartTime = DateTime.Now; - - var wristDuration = DateTime.Now - _wristStartTime.Value; - - // 保持 >1 秒才触发一次 FirstHand - if (!_firstHandTriggered && wristDuration.TotalSeconds >= 1) - { - _firstHandTriggered = true; - return (int)WavingAction.FirstHand; // 举手开始,只触发一次 - } - - // 判断是否完成3秒举手 - var duration = DateTime.Now - _raiseStartTime.Value; - if (duration.TotalSeconds >= 3) - { - _raiseStartTime = null; - _wristStartTime = null; - _firstHandTriggered = false; // 重置状态 - return (int)WavingAction.RaiseHand; // 举手完成 - } - else - { - return (int)WavingAction.Raising; // 举手中 - } + var duration = DateTime.Now - _raiseStartTime.Value; + if (duration.TotalSeconds >= 3) + { + _raiseStartTime = null; + _wristStartTime = null; + _firstHandTriggered = false; // 重置状态 + return (int)WavingAction.RaiseHand; // 举手完成 } else { - // 手放下,重置计时和状态 - _raiseStartTime = null; - _wristStartTime = null; - _firstHandTriggered = false; + return (int)WavingAction.Raising; // 举手中 } } - return (int)WavingAction.None; + else + { + // 手放下,重置状态 + _raiseStartTime = null; + _wristStartTime = null; + _firstHandTriggered = false; + return (int)WavingAction.None; + } } /// diff --git a/Wpf_AiSportsMicrospace/Views/CenterHome.xaml b/Wpf_AiSportsMicrospace/Views/CenterHome.xaml index e53443c..b660f35 100644 --- a/Wpf_AiSportsMicrospace/Views/CenterHome.xaml +++ b/Wpf_AiSportsMicrospace/Views/CenterHome.xaml @@ -65,6 +65,7 @@ Source="/Resources/Img/Album/change_right.png" HorizontalAlignment="right" VerticalAlignment="Top" + MouseDown="GoNew" Width="330" Margin="0,100,0,0" /> diff --git a/Wpf_AiSportsMicrospace/Views/CenterHome.xaml.cs b/Wpf_AiSportsMicrospace/Views/CenterHome.xaml.cs index c62678d..0be02e2 100644 --- a/Wpf_AiSportsMicrospace/Views/CenterHome.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/CenterHome.xaml.cs @@ -33,12 +33,6 @@ namespace Wpf_AiSportsMicrospace.Views /// public partial class CenterHome : UserControl { - private IHumanPredictor _humanPredictor; - private IObjectDetector _objectDetector; - private HumanGraphicsRenderer _humanGraphicsRenderer; - private WebcamClient _webcamClient; - private ConcurrentQueue _frameQueue = new(); - private CancellationTokenSource _cts = new(); private SportOperate _sportOperate; private Main _mainWin => Application.Current.MainWindow as Main; @@ -59,15 +53,6 @@ namespace Wpf_AiSportsMicrospace.Views public CenterHome() { InitializeComponent(); - //_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh); - //_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiMedium); - //_objectDetector = ObjectDetectorFactory.CreateSportGoodsDetector(); - //_humanGraphicsRenderer = new HumanGraphicsRenderer(); - //_humanGraphicsRenderer.DrawLabel = false; - - //_sports = SportBase.GetSports(); - //_detectQueue = new SportDetectionQueue(); - //string imgPath = Path.Combine(projectRoot, "Resources", "Img" , _nowSelect == "test" ? "test_img" : "play_img"); // 默认选中第1张 @@ -103,27 +88,26 @@ namespace Wpf_AiSportsMicrospace.Views switch (wavingaction) { case (int)WavingAction.LeftWave: // 左手挥动 - coverFlow.SlideRight(); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); break; case (int)WavingAction.RightWave: // 右手挥动 - coverFlow.SlideLeft(); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); break; case (int)WavingAction.FirstHand: // 举手开始 - coverFlow.StartSelectedProgress(); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); break; case (int)WavingAction.Raising: // 举手中,实时更新进度 break; case (int)WavingAction.RaiseHand: // 举手完成 - _cts.Cancel(); coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; break; default: // 没有动作 → 取消进度 - coverFlow.CancelSelectedProgress(); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); break; } } @@ -162,91 +146,6 @@ namespace Wpf_AiSportsMicrospace.Views //} } - private void StartFrameProcessing() - { - Task.Run(async () => - { - while (!_cts.Token.IsCancellationRequested) - { - if (_frameQueue.TryDequeue(out var frame)) - { - try - { - ProcessFrame(frame); - } - catch (Exception ex) - { - Console.WriteLine("ProcessFrame error: " + ex.Message); - } - } - else - { - await Task.Delay(5, _cts.Token); - } - } - }, _cts.Token); - } - - - private void ProcessFrame(VideoFrame frame) - { - try - { - var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray(); - var humanResult = _humanPredictor.Predicting(buffer, frame.Number); - - var humans = humanResult?.Humans?.ToList(); - if (humans == null || humans.Count == 0) - return; - - var human = humans.FirstOrDefault(); - - if (human == null) return; - - //检测挥手动作 - var wavingaction = _sportOperate.VerifyWavingAction(human); - - switch (wavingaction) - { - case (int)WavingAction.LeftWave: // 左手挥动 - Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); - break; - - case (int)WavingAction.RightWave: // 右手挥动 - Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); - break; - - case (int)WavingAction.FirstHand: // 举手开始 - Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); - break; - - case (int)WavingAction.Raising: // 举手中,实时更新进度 - break; - - case (int)WavingAction.RaiseHand: // 举手完成 - _cts.Cancel(); - coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; - break; - - default: // 没有动作 → 取消进度 - Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); - break; - } - } - catch (Exception ex) - { - Console.WriteLine("OnFrameExtracted error: " + ex.Message); - } - } - - public void Dispose() - { - _cts.Cancel(); - _webcamClient?.StopExtract(); - _webcamClient = null; - _humanPredictor.Dispose(); - } - public void InitImg() { string projectRoot = Path.Combine(AppContext.BaseDirectory, @"..\..\.."); @@ -277,5 +176,12 @@ namespace Wpf_AiSportsMicrospace.Views var newPage = new GroupJumpRope(); mainWin?.SwitchPage(newPage, true); } + + private void GoNew(object sender, MouseButtonEventArgs e) + { + var mainWin = Application.Current.MainWindow as Main; + var newPage = new GroupJumpRope(); + mainWin?.SwitchPage(newPage, true); + } } } diff --git a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs index ce527e8..f5f7cac 100644 --- a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs @@ -90,61 +90,29 @@ namespace Wpf_AiSportsMicrospace //检测挥手动作 var wavingaction = _mainWin.SportOperate.VerifyWavingAction(human); - // 把低 8 位作为动作类型,高 8 位作为进度 - //int actionType = wavingaction & 0xFF; - //int progress = (wavingaction >> 8) & 0xFF; - - //switch (wavingaction) - //{ - // case (int)WavingAction.LeftWave: // 左手挥动 - // Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); - // break; - - // case (int)WavingAction.RightWave: // 右手挥动 - // Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); - // break; - - // case (int)WavingAction.FirstHand: // 举手开始 - // Application.Current.Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); - // break; - - // case (int)WavingAction.Raising: // 举手中,实时更新进度 - // break; - - // case (int)WavingAction.RaiseHand: // 举手完成 - // _cts.Cancel(); - // coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; - // break; - - // default: // 没有动作 → 取消进度 - // Application.Current.Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); - // break; - //} - switch (wavingaction) { case (int)WavingAction.LeftWave: // 左手挥动 - coverFlow.SlideRight(); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); break; case (int)WavingAction.RightWave: // 右手挥动 - coverFlow.SlideLeft(); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); break; case (int)WavingAction.FirstHand: // 举手开始 - coverFlow.StartSelectedProgress(); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); break; case (int)WavingAction.Raising: // 举手中,实时更新进度 break; case (int)WavingAction.RaiseHand: // 举手完成 - _cts.Cancel(); coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; break; default: // 没有动作 → 取消进度 - coverFlow.CancelSelectedProgress(); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); break; } } @@ -153,22 +121,6 @@ namespace Wpf_AiSportsMicrospace private void Window_Loaded(object sender, RoutedEventArgs e) { _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; - - //_sportOperate = new SportOperate(); - //_webcamClient = _sportOperate.CreateRTSP(); - - //_webcamClient.OnExtractFrame += frame => - //{ - // if (frame != null) - // _frameQueue.Enqueue(frame); - //}; - //_webcamClient.StartExtract(); - - //StartFrameProcessing(); - - //Utils.PlayBackgroundMusic("homeprojectselected.mp3"); - - //coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; } private void CoverFlow_ProgressCompleted(CoverFlowItem item) { @@ -197,101 +149,6 @@ namespace Wpf_AiSportsMicrospace // newWindow = new GroupJumpRope(); // 找到主窗口,切换内容到 GroupJumpRopePage } - private void StartFrameProcessing() - { - Task.Run(async () => - { - while (!_cts.Token.IsCancellationRequested) - { - if (_frameQueue.TryDequeue(out var frame)) - { - try - { - ProcessFrame(frame); - } - catch (Exception ex) - { - Console.WriteLine("ProcessFrame error: " + ex.Message); - } - } - else - { - await Task.Delay(5, _cts.Token); - } - } - }, _cts.Token); - } - - private void ProcessFrame(VideoFrame frame) - { - try - { - var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray(); - var humanResult = _humanPredictor.Predicting(buffer, frame.Number); - - var humans = humanResult?.Humans?.ToList(); - if (humans == null || humans.Count == 0) - return; - - //var human = humans - // .Where(h => - // h.Keypoints.Any(kp => kp.Name == "left_ankle" && kp.X < 1020 && kp.Y > 900 && kp.Y < 1020) && - // h.Keypoints.Any(kp => kp.Name == "right_ankle" && kp.X > 750 && kp.Y > 900 && kp.Y < 1020) - // ) - // .FirstOrDefault(); - - var human = humans.FirstOrDefault(); - - if (human == null) return; - - //检测挥手动作 - var wavingaction = _sportOperate.VerifyWavingAction(human); - - // 把低 8 位作为动作类型,高 8 位作为进度 - //int actionType = wavingaction & 0xFF; - //int progress = (wavingaction >> 8) & 0xFF; - - switch (wavingaction) - { - case (int)WavingAction.LeftWave: // 左手挥动 - Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); - break; - - case (int)WavingAction.RightWave: // 右手挥动 - Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); - break; - - case (int)WavingAction.FirstHand: // 举手开始 - Application.Current.Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); - break; - - case (int)WavingAction.Raising: // 举手中,实时更新进度 - break; - - case (int)WavingAction.RaiseHand: // 举手完成 - _cts.Cancel(); - coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; - break; - - default: // 没有动作 → 取消进度 - Application.Current.Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); - break; - } - } - catch (Exception ex) - { - Console.WriteLine("OnFrameExtracted error: " + ex.Message); - } - } - - public void Dispose() - { - _cts.Cancel(); - _webcamClient?.StopExtract(); - _webcamClient = null; - _humanPredictor.Dispose(); - } - //测试点击 private void GoNew(object sender, MouseButtonEventArgs e) { diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml index 959f92a..d705809 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml @@ -11,6 +11,17 @@ + + diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index 5c3b07f..a055d15 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -38,122 +38,276 @@ namespace Wpf_AiSportsMicrospace.Views /// public partial class GroupJumpRope : UserControl { - private IHumanPredictor _humanPredictor; - private IObjectDetector _objectDetector; - private HumanGraphicsRenderer _humanGraphicsRenderer; - private WebcamClient _webcamClient; - private ConcurrentQueue _frameQueue = new(); - private CancellationTokenSource _cts = new(); - private SportOperate _sportOperate; - private List sports = new(); private List circleTexts = new(); private double[] circlePositionsX = { 0.07, 0.21, 0.36, 0.50, 0.64, 0.78, 0.92 }; private Main _mainWin => Application.Current.MainWindow as Main; - ConfigService configService = new ConfigService(); + + private DispatcherTimer _countdownTimer; + private int _countdownValue; + private bool _isCountingDown = false; + private SportOperate sportOperate; + private List<(double XNorm, double YNorm)> circlePositions = new(); + + private bool IsGameStarted = false; public GroupJumpRope() { InitializeComponent(); - //_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiMedium); - //_objectDetector = ObjectDetectorFactory.CreateSportGoodsDetector(); - //_humanGraphicsRenderer = new HumanGraphicsRenderer(); - //_humanGraphicsRenderer.DrawLabel = false; - - Loaded += UserControl_Loaded; Unloaded += UserControl_Unloaded; + + _countdownTimer = new DispatcherTimer(); + _countdownTimer.Interval = TimeSpan.FromSeconds(1); + _countdownTimer.Tick += CountdownTimer_Tick; + + sportOperate = new SportOperate(); } private void UserControl_Loaded(object sender, RoutedEventArgs e) { - //DrawJumpRope3DPointsWithGlow(); - DrawCirclesWithText(); - _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; - - //_sportOperate = new SportOperate(); - //_webcamClient = _sportOperate.CreateRTSP(); - - //_webcamClient.OnExtractFrame += frame => - //{ - // if (frame != null) - // _frameQueue.Enqueue(frame); - //}; - //_webcamClient.StartExtract(); - - //StartFrameProcessing(); } private void UserControl_Unloaded(object sender, RoutedEventArgs e) { _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; } + private void CountdownTimer_Tick(object sender, EventArgs e) + { + if (_countdownValue > 0) + { + countdownText.Text = _countdownValue.ToString(); + _countdownValue--; + } + else + { + _countdownTimer.Stop(); + countdownText.Visibility = Visibility.Collapsed; + _isCountingDown = false; + IsGameStarted = true; // 倒计时结束,游戏正式开始 + } + } + private void StartCountdown() + { + if (_isCountingDown) return; // 避免重复启动 + _isCountingDown = true; + _countdownValue = 3; // 从3开始倒计时 + countdownText.Visibility = Visibility.Visible; + _countdownTimer.Start(); + } + + private DateTime? _waitStartTime = null; private void OnHumanFrameUpdated(object sender, List humans) { try { - UpdateCircleCounts(humans); - } - catch (Exception ex) - { - Console.WriteLine("OnFrameExtracted error: " + ex.Message); - } - - } - - private void StartFrameProcessing() - { - Task.Run(() => - { - while (!_cts.Token.IsCancellationRequested) + if (!IsGameStarted) { - if (_frameQueue.TryDequeue(out var frame)) + var human = humans.LastOrDefault(); + if (human == null) return; + + //检测挥手动作 + var wavingaction = DetectRightHandRaise(human); + + switch (wavingaction) { - ProcessFrame(frame); - } - else - { - Thread.Sleep(5); + case (int)WavingAction.FirstHand: + // 第一次举手,初始化倒计时 + StartCountdown(3); + break; + + case (int)WavingAction.Raising: + // 持续倒计时中 + UpdateCountdown(); + break; + + case (int)WavingAction.RaiseHand: + // 举手完成,倒计时结束 + FinishCountdown(); + break; + + default: + // 没检测到动作,重置倒计时显示 + countdownText.Text = "3"; + countdownText.Visibility = Visibility.Collapsed; + break; } } - }, _cts.Token); - } - - private void ProcessFrame(VideoFrame frame) - { - try - { - var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray(); - var humanResult = _humanPredictor.Predicting(buffer, frame.Number); - - var humans = humanResult?.Humans?.ToList(); - if (humans == null || humans.Count == 0) - return; - - UpdateCircleCounts(humans); + else + { + UpdateCircleCounts(humans); + } } catch (Exception ex) { Console.WriteLine("OnFrameExtracted error: " + ex.Message); } + } - public Human LocateHuman(List humans, double begin, double end, double frameWidth) + private int _currentCountdown = 3; + private DateTime _lastUpdateTime = DateTime.Now; + private void StartCountdown(int start = 3) + { + _currentCountdown = start; + countdownText.Text = _currentCountdown.ToString(); + countdownText.Visibility = Visibility.Visible; + _lastUpdateTime = DateTime.Now; + } + + private void UpdateCountdown() + { + if ((DateTime.Now - _lastUpdateTime).TotalSeconds >= 1) + { + _lastUpdateTime = DateTime.Now; + _currentCountdown--; + + if (_currentCountdown > 0) + { + countdownText.Text = _currentCountdown.ToString(); + } + else + { + FinishCountdown(); + } + } + } + + private void FinishCountdown() + { + countdownText.Text = "✔"; + countdownText.Visibility = Visibility.Collapsed; + + IsGameStarted = true; + + // 你也可以在这里触发其他动作,例如: + // 播放音效、触发事件、执行下一步逻辑 + } + + + private DateTime? _raiseStartTime; + private DateTime? _wristStartTime; + private bool _firstHandTriggered; + private int _lastCountdownSecond = 3; + private DateTime? _countdownStartTime; + public int DetectRightHandRaise(Human human) + { + var rightWrist = human.Keypoints.FirstOrDefault(k => k.Name == "right_wrist"); + var rightElbow = human.Keypoints.FirstOrDefault(k => k.Name == "right_elbow"); + + if (rightWrist == null || rightElbow == null) + return (int)WavingAction.None; + + double verticalRise = rightElbow.Y - rightWrist.Y; // 手腕高于肘部 → 正值 + const double raiseThreshold = 60; // 举手阈值,可调整 + + if (verticalRise >= raiseThreshold) + { + // 初始化 + _raiseStartTime ??= DateTime.Now; + _wristStartTime ??= DateTime.Now; + _countdownStartTime ??= DateTime.Now; + + var wristDuration = DateTime.Now - _wristStartTime.Value; + + // 第一次举手触发 + if (!_firstHandTriggered && wristDuration.TotalSeconds >= 1) + { + _firstHandTriggered = true; + _lastCountdownSecond = 3; + _countdownStartTime = DateTime.Now; + return (int)WavingAction.FirstHand; + } + + // 倒计时逻辑 + if (_firstHandTriggered) + { + var elapsed = DateTime.Now - _countdownStartTime.Value; + int currentSecond = 3 - (int)Math.Floor(elapsed.TotalSeconds); + + if (currentSecond != _lastCountdownSecond && currentSecond > 0) + { + _lastCountdownSecond = currentSecond; + // 你可以在这里触发倒计时提示(比如语音播报或 UI 更新) + Console.WriteLine($"倒计时:{currentSecond}"); + return (int)WavingAction.Raising; // 可选:倒计时状态 + } + + // 举手完成 + if (elapsed.TotalSeconds >= 3) + { + ResetRaiseState(); + return (int)WavingAction.RaiseHand; // 举手完成 + } + + return (int)WavingAction.Raising; // 举手中 + } + + return (int)WavingAction.Raising; + } + else + { + // 手放下重置状态 + ResetRaiseState(); + return (int)WavingAction.None; + } + } + + private void ResetRaiseState() + { + _raiseStartTime = null; + _wristStartTime = null; + _countdownStartTime = null; + _firstHandTriggered = false; + _lastCountdownSecond = 3; + } + + public Human LocateHuman(List humans, double begin, double end, double frameWidth, double frameHeight, int circleIndex) { if (humans == null || humans.Count == 0) return null; + double circleX = circlePositions[circleIndex].XNorm; + double circleY = circlePositions[circleIndex].YNorm; + double radiusNormX = 0.07; // X方向半径 + double radiusNormY = 0.1; // Y方向半径 + foreach (var hu in humans) { - var nose = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle"); - if (nose == null) + 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; - // 使用 Canvas 宽度归一化 - double xNorm = nose.X / frameWidth; + double xRightNorm = rightFoot.X / frameWidth; + double xLeftNorm = leftFoot.X / frameWidth; - if (xNorm >= begin && xNorm <= end) - return hu; + // 前后出圈检测(Y方向) + double yRightNorm = rightFoot.Y / frameHeight; + double yLeftNorm = leftFoot.Y / frameHeight; + + 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) + { + Application.Current.Dispatcher.BeginInvoke(() => + { + circleTexts[circleIndex].Text = "您已出圈!"; + }); + continue; + } + + // X 范围匹配 → 未出圈的人 + if (xRightNorm >= (circleX - radiusNormX) && xRightNorm <= (circleX + radiusNormX)) + { + IsGameStarted = true; + return hu; // 返回给计数逻辑 + } } return null; @@ -170,7 +324,7 @@ namespace Wpf_AiSportsMicrospace.Views double radius = 100; // 每个圆的位置:X 和 Y 都归一化 0~1 - var circlePositions = new List<(double XNorm, double YNorm)> + circlePositions = new List<(double XNorm, double YNorm)> { (0.07, 0.58), (0.21, 0.88 ), @@ -187,8 +341,11 @@ namespace Wpf_AiSportsMicrospace.Views double y = pos.YNorm * imgHeight; // 绘制发光圆 + //AddGlowEllipse(x, y, overlayCanvas); + AddGlowEllipse(x, y, overlayCanvas); + // 创建文本控件 var text = new TextBlock { @@ -199,6 +356,7 @@ namespace Wpf_AiSportsMicrospace.Views TextAlignment = TextAlignment.Center, Width = radius * 2 }; + Canvas.SetLeft(text, x - radius); Canvas.SetTop(text, y - radius - 25); overlayCanvas.Children.Add(text); @@ -209,7 +367,7 @@ namespace Wpf_AiSportsMicrospace.Views int index = circleTexts.Count - 1; sport.OnTicked += (count, times) => { - Application.Current.Dispatcher.Invoke(() => + Application.Current.Dispatcher.BeginInvoke(() => { circleTexts[index].Text = count.ToString(); }); @@ -227,131 +385,25 @@ namespace Wpf_AiSportsMicrospace.Views double begin = center - range; double end = center + range; - var human = LocateHuman(humans, begin, end, overlayCanvas.ActualWidth); + var human = LocateHuman(humans, begin, end, overlayCanvas.ActualWidth, overlayCanvas.ActualHeight, i); if (human != null) { sports[i].Pushing(human); } } } - private void AddGlowEllipse(double centerX, double centerY, double radius, Canvas canvas) - { - var ellipse = new Ellipse - { - Width = radius * 2, - Height = radius, - Stroke = Brushes.Red, - StrokeThickness = 3, - Opacity = 0.7 - }; - - Canvas.SetLeft(ellipse, centerX - radius); - Canvas.SetTop(ellipse, centerY - radius / 2); - canvas.Children.Add(ellipse); - } - - private void DrawJumpRope3DPointsWithGlow() - { - configService.LoadAllConfigs(); - - ConfigSet jumpRopeConfig; - - double imgWidth = 1920; - double imgHeight = 1080; - - if (imgWidth <= 0 || imgHeight <= 0) return; - - overlayCanvas.Children.Clear(); - overlayCanvas.Width = imgWidth; - overlayCanvas.Height = imgHeight; - - bool needSaveConfig = false; - - if (configService.ConfigDic.TryGetValue("rope-skipping", out jumpRopeConfig) || jumpRopeConfig.Points.Count == 0) - { - jumpRopeConfig = new ConfigSet { Name = "rope-skipping" }; - needSaveConfig = true; - - double yOffset = -0.10; - double frontRadius = 85; - double backRadius = 70; - - double frontY = 0.70 + yOffset; - var frontPositions = new List<(double XNorm, double YNorm)> - { - (0.24, frontY), - (0.48, frontY), - (0.72, frontY) - }; - - foreach (var pos in frontPositions) - { - double x = pos.XNorm * imgWidth; - double y = pos.YNorm * imgHeight; - - jumpRopeConfig.Points.Add(new PointConfig - { - X = x, - Y = y, - Radius = frontRadius, - XNorm = pos.XNorm, - YNorm = pos.YNorm - }); - - AddGlowEllipse(x, y, overlayCanvas); - } - - double backY = 0.88 + yOffset; - var backPositions = new List<(double XNorm, double YNorm)> - { - (0.10, backY), - (0.35, backY), - (0.60, backY), - (0.88, backY) - }; - - foreach (var pos in backPositions) - { - double x = pos.XNorm * imgWidth; - double y = pos.YNorm * imgHeight; - - jumpRopeConfig.Points.Add(new PointConfig - { - X = x, - Y = y, - Radius = backRadius, - XNorm = pos.XNorm, - YNorm = pos.YNorm - }); - - AddGlowEllipse(x, y, overlayCanvas); - } - } - else - { - foreach (var point in jumpRopeConfig.Points) - { - double x = point.XNorm * imgWidth; - double y = point.YNorm * imgHeight; - AddGlowEllipse(x, y, overlayCanvas); - } - } - - if (needSaveConfig) - { - configService.ConfigDic[jumpRopeConfig.Name] = jumpRopeConfig; - configService.SaveAllConfigs(); - } - } - /// /// 添加带渐变光的圆圈(中心红色,边缘蓝色) /// - private void AddGlowEllipse(double centerX, double centerY, Canvas canvas) + private Ellipse AddGlowEllipse(double centerX, double centerY, Canvas canvas) { double radius = 70; // 统一半径 - double flattenFactor = 0.5; // 统一扁平化比例 - double opacity = 0.8; // 统一透明度 + double flattenFactor = 0.5; // 扁平化比例 + double opacity = 0.8; // 透明度 + + // 默认颜色 + Color cColor = Color.FromArgb(220, 255, 255, 0); // 黄 + Color eColor = Color.FromArgb(180, 0, 0, 255); // 蓝 var ellipse = new Ellipse { @@ -366,10 +418,10 @@ namespace Wpf_AiSportsMicrospace.Views RadiusY = 0.5, GradientStops = new GradientStopCollection { - new GradientStop(Color.FromArgb(220, 255, 255, 0), 0.0), // 中心黄 - new GradientStop(Color.FromArgb(180, 255, 255, 0), 0.4), // 中间黄 - new GradientStop(Color.FromArgb(180, 0, 0, 255), 0.7), // 边缘蓝 - new GradientStop(Color.FromArgb(0, 0, 0, 255), 1.0) // 外部透明 + new GradientStop(cColor, 0.0), // 中心颜色 + new GradientStop(cColor, 0.4), // 内部颜色 + new GradientStop(eColor, 0.7), // 边缘颜色 + new GradientStop(Color.FromArgb(0, eColor.R, eColor.G, eColor.B), 1.0) // 外部透明 } } }; @@ -377,8 +429,17 @@ namespace Wpf_AiSportsMicrospace.Views // 定位到中心 Canvas.SetLeft(ellipse, centerX - radius); Canvas.SetTop(ellipse, centerY - (radius * flattenFactor) / 2); + canvas.Children.Add(ellipse); + return ellipse; // 返回 Ellipse 对象方便后续修改或移除 } + + public bool IsInsideCircle(double px, double py, double cx, double cy, double r) + { + double dx = px - cx; + double dy = py - cy; + return dx * dx + dy * dy <= r * r; + } } } diff --git a/Wpf_AiSportsMicrospace/Views/Main.xaml.cs b/Wpf_AiSportsMicrospace/Views/Main.xaml.cs index d20bcf7..1519121 100644 --- a/Wpf_AiSportsMicrospace/Views/Main.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/Main.xaml.cs @@ -42,25 +42,28 @@ namespace Wpf_AiSportsMicrospace.Views }; Yztob.AiSports.Common.SportAppSettingService.Set("inferences", options); - _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiMedium); + _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiLow); _humanGraphicsRenderer = new HumanGraphicsRenderer(); _humanGraphicsRenderer.DrawLabel = false; - SportOperate = new SportOperate(); - WebcamClient = SportOperate.CreateRTSP(); - // 开始抽帧线程 StartFrameProcessing(); + //WebcamClient.OnExtractFrame += this.ProcessFrame; + //WebcamClient.StartExtract();//开始抽帧 + // 默认显示首页 MainContent.Content = new Home(); } private void StartFrameProcessing() { + SportOperate = new SportOperate(); + WebcamClient = SportOperate.CreateRTSP(); + WebcamClient.OnExtractFrame += frame => { - if (frame != null) - _frameQueue.Enqueue(frame); + //if (frame != null) + _frameQueue.Enqueue(frame); }; WebcamClient.StartExtract(); @@ -76,7 +79,17 @@ namespace Wpf_AiSportsMicrospace.Views else { // 避免 CPU 占满 - Thread.Sleep(5); + //Thread.Sleep(5); + //if (frame == null) + //{ + // WebcamClient.OnExtractFrame += frame => + // { + // //if (frame != null) + // _frameQueue.Enqueue(frame); + // }; + //} + //else + Thread.Sleep(5); } } }, _cts.Token); @@ -94,10 +107,11 @@ namespace Wpf_AiSportsMicrospace.Views return; // 触发全局事件 - Application.Current.Dispatcher.Invoke(() => + Application.Current.Dispatcher.BeginInvoke(() => { HumanFrameUpdated?.Invoke(this, humans); }); + } catch (Exception ex) { From 6dacaf1494a4ff1a4584dad3e8bda567f7682ef1 Mon Sep 17 00:00:00 2001 From: tanglong <842690096@qq.com> Date: Sun, 12 Oct 2025 17:21:15 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E9=9F=B3=E4=B9=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Wpf_AiSportsMicrospace/Common/Utils.cs | 26 ++++++++--- .../Resources/Music/comeon.mp3 | Bin 0 -> 4320 bytes .../Resources/Music/countdown_3.mp3 | Bin 0 -> 10656 bytes .../Resources/Music/raisehand.mp3 | Bin 0 -> 10800 bytes .../Views/CenterHome.xaml.cs | 4 +- Wpf_AiSportsMicrospace/Views/Home.xaml.cs | 36 +-------------- .../Views/JumpRope/GroupJumpRope.xaml.cs | 43 +++--------------- .../Wpf_AiSportsMicrospace.csproj | 12 +++++ 8 files changed, 43 insertions(+), 78 deletions(-) create mode 100644 Wpf_AiSportsMicrospace/Resources/Music/comeon.mp3 create mode 100644 Wpf_AiSportsMicrospace/Resources/Music/countdown_3.mp3 create mode 100644 Wpf_AiSportsMicrospace/Resources/Music/raisehand.mp3 diff --git a/Wpf_AiSportsMicrospace/Common/Utils.cs b/Wpf_AiSportsMicrospace/Common/Utils.cs index f316c35..1dcaf0d 100644 --- a/Wpf_AiSportsMicrospace/Common/Utils.cs +++ b/Wpf_AiSportsMicrospace/Common/Utils.cs @@ -11,7 +11,8 @@ namespace Wpf_AiSportsMicrospace.Common public static class Utils { private static MediaPlayer _bgPlayer; - public static void PlayBackgroundMusic(string musicFileName) + + public static void PlayBackgroundMusic(string musicFileName, bool loop) { string projectRoot = Path.Combine(AppContext.BaseDirectory, @"..\..\.."); string musicPath = Path.Combine(projectRoot, "Resources", "Music", musicFileName); @@ -20,11 +21,6 @@ namespace Wpf_AiSportsMicrospace.Common { _bgPlayer = new MediaPlayer(); _bgPlayer.Volume = 0.5; - _bgPlayer.MediaEnded += (s, e) => - { - _bgPlayer.Position = TimeSpan.Zero; // 循环播放 - _bgPlayer.Play(); - }; } else { @@ -33,7 +29,25 @@ namespace Wpf_AiSportsMicrospace.Common _bgPlayer.Open(new Uri(musicPath, UriKind.Absolute)); _bgPlayer.Play(); + + if (loop) + { + // 循环播放 → 直接设置 MediaEnded 事件即可 + _bgPlayer.MediaEnded -= BgPlayer_MediaEnded; // 避免重复绑定 + _bgPlayer.MediaEnded += BgPlayer_MediaEnded; + } + else + { + _bgPlayer.MediaEnded -= BgPlayer_MediaEnded; + } } + + private static void BgPlayer_MediaEnded(object sender, EventArgs e) + { + _bgPlayer.Position = TimeSpan.Zero; + _bgPlayer.Play(); + } + public static void StopBackgroundMusic() { if (_bgPlayer != null) diff --git a/Wpf_AiSportsMicrospace/Resources/Music/comeon.mp3 b/Wpf_AiSportsMicrospace/Resources/Music/comeon.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4c9af9c38909dd333a9d983181bb0bb86ffa22c1 GIT binary patch literal 4320 zcmeI#XHZjF8wcP)ev$g%G+#qzVXvp%XGfn@6qFE%{q?^+zW<0mNXW zF!gv9PY8o)(z6gq3IaGJ_7)2x-QY_1Sj-79&@Joy%q&JN>l{VkS&pF-%PvCUBwgtM z1Ni#4jUwHd3!SLh8eIedW|7Zr(ax-?91;BlHT!AVII^M1C*coFB$O+}WASd={#yu* zH?4Vk|L5u0YG2_b9*CWzvL);mwe8%NUz5&MH-IRL-lb~3(vd=J$+GYdUAH);nwhZ1 zj>uo&)-=@KqLN8BH51V)GPRP|a^lcJ@64OeOAysW1c?43`=sZB`--%?c*QYJ78<(0HUzCzCW9zxh97As)J0{iWPgaP~ zJ*=~GPEhfqTs5@{vjmeQ62&O(K?wG|Wn(}YVb851jb zUmGCo=MQS7jrp%A%5=ts^%S~d92>K|wC^2R3s4i-DK*G2qkEFolKJw0eutOiWC2Ib zzK)JfRwMUPmO1kaO`mI|C|jmm&ULKgDM)*tk;QD=%&z0>j{+8!UCujAzR%`2xiHn` zc*RZm+8rn7jk`moRd)5y{ieqamKx%)Oji%L`SyiRhCQDDq++P50fkN6_X|8ldzv-2 z*rzYfoWH&0Fa+A>hEGbxzY+z(_=rNfM?*T-_z7^^p5lE=dF9IMz0k1Z^{GLZx5Av^ zGji&}t+s6&x&sEr9ptm@zy^oLH&nW5xsCLlQJR7$S*Bsk>C>aI;wQ#R82QlO^ZSdA zM5${0>8~|&^r-R8k2Hn%_fIceiJp0iG`DnCdduxCrNTN>S~te=c97Ogih0L|71Agj zc1riGTqpDra4@t~_p;=W~(%=b4!IJ? z$Y%4GFHRp|@cp$V&284xd2H9GyZlZ~f*E)%-9T!(9LzyZwu7gCvXOXzL8r7^#1pEx*CnOWj?wZDTO7 zO5dIS+!q}-eq3K~YIe$DYS8f7hh=wiH`%>6#ZGz0YvpmpZ~lWOhCQZvRdztP<4QxN z8E?ILv=L&x^*N#|R%$7H;=4yQ_Chs~dzON6lWt}50miVWTgYE+&0_QX?(dPj57yM} zZNCU^*TJSgl*&M5ia;*DYajf5Ttt#syjWSD!P9s&0nv!X8qBV_su(F5gYw;v`^T`O zS5!ie$o%nDjeWs!I~$SOx3*_y%-M2NzBiROrrK=RPM%qMtog#zsh z#f0#9Ru2rXUt)Ba|y zB;%-U>WQ06(ci*7@DGUQ+GnlTZL8@Xm{{nU2SDa9MZdrmg5mmd->K|C`<23#l-d@{r zZkNJQi+0+=1Ga@l#Z0A9sRv;<4l^z|oe4H6HMqU}^2IU-Zp5rjqVeu2aG3+I?eAAX zm;uTp9OxcSc&zmk;1X-onFVH{JF4P03>)=T67sc1^=tXXNweR0sF$Bnbjx%C6L_LY|eGpZUxA zmlYI~RKbrc83 zB|c7sfY1_td4#9$E)3lRKzVsvFANGR#SwvXwA!F3jK=YCjB|NNL%Oxwq zDOJgl@U2d;9(`MH3pe)&r}v}SUlUcZ+l%h8fm{Dh1iWD6!d?-n(2C+>8>BC{p-1pS z)(P-aZJ-hptNAka^|$fh5bMOs(l%1InWg;!sjZ9LZ~g3n%x13X95n?8dnli8wcOOI z9M63C5hgL&cA)Tu58_JSgdU4){R|13yJ$%Ct@6?bqGvU?cf*wA*4k4?q@x-7aUOsK|e k6^I-O`q2XZ>c4(=8uWaA^`}4c`1|Ug^7;ES|K|OF0ft;=rvLx| literal 0 HcmV?d00001 diff --git a/Wpf_AiSportsMicrospace/Resources/Music/countdown_3.mp3 b/Wpf_AiSportsMicrospace/Resources/Music/countdown_3.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ace3e4f1f0912609bdf789e23e2ed5cdf6a7f83c GIT binary patch literal 10656 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 literal 0 HcmV?d00001 diff --git a/Wpf_AiSportsMicrospace/Resources/Music/raisehand.mp3 b/Wpf_AiSportsMicrospace/Resources/Music/raisehand.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7d7fc93a431531bd9c3adae0b19260e0f53426a8 GIT binary patch literal 10800 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 public partial class Home : UserControl { - private IHumanPredictor _humanPredictor; - private HumanGraphicsRenderer _humanGraphicsRenderer; - private WebcamClient _webcamClient; - private ConcurrentQueue _frameQueue = new(); - private CancellationTokenSource _cts = new(); - private SportOperate _sportOperate; public static Uri loadingImage = new Uri("/Resources/Img/Album/1.gif", UriKind.Relative); - private Main _mainWin => Application.Current.MainWindow as Main; public Home() { InitializeComponent(); - //_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh); - //_humanGraphicsRenderer = new HumanGraphicsRenderer(); - //_humanGraphicsRenderer.DrawLabel = false; - - //_sports = SportBase.GetSports(); - //_detectQueue = new SportDetectionQueue(); string projectRoot = Path.Combine(AppContext.BaseDirectory, @"..\..\.."); string albumPath = Path.Combine(projectRoot, "Resources", "Img", "Album"); - // 转换为 Uri - //coverFlow.Images.Add(new Uri(Path.Combine(albumPath, "home_play.png"))); - //coverFlow.Images.Add(new Uri(Path.Combine(albumPath, "home_test.png"))); - //coverFlow.Images.Add(new Uri(Path.Combine(albumPath, "home_history.png"))); - //coverFlow.Images.Add(new Uri(Path.Combine(albumPath, "4.jpg"))); - //coverFlow.Images.Add(new Uri(Path.Combine(albumPath, "5.jpg"))); - coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(albumPath, "home_test.png")), ProgressColor1 = "#215bc7", ProgressColor2 = "#fc640e" }); coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(albumPath, "home_play.png")), ProgressColor1 = "#e73d42", ProgressColor2 = "#fd8212" }); coverFlow.Images.Add(new CoverFlowItem { ImageUri = new Uri(Path.Combine(albumPath, "home_history.png")), ProgressColor1 = "#e73d42", ProgressColor2 = "#215bc7" }); @@ -73,7 +53,7 @@ namespace Wpf_AiSportsMicrospace Loaded += Window_Loaded; Unloaded += Home_Unloaded; - //AnimationBehavior.SetSourceUri(LoadingImage, loadingImage); + Utils.PlayBackgroundMusic("homeprojectselected.mp3", true); } private void Home_Unloaded(object sender, RoutedEventArgs e) @@ -134,20 +114,6 @@ namespace Wpf_AiSportsMicrospace { Debug.WriteLine($"停止抽帧异常: {ex.Message}"); } - - // 解绑事件,防止重复触发 - //coverFlow.ProgressCompleted -= CoverFlow_ProgressCompleted; - - // 根据图片跳转新窗口 - //string uri = item.ImageUri.ToString(); - - //if (uri.EndsWith("1.jpg")) - // newWindow = new GroupJumpRope(); - //else if (uri.EndsWith("2.jpg")) - // newWindow = new GroupJumpRope(); - //else if (uri.EndsWith("3.jpg")) - // newWindow = new GroupJumpRope(); - // 找到主窗口,切换内容到 GroupJumpRopePage } //测试点击 private void GoNew(object sender, MouseButtonEventArgs e) diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index a055d15..39ca8f6 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -42,11 +42,6 @@ namespace Wpf_AiSportsMicrospace.Views private List circleTexts = new(); private double[] circlePositionsX = { 0.07, 0.21, 0.36, 0.50, 0.64, 0.78, 0.92 }; private Main _mainWin => Application.Current.MainWindow as Main; - - private DispatcherTimer _countdownTimer; - private int _countdownValue; - private bool _isCountingDown = false; - private SportOperate sportOperate; private List<(double XNorm, double YNorm)> circlePositions = new(); private bool IsGameStarted = false; @@ -56,47 +51,18 @@ namespace Wpf_AiSportsMicrospace.Views Loaded += UserControl_Loaded; Unloaded += UserControl_Unloaded; - _countdownTimer = new DispatcherTimer(); - _countdownTimer.Interval = TimeSpan.FromSeconds(1); - _countdownTimer.Tick += CountdownTimer_Tick; - - sportOperate = new SportOperate(); } private void UserControl_Loaded(object sender, RoutedEventArgs e) { DrawCirclesWithText(); _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; + + Utils.PlayBackgroundMusic("raisehand.mp3", false); } private void UserControl_Unloaded(object sender, RoutedEventArgs e) { _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; } - private void CountdownTimer_Tick(object sender, EventArgs e) - { - if (_countdownValue > 0) - { - countdownText.Text = _countdownValue.ToString(); - _countdownValue--; - } - else - { - _countdownTimer.Stop(); - countdownText.Visibility = Visibility.Collapsed; - _isCountingDown = false; - IsGameStarted = true; // 倒计时结束,游戏正式开始 - } - } - - private void StartCountdown() - { - if (_isCountingDown) return; // 避免重复启动 - - _isCountingDown = true; - _countdownValue = 3; // 从3开始倒计时 - countdownText.Visibility = Visibility.Visible; - _countdownTimer.Start(); - } - private DateTime? _waitStartTime = null; private void OnHumanFrameUpdated(object sender, List humans) { @@ -129,6 +95,7 @@ namespace Wpf_AiSportsMicrospace.Views default: // 没检测到动作,重置倒计时显示 + Utils.StopBackgroundMusic(); countdownText.Text = "3"; countdownText.Visibility = Visibility.Collapsed; break; @@ -153,6 +120,8 @@ namespace Wpf_AiSportsMicrospace.Views countdownText.Text = _currentCountdown.ToString(); countdownText.Visibility = Visibility.Visible; _lastUpdateTime = DateTime.Now; + + Utils.PlayBackgroundMusic("countdown_3.mp3", false); } private void UpdateCountdown() @@ -182,6 +151,8 @@ namespace Wpf_AiSportsMicrospace.Views // 你也可以在这里触发其他动作,例如: // 播放音效、触发事件、执行下一步逻辑 + + Utils.PlayBackgroundMusic("homeprojectselected1.mp3", true); } diff --git a/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj b/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj index ea0c921..fbb23c9 100644 --- a/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj +++ b/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj @@ -48,10 +48,13 @@ + + + @@ -210,6 +213,12 @@ Always + + Always + + + Always + Always @@ -222,6 +231,9 @@ Always + + Always + From 431d625984214abb8d14a4c9d39a01f593e9eb59 Mon Sep 17 00:00:00 2001 From: tanglong <842690096@qq.com> Date: Sun, 12 Oct 2025 17:55:34 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E9=9F=B3=E4=B9=90=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/JumpRope/GroupJumpRope.xaml.cs | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index 39ca8f6..751911d 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -44,6 +44,7 @@ namespace Wpf_AiSportsMicrospace.Views private Main _mainWin => Application.Current.MainWindow as Main; private List<(double XNorm, double YNorm)> circlePositions = new(); + private MediaPlayer _mediaPlayer = new MediaPlayer(); private bool IsGameStarted = false; public GroupJumpRope() { @@ -52,18 +53,49 @@ namespace Wpf_AiSportsMicrospace.Views Unloaded += UserControl_Unloaded; } - private void UserControl_Loaded(object sender, RoutedEventArgs e) + private async void UserControl_Loaded(object sender, RoutedEventArgs e) { DrawCirclesWithText(); - _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; - + // 播放音乐 Utils.PlayBackgroundMusic("raisehand.mp3", false); + + PlayMusic("raisehand.mp3"); } + private void UserControl_Unloaded(object sender, RoutedEventArgs e) { _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; } - private DateTime? _waitStartTime = null; + + private void PlayMusic(string musicFileName) + { + // 获取项目根目录 + string projectRoot = System.IO.Path.Combine(AppContext.BaseDirectory, @"..\..\.."); + string musicPath = System.IO.Path.Combine(projectRoot, "Resources", "Music", musicFileName); + + if (!File.Exists(musicPath)) + { + Console.WriteLine($"音乐文件不存在: {musicPath}"); + return; + } + + _mediaPlayer.Open(new Uri(musicPath, UriKind.Absolute)); + + // 监听播放完成事件 + _mediaPlayer.MediaEnded += MediaPlayer_MediaEnded; + + _mediaPlayer.Play(); + } + + private void MediaPlayer_MediaEnded(object sender, EventArgs e) + { + // 音乐播放完成后的逻辑 + Console.WriteLine("音乐播放完成!"); + + // 可在这里绑定抽帧事件 + _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; + } + private void OnHumanFrameUpdated(object sender, List humans) { try From 31a3d0af25472b8217cbf1cfa84d73a078d36c60 Mon Sep 17 00:00:00 2001 From: tanglong <842690096@qq.com> Date: Sun, 12 Oct 2025 18:08:16 +0800 Subject: [PATCH 4/5] =?UTF-8?q?60s=20=20=E5=80=92=E8=AE=A1=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/JumpRope/GroupJumpRope.xaml.cs | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index 751911d..4cd7f57 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -57,8 +57,6 @@ namespace Wpf_AiSportsMicrospace.Views { DrawCirclesWithText(); // 播放音乐 - Utils.PlayBackgroundMusic("raisehand.mp3", false); - PlayMusic("raisehand.mp3"); } @@ -174,19 +172,34 @@ namespace Wpf_AiSportsMicrospace.Views } } - private void FinishCountdown() + private async void FinishCountdown() { + // 举手完成,显示 ✔ countdownText.Text = "✔"; countdownText.Visibility = Visibility.Collapsed; - IsGameStarted = true; - - // 你也可以在这里触发其他动作,例如: - // 播放音效、触发事件、执行下一步逻辑 - + // 播放背景音乐(循环) Utils.PlayBackgroundMusic("homeprojectselected1.mp3", true); - } + // 启动60秒倒计时(独立任务) + StartGameCountdown(60); + } + private async void StartGameCountdown(int seconds) + { + countdownText.Visibility = Visibility.Visible; + + for (int i = seconds; i >= 0; i--) + { + countdownText.Text = i.ToString(); + await Task.Delay(1000); // 不阻塞主线程,计数逻辑继续执行 + } + + countdownText.Visibility = Visibility.Collapsed; + IsGameStarted = false; + + // 倒计时完成后可以触发其他逻辑,例如停止音乐 + Utils.StopBackgroundMusic(); + } private DateTime? _raiseStartTime; private DateTime? _wristStartTime; From 954225396bd937bcf8172101ffc96ad80907d1f7 Mon Sep 17 00:00:00 2001 From: tanglong <842690096@qq.com> Date: Sun, 12 Oct 2025 18:24:06 +0800 Subject: [PATCH 5/5] vvv --- Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index 4cd7f57..81ec7ed 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -321,7 +321,6 @@ namespace Wpf_AiSportsMicrospace.Views // X 范围匹配 → 未出圈的人 if (xRightNorm >= (circleX - radiusNormX) && xRightNorm <= (circleX + radiusNormX)) { - IsGameStarted = true; return hu; // 返回给计数逻辑 } }