Showing posts with label indiedev. Show all posts
Showing posts with label indiedev. Show all posts

Thursday, September 21, 2017

It Is Finished!

I did it. I have finally finished a substantial hobby coding project for the first time since...high school, I believe. That was 12 years ago, so yeah, it's been a while. And I have actually made my first video game, to boot. Now, by "finished" I mean "works well enough to be functional and playable". I know I haven't fixed every bug. And I don't really intend to. My goal when I started working on this in January was just to make something that worked; something that I could use to begin to learn. And I must say, it's been really fun so far! :)

I also decided to upload it to the indie game website itch.io as a free download, just because I could.


But anyway, time for a final change log!
  • Remove the incomplete controller support
    I had originally planned for this to have controller support but eventually it just felt like a waste of time, so I gave up haha.
  • Add sounds
    Pretty self explanatory, but this was a big one. It's amazing how even the simplest of sounds can make a game so much more enjoyable. I had downloaded a few free collections of random sound effects and just started going through them until I found the right blips and bloops, lol. I added sounds to the main menu and paused/game over menu, ball-to-paddle collision, and when the ball goes out of bounds. I didn't record any video to demonstrate this, so you'll just have to play it for yourself if you want to hear it in all its glory. ;)
  • Redo the paused and game-over text so it's readable over the dotted line in a 2-player game, and also add a simple menu that allows you to restart/play again and return to the title screen
    It's much more functional now.
  • Only run the score timer on a one-player game
    This is just a behind-the-scenes change. The timer was still running unnecessarily on two-player games, and I wanted to make sure I fixed that just to make sure it wouldn't screw up the high score at all
  • Change how the initial ball movement direction is calculated
    This one bugged me ever since I first wrote this little bit of code. Here's how it initially looked:
    private float GetVelocityValue(float seed)
    {
        int posNeg = random.Next(0, 2);
    
        if (posNeg == 1)
            return seed * -1;
    
        return seed;
    }
    So it's picking a number, either 0 or 1, and if it's 1 I'm negating the "seed" number that was sent to the function. The returned seed number, either positive or negative, was then used for the speed of the ball. This function was horribly written though. For one thing, there was no need to send a seed number to it in the first place, because I was always using the same number for the ball's initial speed. But more importantly, it was terrible at determining a direction because it was only choosing between two numbers. It was common for the ball to start moving in the same direction 4, 5, or even more times in a row. Turns out, randomly selecting between only two values doesn't really work so well, lol. So I changed the function to this:
    private float GetVelocityValue()
    {
        int posNeg = random.Next();
    
        if (posNeg % 2 == 0)
            return BallSpeed * -1;
    
        return BallSpeed;
    }
    
    I'm not sending a seed value; instead I'm using a pre-defined BallSpeed variable that is declared up at the top of the file, which just makes more sense. Also, I'm selecting a random number from the full range of numbers that C#'s random number generator uses, 0 ≤ n < 2,147,483,647, and if it's even I'm making the ball speed negative. After a little bit of testing it seems to be better.
  • Update the window title so it longer says "Pong Clone"
    Consistency is important. :)
Sooooo...that's it! Like I said, it's not perfect. I've seen the ball collision detection act up every once in a while when it gets going pretty fast, but eh, at this point I'm not going to take the time to try to fix it. I did this just to get my feet wet and my hands writing *something*, and I would say "mission accomplished"! My next plan is to start playing around with the Unity game engine (which I may have already started doing) and maybe try making a 2D game with that. Breakout, maybe? It's an idea at least. I'm probably gonna stick with Unity for a long time, because I know there's a lot you can do with it, and I do have the start of an original idea that I eventually want to work toward. I figure Unity will be the perfect tool for the job. So with that...onward! :D

Tuesday, July 4, 2017

Almost There!

Another couple of months have gone by, and while they've gotten busier and busier as spring has turned to summer, I've been able to make another good bit of progress on Pongquetball, including something that I've really been looking forward to doing! In keeping with tradition, here's my changelog:
  • Only change the color of the ball when it hits the inward facing sides of the paddles
    There's not much I need to say about this. I just wanted to refine the way the color changes a bit. No need to change it when the ball is just going to go out of bounds anyway.
  • Make the ball appear above the score text (and other elements) instead of underneath
  • Display the high score on the title screen
  • Add a menu on the title screen to choose between a 1- or 2-player game and to reset the high score
  • Some general code cleanup
    This needed done for a while. I had some random numbers scattered throughout the code with no apparent meaning to them, so I assigned them to variables so they had meaning and weren't repeated. Also, I eliminated a few boolean variables and replaced them with a GameState variable, which is used to keep track of whether the game is at the title screen, is paused briefly just before starting, is actually running, or is paused. Doing that definitely helped make some parts of my code a bit cleaner and easier to read, and it just makes more sense.
  • And finally, the big one: TWO PLAYER MODE!
    This has been on my mind for a while, as a goal that I really wanted to work toward. So I was so happy when I finally got to it. Some of it was fairly simple, such as removing the left wall and replacing it with another paddle. All of the code needed for it to work correctly was simply copied from the code for the right paddle and modified slightly whenever necessary.

    Beyond the paddle, I also added a dotted line down the center, because come on, it's not proper pong without it, lol! The line actually took me a few tries to get right when I was creating it in Pyxel Edit. I couldn't quite get my math right for the correct number of black and white sections and the proper (in my mind, at least) height for them. Eventually I was able to figure out that the problem was the fact that I was trying to make it only as tall as it needed to be; i.e., the height between the top and bottom walls. The solution was to make it as tall as the entire window, which means that most of the very top and bottom "dots" in the line are actually *in* the top and bottom walls. It's a little weird, but hey, it works!

    Now, you can't play a proper two-player game of pong without keeping score, right? So I also had to add in proper point scoring, rather than keeping track of time like a single player game. And I went ahead and limited two player games to a maximum of 10 points.

    The last thing I did was to modify how the ball changes color for two-player games. Rather than being random, I just assigned a color to each side, and the color changes back and forth as the ball is hit back and forth. This was suggested by a friend, and I think it's a nice little touch. :)

Check it out! :D

So, I'm down to the last few changes I want to make to this game before I put it down. I want to add some simple sounds, remove the incomplete controller support it currently has (it just feels unnecessary to me now), and redo the "Paused" and "Game Over" messages so that they aren't obscured by the dotted line in two-player games. I also have a couple obscure bugs that I may work on for a bit, depending on how I'm feeling at the time. :)

That's all for now. Time to go finish this up and move on!

Saturday, April 15, 2017

Adding a Bit of Polish

Well, would you look at that! A blog update! It's been a while. Life tends to get in the way like that. But I have finally managed to get some more work done on Pongquetball; enough to write about I think. So, here's a quick little change log, complete with some code samples for once!:
  • Add a high score feature. If a new high score is set when the ball goes out of bounds, the number is updated, and it's also saved when the game exists and loaded when the game starts up. Currently it's just saved in a plain text file...this is only a practice project after all. :)
  • The ball now starts with a random color, and the color is changed every time it hits a paddle. This was a suggestion of my wife, Ashtan. I figured, why not? It's fun and it'll make it a little bit different. To select the color, I'm simply creating a new Color object with 3 random RGB values:
    private Color GetBallColor()
    {
        return new Color(random.Next(255), random.Next(255), random.Next(255));
    }
    
    There's one obvious downside to this: there's a small chance that the color will be black or a dark gray, making it hard to see. It would make for an interesting game though. :)
  • Add a slight pause after the ball goes out of bounds and before the game restarts. It's the same as the pause after the title screen.
  • Add pause/unpause functionality by pressing 'P'
  • Increase the score counting speed to be twice as fast. Originally I think I had it counting every second, and that just felt too slow. I wanted it to be more in line with something like the timer from Super Mario Bros., to give some more excitement to the game.
  • And finally, the big one: detect collisions between the ball and the top and bottom of the paddle. In other words, react differently based on where the ball hits the paddle. Before I changed this, the same collision code was being executed no matter where the ball hit the paddle.
    else if (ball.BoundingBox.Intersects(rightPaddle.BoundingBox))
    {
        ball.Velocity.X *= -1;
    
        ...
    }
    
    I'm simply reversing the ball's horizontal direction of movement. Of course, that doesn't make sense if it hits the top or bottom of the paddle. It resulted in some...interesting behavior as the ball tried to move back to the left:


    I slowed down the ball so you could clearly see what's happening.

    In those cases I want it to change directions vertically, not horizontally. The first step was figuring out how to detect where the ball hit the paddle. And I'll be honest, I had no idea how to go about doing that. I finally did some Googling and found an excellent and simple solution: if you calculate a straight line from the center of the ball to the center of the paddle, you can then find which side of the paddle that line intersects, and that's the side that the ball hit. Duh. That was one of those forehead-smacking, "why didn't I think of that?" moments. But then the next problem: how to actually go about calculating that line and where it intersects. The info I was reading made it seem like there were some somewhat complex calculations involved in this, but thankfully I eventually found this page, which was amazingly helpful. I adapted the LineSegmentsIntersect() method to fit my needs, because it was checking for more situations other than a simple intersection of two line segments at a single point, and ended up with this:
    private bool LineSegementsIntersect(Vector2 p, Vector2 p2, Vector2 q, Vector2 q2)
    {
        Vector2 r = p2 - p;
        Vector2 s = q2 - q;
        double rxs = Cross(r, s);
        double qpxr = Cross(q - p, r);
    
        // t = (q - p) x s / (r x s)
        double t = Cross(q - p, s) / rxs;
    
        // u = (q - p) x r / (r x s)
        double u = Cross(q - p, r) / rxs;
    
        // If r x s != 0 and 0 <= t <= 1 and 0 <= u <= 1
        // the two line segments meet at the point p + t r = q + u s.
        //if (!IsZero(rxs) && (0 <= t && t <= 1) && (0 <= u && u <= 1))
        if (rxs != 0 && (0 <= t && t <= 1) && (0 <= u && u <= 1))
            // An intersection was found.
            return true;
    
        // Otherwise, the two line segments are not parallel but do not intersect.
        return false;
    }
    
    p and p2 are the endpoints of the line segment between the centers of both objects, and q and q2 are the endpoints of one of the sides of the paddle. So using this, along with some of the other helper code from that web page, I was able to improve my collision detection!
    else if (ball.BoundingBox.Intersects(rightPaddle.BoundingBox) && ball.Velocity.X > 0)
    {
        bool intersect = false;
        int intersectedSide = -1;
    
        for (int i = 0; i <= 3; i++)
        {
            switch (i)
            {
                case 0: // top side
                    intersect = LineSegementsIntersect(ball.BoundingBox.Center.ToVector2(), rightPaddle.BoundingBox.Center.ToVector2(), new Vector2(rightPaddle.BoundingBox.Left, rightPaddle.BoundingBox.Top), new Vector2(rightPaddle.BoundingBox.Right, rightPaddle.BoundingBox.Top));
                    break;
                case 1: // right side
                    intersect = LineSegementsIntersect(ball.BoundingBox.Center.ToVector2(), rightPaddle.BoundingBox.Center.ToVector2(), new Vector2(rightPaddle.BoundingBox.Right, rightPaddle.BoundingBox.Top), new Vector2(rightPaddle.BoundingBox.Right, rightPaddle.BoundingBox.Bottom));
                    break;
                case 2: // bottom side
                    intersect = LineSegementsIntersect(ball.BoundingBox.Center.ToVector2(), rightPaddle.BoundingBox.Center.ToVector2(), new Vector2(rightPaddle.BoundingBox.Left, rightPaddle.BoundingBox.Bottom), new Vector2(rightPaddle.BoundingBox.Right, rightPaddle.BoundingBox.Bottom));
                    break;
                case 3: // left side
                    intersect = LineSegementsIntersect(ball.BoundingBox.Center.ToVector2(), rightPaddle.BoundingBox.Center.ToVector2(), new Vector2(rightPaddle.BoundingBox.Left, rightPaddle.BoundingBox.Top), new Vector2(rightPaddle.BoundingBox.Left, rightPaddle.BoundingBox.Bottom));
                    break;
            }
    
            if (intersect)
            {
                intersectedSide = i;
                break;
            }
        }
    
        if (intersectedSide == (int)PaddleSides.Left)
            ball.Velocity.X *= -1;
        else if (intersectedSide == (int)PaddleSides.Top || intersectedSide == (int)PaddleSides.Bottom)
            ball.Velocity.Y *= -1;
    
        ...
    }
    
    I'm basically just checking for intersection with each side of the paddle. Once I find it, I make note of which side it is and move on. Now, when the ball hits the top or bottom of the paddle, it bounces off vertically and continues moving in the same direction horizontally, like so:


    That's much better. Obviously, there's more I could do with it. It might make more sense for the ball to bounce more up or down rather than continuing in the same direction horizontally. But I don't think I'm going to spend any more time on this. I'm happy just knowing that I was able to handle this situation a little more properly and eliminate that wild "rapid bouncing" bug.
At this point I am very happy with how this is turning out. But I am also ready to move on from it and play around with Unity and some other ideas that I have floating around in my head. So my plan is to work on the last few bugs and feature ideas that I already have written down, and leave it at that. But that's for the next post. :)