8

我正在做一个乒乓球游戏。在调试时,我一直在使用一个简单的 44 x 44 .png 红色正方形作为我的球。游戏在这个方块上运行良好。

当我尝试用正方形以外的任何东西替换纹理时,我看不到屏幕上绘制的球,我不知道为什么。我在 Photoshop 中使我的图像大小完全相同,并使用 .PNG 或 .JPG 并获得相同的结果,但我一生都无法弄清楚。你认为什么可能导致这个问题?

我在下面的文字中留下了我的球的代码。GameplayScreen 正在调用我的球的更新和绘制方法(使用 MS 的 GSM 示例)。

    public class Ball : IGameEntity
{
    #region Fields

    private Random rand;                // Random var
    private Texture2D texture;          // Texture for the ball
    private double direction;           // Directon the ball is traveling in                
    private bool isVisible;
    private bool hasHitLeftBat;         // Checked to see if the ball and bat have just collided
    private bool hasHitRightBat;        // Checked to see if the ball and bat have just collided
    private Vector2 ballPosition, resetBallPos, oldBallPos;
    private Rectangle ballRect;
    public float Speed;
    private SpriteBatch spriteBatch;    // Spritebatch
    private bool isBallStopped;
    private Vector2 origin;             // Locate the mid-point of the ball
    public float RotationAngle;
    private AIBat rightBat;             // Player's Bad
    private Bat leftBat;                // AI Bat
    private float ballRelativePos;
    private Rectangle rectangle3;       // Used to draw the collison rectangle
    private Texture2D blank;            // Texture to be drawn on the collision rectangle

    GameplayScreen gameplayScreen;      // Creates an instance of the GameplayScreen
    Game1 gameInstance;                 // Creates an instance of the Game1 class
    int selectedStage;                  // Pass this into GameplayScreen for selecting easy, medium, or hard


    #endregion

    #region Constructors and Destructors

    /// <summary>
    /// Constructor for the ball
    /// </summary>
    public Ball(ContentManager contentManager, Vector2 ScreenSize, Bat bat, AIBat aiBat)
    {
        Speed = 15f;
        texture = contentManager.Load<Texture2D>(@"gfx/balls/redBall");
        direction = 0;
        ballRect = new Rectangle(0, 0, texture.Width /2, texture.Height /2);
        resetBallPos = new Vector2(ScreenSize.X / 2 + origin.X, ScreenSize.Y / 2 + origin.Y);
        ballPosition = resetBallPos;
        rand = new Random();
        isVisible = true;
        origin = new Vector2(texture.Width / 2, texture.Height / 2);
        leftBat = bat; // Creates a new instance of leftBat so that I can access Position.X/Y for LeftBatPatcicles()
        rightBat = aiBat;// Creates a new instance of leftBat so that  can access Position.X/Y for RightBatPatcicles()
        gameplayScreen = new GameplayScreen(null, selectedStage);
        gameInstance = new Game1();
        Rectangle rectangle3 = new Rectangle();
        blank = contentManager.Load<Texture2D>(@"gfx/blank");               

        //   pes = new ParticleEmitterService(game);
    }

    public Ball(Bat myBat)
    {
        leftBat = myBat;          // this assigns and instantiates the member bat
                                  // with myBat which was passed from the constructor
    }

    #endregion

    #region Methods

    /// <summary>
    /// Draws the ball on the screen
    /// </summary>
    public void Draw(SpriteBatch spriteBatch)
    {
        if (isVisible)
        {
            // Draws the rotaing ball
            spriteBatch.Draw(texture, ballPosition, ballRect, Color.White,
                              RotationAngle, origin, .0f, SpriteEffects.None, 0);

            spriteBatch.Draw(blank, rectangle3, Color.LightCoral);
        }
    }

    /// <summary>
    /// Updates position of the ball. Used in Update() for GameplayScreen.
    /// </summary>
    public void UpdatePosition(GameTime gameTime)
    {
        ballRect.X = (int)ballPosition.X;
        ballRect.Y = (int)ballPosition.Y;
        oldBallPos.X = ballPosition.X;
        oldBallPos.Y = ballPosition.Y;

        ballPosition.X += Speed * ((float)Math.Cos(direction));

        ballPosition.Y += Speed * ((float)Math.Sin(direction));
        bool collided = CheckWallHit();


        // Stops the issue where ball was oscillating on the ceiling or floor
        if (collided)
        {
            ballPosition.X = oldBallPos.X + Speed * (float)1.5 * (float)Math.Cos(direction);
            ballPosition.Y = oldBallPos.Y + Speed * (float)Math.Sin(direction);
        }

        // As long as the ball is to the right of the back, check for an update
        if (ballPosition.X > leftBat.BatPosition.X)
        {
            // When the ball and bat collide, draw the rectangle where they intersect
            BatCollisionRectLeft();
        }

        // As longas the ball is to the left of the back, check for an update
        if (ballPosition.X < rightBat.BatPosition.X)
        {   // When the ball and bat collide, draw the rectangle where they intersec
            BatCollisionRectRight();
        }

        // The time since Update was called last.
        float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

        // Rotation for the ball
        RotationAngle += elapsed;
        float circle = MathHelper.Pi * 2;
        RotationAngle = RotationAngle % circle;

        //      base.Update(gameTime);
        gameInstance.update();

    }


    /// <summary>
    /// Checks for the current direction of the ball
    /// </summary>
    public double GetDirection()
    {
        return direction;
    }

    /// <summary>
    /// Checks for the current position of the ball
    /// </summary>
    public Vector2 GetPosition()
    {
        return ballPosition;
    }

    /// <summary>
    /// Checks for the current size of the ball (for the powerups)
    /// </summary>
    public Rectangle GetSize()
    {
        return ballRect;
    }



    /// <summary>
    /// Checks to see if ball went out of bounds, and triggers warp sfx. Used in GameplayScreen.
    /// </summary>
    public void OutOfBounds()
    {
        AudioManager.Instance.PlaySoundEffect("Muzzle_shot");
    }

    /// <summary>
    /// Speed for the ball when Speedball powerup is activated
    /// </summary>
    public void PowerupSpeed()
    {
        Speed += 20.0f;
    }

    /// <summary>
    /// Check for where to reset the ball after each point is scored
    /// </summary>
    public void Reset(bool left)
    {
        if (left)
        {
            direction = 0;
        }
        else
        {
            direction = Math.PI;
        }

        ballPosition = resetBallPos; // Resets the ball to the center of the screen
        isVisible = true;
        Speed = 15f; // Returns the ball back to the default speed, in case the speedBall was active
        if (rand.Next(2) == 0)
        {
            direction += MathHelper.ToRadians(rand.Next(30));
        }
        else
        {
            direction -= MathHelper.ToRadians(rand.Next(30));
        }
    }

    /// <summary>
    /// Shrinks the ball when the ShrinkBall powerup is activated
    /// </summary>
    public void ShrinkBall()
    {
        ballRect = new Rectangle(0, 0, texture.Width / 2, texture.Height / 2);
    }

    /// <summary>
    /// Stops the ball each time it is reset. Ex: Between points / rounds
    /// </summary>
    public void Stop()
    {
        isVisible = true;
        Speed = 0;
        isBallStopped = true;
    }

    /// <summary>
    /// Checks for collision with the ceiling or floor. 2*Math.pi = 360 degrees
    /// </summary>
    private bool CheckWallHit()
    {
        while (direction > 2 * Math.PI)
        {
            direction -= 2 * Math.PI;
            return true;
        }

        while (direction < 0)
        {
            direction += 2 * Math.PI;
            return true;
        }

        if (ballPosition.Y <= 0 || (ballPosition.Y > resetBallPos.Y * 2 - ballRect.Height))
        {
            direction = 2 * Math.PI - direction;
            return true;
        }
        return true;
    }

    /// <summary>
    /// Used to determine the location where the particles will initialize when the ball and bat collide
    /// </summary>
    private void BatCollisionRectLeft()
    {
        // For the left bat
        if (ballRect.Intersects(leftBat.batRect))
        {
            rectangle3 = Rectangle.Intersect(ballRect, leftBat.batRect);
        }
    }

    /// <summary>
    ///Checks for collision of Right Bat
    /// </summary>
    private void BatCollisionRectRight()
    {
        // for the right bat
        if (ballRect.Intersects(rightBat.batRect))
        {
            rectangle3 = Rectangle.Intersect(ballRect, rightBat.batRect); ;
        }
    }
4

4 回答 4

3

2 things that i'm noticing, I don't think you want to halve your rectangle dimentions when you call

ballRect = new Rectangle(0, 0, texture.Width /2, texture.Height /2);

also, you're drawing this blank sprite on top of your ball, so that if they overlap, you won't see the ball, but if that's the case I don't know why it worked with the square.

于 2012-08-08T17:08:38.360 回答
2

You're passing ballRect into Draw() as the source rectangle, which specifies where the sprite exists on the texture. If you want to use the entire texture, specify null instead. Otherwise, make sure that the value of ballRect always falls within the texture.

You seem to be using it to keep track of the sprite's position on the screen. From your UpdatePosition function:

ballRect.X = (int)ballPosition.X;
ballRect.Y = (int)ballPosition.Y;

This is producing texture coordinates outside of the bounds of the texture, which are being clamped by the sampler. With a texture that consists entirely of red pixels, this appears to work, because the pixels around the edge of the texture are all red. In a texture with a transparent border, the sprite is going to be clamped to that transparent color instead.

于 2012-08-08T20:36:46.650 回答
2

You shouldn't be passing anything as your SourceRect parameter in your draw call unless you don't wan't to draw the entire image.

The way you have it set up now is you're passing 'ballRect' as your SourceRect when trying to draw your ball, and that ballRect parameter is being updated according to the ball's position so you're trying to draw a portion of your image that is way outside of the size of the texture.

If you want to draw the entire ball, just use:

spriteBatch.Draw(texture, ballPosition, null, Color.White,
                          RotationAngle, origin, .0f, SpriteEffects.None, 0);

If you only wanted to draw the top-left quadrant of the ball, you could pass in the following rectangle as your SourceRect:

Rectangle sourceRect = new Rectangle(0, 0, texture.Width / 2, texture.Height / 2);

Then you could use that in your Draw call:

spriteBatch.Draw(texture, ballPosition, sourceRect, Color.White,
                          RotationAngle, origin, .0f, SpriteEffects.None, 0);

EDIT: You're also passing .0f as your "scale" parameter so when it does draw your ball it will be 0 pixels in size, which I'm guessing isn't the intended behavior. Using 1f for your scale will draw it at it's default size.

于 2012-08-08T22:20:31.167 回答
0

Something I noticed is that in the line

resetBallPos = new Vector2(ScreenSize.X / 2 + origin.X, ScreenSize.Y / 2 + origin.Y);

inside your constructor is accessing the properties of the variable 'origin' before you actually assign any value to it; however, you do assign a value to it, just a few lines below this value. I'd move this line:

origin = new Vector2(texture.Width / 2, texture.Height / 2);

to be above it. I would think that should fix your issue; in the transition from red block to proper image, you may have changed more then you thought.

Also, as a side note, when you're working with XNA, try to use mainly PNG images. They're small and load very quickly; additionally, they support transparent background images.

于 2012-08-08T18:05:44.767 回答