XNA – Pong Clone – 当它击中墙壁时reflection球?

我正在尝试在创建2D Pong Clone时让球从我的UI的顶部和底部“Walls”反弹。 这是我的Game.cs

public void CheckBallPosition() { if (ball.Position.Y == 0 || ball.Position.Y >= graphics.PreferredBackBufferHeight) ball.Move(true); else ball.Move(false); if (ball.Position.X = graphics.PreferredBackBufferWidth) ball.Reset(); } 

目前我在Ball.cs中使用它

  public void Move(bool IsCollidingWithWall) { if (IsCollidingWithWall) { Vector2 normal = new Vector2(0, 1); Direction = Vector2.Reflect(Direction,normal); this.Position += Direction; Console.WriteLine("WALL COLLISION"); } else this.Position += Direction; } 

它工作,但我使用手动键入正常,我想知道如何计算屏幕顶部和底部的法线?

好吧,这就是我要处理它的方式

 public void CheckBallPositionAndMove() { if (ball.Position.Y <= 0 || ball.Position.Y >= graphics.PreferredBackBufferHeight) ball.HandleWallCollision(); ball.Move(); if (ball.Position.X < 0 || ball.Position.X >= graphics.PreferredBackBufferWidth) ball.Reset(); } //In Ball.cs: private void HandleWallCollision(Vector2 normal) { Direction.Y *= -1; //Reflection about either normal is the same as multiplying y-vector by -1 } private void Move() { this.Position += Direction; } 

但请注意,使用这种“离散”碰撞检测,您要等到球移过屏幕顶部/底部后才能检测到碰撞; 发生在“两个”框架之间的碰撞可能会明显偏离,特别是如果球快速移动。 如果您使用此碰撞检测方法来检测与桨叶的碰撞,这尤其是一个问题,因为如果球移动得足够快, 球可能会直接穿过桨叶!

该问题的解决方案是使用所谓的连续碰撞检测 。 CCD通常比离散碰撞检测复杂得多; 幸运的是,pong很简单,做CCD只会稍微复杂一些。 但是,你仍然需要扎实掌握高中代​​数来解决方程式。

如果您仍然感兴趣, 本讲座中有一个很好的解释CCD, 这篇GameDev文章更深入一些。 在SO方面也有很多与之相关的问题 。

您可以使用以下枚举更改布尔值IsCollidingWithWall

 enum CollideType { None, Vertical, Horizontal } 

并在创建正常时检查此类型。

您世界上的每个边界都是一条线。 线的一侧是实心的而另一侧不是。 您尝试计算的法线是该线的等式的一部分。 它指向线的非固体侧。 线方程的另一部分是从线到原点的距离。 该线的等式可以从该线上的两个点找到。 您可以根据游戏空间中您想要墙的坐标来定义这两个点。

通过将由两个点定义的线段旋转90度然后归一化来计算法线。

 public static Vector2 ComputeNormal(Vector2 point1, Vector2 point2) { Vector2 normal = new Vector2(); normal.X = point2.Y - point1.Y; normal.Y = point1.X - point2.X; normal.Normalize(); return normal; } 

您正在使用首选的后缓冲区宽度和高度来定义您的世界空间,因此您可以使用这些来定义用于计算法线的点。

 float left = 0.0f; float right = graphics.PreferredBackBufferWidth; float top = 0.0f; float bottom = graphics.PreferredBackBufferHeight; Vector2 topNormal = ComputeNormal(new Vector2(left, top), new Vector2(right, top)); Vector2 bottomNormal = ComputeNormal(new Vector2(right, bottom), new Vector2(left, bottom)); 

请注意,必须以顺时针顺序给出这些点,以便正常方向指向正常方向。

以下XNA 4.0程序演示了这些使用中的概念:

 using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; namespace WindowsGame { public class Ball { const int DIAMETER = 40; const float RADIUS = DIAMETER * 0.5f; const float MASS = 0.25f; const int PIXELS = DIAMETER * DIAMETER; static readonly uint WHITE = Color.White.PackedValue; static readonly uint BLACK = new Color(0, 0, 0, 0).PackedValue; Texture2D m_texture; Vector2 m_position; Vector2 m_velocity; public Ball(GraphicsDevice graphicsDevice) { m_texture = new Texture2D(graphicsDevice, DIAMETER, DIAMETER); uint[] data = new uint[PIXELS]; for (int i = 0; i < DIAMETER; i++) { float iPosition = i - RADIUS; for (int j = 0; j < DIAMETER; j++) { data[i * DIAMETER + j] = new Vector2(iPosition, j - RADIUS).Length() <= RADIUS ? WHITE : BLACK; } } m_texture.SetData(data); } public float Radius { get { return RADIUS; } } public Vector2 Position { get { return m_position; } } public Vector2 Velocity { get { return m_velocity; } set { m_velocity = value; } } public void ApplyImpulse(Vector2 impulse) { Vector2 acceleration = impulse / MASS; m_velocity += acceleration; } public void Update(float dt) { m_position += m_velocity; // Euler integration - innaccurate and unstable but it will do for this simulation } public void Draw(SpriteBatch spriteBatch) { spriteBatch.Draw(m_texture, DrawRectangle, Color.White); } private Rectangle DrawRectangle { get { int x = (int)Math.Round(m_position.X - RADIUS); int y = (int)Math.Round(m_position.Y - RADIUS); return new Rectangle(x, y, DIAMETER, DIAMETER); } } } public class Boundary { private Vector2 m_point1; private Vector2 m_point2; private Vector2 m_normal; private float m_distance; public Boundary(Vector2 point1, Vector2 point2) { m_point1 = point1; m_point2 = point2; m_normal = new Vector2(); m_normal.X = point2.Y - point1.Y; m_normal.Y = point1.X - point2.X; m_distance = point2.X * point1.Y - point1.X * point2.Y; float invLength = 1.0f / m_normal.Length(); m_normal *= invLength; m_distance *= invLength; } public Vector2 Normal { get { return m_normal; } } public void PerformCollision(Ball ball) { float distanceToBallCenter = DistanceToPoint(ball.Position); if (distanceToBallCenter <= ball.Radius) { ResolveCollision(ball); } } public void ResolveCollision(Ball ball) { ball.Velocity = Vector2.Reflect(ball.Velocity, m_normal); } private float DistanceToPoint(Vector2 point) { return m_normal.X * point.X + m_normal.Y * point.Y + m_distance; } } public class World { Boundary m_left; Boundary m_right; Boundary m_top; Boundary m_bottom; public World(float left, float right, float top, float bottom) { m_top = new Boundary(new Vector2(right, top), new Vector2(left, top)); m_right = new Boundary(new Vector2(right, bottom), new Vector2(right, top)); m_bottom = new Boundary(new Vector2(left, bottom), new Vector2(right, bottom)); m_left = new Boundary(new Vector2(left, top), new Vector2(left, bottom)); } public void PerformCollision(Ball ball) { m_top.PerformCollision(ball); m_right.PerformCollision(ball); m_bottom.PerformCollision(ball); m_left.PerformCollision(ball); } } public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Matrix viewMatrix; Matrix inverseViewMatrix; Ball ball; World world; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; IsMouseVisible = true; } protected override void Initialize() { spriteBatch = new SpriteBatch(GraphicsDevice); ball = new Ball(GraphicsDevice); float right = Window.ClientBounds.Width * 0.5f; float left = -right; float bottom = Window.ClientBounds.Height * 0.5f; float top = -bottom; world = new World(left, right, top, bottom); viewMatrix = Matrix.CreateTranslation(Window.ClientBounds.Width * 0.5f, Window.ClientBounds.Height * 0.5f, 0.0f); inverseViewMatrix = Matrix.Invert(viewMatrix); base.Initialize(); } private void ProcessUserInput() { MouseState mouseState = Mouse.GetState(); Vector2 mousePositionClient = new Vector2((float)mouseState.X, (float)mouseState.Y); Vector2 mousePositionWorld = Vector2.Transform(mousePositionClient, inverseViewMatrix); if (mousePositionWorld != ball.Position) { Vector2 impulse = mousePositionWorld - ball.Position; impulse *= 1.0f / impulse.LengthSquared(); ball.ApplyImpulse(-impulse); } } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); float dt = (float)gameTime.ElapsedGameTime.TotalSeconds; ProcessUserInput(); ball.Update(dt); world.PerformCollision(ball); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, viewMatrix); ball.Draw(spriteBatch); spriteBatch.End(); base.Draw(gameTime); } } } 

难道你不是只取球的位置减去墙的位置,然后将该矢量标准化以获得你需要的东西而不用硬编码吗?

 Vector2 normal = Position - WallPosition; normal.Normalize(); 

你的其余代码应该是一样的。