开合跳
This commit is contained in:
parent
9ab4e6861a
commit
c14bdaf8d7
@ -1,34 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Yztob.AiSports.Postures.Sports;
|
||||
using Yztob.AiSports.Postures.Things;
|
||||
|
||||
namespace Wpf_AiSportsMicrospace
|
||||
{
|
||||
public class Optimizes
|
||||
{
|
||||
/// <summary>
|
||||
/// 执行运动微调
|
||||
/// </summary>
|
||||
/// <param name="sport">要微调的运动</param>
|
||||
public static void Optimizeing(SportBase sport)
|
||||
{
|
||||
if (sport == null)
|
||||
throw new ArgumentNullException(nameof(sport));
|
||||
|
||||
//获取运动的检测规则,如果使用的是规则检测时提供
|
||||
Console.WriteLine(JsonConvert.SerializeObject(sport.Rules));
|
||||
//获取运动的检测动作样本姿态,如果使用的是相似度时提供
|
||||
Console.WriteLine(JsonConvert.SerializeObject(sport.Samples));
|
||||
|
||||
switch (sport.Key)
|
||||
{
|
||||
case "squat":
|
||||
var loggicRule = sport.Rules["squat"] as CalcLogicAndRule;
|
||||
//删除手部姿态检查规则
|
||||
loggicRule.Rules.RemoveAt(2);
|
||||
loggicRule.Rules.RemoveAt(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,6 +20,7 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Threading;
|
||||
using Views.JumpingJack;
|
||||
using Views.JumpLong;
|
||||
using Views.JumpRope;
|
||||
using Views.SquatFu;
|
||||
@ -169,7 +170,7 @@ namespace Wpf_AiSportsMicrospace
|
||||
//var newPage = new GroupJumpRope();
|
||||
//mainWin?.SwitchPageWithMaskAnimation(newPage, true);
|
||||
|
||||
var newPage = new WhackAMole();
|
||||
var newPage = new BambooDragonfly();
|
||||
mainWin?.SwitchPageWithMaskAnimation(newPage, true);
|
||||
}
|
||||
else if (coverFlow.SelectedIndex == 1)
|
||||
|
||||
@ -1,12 +1,162 @@
|
||||
<UserControl x:Class="Views.JumpingJack.BambooDragonfly"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Views.JumpingJack"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
|
||||
xmlns:gif="http://wpfanimatedgif.codeplex.com"
|
||||
mc:Ignorable="d" Height="1080" Width="1920" Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded">
|
||||
<Grid>
|
||||
|
||||
<Grid.Background>
|
||||
<ImageBrush ImageSource="/Resources/Img/test_img/test_home_bg.png" Stretch="UniformToFill"/>
|
||||
</Grid.Background>
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/title.png"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Width="615"
|
||||
Margin="0,0,0,0"
|
||||
/>
|
||||
<Grid Height="1080" Width="1920" x:Name="userBox"/>
|
||||
|
||||
<Grid Width="180" Height="90" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="00,20,40,0" Visibility="Hidden" x:Name="countdownGrid">
|
||||
<Border Background="#005fff" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="30" />
|
||||
<TextBlock x:Name="countdownText"
|
||||
Text="60"
|
||||
FontSize="80"
|
||||
FontWeight="Bold"
|
||||
Foreground="#fff"
|
||||
FontStyle="Italic"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,10,0,0"
|
||||
TextAlignment="Center"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<!--排行榜-->
|
||||
<Grid x:Name="RankingGrid" Margin="0,0,0,0" Height="1080" Width="1920" Visibility="Hidden">
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/finish_img/finish_bg.png"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Width="1920"
|
||||
Height="1080"
|
||||
/>
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/finish_img/1.png"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Width="120"
|
||||
Height="92"
|
||||
Margin="0,85"
|
||||
/>
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/finish_img/2.png"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Width="120"
|
||||
Height="92"
|
||||
Margin="451,134,0,0"
|
||||
/>
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/finish_img/3.png"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Width="120"
|
||||
Height="92"
|
||||
Margin="0,149,451,0"
|
||||
/>
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/finish_img/top.png"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
Width="1346"
|
||||
Height="163"
|
||||
Margin="0,168"
|
||||
/>
|
||||
|
||||
<Grid Width="1346" Height="520" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,331,0,0" x:Name="ScoreGrid">
|
||||
<Border Background="#FEDE50" CornerRadius="0,0,20,20"/>
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Top" Width="1344" Height="71">
|
||||
<Border Background="#f7f7f7"/>
|
||||
<TextBlock Text="排名" FontSize="24" FontWeight="Bold" Foreground="#999999" Margin="150 , 0" HorizontalAlignment="Left" VerticalAlignment="Center" TextAlignment="Center" Width="200"/>
|
||||
<TextBlock Text="位置" FontSize="24" FontWeight="Bold" Foreground="#999999" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="成绩" FontSize="24" FontWeight="Bold" Foreground="#999999" Margin="0 ,0,150, 0" HorizontalAlignment="Right" VerticalAlignment="Center" TextAlignment="Center" Width="200"/>
|
||||
</Grid>
|
||||
|
||||
<!--<Grid HorizontalAlignment="Center" Margin="0,57" VerticalAlignment="Top" Width="1344" Height="71">
|
||||
<Border Background="#fff"/>
|
||||
<TextBlock Text="1" FontSize="24" FontWeight="Bold" Foreground="#999999" Margin="150 , 0" HorizontalAlignment="Left" VerticalAlignment="Center" TextAlignment="Center" Width="200"/>
|
||||
<TextBlock Text="一号位" FontSize="24" FontWeight="Bold" Foreground="#999999" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="100" FontSize="24" FontWeight="Bold" Foreground="#999999" Margin="0 ,0,150, 0" HorizontalAlignment="Right" VerticalAlignment="Center" TextAlignment="Center" Width="200"/>
|
||||
</Grid>-->
|
||||
|
||||
</Grid>
|
||||
<Grid Width="368" Height="100" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,200,0,0">
|
||||
<TextBlock x:Name="name1" Text="一号位" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
<Grid HorizontalAlignment="Right">
|
||||
<Border Background="#e54229" CornerRadius="20" Width="110" Height="40"/>
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/finish_img/star.png"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Width="22"
|
||||
Height="30"
|
||||
Margin="0,0,50,2"
|
||||
/>
|
||||
<TextBlock x:Name="number1" Text="999" FontSize="24" FontWeight="Bold" Foreground="#fff" HorizontalAlignment="Left" Margin="50,0,0,0" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid Width="369" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="1225,231,0,0">
|
||||
<TextBlock x:Name="name3" Text="三号位" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
<Grid HorizontalAlignment="Right">
|
||||
<Border Background="#e54229" CornerRadius="20" Width="110" Height="40"/>
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/finish_img/star.png"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Width="22"
|
||||
Height="30"
|
||||
Margin="0,0,50,2"
|
||||
/>
|
||||
<TextBlock x:Name="number3" Text="999" FontSize="24" FontWeight="Bold" Foreground="#fff" HorizontalAlignment="Left" Margin="50,0,0,0" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid Width="368" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="310,226,0,0">
|
||||
<TextBlock x:Name="name2" Text="二号位" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
<Grid HorizontalAlignment="Right">
|
||||
<Border Background="#e54229" CornerRadius="20" Width="110" Height="40"/>
|
||||
<Image
|
||||
Source="/Resources/Img/test_img/one_rope/finish_img/star.png"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Width="22"
|
||||
Height="30"
|
||||
Margin="0,0,50,2"
|
||||
/>
|
||||
<TextBlock x:Name="number2" Text="999" FontSize="24" FontWeight="Bold" Foreground="#fff" HorizontalAlignment="Left" Margin="50,0,0,0" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="0,0,0,50" VerticalAlignment="Bottom" Height="182" Width="340" >
|
||||
<Image
|
||||
Source="/Resources/Img/Album/change_left.png"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Width="330"
|
||||
Margin="0,103,0,0"
|
||||
/>
|
||||
<Image
|
||||
gif:ImageBehavior.AnimatedSource="/Resources/Img/Album/3.gif"
|
||||
gif:ImageBehavior.RepeatBehavior="Forever"
|
||||
gif:ImageBehavior.AnimateInDesignMode="True"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Margin="-20,10,30,20"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
|
||||
@ -1,5 +1,14 @@
|
||||
using System;
|
||||
using AiSportsMicrospaceDB.DBContext;
|
||||
using Dto;
|
||||
using Enum;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SportExpand;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@ -7,9 +16,18 @@ 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 Wpf_AiSportsMicrospace;
|
||||
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;
|
||||
using Yztob.AiSports.Sensors.Things;
|
||||
|
||||
namespace Views.JumpingJack
|
||||
{
|
||||
@ -18,9 +36,702 @@ namespace Views.JumpingJack
|
||||
/// </summary>
|
||||
public partial class BambooDragonfly : UserControl
|
||||
{
|
||||
private Main _mainWin => Application.Current.MainWindow as Main;
|
||||
private MediaPlayer _mediaPlayer = new MediaPlayer();
|
||||
List<RankItem> RankingItemList = new();
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
private GameState _currentGameState = GameState.NotStarted;
|
||||
private readonly object _updateLock = new object();
|
||||
private readonly Dictionary<int, (string lastNumber, DateTime lastChangeTime, string currentState)> _jumpStatus = new Dictionary<int, (string, DateTime, string)>();
|
||||
private GroupJumpRopeContext _groupJumpRopeContext;
|
||||
|
||||
public BambooDragonfly()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += UserControl_Loaded;
|
||||
Unloaded += UserControl_Unloaded;
|
||||
_groupJumpRopeContext = new GroupJumpRopeContext();
|
||||
}
|
||||
|
||||
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 使用局部变量保存事件处理方法,以便解绑
|
||||
Action<VideoFrame> handler = null;
|
||||
|
||||
var app = (App)Application.Current;
|
||||
using var scope = App.AppHost.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var cfg = await db.SixGroupPoints.FirstOrDefaultAsync();
|
||||
//var pointlist = JsonSerializer.Deserialize<List<(double, double)>>(cfg!.PointsJson);
|
||||
|
||||
List<(double XNorm, double YNorm)> pointlist =
|
||||
[(cfg.point1.X, cfg.point1.Y),(cfg.point2.X, cfg.point2.Y),
|
||||
(cfg.point3.X, cfg.point3.Y), (cfg.point4.X, cfg.point4.Y),
|
||||
(cfg.point5.X, cfg.point5.Y),(cfg.point6.X, cfg.point6.Y)];
|
||||
_groupJumpRopeContext.CirclePositions = pointlist;
|
||||
|
||||
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;
|
||||
//_mainWin.WebcamClient.StartExtract();
|
||||
_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 bool _isCountdownStarted = false; // 新增标记
|
||||
private void OnHumanFrameUpdated(object sender, List<Human> humans)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (humans == null || humans.Count == 0) return;
|
||||
|
||||
switch (_currentGameState)
|
||||
{
|
||||
case GameState.NotStarted: // 未开始
|
||||
if (!_isCountdownStarted) // 只有倒计时未开始才检测左手
|
||||
{
|
||||
int leftWaving = DetectLeftHandRaise(humans);
|
||||
if (leftWaving == 5)
|
||||
{
|
||||
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
||||
_mainWin.WebcamClient.StopExtract();
|
||||
|
||||
// 举左手逻辑,例如结束动画或退出
|
||||
var newPage = new Home();
|
||||
_mainWin?.SwitchPageWithMaskAnimation(newPage, true);
|
||||
return; // 提前退出
|
||||
}
|
||||
}
|
||||
|
||||
int rightWaving = DetectRightHandRaise(humans);
|
||||
if (rightWaving >= 3)
|
||||
{
|
||||
switch (rightWaving)
|
||||
{
|
||||
case (int)WavingAction.FirstHand:
|
||||
StartCountdown(3);
|
||||
_isCountdownStarted = true; // 倒计时开始,停止左手检测
|
||||
break;
|
||||
case (int)WavingAction.Raising:
|
||||
UpdateCountdown();
|
||||
break;
|
||||
case (int)WavingAction.RaiseHand:
|
||||
FinishCountdown();
|
||||
_currentGameState = GameState.Running; // 倒计时完成 → 游戏开始
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GameState.Running: // 游戏进行中
|
||||
UpdateCircleCounts(humans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("OnFrameExtracted error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private double _currentCountdown = 3600;
|
||||
private DateTime _lastUpdateTime = DateTime.Now;
|
||||
private void StartCountdown(int start = 3)
|
||||
{
|
||||
_mainWin.ShowCountDownAnimation();
|
||||
|
||||
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
|
||||
|
||||
//开始人脸识别=============================================
|
||||
//Action<VideoFrame> handler = null;
|
||||
|
||||
//handler = async (frame) =>
|
||||
//{
|
||||
// var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
|
||||
// double[] xValues = [.. _groupJumpRopeContext.CirclePositions.Select(p => p.XNorm)];
|
||||
|
||||
// var studentList = Utils.FacialRecognition(buffer, xValues);
|
||||
|
||||
// if (studentList != null && studentList.Count > 0)
|
||||
// {
|
||||
// // 解绑事件
|
||||
// _mainWin.FacialRecognitionEvent -= handler;
|
||||
|
||||
// for (int i = 0; i < studentList.Count; i++)
|
||||
// {
|
||||
// var student = studentList[i];
|
||||
// if (student != null)
|
||||
// {
|
||||
// _groupJumpRopeContext.UserList[i].DisplayText = student.StudentName;
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
//};
|
||||
|
||||
//// 绑定事件
|
||||
//_mainWin.FacialRecognitionEvent += handler;
|
||||
}
|
||||
|
||||
private void UpdateCountdown()
|
||||
{
|
||||
if ((DateTime.Now - _lastUpdateTime).TotalMilliseconds >= 600)
|
||||
{
|
||||
_lastUpdateTime = DateTime.Now;
|
||||
_currentCountdown -= 600;
|
||||
|
||||
if (_currentCountdown > 0)
|
||||
{
|
||||
countdownText.Text = _currentCountdown.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
FinishCountdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void FinishCountdown()
|
||||
{
|
||||
countdownText.Text = "GO!";
|
||||
countdownGrid.Visibility = Visibility.Hidden;
|
||||
|
||||
// 播放背景音乐(循环)
|
||||
Utils.PlayBackgroundMusic("homeprojectselected1.mp3", true);
|
||||
// 启动60秒倒计时(独立任务)
|
||||
StartGameCountdown(60);
|
||||
}
|
||||
private async void StartGameCountdown(int seconds)
|
||||
{
|
||||
countdownGrid.Visibility = Visibility.Visible;
|
||||
|
||||
for (int i = seconds; i >= 0; i--)
|
||||
{
|
||||
countdownText.Text = i.ToString();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
countdownGrid.Visibility = Visibility.Hidden;
|
||||
|
||||
_groupJumpRopeContext.UserList.ForEach(x =>
|
||||
{
|
||||
x.ImageState = "1";
|
||||
});
|
||||
|
||||
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
|
||||
_mainWin.WebcamClient.StopExtract();
|
||||
|
||||
RankingItemList = _groupJumpRopeContext.UpdateRankList();
|
||||
ShowRankingBoard(RankingItemList);
|
||||
Utils.StopBackgroundMusic();
|
||||
|
||||
_currentGameState = GameState.NotStarted;
|
||||
|
||||
_isCountdownStarted = false;
|
||||
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
|
||||
_mainWin.WebcamClient.StartExtract();
|
||||
}
|
||||
|
||||
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();
|
||||
_groupJumpRopeContext.Sports.Clear();
|
||||
_groupJumpRopeContext.UserList.Clear();
|
||||
_groupJumpRopeContext.UserNumberList.Clear();
|
||||
|
||||
double imgWidth = userBox.ActualWidth;
|
||||
double imgHeight = userBox.ActualHeight;
|
||||
|
||||
for (int i = 0; i < _groupJumpRopeContext.CirclePositions.Count; i++)
|
||||
{
|
||||
var pos = _groupJumpRopeContext.CirclePositions[i];
|
||||
double x = pos.XNorm * imgWidth;
|
||||
double y = pos.YNorm * imgHeight;
|
||||
|
||||
// 绘制发光圆
|
||||
var userItem = AddUserItem(x, y, i);
|
||||
|
||||
// 绑定运动对象
|
||||
|
||||
var sport = SportBase.Create("jumping-jack");
|
||||
|
||||
//Optimizes.Optimizeing(sport);
|
||||
|
||||
//var sport = SportBase.Create("jumping-jack");
|
||||
|
||||
int indexCopy = i;
|
||||
var currentItem = userItem;
|
||||
|
||||
// 订阅事件
|
||||
sport.OnTicked += (count, times) =>
|
||||
{
|
||||
// 更新UI
|
||||
userItem.NumberText = count.ToString();
|
||||
// 更新数字源
|
||||
_groupJumpRopeContext.UserNumberList[indexCopy] = count.ToString();
|
||||
// 改变状态为“跳绳中”
|
||||
if (userItem.ImageState != "2")
|
||||
userItem.ImageState = "2";
|
||||
};
|
||||
|
||||
sport.Start();
|
||||
_groupJumpRopeContext.Sports.Add(sport);
|
||||
}
|
||||
}
|
||||
private void UpdateCircleCounts(List<Human> humans)
|
||||
{
|
||||
// 放宽一点的圈半径(归一化)
|
||||
double radiusNormX = 0.10;
|
||||
double radiusNormY = 0.14;
|
||||
|
||||
for (int i = 0; i < _groupJumpRopeContext.CirclePositions.Count; i++)
|
||||
{
|
||||
var circleX = _groupJumpRopeContext.CirclePositions[i].XNorm;
|
||||
var circleY = _groupJumpRopeContext.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;
|
||||
|
||||
bool rightInCircle = false;
|
||||
bool leftInCircle = false;
|
||||
|
||||
// 右脚判断
|
||||
if (rightFoot != null)
|
||||
{
|
||||
double xRightNorm = rightFoot.X / userBox.ActualWidth;
|
||||
double yRightNorm = rightFoot.Y / userBox.ActualHeight;
|
||||
|
||||
rightInCircle =
|
||||
xRightNorm >= (circleX - radiusNormX) &&
|
||||
xRightNorm <= (circleX + radiusNormX) &&
|
||||
yRightNorm >= (circleY - radiusNormY) &&
|
||||
yRightNorm <= (circleY + radiusNormY);
|
||||
}
|
||||
|
||||
// 左脚判断
|
||||
if (leftFoot != null)
|
||||
{
|
||||
double xLeftNorm = leftFoot.X / userBox.ActualWidth;
|
||||
double yLeftNorm = leftFoot.Y / userBox.ActualHeight;
|
||||
|
||||
leftInCircle =
|
||||
xLeftNorm >= (circleX - radiusNormX) &&
|
||||
xLeftNorm <= (circleX + radiusNormX) &&
|
||||
yLeftNorm >= (circleY - radiusNormY) &&
|
||||
yLeftNorm <= (circleY + radiusNormY);
|
||||
}
|
||||
|
||||
// ✅ 只要有一只脚在圈内
|
||||
if (rightInCircle || leftInCircle)
|
||||
{
|
||||
humanInCircle = hu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasHuman = humanInCircle != null;
|
||||
|
||||
lock (_updateLock)
|
||||
{
|
||||
_groupJumpRopeContext.UserList[i].ImageState =
|
||||
GetJumpState(i, hasHuman);
|
||||
}
|
||||
|
||||
if (hasHuman)
|
||||
{
|
||||
_groupJumpRopeContext.Sports[i].Pushing(humanInCircle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string GetJumpState(int circleIndex, bool humanInCircle)
|
||||
{
|
||||
if (!humanInCircle)
|
||||
{
|
||||
// 无人 → 出圈
|
||||
return "3";
|
||||
}
|
||||
|
||||
string currentNumber = _groupJumpRopeContext.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 = _groupJumpRopeContext.UseNameList[index];
|
||||
userItem.VerticalAlignment = VerticalAlignment.Top;
|
||||
userItem.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
userItem.ImageState = "1";
|
||||
//userItem.Margin = new Thickness(centerX - (index == 0 ? 80 : index == 6 ? 190 : 135), centerY - 540, 0, 0);
|
||||
userItem.Margin = new Thickness(centerX - 265, centerY - 500, 0, 0);
|
||||
userBox.Children.Add(userItem);
|
||||
_groupJumpRopeContext.UserList.Add(userItem);
|
||||
_groupJumpRopeContext.UserNumberList.Add("0");
|
||||
return userItem; //
|
||||
}
|
||||
|
||||
public void ShowRankingBoard(List<RankItem> list)
|
||||
{
|
||||
var scoreGrid = FindName("ScoreGrid") as Grid;
|
||||
name1.Text = list[0].Name;
|
||||
name2.Text = list[1].Name;
|
||||
name3.Text = list[2].Name;
|
||||
number1.Text = list[0].Number.ToString();
|
||||
number2.Text = list[1].Number.ToString();
|
||||
number3.Text = list[2].Number.ToString();
|
||||
foreach (var item in list)
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
Width = 1344,
|
||||
Height = 71,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
Margin = new Thickness(0, 69 * item.Rank, 0, 0)
|
||||
};
|
||||
var border = new Border
|
||||
{
|
||||
Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#fff"))
|
||||
};
|
||||
grid.Children.Add(border);
|
||||
var rankText = new TextBlock
|
||||
{
|
||||
Text = item.Rank.ToString(),
|
||||
FontSize = 24,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999999")),
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
Width = 200
|
||||
};
|
||||
grid.Children.Add(rankText);
|
||||
|
||||
var positionText = new TextBlock
|
||||
{
|
||||
Text = item.Name,
|
||||
FontSize = 24,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999999")),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
grid.Children.Add(positionText);
|
||||
|
||||
var scoreText = new TextBlock
|
||||
{
|
||||
Text = item.Number.ToString(),
|
||||
FontSize = 24,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999999")),
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextAlignment = TextAlignment.Center,
|
||||
Width = 200
|
||||
};
|
||||
grid.Children.Add(scoreText);
|
||||
ScoreGrid.Children.Add(grid);
|
||||
}
|
||||
showElement("RankingGrid");
|
||||
//var rankingGrid = FindName("RankingGrid") as Grid;
|
||||
//if (rankingGrid == null) return;
|
||||
|
||||
//rankingGrid.Visibility = Visibility.Visible;
|
||||
//var fadeIn = new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(600));
|
||||
//rankingGrid.BeginAnimation(UIElement.OpacityProperty, fadeIn);
|
||||
}
|
||||
|
||||
/*
|
||||
* 控制元素隐藏(渐隐动画
|
||||
* time: 动画持续时间(毫秒)
|
||||
* name: 元素名称
|
||||
*/
|
||||
public void HideElement(string name, int time)
|
||||
{
|
||||
var Element = FindName(name) as Grid;
|
||||
if (Element == null) return;
|
||||
|
||||
var fadeOut = new DoubleAnimation(1, 0, TimeSpan.FromMilliseconds(time));
|
||||
fadeOut.Completed += (s, e) => Element.Visibility = Visibility.Collapsed;
|
||||
Element.BeginAnimation(UIElement.OpacityProperty, fadeOut);
|
||||
}
|
||||
|
||||
/*
|
||||
* 控制元素限时(渐隐动画
|
||||
* time: 动画持续时间(毫秒)
|
||||
* name: 元素名称
|
||||
*/
|
||||
public void showElement(string name, int time = 600)
|
||||
{
|
||||
var Element = FindName(name) as FrameworkElement;
|
||||
if (Element == null) return;
|
||||
|
||||
Element.Visibility = Visibility.Visible;
|
||||
var fadeIn = new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(time));
|
||||
Element.BeginAnimation(UIElement.OpacityProperty, fadeIn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user