Rectangles and SDL_Rect

Learn to create, render, and interact with basic rectangles using the SDL_Rect and SDL_Color types.
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Get Started for Free
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Posted

So far, we've learned how to create a window and fill it with a solid color. While useful, most applications need to draw more specific shapes and images.

One of the most fundamental shapes in 2D graphics is the rectangle. Rectangles are essential for user interfaces (buttons, text boxes), game elements (sprites, collision boxes), and defining areas on the screen (viewports, selection boxes).

SDL provides tools specifically for working with rectangles. We'll learn about the SDL_Rect type for defining position and size, and the SDL_Color type for managing colors independently of screen formats. We'll then combine these to draw rectangles and make them respond to basic mouse interaction.

Starting Point

We'll start with the basic SDL application structure we developed previously. This includes initializing SDL, creating a Window class to manage the SDL_Window and its surface, and running the main event loop.

#include <SDL.h>
#include "Window.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;

  SDL_Event E;
  while (true) {
    while (SDL_PollEvent(&E)) {
      if (E.type == SDL_QUIT) {
        SDL_Quit();
        return 0;
      }
    }

    GameWindow.Render();
    GameWindow.Update();
  }

  return 0;
}
#pragma once
#include <iostream>
#include <SDL.h>

class Window {
public:
  Window() {
    SDLWindow = SDL_CreateWindow(
      "Hello Window",
      SDL_WINDOWPOS_UNDEFINED,
      SDL_WINDOWPOS_UNDEFINED,
      700, 300, 0
    );
  }

  void Render() {
    SDL_FillRect(
      GetSurface(),
      nullptr,
      SDL_MapRGB(
        GetSurface()->format, 50, 50, 50
      )
    );
  }

  void Update() {
    SDL_UpdateWindowSurface(SDLWindow);
  }

  SDL_Surface* GetSurface() const {
    return SDL_GetWindowSurface(SDLWindow);
  }

  Window(const Window&) = delete;
  Window& operator=(const Window&) = delete;
  ~Window() {
    if (SDLWindow && SDL_WasInit(SDL_INIT_VIDEO)) {
      SDL_DestroyWindow(SDLWindow);
    }
  }

private:
  SDL_Window* SDLWindow{nullptr};
};

Creating a Rectangle Class

Let’s add a Rectangle class to manage our rectangles. We want our rectangle to render itself to the screen, so we’ll add a Render() function that accepts an SDL_Surface pointer. This is the surface that our Rectangle instance will render itself to:

// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  void Render(SDL_Surface* Surface) const {
    // ...
  }
};

Over in our main function, let’s construct a Rectangle and Render() it at the appropriate time.

As we covered in our double buffering lesson, the rendering process starts with the call to GameWindow.Render() - which fills the window surface with a solid color, and it ends with the call to GameWindow.Update() - which triggers the buffer swap.

All of our other objects, such as our Rectangle, should be rendered between these two steps:

#include <SDL.h>
#include "Window.h"
#include "Rectangle.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  Rectangle Rect;

  SDL_Event E;
  while (true) {
    while (SDL_PollEvent(&E)) {
      if (E.type == SDL_QUIT) {
        SDL_Quit();
        return 0;
      }
    }

    GameWindow.Render();
    Rect.Render(GameWindow.GetSurface());
    GameWindow.Update();
  }

  return 0;
}

Rendering the Rectangle

You may remember from our Window class that we can draw a rectangle using SDL_FillRect(), which requires three arguments:

  1. The surface to draw to. This will be the argument provided to our Render() function.
  2. The rectangular area to draw. In our Window class, we’re passing a nullptr as this argument, indicating we want the color to fill the entire surface. In this lesson, we’ll learn how to provide an argument here, in the form of an SDL_Rect
  3. The color we want to use. Like we did before, we can generate this color using SDL_MapRGB().
// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  void Render(SDL_Surface* Surface) const {
    SDL_FillRect(
      Surface,
      
      // We'll provide this later 
      nullptr, 
      
      SDL_MapRGB(Surface->format, 255, 0, 0)
    );
  }
};

The SDL_Rect Type

To represent a rectangle, SDL provides the SDL_Rect type. It is a simple struct with four integer members that stores the position and dimensions of the rectangle:

  • The x and y members represent the position of the top-left corner of the rectangle
  • The w member represents the width of the rectangle
  • The h member represents the height of the rectangle

Visually, it looks like this:

Diagram showing the components of an SDL_Rect

Remember, SDL’s coordinate system uses the "y-down" convention, so increasing the y value corresponds to moving the rectangle down.

Let’s add an SDL_Rect to our Rectangle class. We’ll also add it as a constructor parameter:

// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  Rectangle(const SDL_Rect& Rect)
  : Rect{Rect} {}
  
void Render(SDL_Surface* Surface) {/*...*/} private: SDL_Rect Rect; };

We’ll provide the argument to this constructor in our main.cpp:

#include <SDL.h>
#include "Window.h"
#include "Rectangle.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  Rectangle Rect{SDL_Rect{50, 50, 50, 50}};

  SDL_Event E;
while (true) {/*...*/} return 0; }

To use this rectangle for rendering, we provide a pointer to it as our second argument to SDL_FillRect():

// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  Rectangle(const SDL_Rect& Rect)
  : Rect{Rect} {}
  
  void Render(SDL_Surface* Surface) const {
    SDL_FillRect(
      Surface,
      &Rect,
      SDL_MapRGB(Surface->format, 255, 0, 0)
    );
  }

private:
  SDL_Rect Rect;
};

With these changes, we should now see our red rectangle being rendered to the screen:

Screenshot of our program rendering a red rectangle

Rotated Rectangles

The SDL_Rect type is used to represent a specific subset of rectangles, called axis-aligned rectangles. We can think of an axis-aligned rectangle as not having any rotation:

Diagram comparing axis-aligned and oriented rectangles

SDL doesn’t include a type for rotated rectangles. If we need that capability, it is something we’d have to build ourselves.

The SDL_Color Type

In our Render() function, we’re currently generating a red color for our rectangle. However, what if we want to store this color as a member variable, such that we change it from other functions in our Rectangle class?

// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  // ...

  void SetColor(/* ??? */) {
    // ???
  }
  
  // ...
};

Previously, we saw how we could store a specific color in a specific pixel format using a 32 bit unsigned integer. This is typically the value returned by a function such as SDL_MapRGB():

Uint32 Red{SDL_MapRGB(Surface->format, 255, 0, 0)};

However, storing our color as a Uint32 in the correct pixel format may not be easy.

In our simple program, we know we’re only ever rendering to one surface - the window surface - so, we could design the Rectangle class on that basis. However, this violates the design principle of encapsulation.

Ideally, our Rectangle should be a self-contained component that does not know or care about what is going on elsewhere in our code. In that context, our Rectangle doesn’t know what surface it will be rendering to until Render() is invoked. At that point, the SDL_Surface is available as a parameter, and we can retrieve it’s pixel format from the format member variable.

A simple option for storing a color in a way that lets us apply the pixel format later would be to use three integers - one for each of the red, green, and blue channels:

// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  Rectangle(const SDL_Rect& Rect)
  : Rect{Rect} {}

  void Render(SDL_Surface* Surface) const {
    SDL_FillRect(
      Surface,
      &Rect,
      SDL_MapRGB(
        Surface->format,
        Red, Green, Blue
      )
    );
  }

  void SetColor(int R, int G, int B) {
    Red = R;
    Green = G;
    Blue = B;
  }

private:
  SDL_Rect Rect;
  int Red, Green, Blue;
};

This works, but is a little messy, and it makes writting the getter more difficult. We’d prefer to have a type that can represent a color as a single value. To help with this, SDL provides the SDL_Color type, which is useful for storing a color without locking that color down to a specific pixel format.

SDL_Color is a basic struct with 4 members - r, g, b, and a, representing the red, green, blue, and alpha channels respectively. Each variable is an 8-bit unsigned integer, meaning they can store values from 0 to 255:

SDL_Color Black  {  0,   0,   0, 255};
SDL_Color Gray   {100, 100, 100, 255};
SDL_Color White  {255, 255, 255, 255};
SDL_Color Red    {255,   0,   0, 255};
SDL_Color Green  {  0, 255,   0, 255};
SDL_Color Blue   {  0,   0, 255, 255};
SDL_Color Orange {255, 165,   0, 255};

Let’s update our Rectangle to store its color as this type, alongside a getter and setter:

// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  Rectangle(const SDL_Rect& Rect)
  : Rect{Rect} {}

  void Render(SDL_Surface* Surface) const {
    SDL_FillRect(
      Surface,
      &Rect,
      SDL_MapRGB(
        Surface->format,
        Color.r, Color.g, Color.b
      )
    );
  }

  void SetColor(const SDL_Color& NewColor) {
    Color = NewColor;
  }

  SDL_Color GetColor() const {
    return Color;
  }

private:
  SDL_Rect Rect;

  // Default to red
  SDL_Color Color{255, 0, 0, 255};
};

Performance Considerations

A function like Render() is called on every frame of our program, so we should be cautious about performing too much work here. The more work our program needs to do on each frame, the longer it takes to generate that frame. This makes our program less responsive.

Instead, we may want to move the SDL_MapRGB() step out of the Render() function, and pass the required format to the constructor. This allows us to call SDL_MapRGB() a single time, rather than on every frame:

// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  Rectangle(
    const SDL_Rect& Rect,
    const SDL_PixelFormat* Format
  )
  : Rect{Rect},
    Color{SDL_MapRGB(Format, 255, 0, 0)}
  {}

  void Render(SDL_Surface* Surface) const {
    SDL_FillRect(
      Surface,
      &Rect,
      SDL_MapRGB(
        Surface->format,
        Color.r, Color.g, Color.b
      )
      Color
    );
  }

private:
  SDL_Rect Rect;
  Uint32 Color{0};
};

This is reasonable, however, we should equally be cautious about making changes like this too early, before we have sufficient information to make such decisions. This is a practice known as premature optimization, and has its own set of problems:

  • Optimization takes time, and time is a limited resource. Optimizing something now can mean less time to optimize something more important later, so premature optimization can indirectly make performance worse.
  • Optimization generally adds code complexity, which incurs yet more lost time in the future, and risks causing bugs. This is because more complex code takes longer to change.
  • Heavily optimizing code typically involves making it less flexible. For example, our optimized Rectangle assumes that it only ever renders to surfaces that have the same pixel format. Our simpler, less optimized implementation had no such restriction.

Unless we’re certain something will be a problem, optimization is generally best left until later in the project. This has a few advantages:

  • We’re much more confident about what features the final project will have and how they work, so we’re not wasting time optimizing something that might get deleted or changed
  • We can see the relative performance impact of all of our components in the near-final state of our program. This allows us to make more informed decisions, like which components have the biggest performance impact, so we can prioritize our time on those.

As we get more experienced, we’ll develop better intuition about where early optimization is justified, and we’ll get better at minimising the negative impact of those optimizations. But otherwise, if we’re not sure, we should prioritise approaches that are quick to implement, simple to understand and easy to change in the future.

Reacting to Mouse Motion Events

Let’s give our Rectangle the ability to react to events. To set this up, we’ll add a HandleEvent() function to our class:

// Rectangle.h
// ...

class Rectangle {
 public:
  // ...

  void HandleEvent(SDL_Event& E) {
    // We'll implement this next
  }

  // ...
};

We’ll also forward events to this function from our event loop:

// main.cpp
#include <SDL.h>
#include "Window.h"
#include "Rectangle.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  Rectangle Rect{SDL_Rect{50, 50, 50, 50}};

  SDL_Event E;
  while (true) {
    while (SDL_PollEvent(&E)) {
      Rect.HandleEvent(E);
      if (E.type == SDL_QUIT) {
        SDL_Quit();
        return 0;
      }
    }

    GameWindow.Render();
    Rect.Render(GameWindow.GetSurface());
    GameWindow.Update();
  }

  return 0;
}

To detect when the user hovers their mouse over the rectangle, we’ll apply the techniques we introduced earlier in the chapter. We’ll check for SDL_MOUSEMOTION events and, when they occur, we’ll update an isPointerHovering boolean to true if the cursor was moved over our rectangle:

// Rectangle.h
// ...

class Rectangle {
 public:
  // ...
  void HandleEvent(SDL_Event& E) {
    if (E.type == SDL_MOUSEMOTION) {
      isPointerHovering = isWithinRect(
        E.motion.x, E.motion.y
      );
    }
  }
  // ...

private:
  // ...

  bool isPointerHovering{false};
  bool isWithinRect(int x, int y) {
    // We'll implement this next
    return false;
  }
};

Detecting if a Point is within an SDL_Rect

Our isWithinRect() function needs to return true if the x and y coordinates are within the bounds of our Rect variable. There are a few ways of determining this this. One approach is to check if the point is not outside the bounds in all 4 directions:

Diagram showing the logic of calculating if a point is inside a rectangle

If the point is not in any of the areas we marked in red, we can deduce that it must be within the rectangle. In code, we can implement the check like this:

// Rectangle.h
// ...

class Rectangle {
 // ...

private:
  // ...
  bool isWithinRect(int x, int y) {
    // Too far left
    if (x < Rect.x) return false;
    // Too far right
    if (x > Rect.x + Rect.w) return false;
    // Too far up
    if (y < Rect.y) return false;
    // Too far down
    if (y > Rect.y + Rect.h) return false;

    // If we got this far, that means the
    // point is within the rectangle
    return true;
  }
};

Changing Color on Hover

Now that our Rectangle is keeping track of whether the user’s cursor is hovering over it, other functions in our class can react to it. For example, let’s have our rectangle change color when hovered. We’ll add a new member variable, getter, and setter:

// Rectangle.h
// ...

class Rectangle {
 public:
  // ...

  void SetHoverColor(const SDL_Color& NewColor) {
    HoverColor = NewColor;
  }

  SDL_Color GetHoverColor() const {
    return HoverColor;
  }

private:
  // ...
  SDL_Color HoverColor{0, 0, 255, 255};
};

Let’s update our Render() function to select a color based on the isPointerHovering state. This is an ideal place to use structured binding:

// Rectangle.h
// ...

class Rectangle {
public:
  // ...

  void Render(SDL_Surface* Surface) const {
    auto [r, g, b, a]{
      isPointerHovering ? HoverColor : Color
    };
    
    SDL_FillRect(
      Surface, &Rect,
      SDL_MapRGB(Surface->format, r, g, b)
    );
  }
  // ...
};

Note that, to use structured binding, we need to use all the variables of the SDL_Color type, even though we don’t need the a (alpha) channel in this scenario.

Now, when we hover our rectangle, it should change color from red to blue:

Screenshot showing the program rendering a blue rectangle

Rendering Coordinates vs Mouse Coordinates

Note that, in the previous example, we’re comparing the x and y coordinates from the mouse motion event to the x and y coordinates of SDL_Rect controlling where our rectangle is rendered.

The x and y coordinates of the event represent the distance from the top left corner of the window, whilst the x and y coordinates of the SDL_Rect represent the distance from the top-left corner of the surface we’re rendering on.

Our logic is assuming that these values are equivalent. In our simple program, they are. That is because the surface we’re rendering to is the window surface. In more complicated programs where this is not always the case, we would need more advanced logic that accounts for the fact that these positions may be in different coordinate spaces.

We cover coordinate spaces and mapping between them in more detail later in the course.

We’re also assuming our program only has a single window, so we’re not checking which window the event came from.

The SDL_Point Type

SDL includes a simple SDL_Point type that stores an x and y integer:

SDL_Point SomePoint{100, 200};

SDL also includes a few functions that work with SDL_Point objects. This includes SDL_PointInRect(), which can replicate our isWithinRect() method. To use SDL_PointInRect(), we pass a pointer to an SDL_Point and a pointer to an SDL_Rect:

// Rectangle.h
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  // ...

  void HandleEvent(SDL_Event& E) {
    if (E.type == SDL_MOUSEMOTION) {
      // Before
      isPointerHovering = isWithinRect(
        E.motion.x, E.motion.y
      );
      
      // After
      SDL_Point MousePosition{
        E.motion.x, E.motion.y};
      isPointerHovering = SDL_PointInRect(
        &MousePosition, &Rect
      );
    }
  }
  // ...
};

However, SDL_Point is rarely used, even within the SDL API itself. As we’ve seen, SDL events represent positions as individual x and y values, rather than as SDL_Points.

We’ll also stick to representing points as individual x and y coordinates in our own examples for now. Later in the course, we’ll create a more powerful Vec2 type that uses concepts that are more common in the industry.

Reacting to Window Events

By default, SDL stops sending us mouse motion events once the pointer leaves our window.

If the rectangle is at the edge of the window, or the mouse is moving quickly, this means we may not be notified that the cursor has left our rectangle through leaving the window entirely.

To address this, we can additionally keep track of SDL_WINDOWEVENT_LEAVE events, using the techniques we covered in the previous lesson:

// Rectangle.h
// ...

class Rectangle {
 public:
  // ...
  void HandleEvent(SDL_Event& E) {
    if (E.type == SDL_MOUSEMOTION) {
      isPointerHovering = isWithinRect(
        E.motion.x, E.motion.y
      );
    } else if (
      E.type == SDL_WINDOWEVENT &&
      E.window.event == SDL_WINDOWEVENT_LEAVE
    ) {
      isPointerHovering = false;
    }
  }
  // ...

};

Reacting to Mouse Click Events

Let’s also allow our Rectangle objects to react to mouse click events - that is, events with a type of SDL_MOUSEBUTTONDOWN:

// Rectangle.h
// ...

class Rectangle {
 public:
  // ...

  void HandleEvent(SDL_Event& E) {
    if (E.type == SDL_MOUSEMOTION) {
      isPointerHovering = isWithinRect(
        E.motion.x, E.motion.y
      );
    } else if (
      E.type == SDL_WINDOWEVENT &&
      E.window.event == SDL_WINDOWEVENT_LEAVE
    ) {
      isPointerHovering = false;
    } else if (E.type == SDL_MOUSEBUTTONDOWN) {
      if (E.button.button == SDL_BUTTON_LEFT) {
        std::cout << "A left-click happened "
          "somewhere in the window\n";
      }
    }
  }
  // ...
};

The click events include x and y members, informing us of where the cursor was when the user clicked on our window. We can use this to determine whether they specifically clicked on the Rectangle instance:

// Rectangle.h
// ...

class Rectangle {
 public:
  // ...

  void HandleEvent(SDL_Event& E) {
    if (E.type == SDL_MOUSEMOTION) {
      isPointerHovering = isWithinRect(
        E.motion.x, E.motion.y
      );
    } else if (
      E.type == SDL_WINDOWEVENT &&
      E.window.event == SDL_WINDOWEVENT_LEAVE
    ) {
      isPointerHovering = false;
    } else if (E.type == SDL_MOUSEBUTTONDOWN) {
      if (isWithinRect(E.button.x, E.button.y)
          && E.button.button == SDL_BUTTON_LEFT
      ) {
        std::cout << "A left-click happened "
          "on me!\n";
      }
    }
  }
  // ...
};

Alternatively, we can use the isHovering boolean we added in the previous section:

// Rectangle.h
// ...

class Rectangle {
 public:
  // ...

  void HandleEvent(SDL_Event& E) {
    if (E.type == SDL_MOUSEMOTION) {
      isPointerHovering = isWithinRect(
        E.motion.x, E.motion.y
      );
    } else if (
      E.type == SDL_WINDOWEVENT &&
      E.window.event == SDL_WINDOWEVENT_LEAVE
    ) {
      isPointerHovering = false;
    } else if (E.type == SDL_MOUSEBUTTONDOWN) {
      if (isPointerHovering && 
          E.button.button == SDL_BUTTON_LEFT
      ) {
        std::cout << "A left-click happened "
          "on me!\n";
      }
    }
  }
  // ...
};

Complete Code

Our program now includes a dedicated Rectangle.h file defining our Rectangle class. The main.cpp file creates an instance of this class and integrates its rendering and event handling into the main loop. This setup provides a good starting point for adding more complex features through the rest of this chapter.

#include <SDL.h>
#include "Window.h"
#include "Rectangle.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  Rectangle Rect{SDL_Rect{50, 50, 50, 50}};

  SDL_Event E;
  while (true) {
    while (SDL_PollEvent(&E)) {
      Rect.HandleEvent(E);
      if (E.type == SDL_QUIT) {
        SDL_Quit();
        return 0;
      }
    }

    GameWindow.Render();
    Rect.Render(GameWindow.GetSurface());
    GameWindow.Update();
  }

  return 0;
}
#pragma once
#include <iostream>
#include <SDL.h>

class Window {
public:
  Window() {
    SDLWindow = SDL_CreateWindow(
      "Hello Window",
      SDL_WINDOWPOS_UNDEFINED,
      SDL_WINDOWPOS_UNDEFINED,
      700, 300, 0
    );
  }

  void Render() {
    SDL_FillRect(
      GetSurface(),
      nullptr,
      SDL_MapRGB(
        GetSurface()->format, 50, 50, 50
      )
    );
  }

  void Update() {
    SDL_UpdateWindowSurface(SDLWindow);
  }

  SDL_Surface* GetSurface() const {
    return SDL_GetWindowSurface(SDLWindow);
  }

  Window(const Window&) = delete;
  Window& operator=(const Window&) = delete;
  ~Window() {
    if (SDLWindow && SDL_WasInit(SDL_INIT_VIDEO)) {
      SDL_DestroyWindow(SDLWindow);
    }
  }

private:
  SDL_Window* SDLWindow{nullptr};
};
#pragma once
#include <SDL.h>

class Rectangle {
 public:
  Rectangle(const SDL_Rect& Rect)
  : Rect{Rect} {}

  void Render(SDL_Surface* Surface) const {
    auto [r, g, b, a]{
      isPointerHovering ? HoverColor : Color
    };
    SDL_FillRect(
      Surface, &Rect,
      SDL_MapRGB(Surface->format, r, g, b)
    );
  }

  void HandleEvent(SDL_Event& E) {
    if (E.type == SDL_MOUSEMOTION) {
      isPointerHovering = isWithinRect(
        E.motion.x, E.motion.y
      );
    } else if (
      E.type == SDL_WINDOWEVENT &&
      E.window.event == SDL_WINDOWEVENT_LEAVE
    ) {
      isPointerHovering = false;
    } else if (E.type == SDL_MOUSEBUTTONDOWN) {
      if (isPointerHovering &&
        E.button.button == SDL_BUTTON_LEFT
      ) {
        std::cout << "A left-click happened "
          "on me!\n";
      }
    }
  }

  void SetColor(const SDL_Color& NewColor) {
    Color = NewColor;
  }

  SDL_Color GetColor() const {
    return Color;
  }

  void SetHoverColor(const SDL_Color& NewColor) {
    HoverColor = NewColor;
  }

  SDL_Color GetHoverColor() const {
    return HoverColor;
  }

private:
  SDL_Rect Rect;
  SDL_Color Color{255, 0, 0, 255};
  SDL_Color HoverColor{0, 0, 255, 255};

  bool isPointerHovering{false};

  bool isWithinRect(int x, int y) {
    if (x < Rect.x) return false;
    if (x > Rect.x + Rect.w) return false;
    if (y < Rect.y) return false;
    if (y > Rect.y + Rect.h) return false;
    return true;
  }
};

Summary

In this lesson, we learned how to define, render, and interact with rectangles in SDL2. We created a Rectangle class encapsulating an SDL_Rect for geometry and an SDL_Color for appearance, allowing us to draw shapes and respond to mouse input.

Key Takeaways:

  • SDL_Rect defines a rectangle using x, y (top-left corner), w (width), and h (height).
  • SDL_Color stores RGBA color values (0-255) independently of pixel formats.
  • SDL_FillRect() draws a filled rectangle onto an SDL_Surface.
  • SDL_MapRGB() converts RGBA values into a surface-specific color format.
  • We can check if a point is inside an SDL_Rect by comparing its coordinates against the rectangle's boundaries.
  • Mouse motion (SDL_MOUSEMOTION) and click (SDL_MOUSEBUTTONDOWN) events provide cursor coordinates.
  • Window events (SDL_WINDOWEVENT_LEAVE) help track when the cursor leaves the window.
Free and Unlimited Access

Professional C++

Unlock the true power of C++ by mastering complex features, optimizing performance, and learning expert workflows used in professional development

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Ryan McCombe
Ryan McCombe
Posted
sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Get Started for Free
Implementing User Interaction
Free and Unlimited Access

Professional C++

Unlock the true power of C++ by mastering complex features, optimizing performance, and learning expert workflows used in professional development

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2025 - All Rights Reserved