XNA - Pong Clone - Reflective ball when it hits the wall?

I am trying to make the ball bounce off the top and bottom "Walls" of my user interface when creating a 2D clone. This is my 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 < 0 || ball.Position.X >= graphics.PreferredBackBufferWidth) ball.Reset(); } 

I am currently using this in my 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; } 

It works, but I'm using a manual typed Normal, and I want to know how to calculate the normal top and bottom of the screen?

+4
source share
4 answers

Well, so I could handle it

 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; } 

Please note, however, that using this β€œdiscrete” collision detection, you wait until the ball passes the top / bottom of the screen to detect a collision; collisions that occur between shots can be noticeably turned off, especially if the ball moves fast. This is especially a problem if you use this collision detection method to detect collisions with an oar, if the ball moves fast enough , the ball can move right through the oar!

The solution to this problem is to use the so-called Continuous Conflict Detection . CCDs are usually much more complex than discrete collision detection; fortunately, pong is simple enough that CCD will only be a little trickier. However, to solve the equations you still need a solid knowledge of high school algebra.

If you are still interested, there is a good explanation of CCD in this lecture and this GameDev article goes in more detail. There are also many questions related to him on SO.

+4
source

You can change boolean IsCollidingWithWall with some enumeration like:

 enum CollideType { None, Vertical, Horizontal } 

and check this type when creating normal.

+1
source

Each of the boundaries in your world is a line. One side of the line is solid and the other is not. The usual one you are trying to calculate is part of the equation for this line. It indicates the non-continuous side of the line. Another part of the linear equation is the distance from the line to the origin. The equation for a line can be found from two points on this line. You can define these two points based on the coordinates in the playing space where you need the wall.

The rate is calculated by rotating the line segment, defined by two points of 90 degrees, and then normalizing.

 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; } 

You use the preferred width and height of the buffer to determine your space in the world, to use them to determine the points used to calculate the normals.

 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)); 

Please note that points must be indicated in clockwise order so that normal points are in the right direction.

The following XNA 4.0 program demonstrates the use of these concepts:

 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<uint>(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); } } } 
+1
source

Could you just take the position of the ball minus the position of the wall and then normalize this vector to get what you need without hard coding it?

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

The rest of your code should work the same.

0
source

Source: https://habr.com/ru/post/1335273/


All Articles