C# WPF平台游戏多平台碰撞检测异常问题求助
A Level平台游戏碰撞检测异常:仅单个平台可触发弹跳,其余平台穿模
问题描述
- 为A Level编程项目制作平台游戏,设计逻辑为玩家触碰平台时自动跳跃
- 碰撞检测存在问题:仅能在一个平台上正常弹跳,其余平台均会出现穿模现象
- 所有平台标签均设为
platform,可排除标签匹配问题 - 代码逻辑是遍历GameScreen中所有标签为
platform的Rectangle进行碰撞检测,但实际仅能检测到一个矩形生效
原始代码
public partial class MainWindow : Window { private DispatcherTimer GameTimer = new DispatcherTimer(); private bool LeftKeyPressed, RightKeyPressed, gravity; double score; // 该值会增加到最高Y值的5倍,让角色位置越高分数越高 private float SpeedX, SpeedY, FrictionX = 0.88f, Speed = 1, FrictionY = 0.80f; // SpeedX控制水平移动,SpeedY控制垂直移动 private void Collide(string Dir) { foreach (var x in GameScreen.Children.OfType<Rectangle>()) { if (x.Tag != null) { var platformID = (string)x.Tag; if (platformID == "platform") { x.Stroke = Brushes.Black; Rect MeekHB = new Rect(Canvas.GetLeft(Meek), Canvas.GetTop(Meek), Meek.Width, Meek.Height); Rect PlatformHB = new Rect(Canvas.GetLeft(x), Canvas.GetTop(x), x.Width, x.Height); int Jumpcount = 1; if (MeekHB.IntersectsWith(PlatformHB)) { if (Dir == "y") { while (Jumpcount != 700) { gravity = false; Jumpcount = Jumpcount + 1; } } } else { gravity = true; } } } } } private void KeyboardUp(object sender, KeyEventArgs e) { // 检测'A'键是否松开 if (e.Key == Key.A) { LeftKeyPressed = false; } if (e.Key == Key.D) { RightKeyPressed = false; } } private void KeyboardDown(object sender, KeyEventArgs e) { if (e.Key == Key.A) { LeftKeyPressed = true; } if (e.Key == Key.D) { RightKeyPressed = true; } } public MainWindow() { InitializeComponent(); GameScreen.Focus(); GameTimer.Interval = TimeSpan.FromMilliseconds(16); GameTimer.Tick += GameTick; GameTimer.Start(); } private void GameTick(Object Sender, EventArgs e) { txtScore.Content = "Score: " + score; if (LeftKeyPressed) { SpeedX -= Speed; } if (RightKeyPressed) { SpeedX += Speed; } if (gravity == true) { SpeedY += Speed; } else if (gravity == false) { SpeedY -= Speed+50; } SpeedX = SpeedX * FrictionX; SpeedY = SpeedY * FrictionY; Canvas.SetLeft(Meek, Canvas.GetLeft(Meek) + SpeedX); Collide("x"); Canvas.SetTop(Meek, Canvas.GetTop(Meek) + SpeedY); Collide("y"); double maxY = 0; if (Canvas.GetBottom(Meek) > maxY) { maxY = Canvas.GetBottom(Meek); } score = maxY; } }
问题根源
gravity变量被循环覆盖:遍历平台时,每检查一个平台就会重新赋值gravity。如果最后一个遍历到的平台没有碰撞,不管之前有没有平台触发碰撞,gravity都会被设为true,导致只有最后一个平台能生效。- 无效的
Jumpcount循环:这个while循环只是原地计数,会瞬间执行完毕,完全起不到控制跳跃时长的作用。 - 碰撞后未修正玩家位置:检测到碰撞时,没有将玩家的位置调整到平台上方,导致玩家可能卡在平台内部,后续碰撞检测失效。
修复方案
修改Collide方法
private void Collide(string Dir) { // 默认重力生效,只有检测到碰撞才关闭重力 bool hasCollision = false; foreach (var x in GameScreen.Children.OfType<Rectangle>()) { if (x.Tag != null && (string)x.Tag == "platform") { x.Stroke = Brushes.Black; Rect MeekHB = new Rect(Canvas.GetLeft(Meek), Canvas.GetTop(Meek), Meek.Width, Meek.Height); Rect PlatformHB = new Rect(Canvas.GetLeft(x), Canvas.GetTop(x), x.Width, x.Height); if (MeekHB.IntersectsWith(PlatformHB)) { hasCollision = true; if (Dir == "y") { // 将玩家推到平台顶部,避免穿模 Canvas.SetTop(Meek, PlatformHB.Top - Meek.Height); // 设置向上的跳跃速度,替代无效的while循环 SpeedY = -15f; } } } } // 根据是否有碰撞设置重力状态 gravity = !hasCollision; }
调整GameTick中的重力逻辑
去掉原有的else if (gravity == false)分支,简化为:
if (gravity) { SpeedY += Speed; } // 跳跃速度已经在Collide中设置,无需额外处理
修复说明
- 用
hasCollision变量记录是否有任何平台碰撞,避免gravity被后续平台覆盖 - 碰撞时直接修正玩家Y坐标到平台顶部,彻底解决穿模问题
- 用直接设置
SpeedY的方式触发跳跃,替代原来无意义的计数循环,逻辑更清晰
内容的提问来源于stack exchange,提问作者Ainsley Crossley
相关产品推荐
相关产品推荐

