音乐跳绳
This commit is contained in:
parent
3d97ce0dc7
commit
4d9f5d262a
@ -215,8 +215,5 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
_lastActionTime = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,13 +4,14 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Wpf_AiSportsMicrospace.Common
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
private static MediaPlayer _bgPlayer;
|
||||
public static MediaPlayer _bgPlayer;
|
||||
|
||||
public static void PlayBackgroundMusic(string musicFileName, bool loop)
|
||||
{
|
||||
@ -21,6 +22,7 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
{
|
||||
_bgPlayer = new MediaPlayer();
|
||||
_bgPlayer.Volume = 0.5;
|
||||
_bgPlayer.MediaOpened += BgPlayer_MediaOpened;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -32,8 +34,7 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
|
||||
if (loop)
|
||||
{
|
||||
// 循环播放 → 直接设置 MediaEnded 事件即可
|
||||
_bgPlayer.MediaEnded -= BgPlayer_MediaEnded; // 避免重复绑定
|
||||
_bgPlayer.MediaEnded -= BgPlayer_MediaEnded;
|
||||
_bgPlayer.MediaEnded += BgPlayer_MediaEnded;
|
||||
}
|
||||
else
|
||||
@ -42,6 +43,17 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
}
|
||||
}
|
||||
|
||||
// 当音乐加载完毕时触发
|
||||
private static void BgPlayer_MediaOpened(object sender, EventArgs e)
|
||||
{
|
||||
var player = sender as MediaPlayer;
|
||||
if (player != null && player.NaturalDuration.HasTimeSpan)
|
||||
{
|
||||
TimeSpan duration = player.NaturalDuration.TimeSpan;
|
||||
Console.WriteLine($"音乐总时长: {duration.TotalSeconds:F2} 秒");
|
||||
}
|
||||
}
|
||||
|
||||
private static void BgPlayer_MediaEnded(object sender, EventArgs e)
|
||||
{
|
||||
_bgPlayer.Position = TimeSpan.Zero;
|
||||
@ -53,8 +65,31 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
if (_bgPlayer != null)
|
||||
{
|
||||
_bgPlayer.Stop();
|
||||
//_bgPlayer.Close(); // 如果想释放资源,可以取消注释
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 安全获取当前播放时间(秒)
|
||||
public static double GetMusicCurrentTime()
|
||||
{
|
||||
double currentTime = 0;
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (_bgPlayer != null)
|
||||
currentTime = _bgPlayer.Position.TotalSeconds;
|
||||
});
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
// 安全获取总时长(秒)
|
||||
public static int GetMusicTotalDuration()
|
||||
{
|
||||
int total = 0;
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (_bgPlayer != null && _bgPlayer.NaturalDuration.HasTimeSpan)
|
||||
total = (int)_bgPlayer.NaturalDuration.TimeSpan.TotalSeconds;
|
||||
});
|
||||
return total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,29 +15,44 @@ namespace Dto
|
||||
public List<SportUserItem> UserList { get; private set; }
|
||||
public List<string> UserNumberList { get; private set; }
|
||||
public List<string> UserScoreList { get; private set; }
|
||||
public List<string> UserBeatSyncList { get; private set; }
|
||||
public List<SportBase> Sports { get; private set; }
|
||||
public List<string> UseNameList { get; private set; } = new() { "一号位", "二号位" };
|
||||
public List<int> UserBeatSyncList = new List<int>();
|
||||
|
||||
public Dictionary<string, List<double>> MusicBeats { get; private set; } = new()
|
||||
{
|
||||
{
|
||||
"1", new List<double>
|
||||
{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
||||
51, 52, 53, 54, 55, 56, 57, 58, 59, 60
|
||||
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
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
public MusicJumpRopeContext()
|
||||
{
|
||||
CirclePositions = new List<(double XNorm, double YNorm)>
|
||||
{
|
||||
(0.21, 0.88),
|
||||
//(0.50, 0.88),
|
||||
(0.78, 0.88),
|
||||
};
|
||||
|
||||
@ -48,9 +63,9 @@ namespace Dto
|
||||
}
|
||||
|
||||
// 更新排行榜方法
|
||||
public void UpdateRankList()
|
||||
public List<RankItem> UpdateRankList()
|
||||
{
|
||||
RankList = UserNumberList
|
||||
var rankList = UserNumberList
|
||||
.Select((numStr, index) => new
|
||||
{
|
||||
Index = index,
|
||||
@ -65,6 +80,8 @@ namespace Dto
|
||||
Number = x.Number
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return rankList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Wpf_AiSportsMicrospace/Resources/Music/1.MP3
Normal file
BIN
Wpf_AiSportsMicrospace/Resources/Music/1.MP3
Normal file
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
using Dto;
|
||||
using Enum;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -13,6 +14,7 @@ using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Windows.Threading;
|
||||
using Wpf_AiSportsMicrospace.Common;
|
||||
using Wpf_AiSportsMicrospace.Enum;
|
||||
using Wpf_AiSportsMicrospace.MyUserControl;
|
||||
@ -29,10 +31,42 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
{
|
||||
private Main _mainWin => Application.Current.MainWindow as Main;
|
||||
private MediaPlayer _mediaPlayer = new MediaPlayer();
|
||||
private bool IsGameStarted = false;
|
||||
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;
|
||||
|
||||
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
|
||||
|
||||
public MusicJumpRope()
|
||||
{
|
||||
InitializeComponent();
|
||||
@ -74,14 +108,6 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
|
||||
_mediaPlayer.Play();
|
||||
}
|
||||
private void MediaPlayer_MediaEnded(object sender, EventArgs e)
|
||||
{
|
||||
// 音乐播放完成后的逻辑
|
||||
Console.WriteLine("音乐播放完成!");
|
||||
|
||||
// 可在这里绑定抽帧事件
|
||||
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
|
||||
}
|
||||
private void ShowCenterTip(string imagePath, TimeSpan duration)
|
||||
{
|
||||
var tipImage = new Image
|
||||
@ -131,84 +157,66 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
});
|
||||
});
|
||||
}
|
||||
private void DrawCirclesWithText()
|
||||
private void MediaPlayer_MediaEnded(object sender, EventArgs e)
|
||||
{
|
||||
userBox.Children.Clear();
|
||||
_musicJumpRopeContext.Sports.Clear();
|
||||
_musicJumpRopeContext.UserList.Clear();
|
||||
_musicJumpRopeContext.UserNumberList.Clear();
|
||||
// 音乐播放完成后的逻辑
|
||||
Console.WriteLine("音乐播放完成!");
|
||||
|
||||
double imgWidth = userBox.ActualWidth;
|
||||
double imgHeight = userBox.ActualHeight;
|
||||
double radius = 100;
|
||||
|
||||
for (int i = 0; i < _musicJumpRopeContext.CirclePositions.Count; i++)
|
||||
// 可在这里绑定抽帧事件
|
||||
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
|
||||
}
|
||||
private void OnHumanFrameUpdated(object sender, List<Human> humans)
|
||||
{
|
||||
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) =>
|
||||
try
|
||||
{
|
||||
// 更新UI
|
||||
userItem.NumberText = count.ToString();
|
||||
// 更新数字源
|
||||
_musicJumpRopeContext.UserNumberList[indexCopy] = count.ToString();
|
||||
// 改变状态为“跳绳中”
|
||||
if (userItem.ImageState != "2")
|
||||
userItem.ImageState = "2";
|
||||
};
|
||||
|
||||
sport.Start();
|
||||
_musicJumpRopeContext.Sports.Add(sport);
|
||||
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;
|
||||
|
||||
private SportUserItem AddUserItem(double centerX, double centerY, int index)
|
||||
case GameState.Running: // 游戏进行中
|
||||
UpdateCircleCounts(humans);
|
||||
|
||||
// 可选:判断游戏结束条件,将状态切换到 Finished
|
||||
// if (CheckGameFinished()) _currentGameState = GameState.Finished;
|
||||
break;
|
||||
|
||||
case GameState.Finished: // 游戏完成
|
||||
int leftWaving = DetectLeftHandRaise(humans);
|
||||
if (leftWaving == 5)
|
||||
{
|
||||
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);
|
||||
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
||||
_mainWin.WebcamClient.StopExtract();
|
||||
|
||||
// ----------- 创建上方 TextBlock ------------
|
||||
var textBlock = new TextBlock
|
||||
// 举左手逻辑,例如结束动画或退出
|
||||
var newPage = new Home();
|
||||
_mainWin?.SwitchPageWithMaskAnimation(newPage, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Text = "0", // 卡点个数
|
||||
FontSize = 36,
|
||||
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");
|
||||
|
||||
return userItem;
|
||||
Console.WriteLine("OnFrameExtracted error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private int _currentCountdown = 3;
|
||||
@ -216,12 +224,9 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
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);
|
||||
}
|
||||
|
||||
@ -247,16 +252,17 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
{
|
||||
countdownText.Text = "GO!";
|
||||
countdownGrid.Visibility = Visibility.Hidden;
|
||||
IsGameStarted = true;
|
||||
|
||||
// 播放背景音乐(循环)
|
||||
Utils.PlayBackgroundMusic("homeprojectselected1.mp3", true);
|
||||
|
||||
Utils.PlayBackgroundMusic("1.MP3", true);
|
||||
// 启动60秒倒计时(独立任务)
|
||||
StartGameCountdown(60);
|
||||
StartGameCountdown(108);
|
||||
}
|
||||
|
||||
private async void StartGameCountdown(int seconds)
|
||||
{
|
||||
//_musicCurrentTime = Utils.GetMusicTotalDuration();
|
||||
|
||||
countdownGrid.Visibility = Visibility.Visible;
|
||||
|
||||
for (int i = seconds; i >= 0; i--)
|
||||
@ -272,10 +278,16 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
x.ImageState = "1";
|
||||
});
|
||||
|
||||
_musicJumpRopeContext.UpdateRankList();
|
||||
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
||||
_mainWin.WebcamClient.StopExtract();
|
||||
|
||||
IsGameStarted = false;
|
||||
RankingItemList = _musicJumpRopeContext.UpdateRankList();
|
||||
//ShowRankingBoard(RankingItemList);
|
||||
Utils.StopBackgroundMusic();
|
||||
|
||||
_currentGameState = GameState.Finished;
|
||||
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
|
||||
_mainWin.WebcamClient.StartExtract();
|
||||
}
|
||||
|
||||
private DateTime? _raiseStartTime;
|
||||
@ -300,12 +312,8 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
double xNorm = rightAnkle.X / 1920;
|
||||
double yNorm = rightAnkle.Y / 1080;
|
||||
|
||||
// 左侧区域
|
||||
bool inLeft = xNorm >= 0.14 && xNorm <= 0.28 && yNorm >= 0.81;
|
||||
// 右侧区域
|
||||
bool inRight = xNorm >= 0.71 && xNorm <= 0.85 && yNorm >= 0.81;
|
||||
// 如果不在任意一个区域,就跳过
|
||||
if (!inLeft && !inRight)
|
||||
// 仅检测中心区域内的人
|
||||
if (!(xNorm >= 0.44 && xNorm <= 0.57 && yNorm >= 0.81))
|
||||
continue;
|
||||
|
||||
// --- 获取右臂关键点 ---
|
||||
@ -372,6 +380,46 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
// 没有任何中心区域内的人举手
|
||||
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()
|
||||
{
|
||||
@ -380,58 +428,68 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
_firstHandTriggered = false;
|
||||
_lastCountdownSecond = 3;
|
||||
}
|
||||
private void OnHumanFrameUpdated(object sender, List<Human> humans)
|
||||
|
||||
private void DrawCirclesWithText()
|
||||
{
|
||||
try
|
||||
userBox.Children.Clear();
|
||||
_musicJumpRopeContext.Sports.Clear();
|
||||
_musicJumpRopeContext.UserList.Clear();
|
||||
_musicJumpRopeContext.UserNumberList.Clear();
|
||||
|
||||
double imgWidth = userBox.ActualWidth;
|
||||
double imgHeight = userBox.ActualHeight;
|
||||
double radius = 100;
|
||||
|
||||
for (int i = 0; i < _musicJumpRopeContext.CirclePositions.Count; i++)
|
||||
{
|
||||
if (!IsGameStarted)
|
||||
//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) =>
|
||||
{
|
||||
if (humans == null || humans.Count == 0) return;
|
||||
// 更新UI
|
||||
userItem.NumberText = count.ToString();
|
||||
// 更新数字源
|
||||
_musicJumpRopeContext.UserNumberList[indexCopy] = count.ToString();
|
||||
|
||||
int wavingaction = 0;
|
||||
if (userItem.ImageState != "2")
|
||||
userItem.ImageState = "2";
|
||||
|
||||
wavingaction = DetectRightHandRaise(humans);
|
||||
var _currentTime = Utils.GetMusicCurrentTime();
|
||||
|
||||
if (wavingaction < 3)
|
||||
return;
|
||||
|
||||
switch (wavingaction)
|
||||
bool isOnBeat = _musicBeats.Any(bt => Math.Abs(bt - _currentTime) <= _beatTolerance);
|
||||
if (isOnBeat)
|
||||
{
|
||||
case (int)WavingAction.FirstHand:
|
||||
// 第一次举手,初始化倒计时
|
||||
StartCountdown(3);
|
||||
break;
|
||||
_musicJumpRopeContext.UserBeatSyncList[i]++;
|
||||
|
||||
case (int)WavingAction.Raising:
|
||||
// 持续倒计时中
|
||||
UpdateCountdown();
|
||||
break;
|
||||
|
||||
case (int)WavingAction.RaiseHand:
|
||||
// 举手完成,倒计时结束
|
||||
FinishCountdown();
|
||||
break;
|
||||
|
||||
default:
|
||||
// 没检测到动作,重置倒计时显示
|
||||
Utils.StopBackgroundMusic();
|
||||
countdownText.Text = "3";
|
||||
countdownGrid.Visibility = Visibility.Hidden;
|
||||
break;
|
||||
if (_musicJumpRopeContext.UserBeatSyncList[i] < count)
|
||||
{
|
||||
_musicBeatTextBlock[i].Text = $"卡点 x{_musicJumpRopeContext.UserBeatSyncList[i]}";
|
||||
}
|
||||
|
||||
//Application.Current.Dispatcher.BeginInvoke(() =>
|
||||
//{
|
||||
// _musicBeatTextBlock[i].Text = $"卡点 x{_musicJumpRopeContext.UserBeatSyncList[i]}";
|
||||
//});
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCircleCounts(humans);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("OnFrameExtracted error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sport.Start();
|
||||
_musicJumpRopeContext.Sports.Add(sport);
|
||||
}
|
||||
}
|
||||
private void UpdateCircleCounts(List<Human> humans)
|
||||
{
|
||||
double radiusNormX = 0.07;
|
||||
@ -480,6 +538,8 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
// 推送计数
|
||||
if (hasHuman)
|
||||
_musicJumpRopeContext.Sports[i].Pushing(humanInCircle);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
private string GetJumpState(int circleIndex, bool humanInCircle)
|
||||
@ -517,5 +577,46 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
|
||||
// 维持上次状态
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@
|
||||
<None Remove="Resources\Img\test_img\test_rope.png" />
|
||||
<None Remove="Resources\Img\test_img\test_situp.png" />
|
||||
<None Remove="Resources\Img\提示图.png" />
|
||||
<None Remove="Resources\Music\1.MP3" />
|
||||
<None Remove="Resources\Music\comeon.mp3" />
|
||||
<None Remove="Resources\Music\countdown_3.mp3" />
|
||||
<None Remove="Resources\Music\homeprojectselected.mp3" />
|
||||
@ -285,6 +286,9 @@
|
||||
<Resource Include="Resources\Img\提示图.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Music\1.MP3">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Music\comeon.mp3">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user