XNA Rectangle intersection

ぃ、小莉子 提交于 2020-01-23 17:19:06

问题


I recently started on XNA development and having some (very limited) experience with it in the past, I decided to try and make a Pong clone to see how much I could remember. Most of what I knew came back to me, but I am having problems with collision detection between the bats and the ball. I have 3 rectangles set to update position along with the sprites that I am using, and when the ball rectangle intersects with a bat rectangle, I multiply the X speed of the ball by -1. However this produces an unusual effect by which the ball bounces around the bat as shown in this video.What am I doing wrong here?

Here is the code for this project (poor, I know):

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Pong
{

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    System.Random generator = new Random();

    Texture2D ball;
    Texture2D bat1;
    Texture2D bat2;
    Texture2D middle;

    Vector2 midPos;
    Vector2 bat1Pos;
    Vector2 bat2Pos;
    Vector2 ballPos;
    Vector2 ballVelo;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void Initialize()
    {
        base.Initialize();
    }

    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);

        //Load sprites
        ball = Content.Load<Texture2D>(@"sprites/pongball");
        bat1 = Content.Load<Texture2D>(@"sprites/pongbat");
        bat2 = Content.Load<Texture2D>(@"sprites/pongbat");
        middle = Content.Load<Texture2D>(@"sprites/pongmiddle");

        //Set default sprite positions
        midPos.X = (Window.ClientBounds.Width / 2) - 5;
        midPos.Y = 0;

        bat1Pos.X = 10;
        bat1Pos.Y = (Window.ClientBounds.Height/2) - 50;

        bat2Pos.X = Window.ClientBounds.Width - 20;
        bat2Pos.Y = (Window.ClientBounds.Height/2) - 50;

        ballPos.X = (Window.ClientBounds.Width / 2) - 5;
        ballPos.Y = (Window.ClientBounds.Height / 2) - 5;

        //Generate random ball velocity
        ballVelo.X = generator.Next(5,10);
        ballVelo.Y = generator.Next(4, 7);
    }

    protected override void UnloadContent()
    {

    }

    protected override void Update(GameTime gameTime)
    {


        //Update rectangle values
        Rectangle bat1Rect = new Rectangle((int)bat1Pos.X, (int)bat1Pos.Y, 10, 100);
        Rectangle bat2Rect = new Rectangle((int)bat2Pos.X, (int)bat2Pos.Y, 10, 100);
        Rectangle ballRect = new Rectangle((int)ballPos.X, (int)ballPos.Y, 10, 100);

        //Move ball
        ballPos += ballVelo;

        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();
        if (Keyboard.GetState().IsKeyDown(Keys.Escape))
            this.Exit();

        //Bat 1 movement and restriction
        if (Keyboard.GetState().IsKeyDown(Keys.Up))
            bat1Pos.Y -= 4;

        if (Keyboard.GetState().IsKeyDown(Keys.Down))
            bat1Pos.Y += 4;

        if (bat1Pos.Y <= 0)
            bat1Pos.Y = 0;

        if (bat1Pos.Y >= Window.ClientBounds.Height - 100)
            bat1Pos.Y = Window.ClientBounds.Height - 100;


        //Bat 2 movement and restriction
        if (Keyboard.GetState().IsKeyDown(Keys.W))
            bat2Pos.Y -= 4;

        if (Keyboard.GetState().IsKeyDown(Keys.S))
            bat2Pos.Y += 4;

        if (bat2Pos.Y <= 0)
            bat2Pos.Y = 0;

        if (bat2Pos.Y >= Window.ClientBounds.Height - 100)
            bat2Pos.Y = Window.ClientBounds.Height - 100;

        //Ball movement restrictions
        if (ballPos.X <= 0)
            ballVelo.X *= -1;

        if (ballPos.Y <= 0)
            ballVelo.Y *= -1;

        if (ballPos.X >= Window.ClientBounds.Width - 5)
            ballVelo.X *= -1;

        if (ballPos.Y >= Window.ClientBounds.Height - 5)
            ballVelo.Y *= -1;


        //Collision detection between bats and ball
        if (ballRect.Intersects(bat1Rect))
        {
            ballVelo.X *= -1;
        }

        if (ballRect.Intersects(bat2Rect))
        {
            ballVelo.X *= -1;
        }

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Black);

        spriteBatch.Begin();
        spriteBatch.Draw(middle, midPos, Color.White);
        spriteBatch.Draw(bat1, bat1Pos, Color.White);
        spriteBatch.Draw(bat2, bat2Pos, Color.White);
        spriteBatch.Draw(ball, ballPos, Color.White);
        spriteBatch.End();

        base.Draw(gameTime);
    }
}
}

回答1:


One way around this would be to do make the following change to your ballRect.Intersect(barXRect)) if statements:

    if (ballRect.Intersects(bat1Rect))
    {
        ballVelo.X = Math.Abs(ballVelo.X) * -1;
    }

    if (ballRect.Intersects(bat2Rect))
    {
        ballVelo.X = Math.Abs(ballVelo.X);
    }

This way the left bar will only send the ball right, and the right bar will only send it left. I may have the bars around the wrong way, so it might be best to double check, but it just means moving the * -1 to the other bat.




回答2:


Lyise's solution will work, but it does not attack the actual source of the problem. In bigger games you will require a more robust solution, so I'll explain the high level fix.

The issue is that you are changing the X velocity while the ball is still inside the bat. In the next frame, the most probable outcome is that the ball will move away, but not enough to exit the bat, so a new collision is detected. The speed is changed again, and the cycle repeats until the ball exits the bat by going below or above it.

The precise solution requires moving the ball out of the bat and then inverting the speed.
Options:

  • Return the ball to where it was at the beginning of the frame. The ball will never touch the bats. If the ball speed is high enough or the frame rate low enough, this will be noticeable.
  • Calculate how far into the bat the ball has gone, and substract that amount from the ball's position.
    // Colliding from the right
    impactCorrection = bat.Right - ball.Left;
    // Colliding from the left
    impactCorrection = bat.Left - ball.Right;

    For extra points you might move the ball away 2 * impactCorrection in X so the ball always travels the same distance every frame.


来源:https://stackoverflow.com/questions/5636607/xna-rectangle-intersection

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!