update:打地鼠

This commit is contained in:
ltx 2026-03-05 14:04:10 +08:00
parent c6c50f3d6a
commit fd638d91cd
9 changed files with 341 additions and 759 deletions

View File

@ -26,12 +26,12 @@ namespace Dto
{
CirclePositions = new List<(double XNorm, double YNorm)>
{
(0.21, 0.88 ),
(0.36, 0.58 ),
(0.50, 0.88),
(0.64, 0.58 ),
(0.78, 0.88),
(0.92, 0.58 )
(403.2, 950.4 ),
(691.2, 626.4 ),
(960, 950.4 ),
(1228.8, 626.4 ),
(1497.6, 950.4),
(1766.4, 626.4)
};
UserList = new List<WhackAMoleUserControl>();

View File

@ -6,11 +6,13 @@
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:gif="http://wpfanimatedgif.codeplex.com">
<Grid >
<Grid Width="410" Height="580">
<Image gif:ImageBehavior.AnimatedSource="/Resources/Img/play_img/whack_mole/mole_u.gif"
x:Name="image1"
gif:ImageBehavior.AutoStart="False"
gif:ImageBehavior.RepeatBehavior="Forever"
gif:ImageBehavior.RepeatBehavior="1x"
gif:ImageBehavior.AnimateInDesignMode="True"
gif:ImageBehavior.AnimationSpeedRatio="0.82"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Width="420"
@ -36,7 +38,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Top"
Width="420"
Visibility="Visible"/>
Visibility="Hidden"/>
<Image gif:ImageBehavior.AnimatedSource="/Resources/Img/play_img/whack_mole/rabbit_hit_u.gif"
@ -48,34 +50,22 @@
Width="420"
Visibility="Hidden"/>
<TextBlock x:Name="TimerText"
Text="q"
HorizontalAlignment="Right"
VerticalAlignment="Top"
FontSize="5"
FontWeight="Bold"
Foreground="White"
Visibility="Collapsed">
<TextBlock.Effect>
<DropShadowEffect Color="#497600" BlurRadius="1" ShadowDepth="4"/>
</TextBlock.Effect>
</TextBlock>
<Image
Source="/Resources/Img/play_img/whack_mole/score-bg.png"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="145"
Margin="234,511,0,0"
/>
Source="/Resources/Img/play_img/whack_mole/score-bg.png"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="145"
Margin="234,511,0,0"
/>
<Image
Source="/Resources/Img/play_img/whack_mole/name-bot.png"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="406"
Margin="0,571,0,0"
/>
<TextBlock x:Name="NumberText"
Text="0"
Source="/Resources/Img/play_img/whack_mole/name-bot.png"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="150"
Margin="40,571,0,0"
/>
<TextBlock
Text="{Binding NumberText, RelativeSource={RelativeSource AncestorType=UserControl}}"
Width="135"
TextAlignment="Center"
HorizontalAlignment="Left"
@ -89,8 +79,7 @@
</TextBlock.Effect>
</TextBlock>
<TextBlock x:Name="name"
Text="Name"
<TextBlock Text="{Binding NameText, RelativeSource={RelativeSource AncestorType=UserControl}}"
HorizontalAlignment="Right"
VerticalAlignment="Top"
FontSize="45"

View File

@ -18,671 +18,143 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
/// <summary>
/// 打地鼠用户控件
/// </summary>
public partial class WhackAMoleUserControl : UserControl, INotifyPropertyChanged
public partial class WhackAMoleUserControl : UserControl
{
#region 依赖属性
/// <summary>
/// 玩家索引
/// </summary>
public static readonly DependencyProperty IndexProperty =
DependencyProperty.Register(nameof(Index), typeof(int), typeof(WhackAMoleUserControl),
new PropertyMetadata(0, OnIndexChanged));
/// <summary>
/// 是否激活游戏
/// </summary>
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.Register(nameof(IsActive), typeof(bool), typeof(WhackAMoleUserControl),
new PropertyMetadata(false, OnIsActiveChanged));
/// <summary>
/// 触发值,用于检测打地鼠动作
/// </summary>
public static readonly DependencyProperty TriggerValueProperty =
DependencyProperty.Register(nameof(TriggerValue), typeof(double), typeof(WhackAMoleUserControl),
new PropertyMetadata(0.0, OnTriggerValueChanged));
#endregion
#region 属性
/// <summary>
/// 玩家索引
/// </summary>
public int Index
{
get => (int)GetValue(IndexProperty);
set => SetValue(IndexProperty, value);
}
/// <summary>
/// 是否激活游戏
/// </summary>
public bool IsActive
{
get => (bool)GetValue(IsActiveProperty);
set => SetValue(IsActiveProperty, value);
}
/// <summary>
/// 触发值
/// </summary>
public double TriggerValue
{
get => (double)GetValue(TriggerValueProperty);
set => SetValue(TriggerValueProperty, value);
}
#endregion
#region 私有字段
private int _score = 0;
private int _gameTime = 60;
private bool _isCharacterVisible = false;
private CharacterType _currentCharacter = CharacterType.None;
private DateTime _characterAppearTime;
private DispatcherTimer _gameTimer;
private DispatcherTimer _appearanceTimer;
private DispatcherTimer _hitDetectionTimer;
private double _lastTriggerValue;
private bool _isProcessingHit = false;
private readonly Random _random = new Random();
ImageAnimationController controller1;
ImageAnimationController controller2;
ImageAnimationController controller3;
ImageAnimationController controller4;
private bool _isInitialized = false;
/// <summary>
/// 外部上下文,用于更新用户分数列表
/// 注意:需要根据实际项目调整这个引用方式
/// </summary>
private WhackAMoleContext _whackAMoleContext;
#endregion
#region 事件
/// <summary>
/// 分数改变事件
/// </summary>
public event Action<int, int> ScoreChanged;
/// <summary>
/// 游戏结束事件
/// </summary>
public event Action<int> GameEnded;
/// <summary>
/// 属性改变事件
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region 枚举
/// <summary>
/// 角色类型
/// </summary>
private enum CharacterType
{
None,
Mole,
Rabbit
}
#endregion
#region 构造方法
public WhackAMoleUserControl()
{
InitializeComponent();
// 初始化计时器
InitializeTimers();
// 初始化外部上下文引用
InitializeContext();
//InitGif();
ImageBehavior.AddAnimationCompletedHandler(image1, TimeDown1);
ImageBehavior.AddAnimationCompletedHandler(image2, TimeDown);
ImageBehavior.AddAnimationCompletedHandler(image3, TimeDown1);
ImageBehavior.AddAnimationCompletedHandler(image4, TimeDown);
}
#region 依赖属性
public string NumberText
{
get => (string)GetValue(NumberTextProperty);
set => SetValue(NumberTextProperty, value);
}
public static readonly DependencyProperty NumberTextProperty =
DependencyProperty.Register(nameof(NumberText), typeof(string), typeof(WhackAMoleUserControl), new PropertyMetadata("0"));
public string NameText
{
get => (string)GetValue(NameTextProperty);
set => SetValue(NameTextProperty, value);
}
public static readonly DependencyProperty NameTextProperty =
DependencyProperty.Register(nameof(NameText), typeof(string), typeof(WhackAMoleUserControl), new PropertyMetadata("游客"));
#endregion
#region 私有方法
/// <summary>
/// 初始化计时器
/// </summary>
private void InitializeTimers()
public string ImageState
//1 地鼠 2 地鼠打中 3 兔子 4 兔子打中
{
// 游戏主计时器(60秒倒计时)
_gameTimer = new DispatcherTimer();
_gameTimer.Interval = TimeSpan.FromSeconds(1);
_gameTimer.Tick += GameTimer_Tick;
get => (string)GetValue(ImageStateProperty);
set => SetValue(ImageStateProperty, value);
}
public static readonly DependencyProperty ImageStateProperty =
DependencyProperty.Register(nameof(ImageState), typeof(string), typeof(WhackAMoleUserControl), new PropertyMetadata("1", OnImageStateChanged));
// 角色出现计时器(随机间隔)
_appearanceTimer = new DispatcherTimer();
_appearanceTimer.Tick += AppearanceTimer_Tick;
// 打击检测计时器(200ms检测窗口)
_hitDetectionTimer = new DispatcherTimer();
_hitDetectionTimer.Interval = TimeSpan.FromMilliseconds(50);
_hitDetectionTimer.Tick += HitDetectionTimer_Tick;
_whackAMoleContext = new WhackAMoleContext();
private static void OnImageStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (WhackAMoleUserControl)d;
control.ShowImage((string)e.NewValue);
}
/// <summary>
/// 初始化外部上下文引用
/// </summary>
private void InitializeContext()
private DispatcherTimer _imageTimer;
private Image _currentImage;
private void ShowImage(string imageIndex)
{
// 根据项目实际情况获取_context引用
// 这里需要根据你的项目结构调整
try
// 隐藏所有图片
image1.Visibility = Visibility.Hidden;
image2.Visibility = Visibility.Hidden;
image3.Visibility = Visibility.Hidden;
image4.Visibility = Visibility.Hidden;
// 显示指定的图片
Image targetImage = null;
switch (imageIndex)
{
// 示例:如果存在全局上下文类
// _groupJumpRopeContext = GlobalContext.Instance?.GetProperty("_groupJumpRopeContext");
// 或者通过查找父窗口/用户控件获取
var parent = this.Parent as FrameworkElement;
while (parent != null)
{
var contextProperty = parent.GetType().GetProperty("_whackAMoleContext");
if (contextProperty != null)
{
_whackAMoleContext = (WhackAMoleContext)contextProperty.GetValue(parent);
break;
}
parent = parent.Parent as FrameworkElement;
}
case "1":
targetImage = image1;
break;
case "2":
targetImage = image2;
break;
case "3":
targetImage = image3;
break;
case "4":
targetImage = image4;
break;
}
catch (Exception)
{
// 忽略初始化错误,在实际使用时会再次尝试获取
}
}
private void InitImageContal()
{
controller1 = ImageBehavior.GetAnimationController(image1);
controller2 = ImageBehavior.GetAnimationController(image2);
controller3 = ImageBehavior.GetAnimationController(image3);
controller4 = ImageBehavior.GetAnimationController(image4);
controller2.GotoFrame(controller2.FrameCount - 1);
controller4.GotoFrame(controller4.FrameCount - 1);
image2.Visibility = Visibility;
image4.Visibility = Visibility;
if(controller1 != null && controller2 != null && controller3 != null && controller4 != null)
{
_isInitialized = true;
}
}
/// <summary>
/// 获取外部上下文
/// </summary>
private void TryGetContext()
{
if (_whackAMoleContext == null)
{
InitializeContext();
}
}
/// <summary>
/// 更新外部分数列表
/// </summary>
private void UpdateExternalScoreList()
{
TryGetContext();
if (_whackAMoleContext != null)
{
try
{
var userNumberList = _whackAMoleContext.GetType().GetProperty("UserNumberList")?.GetValue(_whackAMoleContext) as List<int>;
if (userNumberList != null && Index >= 0 && Index < userNumberList.Count)
{
userNumberList[Index] = _score;
}
}
catch (Exception)
{
// 忽略更新错误
}
}
}
/// <summary>
/// 开始游戏
/// </summary>
private void StartGame()
{
if (!_isInitialized) {
InitImageContal();
name.Text = _whackAMoleContext.UserNameList[Index];
}
// 重置分数
_score = 0;
UpdateScoreDisplay();
// 重置计时器
_gameTime = 60;
UpdateTimerDisplay();
// 隐藏所有角色
HideAllCharacters();
// 启动游戏计时器
_gameTimer.Start();
// 启动角色出现计时器(1-3秒随机间隔)
ScheduleNextAppearance();
// 启动打击检测计时器
_hitDetectionTimer.Start();
// 记录初始触发值
_lastTriggerValue = TriggerValue;
}
/// <summary>
/// 停止游戏
/// </summary>
private void StopGame()
{
// 停止所有计时器
_gameTimer.Stop();
_appearanceTimer.Stop();
_hitDetectionTimer.Stop();
// 隐藏所有角色
HideAllCharacters();
// 重置状态
_isCharacterVisible = false;
_currentCharacter = CharacterType.None;
_isProcessingHit = false;
// 发送游戏结束事件
GameEnded?.Invoke(_score);
}
/// <summary>
/// 安排下一次角色出现
/// </summary>
private void ScheduleNextAppearance()
{
// 随机间隔3-5秒
int interval = _random.Next(2000, 3501);
_appearanceTimer.Interval = TimeSpan.FromMilliseconds(interval);
_appearanceTimer.Start();
}
/// <summary>
/// 显示角色
/// </summary>
private void ShowCharacter()
{
if (_isProcessingHit) return;
// 随机决定出现地鼠还是兔子(70%地鼠,30%兔子)
bool isMole = _random.Next(100) < 20;
_currentCharacter = isMole ? CharacterType.Mole : CharacterType.Rabbit;
_isCharacterVisible = true;
_characterAppearTime = DateTime.Now;
image2.Visibility = Visibility.Collapsed;
image4.Visibility = Visibility.Collapsed;
if (isMole)
{
image1.Visibility = Visibility.Visible;
image3.Visibility = Visibility.Collapsed;
//ShowGif(controller1 , 0);
controller1.Play();
Task.Delay(1450).ContinueWith(_ =>
{
controller1.Pause();
controller1.GotoFrame(0);
_isCharacterVisible = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
//image3.Visibility = Visibility.Collapsed;
// 显示新图片
if (targetImage != null) {
ImageBehavior.SetRepeatBehavior(targetImage, new RepeatBehavior(2));
ImageBehavior.SetRepeatBehavior(targetImage, new RepeatBehavior(1));
targetImage.Visibility = Visibility.Visible;
_currentImage = targetImage;
ImageBehavior.GetAnimationController(targetImage).Play();
}
else
{
//ShowGif(controller2 , 0);
image3.Visibility = Visibility.Visible;
image1.Visibility = Visibility.Collapsed;
controller3.Play();
Task.Delay(1300).ContinueWith(_ =>
{
controller3.Pause();
controller3.GotoFrame(0);
_isCharacterVisible = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
ImageBehavior.SetRepeatBehavior(image1, new RepeatBehavior(2));
ImageBehavior.SetRepeatBehavior(image1, new RepeatBehavior(1));
image1.Visibility = Visibility.Visible;
}
}
/// <summary>
/// 隐藏所有角色
/// </summary>
private void HideAllCharacters()
{
//image1.Visibility = Visibility.Collapsed;
image2.Visibility = Visibility.Collapsed;
//image3.Visibility = Visibility.Collapsed;
image4.Visibility = Visibility.Collapsed;
}
/// <summary>
/// 处理打击
/// </summary>
private void ProcessHit()
{
if (!_isCharacterVisible || _isProcessingHit) return;
_isProcessingHit = true;
image1.Visibility = Visibility.Collapsed;
image3.Visibility = Visibility.Collapsed;
if (_currentCharacter == CharacterType.Mole)
{
// 打中地鼠
image2.Visibility = Visibility.Visible;
image4.Visibility = Visibility.Collapsed;
controller2.Play();
// 分数+10
_score += 10;
UpdateScoreDisplay();
UpdateExternalScoreList();
// 发送分数改变事件
ScoreChanged?.Invoke(Index, _score);
Task.Delay(1500).ContinueWith(_ =>
{
controller2.Pause();
controller2.GotoFrame(controller2.FrameCount - 1);
_isCharacterVisible = false;
_currentCharacter = CharacterType.None;
_isProcessingHit = false;
// 安排下一次出现
ScheduleNextAppearance();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else if (_currentCharacter == CharacterType.Rabbit)
{
// 打中兔子(干扰)
image2.Visibility = Visibility.Visible;
image4.Visibility = Visibility.Collapsed;
controller4.Play();
// 分数-10
_score -= 5;
UpdateScoreDisplay();
UpdateExternalScoreList();
// 发送分数改变事件
ScoreChanged?.Invoke(Index, _score);
Task.Delay(1500).ContinueWith(_ =>
{
controller4.Pause();
controller4.GotoFrame(controller4.FrameCount - 1);
_isCharacterVisible = false;
_currentCharacter = CharacterType.None;
_isProcessingHit = false;
// 安排下一次出现
ScheduleNextAppearance();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
/// <summary>
/// 超时未打击
/// </summary>
private void HandleTimeout()
{
if (_isProcessingHit) return;
_isCharacterVisible = false;
_currentCharacter = CharacterType.None;
HideAllCharacters();
// 安排下一次出现
ScheduleNextAppearance();
}
private void ShowGif(ImageAnimationController controller , double number)
{
//ImageBehavior.SetRepeatBehavior(gif, new RepeatBehavior(2));
//ImageBehavior.SetRepeatBehavior(gif, new RepeatBehavior(1));
//ImageBehavior.SetAutoStart(gif, true);
////播放完成跳转到第一个画面
//Task.Delay(2000).ContinueWith(_ => {
// gif.Dispatcher.Invoke(() =>
// {
// var controller = ImageBehavior.GetAnimationController(gif);
// //ImageBehavior.SetAutoStart(gif, false);
// controller.GotoFrame(0);
// });
//});
controller.Play();
Task.Delay(1500).ContinueWith(_ =>
}
//当状态为2或4时候 不处理
private void TimeDown1(object? sender, RoutedEventArgs? e)
{
//MessageBox.Show("播放完啦");// 弹提示框22
if(ImageState == "2" || ImageState == "4" )
{
controller.Pause();
if(number == 0)
{
controller.GotoFrame(0);
}
else
{
controller.GotoFrame(controller.FrameCount - 1);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
/// <summary>
/// 更新分数显示
/// </summary>
private void UpdateScoreDisplay()
{
NumberText.Text = _score.ToString();
}
/// <summary>
/// 更新计时器显示
/// </summary>
private void UpdateTimerDisplay()
{
TimerText.Text = _gameTime.ToString();
}
/// <summary>
/// 检查触发值变化
/// </summary>
private bool CheckTriggerValueChanged()
{
// 检测触发值是否发生变化
// 根据实际需求调整检测逻辑
double currentValue = TriggerValue;
bool hasChanged = Math.Abs(currentValue - _lastTriggerValue) > 0.001;
if (hasChanged)
{
_lastTriggerValue = currentValue;
return;
}
return hasChanged;
}
/// <summary>
/// 检查是否在有效打击窗口内
/// </summary>
private bool IsWithinHitWindow()
{
if (!_isCharacterVisible) return false;
TimeSpan elapsed = DateTime.Now - _characterAppearTime;
return elapsed.TotalMilliseconds <= 2100;
}
#endregion
#region 事件处理
/// <summary>
/// Index属性改变回调
/// </summary>
private static void OnIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as WhackAMoleUserControl;
control?.OnPropertyChanged(nameof(Index));
}
/// <summary>
/// IsActive属性改变回调
/// </summary>
private static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as WhackAMoleUserControl;
if (control != null)
else
{
if ((bool)e.NewValue)
{
control.StartGame();
}
else
{
control.StopGame();
}
Task.Delay(1300);
ImageBehavior.GetAnimationController(image1).Pause();
ImageBehavior.GetAnimationController(image2).Pause();
ImageBehavior.GetAnimationController(image3).Pause();
ImageBehavior.GetAnimationController(image4).Pause();
ImageBehavior.SetRepeatBehavior(image1, new RepeatBehavior(2));
ImageBehavior.SetRepeatBehavior(image1, new RepeatBehavior(1));
image1.Visibility = Visibility.Visible;
image2.Visibility = Visibility.Hidden;
image3.Visibility = Visibility.Hidden;
image4.Visibility = Visibility.Hidden;
ImageState = "0";
}
}
/// <summary>
/// TriggerValue属性改变回调
/// </summary>
private static void OnTriggerValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
private void TimeDown(object? sender, RoutedEventArgs? e)
{
var control = d as WhackAMoleUserControl;
control?.OnTriggerValueChangedHandler();
//MessageBox.Show("播放完啦");// 弹提示框22
Task.Delay(1300);
ImageBehavior.GetAnimationController(image1).Pause();
ImageBehavior.GetAnimationController(image2).Pause();
ImageBehavior.GetAnimationController(image3).Pause();
ImageBehavior.GetAnimationController(image4).Pause();
ImageBehavior.SetRepeatBehavior(image1, new RepeatBehavior(2));
ImageBehavior.SetRepeatBehavior(image1, new RepeatBehavior(1));
image1.Visibility = Visibility.Visible;
image2.Visibility = Visibility.Hidden;
image3.Visibility = Visibility.Hidden;
image4.Visibility = Visibility.Hidden;
ImageState = "0";
}
/// <summary>
/// 触发值改变处理
/// </summary>
private void OnTriggerValueChangedHandler()
{
// 当检测到触发值变化且在有效窗口内时处理打击
if (IsActive && IsWithinHitWindow())
{
ProcessHit();
}
}
/// <summary>
/// 游戏计时器tick
/// </summary>
private void GameTimer_Tick(object sender, EventArgs e)
{
_gameTime--;
UpdateTimerDisplay();
if (_gameTime <= 0)
{
StopGame();
}
}
/// <summary>
/// 角色出现计时器tick
/// </summary>
private void AppearanceTimer_Tick(object sender, EventArgs e)
{
_appearanceTimer.Stop();
if (IsActive && !_isCharacterVisible && !_isProcessingHit)
{
ShowCharacter();
}
}
/// <summary>
/// 打击检测计时器tick
/// </summary>
private void HitDetectionTimer_Tick(object sender, EventArgs e)
{
// 检测是否超时未打击
if (_isCharacterVisible && !_isProcessingHit)
{
TimeSpan elapsed = DateTime.Now - _characterAppearTime;
if (elapsed.TotalMilliseconds > 2100)
{
HandleTimeout();
}
}
}
/// <summary>
/// 属性改变通知
/// </summary>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region 公共方法
/// <summary>
/// 手动开始游戏
/// </summary>
public void Start()
{
IsActive = true;
}
/// <summary>
/// 手动停止游戏
/// </summary>
public void Stop()
{
IsActive = false;
}
/// <summary>
/// 重置分数
/// </summary>
public void ResetScore()
{
_score = 0;
UpdateScoreDisplay();
UpdateExternalScoreList();
}
/// <summary>
/// 设置外部上下文
/// </summary>
/// <param name="context">外部上下文对象</param>
public void SetContext(dynamic context)
{
_whackAMoleContext = context;
}
#endregion
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

View File

@ -60,7 +60,8 @@ namespace Wpf_AiSportsMicrospace
});
coverFlow.Images.Add(new CoverFlowItem
{
ImageUri = GetResourceUri("Album/home_history.png"),
//ImageUri = GetResourceUri("Album/home_history.png"),
ImageUri = GetResourceUri("play_img/whack_mole.png"),
ProgressColor1 = "#e73d42",
ProgressColor2 = "#215bc7"
});
@ -164,7 +165,6 @@ namespace Wpf_AiSportsMicrospace
//mainWin?.SwitchPageWithMaskAnimation(newPage, true);
//var newPage = new BambooDragonfly();
//var newPage = new WhackAMole();
mainWin?.SwitchPageWithMaskAnimation(newPage, true);
}
else if (coverFlow.SelectedIndex == 1)
@ -175,8 +175,8 @@ namespace Wpf_AiSportsMicrospace
else
{
//var newPage = new TrainingRecords();
//var newPage = new WhackAMole();
var newPage = new StandingLeap();
var newPage = new WhackAMole();
//var newPage = new StandingLeap();
mainWin?.SwitchPageWithMaskAnimation(newPage, true);
}
}

View File

@ -246,37 +246,37 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
//UpdateCountdown();
//开始人脸识别=============================================
Action<VideoFrame> handler = null;
//Action<VideoFrame> handler = null;
handler = async (frame) =>
{
double[] xValues = [.. _musicJumpRopeContext.CirclePositions.Select(p => p.XNorm)];
var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
var studentList = Utils.FacialRecognition(buffer, xValues);
//handler = async (frame) =>
//{
// double[] xValues = [.. _musicJumpRopeContext.CirclePositions.Select(p => p.XNorm)];
// var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
// var studentList = Utils.FacialRecognition(buffer, xValues);
if (studentList != null && studentList.Count > 0)
{
// 解绑事件
_mainWin.FacialRecognitionEvent -= handler;
name1.Text = studentList[0]?.StudentName ?? "一号玩家";
name2.Text = studentList[1]?.StudentName ?? "二号玩家";
if (studentList.Count > 1 && studentList[0].Photo != "")
{
await LoadImageFromUrlAsync(studentList[0]?.Photo ?? "", avatar1);
}
if (studentList.Count > 2 && studentList[1].Photo != "")
{
await LoadImageFromUrlAsync(studentList[1]?.Photo ?? "", avatar2);
}
// if (studentList != null && studentList.Count > 0)
// {
// // 解绑事件
// _mainWin.FacialRecognitionEvent -= handler;
// name1.Text = studentList[0]?.StudentName ?? "一号玩家";
// name2.Text = studentList[1]?.StudentName ?? "二号玩家";
// if (studentList.Count > 1 && studentList[0].Photo != "")
// {
// await LoadImageFromUrlAsync(studentList[0]?.Photo ?? "", avatar1);
// }
// if (studentList.Count > 2 && studentList[1].Photo != "")
// {
// await LoadImageFromUrlAsync(studentList[1]?.Photo ?? "", avatar2);
// }
}
// }
// 这里可以继续处理 studentList
};
// // 这里可以继续处理 studentList
//};
//// 绑定事件
_mainWin.FacialRecognitionEvent += handler;
////// 绑定事件
//_mainWin.FacialRecognitionEvent += handler;
await Task.Delay(3000);
FinishCountdown();

View File

@ -36,9 +36,12 @@
</LinearGradientBrush>
</TextBlock.Foreground>
</TextBlock>-->
<Grid Height="1080" Width="1920" x:Name="userBox"/>
<Grid Width="200" Height="120" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="00,150,0,0" Visibility="Visible" x:Name="countdownGrid">
<!--<Grid Height="1080" Width="1920" x:Name="userBox"/>-->
<Grid Height="1080" Width="1920" x:Name="userListBox">
</Grid>
<Grid Width="200" Height="120" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="00,150,0,0" Visibility="Hidden" x:Name="countdownGrid">
<Border Background="#000" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="30" Opacity="0.6" />
<TextBlock x:Name="countdownText"
Text="60"
@ -95,7 +98,7 @@
Margin="0,168"
/>
<Grid Width="1346" Height="520" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,331,0,0" x:Name="ScoreGrid">
<Grid Width="1346" Height="550" 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"/>

View File

@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection;
using SportExpand;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
@ -53,7 +54,7 @@ namespace Views.SquatFu
_WhackAMoleContext = new WhackAMoleContext();
}
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
// 使用局部变量保存事件处理方法,以便解绑
Action<VideoFrame> handler = null;
@ -78,9 +79,10 @@ namespace Views.SquatFu
_WhackAMoleContext.CirclePositions = pointlist;
DrawCirclesWithText();
// 播放音乐
PlayMusic("raisehand.mp3");
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
}
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
@ -106,9 +108,11 @@ namespace Views.SquatFu
ShowCenterTip(imgPath, TimeSpan.FromSeconds(3));
// 监听播放完成事件
_mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;
_mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;
//_mainWin.WebcamClient.StartExtract();
_mediaPlayer.Play();
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
}
private void ShowCenterTip(string imagePath, TimeSpan duration)
{
@ -126,7 +130,7 @@ namespace Views.SquatFu
tipImage.Height = 1080 * 0.6; // 高度为 Canvas 高度的 60%
// 将图片添加到 Overlay Canvas
userBox.Children.Add(tipImage);
userListBox.Children.Add(tipImage);
// 渐变出现动画
var fadeInAnimation = new DoubleAnimation
@ -157,7 +161,7 @@ namespace Views.SquatFu
// 完成后移除图片
fadeOutAnimation.Completed += (s, e) =>
{
userBox.Children.Remove(tipImage);
userListBox.Children.Remove(tipImage);
};
});
});
@ -178,7 +182,7 @@ namespace Views.SquatFu
try
{
if (humans == null || humans.Count == 0) return;
switch (_currentGameState)
{
case GameState.NotStarted: // 未开始
@ -198,7 +202,7 @@ namespace Views.SquatFu
}
int rightWaving = sportOperate.VerifyRightWavingAction(humans);
if (rightWaving >= 3)
if (rightWaving >= 3 && RankingGrid.Visibility != Visibility.Visible)
{
switch (rightWaving)
{
@ -206,13 +210,13 @@ namespace Views.SquatFu
StartCountdown(3);
_isCountdownStarted = true; // 倒计时开始,停止左手检测
break;
case (int)WavingAction.Raising:
UpdateCountdown();
break;
case (int)WavingAction.RaiseHand:
FinishCountdown();
_currentGameState = GameState.Running; // 倒计时完成 → 游戏开始
break;
//case (int)WavingAction.Raising:
// UpdateCountdown();
// break;
//case (int)WavingAction.RaiseHand:
// FinishCountdown();
// _currentGameState = GameState.Running; // 倒计时完成 → 游戏开始
// break;
}
}
break;
@ -220,6 +224,19 @@ namespace Views.SquatFu
case GameState.Running: // 游戏进行中
UpdateCircleCounts(humans);
break;
case GameState.Finished: // 游戏结束
int leftWave = sportOperate.VerifyLeftWavingAction(humans);
if (leftWave == 5)
{
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
_mainWin.WebcamClient.StopExtract();
// 举左手逻辑,例如结束动画或退出
var newPage = new Home();
_mainWin?.SwitchPageWithMaskAnimation(newPage, true);
return; // 提前退出
}
break;
}
}
catch (Exception ex)
@ -230,19 +247,21 @@ namespace Views.SquatFu
private double _currentCountdown = 3600;
private DateTime _lastUpdateTime = DateTime.Now;
private void StartCountdown(int start = 3)
private async void StartCountdown(int start = 3)
{
_currentGameState = GameState.Running;
_mainWin.ShowCountDownAnimation();
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
//开始人脸识别=============================================
//Action<VideoFrame> handler = null;
//开始人脸识别 =============================================
//Action < VideoFrame > handler = null;
//handler = async (frame) =>
//{
// var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
// double[] xValues = [.. _groupJumpRopeContext.CirclePositions.Select(p => p.XNorm)];
// double[] xValues = [.. _WhackAMoleContext.CirclePositions.Select(p => p.XNorm)];
// var studentList = Utils.FacialRecognition(buffer, xValues);
@ -256,15 +275,17 @@ namespace Views.SquatFu
// var student = studentList[i];
// if (student != null)
// {
// _groupJumpRopeContext.UserList[i].DisplayText = student.StudentName;
// _WhackAMoleContext.UserList[i].NameText = student.StudentName;
// }
// }
// }
//};
//// 绑定事件
// 绑定事件
//_mainWin.FacialRecognitionEvent += handler;
await Task.Delay(3000);
FinishCountdown();
}
private void UpdateCountdown()
@ -285,19 +306,62 @@ namespace Views.SquatFu
}
}
private System.Timers.Timer _gameTimer;
private Random _random = new Random();
private async void FinishCountdown()
{
countdownText.Text = "GO!";
countdownGrid.Visibility = Visibility.Hidden;
_WhackAMoleContext.UserList.ForEach(x =>
{
x.Start();
});
// 播放背景音乐(循环)
Utils.PlayBackgroundMusic("homeprojectselected1.mp3", true);
// 启动60秒倒计时(独立任务)
StartGameCountdown(60);
// 开始游戏定时器,每2-3秒随机选取用户出现地鼠或兔子
StartGameTimer();
}
private void StartGameTimer()
{
_gameTimer = new System.Timers.Timer();
_gameTimer.Elapsed += OnGameTimerElapsed;
_gameTimer.AutoReset = true;
// 设置第一次触发时间为2-3秒之间的随机值
int firstInterval = _random.Next(2000, 3000);
_gameTimer.Interval = firstInterval;
_gameTimer.Start();
}
private void OnGameTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Dispatcher.Invoke(() =>
{
// 随机选取2-4个用户
int userCount = _random.Next(2, 5); // 2-4个
int actualCount = Math.Min(userCount, _WhackAMoleContext.UserList.Count);
// 获取所有用户索引
var allIndices = Enumerable.Range(0, _WhackAMoleContext.UserList.Count).ToList();
// 随机打乱顺序
var shuffled = allIndices.OrderBy(x => _random.Next()).Take(actualCount).ToList();
foreach (var index in shuffled)
{
// 随机决定是地鼠(1)还是兔子(3)
int imageState = _random.Next(2) == 0 ? 1 : 3;
if(_WhackAMoleContext.UserList[index].ImageState == "0")
{
_WhackAMoleContext.UserList[index].ImageState = imageState.ToString();
}
}
// 设置下一次触发时间为2-3秒之间的随机值
_gameTimer.Interval = _random.Next(2000, 3000);
});
}
double Tiggle = 1;
private async void StartGameCountdown(int seconds)
@ -311,11 +375,11 @@ namespace Views.SquatFu
}
countdownGrid.Visibility = Visibility.Hidden;
_gameTimer.Stop();
_WhackAMoleContext.UserList.ForEach(x =>
{
Tiggle += 1;
x.TriggerValue = Tiggle;
//x.TriggerValue = Tiggle;
});
_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
@ -325,13 +389,13 @@ namespace Views.SquatFu
ShowRankingBoard(RankingItemList);
Utils.StopBackgroundMusic();
_currentGameState = GameState.NotStarted;
_currentGameState = GameState.Finished;
_isCountdownStarted = false;
_mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
_mainWin.WebcamClient.StartExtract();
}
private DateTime? _raiseStartTime;
private bool _firstHandTriggered;
private int _lastCountdownSecond = 3;
@ -346,7 +410,7 @@ namespace Views.SquatFu
}
private void DrawCirclesWithText()
{
userBox.Children.Clear();
userListBox.Children.Clear();
_WhackAMoleContext.Sports.Clear();
_WhackAMoleContext.UserList.Clear();
_WhackAMoleContext.UserNumberList.Clear();
@ -355,32 +419,59 @@ namespace Views.SquatFu
for (int i = 0; i < _WhackAMoleContext.CirclePositions.Count; i++)
{
var pos = _WhackAMoleContext.CirclePositions[i];
double x = pos.XNorm ;
double y = pos.YNorm ;
double x = pos.XNorm;
double y = pos.YNorm;
// 绘制发光圆
var userItem = AddUserItem(x, y, i);
AddUserItem(x, y, i);
// 绑定运动对象
var sport = new FrontSquat();
Debug.WriteLine($"userListBox长度:{userListBox.Children.Count}");
//Optimizes.Optimizeing(sport);
//var sport = SportBase.Create("rope-skipping");
int indexCopy = i;
var currentItem = userItem;
//var currentItem = userItem;
// 订阅事件
// 订阅事件
sport.OnTicked += (count, times) =>
{
// 更新UI
//userItem.NumberText = count.ToString();
// 更新数字源
_WhackAMoleContext.UserNumberList[indexCopy] = count.ToString();
// 改变状态为“跳绳中”
if (userItem.TriggerValue != 2)
userItem.TriggerValue = 2;
// 获取当前用户的ImageState
var imageState = _WhackAMoleContext.UserList[indexCopy].ImageState;
// 获取当前分数
int currentScore = 0;
int.TryParse(_WhackAMoleContext.UserNumberList[indexCopy], out currentScore);
// 根据ImageState判断加分还是减分
if (imageState == "1") // 地鼠:加10分
{
currentScore += 10;
}
else if (imageState == "3") // 兔子:减5分
{
currentScore -= 5;
// 分数不能为负数
//if (currentScore < 0) currentScore = 0;
}
// 更新分数
string newScore = currentScore.ToString();
_WhackAMoleContext.UserNumberList[indexCopy] = newScore;
_WhackAMoleContext.UserList[indexCopy].NumberText = newScore;
// 如果打中地鼠或兔子,重置ImageState为0
if (imageState == "1")
{
_WhackAMoleContext.UserList[indexCopy].ImageState = "2";
}
else if(imageState == "3")
{
_WhackAMoleContext.UserList[indexCopy].ImageState = "4";
}
};
sport.Start();
@ -424,24 +515,39 @@ namespace Views.SquatFu
}
}
private WhackAMoleUserControl AddUserItem(double centerX, double centerY, int index)
void AddUserItem(double centerX, double centerY, int index)
{
var userItem = new WhackAMoleUserControl();
userItem.Width = 400;
userItem.Height = 600;
//userItem.DisplayText = _groupJumpRopeContext.UseNameList[index]; //名字
userItem.VerticalAlignment = VerticalAlignment.Top;
userItem.HorizontalAlignment = HorizontalAlignment.Left;
userItem.Index = index;
//var userItem = new WhackAMoleUserControl();
//userItem.Width = 400;
//userItem.Height = 600;
//userItem.NameText = _WhackAMoleContext.UserNameList[index]; //名字
//userItem.VerticalAlignment = VerticalAlignment.Top;
//userItem.HorizontalAlignment = HorizontalAlignment.Left;
//userItem.Index = index;
//userItem.ImageState = "1";
//userItem.Margin = new Thickness(centerX - (index == 0 ? 80 : index == 6 ? 190 : 135), centerY - 540, 0, 0);
userItem.Margin = new Thickness(centerX , centerY - 600 - 50, 0, 0);
userBox.Children.Add(userItem);
var userItem = new WhackAMoleUserControl
{
Width = 410,
Height = 580,
NameText = _WhackAMoleContext.UserNameList[index],
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
ImageState = "0"
};
var top = (centerX - 200) > 0 ? (centerX - 200) : 0;
userItem.Margin = new Thickness(top, centerY - 570, 0, 0);
userListBox.Children.Add(userItem);
_WhackAMoleContext.UserList.Add(userItem);
_WhackAMoleContext.UserNumberList.Add("0");
return userItem; //
//return userItem; //
}
public void ShowRankingBoard(List<RankItem> list)
{
var scoreGrid = FindName("ScoreGrid") as Grid;

View File

@ -69,13 +69,16 @@
<None Remove="Resources\Img\play_img\play_jump.png" />
<None Remove="Resources\Img\play_img\play_music.png" />
<None Remove="Resources\Img\play_img\play_vs.png" />
<None Remove="Resources\Img\play_img\whack_mole.png" />
<None Remove="Resources\Img\play_img\whack_mole\+10.gif" />
<None Remove="Resources\Img\play_img\whack_mole\-10.gif" />
<None Remove="Resources\Img\play_img\whack_mole\hit_u.gif" />
<None Remove="Resources\Img\play_img\whack_mole\mole_u.gif" />
<None Remove="Resources\Img\play_img\whack_mole\name-bot.png" />
<None Remove="Resources\Img\play_img\whack_mole\play_bg.png" />
<None Remove="Resources\Img\play_img\whack_mole\rabbit_hit_u.gif" />
<None Remove="Resources\Img\play_img\whack_mole\rabbit_u.gif" />
<None Remove="Resources\Img\play_img\whack_mole\score-bg.png" />
<None Remove="Resources\Img\play_img\whack_mole\title.png" />
<None Remove="Resources\Img\play_img\whack_mole\title_bg.png" />
<None Remove="Resources\Img\test_img\jump_long\bottom.png" />
@ -138,6 +141,9 @@
<Resource Include="Resources\app.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
<Resource Include="Resources\Img\play_img\whack_mole.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
<Resource Include="Resources\Img\play_img\whack_mole\+10.gif">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
@ -150,6 +156,9 @@
<Resource Include="Resources\Img\play_img\whack_mole\mole_u.gif">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
<Resource Include="Resources\Img\play_img\whack_mole\name-bot.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
<Resource Include="Resources\Img\play_img\whack_mole\play_bg.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
@ -159,6 +168,9 @@
<Resource Include="Resources\Img\play_img\whack_mole\rabbit_u.gif">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
<Resource Include="Resources\Img\play_img\whack_mole\score-bg.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
<Resource Include="Resources\Img\play_img\whack_mole\title.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>