We make each other better

What a well-articulated set of lessons! I ran with the challenges a bit second-guessing what we’d do next and often overdoing it.

So here I am in the comments to the final lesson looking at the projects others posted and it inspires me to “complete” this project. I am first left to my observations.

  • That’s not an ax, it’s a rectangle.
  • Is this even a game yet or just a toy?
  • There are a lot of different colors…
  • anto made a great front-end and game loop
  • zfbx used lots of math and syntax I hadn’t seen yet.

These then led to a slew of questions:

  • How come pasting online code into a new project doesn’t build right?
    • One time I ended up with .json errors
    • One time it got hung up because the version of double quotes was wrong
    • Often it seems to complain about other files in the project being wrong
  • How can I use new features when I don’t know they’re there?
    • I tried RED, BLUE, BLACK, GREY, and VIOLET
    • CYAN and TURQUOISE were a no-go.

Finally, what can I do to make my project awesome and adopt the things I like?

  1. Add a front end and game loop for easy replay.
  2. Add more movement and a scoring mechanic.
  3. Make them both balls so I can simplify collision and call it dodgeball.

Right now, this is where I’m at…
DodgeBall

#include "raylib.h"

int main()

{

    // window dimensions

    int WindowWidth = 800;

    int WindowHeight = 450;

    InitWindow(WindowWidth, WindowHeight, "Dodge Ball");

    // circle coordinates

    int CircleCenterX = 200;

    int CircleCenterY = 200;

    int CircleRadius = 25;

    // circle edges

    int LeftCircleEdge = CircleCenterX - CircleRadius;

    int RightCircleEdge = CircleCenterX + CircleRadius;

    int UpperCircleEdge = CircleCenterY - CircleRadius;

    int BottomCircleEdge = CircleCenterY + CircleRadius;

   

    // axe coordinates

    int AxeX = 400;

    int AxeY = 0;

    int AxeLength = 50;

    // axe edges

    int LAxeX = AxeX;

    int RAxeX = AxeX+AxeLength;

    int UAxeY = AxeY;

    int BAxeY = AxeY+AxeLength;

    float DirectionV = 5;

    float DirectionH = 5;

   

    bool CollisionWithAxe =

            (BAxeY >= UpperCircleEdge) &&

            (UAxeY <= BottomCircleEdge) &&

            (LAxeX <= RightCircleEdge) &&

            (RAxeX >= LeftCircleEdge);

    SetTargetFPS(60);

    while(!WindowShouldClose())

    {

        BeginDrawing();

        ClearBackground(BLACK);

        if(CollisionWithAxe)

        {

            DrawText("Game Over!", 400, 200, 20, RED);

            DrawText("Score:", WindowWidth/2, WindowHeight-200, 20, VIOLET);

            DrawText("000", 2*WindowWidth/3, WindowHeight-200, 20, VIOLET);

        }

        else

        {

            //draw score            

            DrawText("Score:", WindowWidth-150, WindowHeight-50, 20, VIOLET);

            DrawText("000", WindowWidth-50, WindowHeight-50, 20, VIOLET);

            // update the edges

            LeftCircleEdge = CircleCenterX - CircleRadius;

            RightCircleEdge = CircleCenterX + CircleRadius;

            UpperCircleEdge = CircleCenterY - CircleRadius;

            BottomCircleEdge = CircleCenterY + CircleRadius;

            LAxeX = AxeX;

            RAxeX = AxeX + AxeLength;

            UAxeY = AxeY;

            BAxeY = AxeY + AxeLength;

            // update collision with axe

            CollisionWithAxe =

                    (BAxeY >= UpperCircleEdge) &&

                    (UAxeY <= BottomCircleEdge) &&

                    (LAxeX <= RightCircleEdge) &&

                    (RAxeX >= LeftCircleEdge);

           

            DrawCircle(CircleCenterX, CircleCenterY, CircleRadius, GRAY);

            DrawRectangle(AxeX, AxeY, AxeLength, AxeLength, RED);

            // Move the axe

            AxeY += DirectionV;

            if (AxeY > (WindowHeight-AxeLength) || AxeY < 0)

            {

                DirectionV = -DirectionV;

            }

            AxeX += DirectionH;

            if (AxeX > (WindowWidth-AxeLength) || AxeX < 0)

            {

                DirectionH = -DirectionH;

            }

            // Left Right Control input

            if (IsKeyDown(KEY_D) && (CircleCenterX+CircleRadius) < WindowWidth)

            {

                CircleCenterX += 10;

            }

            if (IsKeyDown(KEY_A) && (CircleCenterX-CircleRadius) > 0)

            {

                CircleCenterX -= 10;

            }            

            //Up and Down Movement

            if (IsKeyDown(KEY_W) && (CircleCenterY-CircleRadius) > 0)

            {

                CircleCenterY = CircleCenterY - 10;

            }

            if (IsKeyDown(KEY_S) && (CircleCenterY+CircleRadius) < WindowHeight)

            {

                CircleCenterY = CircleCenterY + 10;

            }

             

            // Game logic ends

        }

        EndDrawing();        

    }

}
1 Like

Not too bad :smiley:

On copy and pasting code, some operating systems (especially mac ones) like to force replace normal quotes " with curly quotes . This can be a real pain sometimes. Also sometimes errors you might run into are from people using different variable names even though we were told one thing sometimes people (including myself) decide we have better variable names in mind that fit our style. as for files being wrong, not sure about that one unless your build config isn’t right or something :thinking:

Also raylib has an amazing cheatsheet that tells you everything it has available:

Personally I haven’t figured out how to add custom colors yet xD

That explains the double quotes, I also saw it with one project where I had to change out all the assignment operators.

That sheet you sent is intense. Not exactly light reading but quite useful.

In other news, when I build my copy of your project but I always get:
raymathsquiggles

C++Error2

> Executing task in folder SpecialCollisionProject: C:/raylib/w64devkit/bin/mingw32-make.exe RAYLIB_PATH=C:/raylib/raylib PROJECT_NAME=axe_game OBJS=*.cpp BUILD_MODE=DEBUG <

mingw32-make axe_game
make[1]: Entering directory 'C:/Users/maver/Documents/Js Games/RayLibGameDev/SpecialCollisionProject'
make[1]: *** No rule to make target '*.cpp', needed by 'axe_game'.  Stop.
make[1]: Leaving directory 'C:/Users/maver/Documents/Js Games/RayLibGameDev/SpecialCollisionProject' 
make: *** [Makefile:388: all] Error 2
The terminal process "C:\raylib\w64devkit\bin\mingw32-make.exe 'RAYLIB_PATH=C:/raylib/raylib', 'PROJECT_NAME=axe_game', 'OBJS=*.cpp', 'BUILD_MODE=DEBUG'" terminated with exit code: 2.

Any guesses what happens there?

So my project building actually says

mingw32-make axe_game
make[1]: Entering directory 'C:/Dev/cpp learning/project1'
g++ -o axe_game *.cpp -Wall -std=c++14 -D_DEFAULT_SOURCE -Wno-missing-braces -s -O1 C:/raylib/raylib/src/raylib.rc.data -I. -IC:/raylib/raylib/src -IC:/raylib/raylib/src/external -L. -LC:/raylib/raylib/src -LC:/raylib/raylib/src -lraylib -lopengl32 -lgdi32 -lwinmm -DPLATFORM_DESKTOP
make[1]: Leaving directory 'C:/Dev/cpp learning/project1'

without the “no rule to make target…” bit, quick google takes me here https://stackoverflow.com/a/17888865/3200040 which suggests your directory might be messed up a bit? my folder structure has all the template files stock like they come from the gitlab and raylib installed under C:/raylib so I’d double check all that?


Also I figured out the colors! I was being silly and messed up my project trying to figure them out and it confused me xD To define a custom color as a variable it’s simply

//              R    G    B    A
Color pink = { 241, 171, 185, 255 };

you can also just pass it as it is to anywhere that takes Color as a value. like ClearBackground({20, 20, 20, 255});
Those are Red, Green, Blue and Alpha values in decimal (0-255). You can also define a color from it’s HSV using this helper function built into raylib ColorFromHSV(346, 100, 0.91);

1 Like

Nice! I was just wondering how one would know what the preset color keywords are because ctrl-space on one didn’t make intellisense show me others but this is better.
Cyan
…and so now we have Cyan.

I did try dropping my RayLib project folder into C:/ but nothing improved.
THEN in a stroke of moderately passable intelligence, I noticed that all the projects I was having a problem running had their filename created without .cpp :man_facepalming:

Tested… Yep.

That was it.

My comments helped me work through the logic while it was broken.
This does what I want and just needs refactoring.
DodgeBall2

#include "raylib.h"
#include "raymath.h"        // allows math functions like square root and powers

int Bounce = 10;

void DisplayTitle(int Width, int Height, Color ColorA, Color ColorB)
{   
    DrawText(TextFormat(" D   D   E   A   L "), Width-10, Height, 80, ColorA);
    DrawText(TextFormat("   O   G   B   L   ! "), Width, Height+5, 80, ColorB);
}

int main()
{
    // window dimensions
    int WindowWidth = 800;  // Horizontal Window Size
    int WindowHeight = 450;  // Vertical Window Size
    InitWindow(WindowWidth, WindowHeight, "W/A/S/D Finger Trainer");  // Open a window

    // player circle coordinates
    int CircleACenterX = 200;   // Player ball starting X
    int CircleACenterY = 200;   // Player ball starting Y
    int CircleARadius = 25;     // Player ball radius
    // obstacle circle coordinates
    int CircleBCenterX = 400;   // Obstacle ball starting X
    int CircleBCenterY = 25;    // Obstacle ball starting Y
    int CircleBRadius = 25;     // Obstacle ball radius

    float DirectionV = 5;       // Obstacle vertical movement speed
    float DirectionH = 5;       // Obstacle horizontal movement speed
    // use the distance formula to measure between the circle radii
    float Distance = sqrt(pow(CircleACenterX - CircleBCenterX, 2) + pow(CircleACenterY - CircleBCenterY, 2));
    // define a collision by the addition of both radii
    float Tolerance = CircleARadius+CircleBRadius;
    // define a score by a perimeter  around the obstacle
    float Goal = (5*CircleARadius)+CircleBRadius;
    // the player and obstacle do not start the game overlapped
    bool CollisionWithObstacle = false;
    // the player does not start the game close enough to score
    bool CollisionWithScoringZone = false;

    // Game Color Definitions
    Color ScoreColor = {0, 255,255,255};
    Color PlayerColor = GRAY;
    Color ObstacleColor = RED;
    int Score = 0;              // Game Score
    int Delay = 120;            // 2 second timer
    int Timer = Delay;          // Working variable for timer
    int GameStarted = false;    // When false title will trigger

    SetTargetFPS(60);   // Set game speed
    while(!WindowShouldClose())     // Main game loop
    {
        BeginDrawing();             // Setup canvas (framebuffer) to start drawing
        ClearBackground(BLACK);     // Set background color (framebuffer clear color)
        if (!GameStarted)           // if game has not started display title and keys and wait for input
        {
            DisplayTitle(WindowWidth*.05, WindowHeight*.2, ObstacleColor, ScoreColor);
            DrawText("Press W, A, S, or D", (WindowWidth*.4), (WindowHeight*.65), 15, WHITE);
            if(IsKeyDown(KEY_W) || IsKeyDown(KEY_A) || IsKeyDown(KEY_S) || IsKeyDown(KEY_D))
            {
                GameStarted = true;
            }
        }
        else                        // when game starts proceed to the main loop
        {        
            if(CollisionWithObstacle)   // if player bumps the obstacle
            {
                //draw final score            https://stackoverflow.com/questions/53500874/how-to-show-variables-in-text-in-raylib
                DisplayTitle(WindowWidth/20, WindowHeight/6, ObstacleColor, ScoreColor);
                DrawText(TextFormat("Score: %i", Score),WindowWidth/3, WindowHeight/2.5, 55, ScoreColor);
                DrawText("Did You Improve?",WindowWidth/3, WindowHeight/2, 30, RED);
                if(Timer <= 0)          // wait a second...
                {
                                        // offer to restart if input is pressed
                    DrawText("Try Again!",WindowWidth/2.4, WindowHeight*.6, 20, WHITE);
                    DrawText("Press W, A, S, or D", (WindowWidth*.4), (WindowHeight*.65), 15, WHITE);
                    if(IsKeyDown(KEY_W) || IsKeyDown(KEY_A) || IsKeyDown(KEY_S) || IsKeyDown(KEY_D))
                    {
                        // Reset the circles, timer, score and collision bool if a key is pressed
                        CircleACenterX = 200;
                        CircleACenterY = 200;
                        CircleBCenterX = 400;
                        CircleBCenterY = 25; 
                        Timer = Delay;
                        Score = 0;
                        CollisionWithObstacle = false;
                    }
                }
                else                    // if enough time hasnt elapsed decrease the timer
                {
                    Timer--;
                }
            }
            else                        // until a collision has been detected play the game
            {
                //draw score HUD in the bottom corner
                DrawText(TextFormat("Score: %i", Score), WindowWidth-150, WindowHeight-50, 20, ScoreColor);
                
                // draw player
                DrawCircle(CircleACenterX, CircleACenterY, CircleARadius, PlayerColor);
                // draw obstacle
                DrawCircle(CircleBCenterX, CircleBCenterY, CircleBRadius, ObstacleColor);
                
                // update collision with scoring zone
                Distance = sqrt(pow(CircleACenterX - CircleBCenterX, 2) + pow(CircleACenterY - CircleBCenterY, 2));
                CollisionWithObstacle = Distance < Tolerance;

                // update collision with obstacle
                Distance = sqrt(pow(CircleACenterX - CircleBCenterX, 2) + pow(CircleACenterY - CircleBCenterY, 2));
                CollisionWithScoringZone = Distance < Goal;
                if (CollisionWithScoringZone)   //  if the player is scoring recolor the obstacle and add score
                {
                    Score++;
                    DrawCircle(CircleBCenterX, CircleBCenterY, CircleBRadius, ScoreColor);
                }

                // Move obstacle
                CircleBCenterY += DirectionV;
                // Bounce back when the edge is reached
                if (CircleBCenterY > (WindowHeight-CircleBRadius) || (CircleBCenterY < CircleBRadius))
                {
                    DirectionV = -DirectionV;
                }
                CircleBCenterX += DirectionH;
                if (CircleBCenterX > (WindowWidth-CircleBRadius) || (CircleBCenterX < CircleBRadius))
                {
                    DirectionH = -DirectionH;
                }

                // Left Right Control input
                if (IsKeyDown(KEY_D) && (CircleACenterX+CircleARadius) < WindowWidth)
                {
                    CircleACenterX += 10;
                }
                if (IsKeyDown(KEY_A) && (CircleACenterX-CircleARadius) > 0)
                {
                    CircleACenterX -= 10;
                }            
                //Up and Down Movement
                if (IsKeyDown(KEY_W) && (CircleACenterY-CircleARadius) > 0)
                {
                    CircleACenterY = CircleACenterY - 10;
                }
                if (IsKeyDown(KEY_S) && (CircleACenterY+CircleARadius) < WindowHeight)
                {
                    CircleACenterY = CircleACenterY + 10;
                }
                // Game logic ends
            } 
        }
        EndDrawing();        
    }
}

I’m BACK from the FUTURE!
DodgeBall3

There’s a video on refactoring in the next project but the job is quite tricky. Its aim is to reduce the need for comments and keep things clear by breaking your code out into functions and variables with descriptive names so that main() reads in a way that makes more sense as to what it does. I went back to this project to try to get more practice with it. I’m not sure how well I did it but this is my attempt and I thought I’d share to help make yours more awesome or help make more sense of something. If you read through it and can see you know a better way or I clearly don’t “get it” yet, teach me! If I confused you, ask. It will help.
Points if you see I also snuck a few new features in there.
:wink: Thanks for reading!

#include "raylib.h"
#include "raymath.h"        // allows math functions like square root and powers

    // window dimensions
    int WindowWidth = 800;
    int WindowHeight = 450;
    // player circle
    int PlayerCenterX = 200;
    int PlayerCenterY = 200;
    int PlayerRadius = 25;
    // obstacle circle
    int ObstacleCenterX = 400;
    int ObstacleCenterY = 25;
    int ObstacleRadius = 25;
    float DirectionV = 5;
    float DirectionH = 5;
    // collisions
    float Tolerance = PlayerRadius+ObstacleRadius;
    float Goal = (5*PlayerRadius)+ObstacleRadius; 
    bool CollisionWithObstacle{};
    bool CollisionWithScoringZone{};
    float Distance{};
    // color definitions
    Color ScoreColor = SKYBLUE;
    Color PlayerColor = DARKGRAY;
    Color ObstacleColor = MAROON;
    Color BackgroundColor = BLACK;
    Color Flicker[]{DARKGRAY, GRAY, LIGHTGRAY, WHITE};
    int FlickerSize = 4;
    //  title
    int BouncingAmount = 20;
    int BouncingDirection = 1;
    int BouncingOffset = 0;
    int LetterOffset = 10;
    // gameplay
    int PlayerSpeed = 10;
    int Score = 0;
    int Delay = 2*60;
    int Timer = Delay;
    int GameStarted = false;
    // Any magic numbers from here on are probably percentages to place elements onscreen

void DisplayTitle(float PercentScreenWidth, float PercentScreenHeight)
{   
    if(BouncingOffset < -BouncingAmount || BouncingOffset > BouncingAmount)
    {
        BouncingDirection = -BouncingDirection;        
    }
    BouncingOffset += BouncingDirection;
    DrawText(TextFormat(" D   D   E   A   L "),
                        (WindowWidth*(PercentScreenWidth/100))-LetterOffset, 
                        (WindowHeight*(PercentScreenHeight/100))+BouncingOffset, 
                        80, 
                        ObstacleColor);
    DrawText(TextFormat("   O   G   B   L   ! "), 
                        (WindowWidth*(PercentScreenWidth/100)), 
                        (WindowHeight*(PercentScreenHeight/100))-BouncingOffset, 
                        80, 
                        ScoreColor);
}

bool UseWASDToProceed(float PercentScreenWidth, float PercentScreenHeight)
{
    // Current Game Time (reduced to a whole number) divided by the length of the Flicker array 
    // leaves me a remainder I may use to count through the colors  Modulus FTW!
    int Sequencer = (int)GetTime() % FlickerSize; 
    DrawText("Press W, A, S, or D\n  or   Esc to Quit", (WindowWidth*(PercentScreenWidth/100)), (WindowHeight*(PercentScreenHeight/100)), 15, Flicker[Sequencer]);
    if(IsKeyDown(KEY_W) || IsKeyDown(KEY_A) || IsKeyDown(KEY_S) || IsKeyDown(KEY_D))
    {
        return true;
    }
    return false;
}

void UpdateDistanceCheck()
{
    Distance = sqrt(pow(PlayerCenterX - ObstacleCenterX, 2) + pow(PlayerCenterY - ObstacleCenterY, 2));
    CollisionWithObstacle = Distance < Tolerance;
    CollisionWithScoringZone = Distance < Goal;
}

void ResetGameVariables()
{
    PlayerCenterX = 200;
    PlayerCenterY = 200;
    ObstacleCenterX = 400;
    ObstacleCenterY = 25; 
    Timer = Delay;
    Score = 0;
    CollisionWithObstacle = false;
}

void CalculateAndIndicateScoring()
{
    if (CollisionWithScoringZone)
    {
        // increase score based on the distance from the obstacle but never less than 1 
        Score += ((Goal-Distance)/PlayerRadius)+1;
        // recolor the obstacle
        DrawCircle(ObstacleCenterX, ObstacleCenterY, ObstacleRadius, ScoreColor);   
    }
}

void DrawScorePlayerAndObstacle()
{
    DrawText(TextFormat("Score: %i", Score), WindowWidth*.85, WindowHeight*.9, 20, ScoreColor);
    DrawCircle(PlayerCenterX, PlayerCenterY, PlayerRadius, PlayerColor);
    DrawCircle(ObstacleCenterX, ObstacleCenterY, ObstacleRadius, ObstacleColor);
}

void BouncyBallMovement()
{
    ObstacleCenterY += DirectionV;
    if (ObstacleCenterY > (WindowHeight-ObstacleRadius) || (ObstacleCenterY < ObstacleRadius))
    {
        DirectionV = -DirectionV;
    }
    ObstacleCenterX += DirectionH;
    if (ObstacleCenterX > (WindowWidth-ObstacleRadius) || (ObstacleCenterX < ObstacleRadius))
    {
        DirectionH = -DirectionH;
    }    
}

void ProcessGameControls()
{
    if (IsKeyDown(KEY_D) && (PlayerCenterX+PlayerRadius) < WindowWidth)
    {
        PlayerCenterX += PlayerSpeed;
    }
    if (IsKeyDown(KEY_A) && (PlayerCenterX-PlayerRadius) > 0)
    {
        PlayerCenterX -= PlayerSpeed;
    }
    if (IsKeyDown(KEY_W) && (PlayerCenterY-PlayerRadius) > 0)
    {
        PlayerCenterY = PlayerCenterY - PlayerSpeed;
    }
    if (IsKeyDown(KEY_S) && (PlayerCenterY+PlayerRadius) < WindowHeight)
    {
        PlayerCenterY = PlayerCenterY + PlayerSpeed;
    }
}

int main()
{
    InitWindow(WindowWidth, WindowHeight, "W/A/S/D Finger Trainer");
    SetTargetFPS(60);

    while(!WindowShouldClose())          
    {
        BeginDrawing();                  
        ClearBackground(BackgroundColor);      
        UpdateDistanceCheck();
        // Start Screen
        if (!GameStarted)
        {
            DisplayTitle(5, 40);
            if(UseWASDToProceed(40, 70))
            {
                GameStarted = true;
            }
        }
        else
        {
            // End Screen   
            if(CollisionWithObstacle)
            {
                DisplayTitle(5, 10);
                DrawText(TextFormat("Score: %i", Score),WindowWidth*.33, WindowHeight*.4, 55, ScoreColor);
                DrawText("Did You Improve?",WindowWidth*.33, WindowHeight*.5, 30, ObstacleColor);
                if(Timer <= 0)
                {
                    DrawText("Try Again!",WindowWidth*.42, WindowHeight*.58, 20, PlayerColor);
                    if(UseWASDToProceed(40, 65))
                    {
                        ResetGameVariables();
                    }
                }
                else
                {
                    Timer--;
                }
            }
            // GamePlay
            else
            {
                DrawScorePlayerAndObstacle();
                CalculateAndIndicateScoring();
                BouncyBallMovement();
                ProcessGameControls();
            } 
        }
        EndDrawing();        
    }
}

I also found this super useful selection mode in VSCode.

ColumnSelect
I wish it had a hotkey so I could quickly flip it on and off.

Holding down the ALT key before you drag should give you similar if not the exact same results.

1 Like

For me, that has more of a “select this too” feature that I would actually expect from ctrl.
SelectThisToo

OMG, you just upped my Notepad++ game. ALT works to column select there.
:+1:

The best part is that this also works in VS Code and other editors, though your mileage may vary.

Privacy & Terms