Learning Monogame

Discussion in 'Programming' started by Remixful, Oct 23, 2014.

  1. Remixful

    Remixful Active Member

    I don't know.. I'm still having so many problems with the collisions...
    Maybe I'm just getting confused.. but the ball is stuck on the paddle now, and it stops at the X position of the ball rectangle. First I want to get it unstuck, and also I want the collision detection to occur when the Ball Rectangle's width touches the paddle...
    [​IMG]
    This collision stuff is really getting over complicated, especially for just a pong game..
    Lol.. you won't believe how stressed I have gotten over this, I've spent more than enough hours just trying to find a satisfying method, but like I said, my logic is one of my weaknesses.
    I just want a smooth method to detects collisions that I understand, and can also use for games in the future.
    I mean, of course I can just finish this game with shitty collisions, and I know I shouldn't expect it to be perfect, but it just doesn't satisfy me.
    If you have any methods I can use, at least the simplest way, just explain a bit more, in the simplest way you can, if you could please, because I have trouble understanding things quickly.
    Last edited: Nov 2, 2014
  2. francot514

    francot514 Well-Known Member

    You can post the collision code snippet please, to understand better, what are you doing...
  3. Remixful

    Remixful Active Member

    Code:
      bool hitSomething = false;
                        for (int i = 1; i <= Ball.CurrentSpeed; i++)
                        {
                            if (!hitSomething)
                            {
                                if (CheckCollision(ballCollisionRect, playerCollisionRectMiddle, "right"))
                                {
                                    SoundManager.playSound(SoundManager.Sounds.playerHit);
                                    Ball.Rebound(Ball.Direction.Straight);
                                    Ball.lastHit = Ball.Direction.Straight;
                                    Ball.Position.X -= playerCollisionRectMiddle.Width;
                                    hitSomething = true;
                                }
                                if (CheckCollision(ballCollisionRect, playerCollisionRectTop, "right"))
                                {
                                    SoundManager.playSound(SoundManager.Sounds.playerHit);
                                    Ball.Rebound(Ball.Direction.Top);
                                    Ball.lastHit = Ball.Direction.Top;
                                    Ball.Position.X -= playerCollisionRectMiddle.Width;
                                    hitSomething = true;
                                }
                                if (CheckCollision(ballCollisionRect, playerCollisionRectBottom, "right"))
                                {
                                    SoundManager.playSound(SoundManager.Sounds.playerHit);
                                    Ball.Rebound(Ball.Direction.Bottom);
                                    Ball.lastHit = Ball.Direction.Bottom;
                                    Ball.Position.X -= playerCollisionRectMiddle.Width;
                                    hitSomething = true;
                                }
                                /////
                                if (Ball.Direction2 == Ball.Direction2s.Right)
                                {
                                    Ball.Position.X += 1;
                                }
                                else if (Ball.Direction2 == Ball.Direction2s.Left)
                                {
                                    Ball.Position.X += -1;
                                }
                                else if (Ball.Direction2 == Ball.Direction2s.NorthWest)
                                {
                                    Ball.Position.X += -1;
                                    Ball.Position.Y += -0.5f;
                                }
                                else if (Ball.Direction2 == Ball.Direction2s.SouthWest)
                                {
                                    Ball.Position.X += -1;
                                    Ball.Position.Y += 0.5f;
                                }
                            }
                        }
    Then we have the CheckCollision Method.. Pretty messy...

    Code:
      bool CheckCollision(Rectangle rect1, Rectangle rect2, string direction)
            {
                if (direction == "right")
                {
                    if (rect1.X + rect1.Width >= rect2.X &&
                        rect1.X + rect1.Width <= rect2.X +rect2.Width &&
                    ((rect1.Y <= rect2.Height + rect2.Y &&
                    rect1.Y >= rect2.Y) ||
                    (rect1.Y + rect1.Height <= rect2.Height + rect2.Y &&
                    rect1.Y + rect1.Height >= rect2.Y))
                    )
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                if (direction == "left")
                {
                    //rect1 = ball
                    //rect2 = enemy
    
                    //if (rect1.X  == rect2.X + rect2.Width &&
                    //((rect1.Y <= rect2.Height + rect2.Y &&
                    //rect1.Y >= rect2.Y) ||
                    //(rect1.Y + rect1.Height <= rect2.Height + rect2.Y &&
                    //rect1.Y + rect1.Height >= rect2.Y)))
                    if (rect1.X <= rect2.X + rect2.Width)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                if (direction == "bottom")
                {
                    //rect1 = ball
                    //rect2 = wall1
                    if (rect1.Y <= rect2.Y + rect2.Height)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                if (direction == "top")
                {
                    //rect1 = ball
                    //rect2 = wall1
                    if (rect1.Y + rect1.Width >= rect2.Y)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                return false;
            }
    I made it so the ball's position sets back to the position it should be, before hitting the paddle.
    But for a quick second, you can still see the ball going past the paddle's X.. But I thought I fixed this with the for loop? But the game is still not smooth... I don't know anymore... Maybe it's not so bad for a fast paced game???
    I still think that's the wrong attitude to have though.

    [​IMG]
    (Sorry for the cheap gif recording, it's pretty slow, So it doesn't capture at the framerate the ball moves. Also I have a hotkey (F11) used to reset the scene back to the menu and reset values instead of restarting the game over and over. )

    Man.. I never thought collisions could be such a bitch.. It's the only damn thing stopping me from finishing this game. But I don't feel achieved if I can't get the game to at least be smooth.
    Last edited: Nov 2, 2014
  4. francot514

    francot514 Well-Known Member

    I can tell you some things, but i have dont understand at all, this is why is better to share source code.

    -Why are you using rectangles to check collision:

    Code:
    if (CheckCollision(ballCollisionRect, playerCollisionRectBottom, "right"))
                                {
                                    SoundManager.playSound(SoundManager.Sounds.playerHit);
                                    Ball.Rebound(Ball.Direction.Bottom);
                                    Ball.lastHit = Ball.Direction.Bottom;
                                    Ball.Position.X -= playerCollisionRectMiddle.Width;
                                    hitSomething = true;
                                }
    Who is ball, and who is player?? Better will be to detect collision between ball(class) x position, y position and player (class) x position, y position.

    Something like this:
    bool CheckCollision(player(class) player, ball(class) ball, string direction)
    {
    if (direction == "right")
    {
    if (ball.x + ball.Width >= player.x - player.Width (to avoid position intersection)
    && (ball.y + ball.Heigh >= player.y + player.Heigh)
    {
    return true;
    }

    The code must be relative, based on player, ball movement, that i dont understand well:
    Code:
    Ball.Rebound(Ball.Direction.Bottom);
                                    Ball.lastHit = Ball.Direction.Bottom;
                                    Ball.Position.X -= playerCollisionRectMiddle.Width;
    What is ball.rebound???
  5. Remixful

    Remixful Active Member

    It's a method I created, that's not yet finished.
    Code:
    static public void Rebound(Direction direction)
            {
                ///PLAYER REBOUNDS
                if (direction == Direction.Straight)
                {
                    //CurrentSpeed *= -1;
                    Direction2 = Direction2s.Left;
                }
                if (direction == Direction.Top)
                {
                    Direction2 = Direction2s.NorthWest;
                }
                if (direction == Direction.Bottom)
                {
                    Direction2 = Direction2s.SouthWest;
                }
                ///Enemy REBOUNDS
                if (direction == Direction.EnemyStraight)
                {
                    CurrentSpeed *= -1;
                }
            }
    Thanks for the tips, I'll try to check collisions on the classes instead of rectangles. I'll get working on it , and tell you the outcome.
  6. Remixful

    Remixful Active Member

    Yeah um, there's a problem with your method I forgot to even mention. XD

    I have 3 different collisions, the top of the paddle, middle, then bottom.
  7. francot514

    francot514 Well-Known Member

    Yes, i resumed the method, you have to implement it in your way, but of course do not use extra rectangles, use the (classes) properties, like width, height, x, y instead...
  8. RHY3756547

    RHY3756547 FreeSO Developer Staff Member Moderator

    Rectangle has a built in method intersects() which you can use for this kind of simple collision. Just make sure your rectangle positions and collision response are correct too!
  9. Remixful

    Remixful Active Member

    Yeah um, I already tried both .Intersects() and my method, the rectangle won't stop before the Width * X touches the X of rectangle I'm checking the collision for.
    Also, don't forget that it is in the for loop which loops like this:

    for (int i = 1; i <= Ball.Speed; i++)
    {
    Ball.Position.X += 1;
    }

    (Ball speed is also 20)

    Also, what's so bad about using Rectangles, as to just using Class properties, Francot?

    Today, I'm going to rewrite a lot of the code, and try to go for a more organized approach, so I'm not mixed in a complicated mess. I'm going to first finish Enemy and Player collisions with only hitting the ball straight, meaning I won't do Player and Enemy top, middle, and bottom.
    Last edited: Nov 4, 2014
  10. RHY3756547

    RHY3756547 FreeSO Developer Staff Member Moderator

    Did you make sure the rectangle positions and your collision response code are correct, like I said? There's no point in detecting collision if you don't react to it correctly. One possibility is that once colliding you could be performing the rebound twice, meaning there is no net direction change as it flips the other way then back.
  11. Remixful

    Remixful Active Member

    If you don't mind, going to do a little journal entry on this as I finish the game. This is going to be pretty long, I just feel like sharing this.
    Why am I rewriting all this code?
    - I'm not rewriting everything, but how most things function now. Also it's great practice, since I'm really lazy.

    Step 1:
    -Testing if the ball stops at the x of the player's paddle.
    Create a detectCollision method, return type bool, call the method every time in the for loop, also make sure to create the ball rectangle after the for loop. (which I didn't do last time, the ball rectangle was drawn before.) Also this time, the Ball class has more control of ball movement.
    Code:
            bool detectCollisions()
            {
                if (Ball.Position.X + Ball.Size.X >= Player.Position.X)
                {
                    return true;
                }
    
                return false;
    
            }
    Step 2:
    Make sure the ball stops exactly at the right pixel. (The ball will stay there forever because the for loop always breaks if the detectCollision method returns true.
    [​IMG]

    Step 2:
    - Make the ball rebound off of the paddles.
    Made the rebound method, now I'm trying to get a perfect screenshot of the ball hitting the paddle, to see if the pixels are exact. I didn't want to keep restarting the game, so I made the ball rebound East when Ball.X reaches 0. I also fixed up the detectCollision method and for loop.
    Code:
            bool detectCollisions(out Ball.HitTypes hitType)
            {
                if (Ball.Position.X + Ball.Size.X >= Player.Position.X)
                {
                    hitType = Ball.HitTypes.PlayerMiddle;
                    return true;
                }
    
                if (Ball.Position.X <= 0)
                {
                    hitType = Ball.HitTypes.EnemyMiddle;
                    return true;
                }
    
                hitType = Ball.HitTypes.None;
                return false;
    
            }
    The detect collision now requires a Ball HitType out parameter to determine what type of collision it was.
    The for loop now uses this new change for the Ball.Rebound method to determine which direction to change the ball.
    Code:
                    for (int i = 1; i <= Ball.CurrentSpeed; i++ )
                    {
                        Ball.HitTypes hitType;
                        if (detectCollisions(out hitType))
                        {
                            Ball.Rebound(hitType);
                            Ball.Move();
                            break;
                        }
    
                        Ball.Move();
                    }
    
    ///////////////////// THIS IS THE BALL.REBOUND METHOD
            static public void Rebound(HitTypes hitType)
            {
                switch (hitType)
                {
                    case HitTypes.PlayerMiddle:
                        CurrentDirection = Directions.West;
                        break;
                    case HitTypes.EnemyMiddle:
                        CurrentDirection = Directions.East;
                        break;
                }
            }
    Also the reason why I wanted to try and get the perfect screenshot of the ball moving 13 pixels every Draw() call was to make sure the ball never went past the paddle.
    [​IMG] (Sorry for not showing the pixel grid in this one. But that's one pixel)
    All I wanted is that one more pixel, then the ball goes back. I tried to figure this one out for a while, but I just couldn't. So I just decided to move along, ignore that one pixel off, because the ball is moved at such a speed that I doubt anybody will have time to notice that one pixel off, no matter what...
    (Like 1 hour later)
    Ok, I finally found out the ball wasn't ever a pixel off, I'm not sure why, maybe I could NEVER get that screenshot of the ball on that one pixel, but some how have a million other photos with the ball one pixel off. I set the speed to 1, and it actually did hit the paddle. The speed shouldn't make any differences because I even tested with a speed with 100, it still never passes the paddle.
    Next, I extend the collision method to only detect when the ball hits the paddle, not just the X.
    Code:
    if (
                    Ball.Position.X + Ball.Size.X == Player.Position.X &&
                    (Ball.Position.Y >= Player.Position.Y || Ball.Position.Y + Ball.Size.Y >= Player.Position.Y) &&
                    (Ball.Position.Y <= Player.Position.Y + Player.Size.Y || Ball.Position.Y + Ball.Size.Y <= Player.Position.Y + Player.Size.Y)
                    )
                {
                    hitType = Ball.HitTypes.PlayerMiddle;
                    return true;
                }
    Next, we have the Enemy collision, but first we need to make the Enemy follow the Ball's Y (Since I actually made the Enemy start elsewhere on the Y axis). This method will also be used as the Enemy's "AI", but not yet, and it's not going to be exactly the Ball's Y, but near it.
    I actually have a method already, from earlier, set up.
    Code:
            static public void UpdateEnemy()
            {
                Enemy.Position.Y = Ball.Position.Y + (Ball.Size.Y / 2) - (Enemy.Size.Y / 2);
                if (Enemy.Position.Y + Enemy.Size.Y >= BottomWallY - 5)
                {
                    Enemy.Position.Y = (BottomWallY - Enemy.Size.Y) - 5;
                }
                if (Enemy.Position.Y <= TopWallHeight + 5)
                {
                    Enemy.Position.Y = (TopWallHeight) + 5;
                }
            }
    [​IMG]
    Oh, the fun and possibilities. And the horrible GIF recording too. But actually, you can see the ball actually hits the paddle once, on the right, which relieves me of that worry I still have on the "one or more pixels off" collision detection. On the left you can see one frame in the GIF of the ball hitting the enemy paddle, but one pixel off. This is because the ball is moved 1 pixel to the right in less than milliseconds, to make sure the ball isn't still being detected as a collision, and can move along to the East.
  12. Remixful

    Remixful Active Member

    Step 3-
    What next? Time to set up the paddle's to have a top, middle, bottom, and add detection for the corresponding areas.
    Just to play around a little, and really test the detection, I updated Ball.Move() to always set Ball.Position.Y to Player.Position.Y. Fun.

    [​IMG]
    Now I was thinking, should I just make 3 different rectangles drawn as the top bottom left, and put together? But then I just thought, nah, I should just go with having 3 struct in the Player class.
    Code:
        struct Area
        {
            public Vector2 Position;
            public Point Size;
    
            public Area(Vector2 position, Pointsize)
            {
                this.Position = position;
                this.Size = size;
            }
        }
    We now have this is the player class.
    Code:
            //PLAYER TOP, MIDDLE, BOTTOM
            static public Area Top;
            static public Area Middle;
            static public Area Bottom;
    
            ////
    I need to now set the Top, Middle, and Bottom sizes, and positions. This I'm going to make sure the areas (Top, middle, and bottom) are more accurate and less messy. The paddle is also a height of 102, so it could be sliced into 3 evenly. (those 3 parts will be the top, middle, and bottom)
    After I was done, I made rectangles for debug mode to show these areas being drawn. I took a screenshot of the paddle (with the areas) and made sure the pixels were exactly 34. (102 / 3)

    Next up, I need separate collision detection for each area. I changed the if statement for the middle, since the box needs to be exactly in between the middle, anything higher or lower will be considered top or bottom.
    Code:
                if (
                    Ball.Position.X + Ball.Size.X == Player.Middle.Position.X &&
                    (Ball.Position.Y >= Player.Middle.Position.Y &&
                    Ball.Position.Y + Ball.Size.Y <= Player.Middle.Position.Y + Player.Middle.Size.Y)
                    )
                {
                    hitType = Ball.HitTypes.PlayerMiddle;
                    Ball.lastHit = hitType;
                    return true;
                }
    Now, I have the top and bottom. Here's something I bumped into. What if the ball touches both the top and middle? Let's make it random.
    I made an if statement and that's when I noticed my enemy...
    [​IMG]
    This could be what was happening last time.. The ball is dealing with 2 collisions at once. Never mind me.. I just made a simple mistake in the if statement. XD
    Actually took me a while to get the in between if statement all settled... (Actually had to rewrite it a few times)
    Finally the collision method is done for the player.. Later I have to repeat the same thing for the Enemy. For now, anything that hits the enemy will go eastward.
    I don't want to make this post longer than it already is going to be (85 lines more, exactly) so I just pasted the code here, if you are interested in seeing how the detectCollisions method turned out so far.

    Phew.. this is a lot of work for a pong game.. isn't it... o.o

    Step 4-
    Next, make the Ball's Y change, NorthWest if hitting Player's Top, SouthWest if hitting Player's Bottom, and bounce off the walls.

    Since I already have the Rebound and Move methods set up to easily adapt to these changes, I just have to edit. So I was testing things, the Y looks fine to me, but then guess what I ran into?
    [​IMG]

    If I slid the paddle at a fast speed the ball stops.. right in it's tracks. Doesn't go anywhere. Well, back to the old drawing board.
    (10 minutes later..)
    Code:
     switch (CurrentDirection)
                {
                    case Directions.East:
                        Position.X += 1;
                        break;
                    case Directions.West:
                        Position.X -= 1;
                        break;
                    case Directions.NorthWest:
                        Position.X -= 1;
                        Position.Y -= .5F;
                        break;
                }
    Where's SouthWest, Remixful? xd
    Fail. I forgot to finish the Ball.Move() method xD

    Phew... That's a relief. I really thought I had to spend hours on the collision method.

    [​IMG]

    In the video, you see that the Y changes depending on the area it hits. (You can also see that the "LastHit" value changes in the text when i turn on debug mode)
    Just so you know, I have all the Hit Types in the enum already.
    Code:
            public enum HitTypes
            {
                None,
                PlayerBottom,
                PlayerTop,
                PlayerMiddle,
                EnemyBottom,
                EnemyTop,
                EnemyMiddle,
                TopWall,
                BottomWall,
                EnemyGoal,
                PlayerGoal
            }
  13. Remixful

    Remixful Active Member

    Step 4-
    Next up... Wall Collisions, Enemy Areas, and Enemy Area Collisions.. Woo Hoo! (sigh...)

    This time, I'm going to add the directions for the Enemy and Walls in the Ball.Move and Ball.Rebound methods first, so I don't forget them.
    The Wall hits change the direction depending on which direction it's coming from.

    Code:
                    case HitTypes.TopWall:
                        if (CurrentDirection == Directions.NorthEast)
                        {
                            CurrentDirection = Directions.SouthEast;
                        }
                        else if (CurrentDirection == Directions.NorthWest)
                        {
                            CurrentDirection = Directions.SouthWest;
                        }
                        break;
                    case HitTypes.BottomWall:
                        if (CurrentDirection == Directions.SouthEast)
                        {
                            CurrentDirection = Directions.NorthEast;
                        }
                        else if (CurrentDirection == Directions.SouthWest)
                        {
                            CurrentDirection = Directions.NorthWest;
                        }
                        break;
    I have a feeling I'm going to have to mess around with the Y in the future though, but for now it'll stay at 0.5f.
    I'm going to make the Top wall and bottom wall both of type struct Area. After that, I make the collision detection for both walls, and it seems like 0.5f makes the ball always land in the same area. So why don't I make a Y speed variable to spice things up? Also I can make it random. While doing this.. I noticed one thing.. That this is what starts causing the Ball to go past the collisions. So I need to do this elsewhere.. in the for loop.
    Give me a few minutes...
    (About 2 hours later.. o.o)
    I started thinking about angles.. To change the angle, I need to change the X and Y, but how do I change the X because it will just remove the purpose of the for loop.. Then.. Aha! The ball speed needs to be changed, which affects the for loop. No, no no.. that won't work... That only speeds up the pixels moving by 1...

    Ok blah blah blah, whatever. The way the ball is moving right now is fine, and looks like similar to the game.
    Time to move on to the damn Enemy Areas. >.<
    I don't really need to go over this process again, it's made the same exact way as the Player, same height and width. Just need to change everything that says Player to Enemy.. so just going to skip it. All 3 areas are finished now onto Collision. The if statements are close to Player's but coming from a left side. (Meaning all >= for X will be changed to <= and stuff)
    Again, just a simple copy and paste and a few edits. (Yes, I could code it, but I see no need, besides just practice. Also I'm lazy.)
    Alright finished what that. Now for the Enemy AI so I can actually test out the top and bottom, since right now it's always the middle.
    Did some research on Pong AI, since I'm not that smart myself..
    I found something online.
    Still.. the AI took me a while to make.. I searched up some more..
    (A hour or so later...)
    Still trying to make a decent AI.. o.o On the way I fixed the detection a bit so there's no sitting on top of the paddle or just going through. (pretty easy)
    (More hours later)
    Ok the most simple way to do this is.. Just make the paddle go to the ball. But not fast enough.... Easier said than done still.
    Also found out the ball should always be moving by at least a little Y. But ugh.. how do I achieve this since the wall doesn't know which way to bounce the ball back. Do'h, never mind.
    Everything is fixed now. Now to make the enemy slower and stop doing that stupid jumping up and down dance when the ball is the same y as the Enemy. -.-
    (Going to take a small break now.. maybe I'm over complicating this..)
    Ok, I know the problem. The Enemy paddle is always moving too much when it isn't the Ball.Position.Y, that's pretty obvious.
    (later...)
    Well I'm stuck.. I'm not sure how to move the Enemy's Y pixel by pixel, but at a fast speed...

    I'm going to take a break.. I'll continue this later. Gotta take a break. If anyone has suggestions, please tell me!
    Sorry this was so long. o.o (Had to fit this into 3 posts since it was more than 1000 characters)
  14. francot514

    francot514 Well-Known Member

    Take it easy bro, youre writing so much stuff, and getting me confused...

    Code:
    Also, what's so bad about using Rectangles, as to just using Class properties, Francot?
    I just recommneded you to do that, to do a more precisely pixel collision and not rectangular, why not??? becuase not all images will be rectangle base, and if you use it for balls, will not work in the best way...

    [​IMG]
  15. Remixful

    Remixful Active Member

    My bad, I wrote that all in like 12 hours or more. Probably best not to read it all at once? Not sure lol.

    Alright and I did do the class properties and such.
  16. Remixful

    Remixful Active Member

    I've tried so many different methods, and I can't get the AI to be good, so I'm just going to finish the game with a sloppy AI, sadly.
  17. francot514

    francot514 Well-Known Member

    Can you post some snippet of the ai methods youre using???, i want to know what have you done...
  18. Remixful

    Remixful Active Member

    I finished the game, the only thing that is really cheap is the enemy AI, and the ball.
    I want to start trying out with Tile Engines, and I want to to try remaking a game boy game or two in the future. I'm going to research more on XNA, and follow some more tutorials, like this for Tile Engines.

    If you want the source for the pong game, here it is. That game was pretty stressful to make surprisingly, I probably over complicated it. I'm definitely not satisfied with the outcome, though.
  19. RHY3756547

    RHY3756547 FreeSO Developer Staff Member Moderator

    Did you use to have any projects on your scratch account? It probably might have been a better idea to try making some games there first.
  20. Remixful

    Remixful Active Member

    I always deleted my games, but yes I've made multiple games such as Pokemon, and Mario. I also recreated a decent Pokemon battle engine a while back. I've been a member of Scratch since 2009, and I was never too keen on actually uploading my games.
    Last edited: Nov 6, 2014

Share This Page