Back to C++
2026-02-239 min read

SwiftUI Animations (C++)

Learn SwiftUI Animations (C++) step by step with clear examples and exercises.

Why This Matters

SwiftUI animations are a powerful tool for creating engaging and interactive user interfaces in Apple's Swift programming language. While SwiftUI is specific to Swift, we can draw inspiration from it and implement animations using C++ and OpenGL. In this lesson, we will explore how to create basic animations in C++ using OpenGL.

By learning how to implement animations in C++, you'll be able to create more interactive applications that can compete with those built using SwiftUI or other modern UI frameworks. Moreover, understanding the underlying principles of animations can help you troubleshoot issues and optimize performance in your projects.

Prerequisites

Before diving into creating animations, ensure you have a solid foundation in C++ programming and are familiar with OpenGL for rendering graphics. You should also be comfortable working with basic data structures such as arrays and vectors. If you're new to OpenGL, consider reviewing the basics of setting up an OpenGL context and handling input before proceeding.

Additional Resources

  • Learn OpenGL - A comprehensive tutorial series on learning OpenGL from scratch.
  • Modern C++ Tutorials - A collection of high-quality C++ tutorials covering various topics.

Core Concept

To create animations in C++ using OpenGL, we will use a technique called _frame-based animation_. This approach involves updating the state of our graphics objects (such as vertices or textures) at regular intervals, typically based on the current time or frame count. By redrawing these updated objects at each frame, we can create the illusion of movement and change over time.

Frame-Based Animation Steps

  1. Initialize our graphics objects with initial states (e.g., positions, colors, etc.).
  2. Set up a timer or counter to keep track of the current frame.
  3. In each frame, update the state of our graphics objects based on the elapsed time or frame count.
  4. Redraw the updated graphics objects using OpenGL commands.
  5. Repeat steps 3 and 4 at regular intervals (e.g., 60 times per second) to create smooth animations.

Worked Example

Let's create a simple animation example where we move a square across the screen from left to right. We will use OpenGL ES 2.0 for this example, as it is commonly used in mobile and web applications.

#include <GLES2/gl2.h>
#include <GLFW/glfw3.h>
#include <iostream>

constexpr float SQUARE_WIDTH = 100.0f;
constexpr float SQUARE_HEIGHT = 100.0f;
float squarePosition[2] = { -SQUARE_WIDTH, 0.0f }; // Initial position: left edge of the screen
float speed = 2.0f; // Animation speed
float lastTime = 0.0f; // Time of the last frame

void drawSquare() {
glColor4f(1.0f, 0.0f, 0.0f, 1.0f); // Set red color for square
float vertices[] = {
squarePosition[0], 0.0f, 0.0f, 1.0f, // bottom-left
squarePosition[0] + SQUARE_WIDTH, 0.0f, 0.0f, 1.0f, // bottom-right
squarePosition[0], 0.0f + SQUARE_HEIGHT, 0.0f, 1.0f, // top-left
squarePosition[0] + SQUARE_WIDTH, 0.0f + SQUARE_HEIGHT, 0.0f, 1.0f
};
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), vertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

void updateSquarePosition() {
float currentTime = glfwGetTime();
float elapsedTime = currentTime - lastTime;
lastTime = currentTime;

if (squarePosition[0] + SQUARE_WIDTH <= glfwGetWindowWidth()) {
squarePosition[0] += speed * elapsedTime;
}
}

int main() {
glfwInit();
GLFWwindow* window = glfwCreateWindow(800, 600, "Animation Example", nullptr, nullptr);
if (!window) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);

// Initialize OpenGL and set up rendering pipeline (not shown here)

float lastFrameTime = glfwGetTime();
while (!glfwWindowShouldClose(window)) {
float currentTime = glfwGetTime();
float elapsedTime = currentTime - lastFrameTime;
lastFrameTime = currentTime;

// Clear the screen and depth buffer
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Update square position based on elapsed time
updateSquarePosition();

// Draw the updated square
drawSquare();

// Swap buffers and poll events
glfwSwapBuffers(window);
glfwPollEvents();
}

// Clean up and terminate GLFW and OpenGL resources (not shown here)

return 0;
}

In this example, we define a drawSquare() function to draw the square using OpenGL commands and an updateSquarePosition() function to update its position based on the elapsed time. In the main loop, we clear the screen, update the square position, draw the updated square, and then swap buffers to display the changes.

Common Mistakes

  1. Not updating the graphics objects frequently enough: If you don't update your graphics objects at a fast enough rate (e.g., less than 60 times per second), the animations may appear choppy or jerky.
  2. Not redrawing graphics objects in each frame: If you forget to redraw updated graphics objects, they won't be visible on the screen.
  3. Using fixed update intervals: Using a fixed interval for updating graphics objects (e.g., every 16 milliseconds) can lead to inconsistent animation speeds across different devices or operating systems. Instead, use elapsed time or frame count for more accurate updates.
  4. Not handling user input: For interactive animations, it's essential to handle user input (e.g., mouse clicks or touch events) and update the graphics objects accordingly.
  5. Ignoring performance optimization: Animations can be resource-intensive, so it's crucial to optimize your code for better performance. This may involve using more efficient data structures, minimizing unnecessary redraws, and reducing the number of vertices or textures in your scenes.
  6. Not normalizing vertex coordinates: Ensure that all vertex coordinates are normalized (i.e., between 0 and 1) before passing them to OpenGL functions for proper rendering.
  7. Forgetting to enable vertex attribute arrays: Don't forget to call glEnableVertexAttribArray() before calling glDrawArrays() or glDrawElements().
  8. Not binding the vertex buffer object (VBO): Bind the VBO containing your vertices before calling glVertexAttribPointer().
  9. Mismanaging memory: Be mindful of memory usage when working with large amounts of data, such as high-resolution textures or complex 3D models. Consider using techniques like texture atlases to reduce memory footprint.
  10. Not cleaning up resources: Always clean up and delete OpenGL resources (e.g., shaders, vertex arrays, and textures) when they are no longer needed to avoid memory leaks and performance issues.

Practice Questions

  1. How would you modify the example above to move the square from right to left instead? (Answer: Change the initial position of squarePosition to SQUARE_WIDTH and adjust the update function to check if squarePosition[0] >= 0)
  2. Implement a simple bouncing ball animation where the ball bounces off the edges of the screen. (Answer: Create a ballPosition array with two elements for x and y positions, add a speed variable, and update the position based on user-defined gravity and collision detection with screen edges)
  3. Create an animation that fades in and out a text label using OpenGL. (Answer: Use a texture atlas to create a transparent gradient image, then modify the texture coordinates of the quad to reveal or hide the desired portion of the gradient)
  4. Optimize the example code to improve performance by reducing the number of vertices or minimizing unnecessary redraws. (Answer: Consider using indexed drawing for the square, which reduces the number of vertices sent to the GPU; also, only redraw the square when its position has changed significantly to minimize unnecessary updates)
  5. Implement a simple parallax scrolling background effect where different layers of the scene move at varying speeds to create depth and immersion. (Answer: Create multiple layers with different speeds and draw them in the correct order using z-sorting or depth testing)
  6. Create an animation that follows the mouse cursor or user's touch position on a mobile device. (Answer: Use OpenGL's event handling functions to detect the cursor or touch position, then update the position of your graphics objects accordingly)
  7. Implement a simple particle system where particles are emitted from a source and move in different directions with varying speeds. (Answer: Create an array of particle structures containing position, velocity, and lifetime; update their positions and velocities based on elapsed time; draw them using vertex arrays or instanced rendering)
  8. Create an animation that simulates a fluid or liquid flow using shader techniques like noise functions or particle systems. (Answer: Use shaders to generate noise patterns for the fluid's surface, then update the positions of particles based on their velocities and the generated noise)
  9. Implement a simple physics simulation where objects collide and bounce off each other realistically. (Answer: Use velocity and acceleration vectors to simulate object movement; implement collision detection between objects using bounding boxes or more advanced techniques like bounding spheres; update velocities based on the laws of motion and restitution)
  10. Create an animation that simulates a 3D world with dynamic lighting, shadows, and reflections using OpenGL's shader capabilities. (Answer: Use shaders to calculate lighting equations, cast and receive shadows, and generate reflection maps; implement techniques like deferred shading or screen-space reflections for improved performance)

FAQ

  1. Why is my animation choppy or jerky? You may need to update your graphics objects more frequently (e.g., at least 60 times per second) or optimize your code for better performance.
  2. How can I handle user input in my animations? Use OpenGL's event handling functions (such as glfwGetMouseButton() or glfwGetKey()) to detect user input and update the graphics objects accordingly.
  3. Why should I use elapsed time or frame count instead of a fixed interval for updating graphics objects? Using elapsed time or frame count provides more accurate updates, as it takes into account variations in hardware performance and screen refresh rates.
  4. How can I create more complex animations using OpenGL? Experiment with different techniques such as keyframe animation, inverse kinematics, or particle systems to create more advanced animations. You may also want to consider using libraries like Assimp for loading 3D models or GLM for mathematical operations.
  5. What are some best practices for optimizing OpenGL code? Use efficient data structures (e.g., vertex arrays and indexed drawing), minimize unnecessary redraws, reduce the number of vertices or textures in your scenes, use texture atlases to save memory, and clean up resources when they are no longer needed.
  6. How can I create animations that run smoothly on different devices? Optimize your code for performance, use elapsed time or frame count for updates, and consider using techniques like dynamic resolution scaling or adaptive frame rates to ensure smooth animations on various hardware configurations.
  7. What are some common pitfalls to avoid when working with OpenGL animations? Be mindful of memory usage, normalize vertex coordinates, enable vertex attribute arrays, bind the vertex buffer object (VBO), and clean up resources when they are no longer needed.
  8. How can I create more realistic animations using shaders in OpenGL? Use shaders to generate noise patterns for fluid simulations, simulate lighting equations, cast and receive shadows, and generate reflection maps. Experiment with techniques like deferred shading or screen-space reflections for improved performance.
  9. What are some advanced animation techniques I can use in OpenGL? Consider using keyframe animation, inverse kinematics, particle systems, physics simulations, or 3D world simulations to create more complex and immersive animations.
  10. How can I learn more about OpenGL animations and optimization techniques?
SwiftUI Animations (C++) | C++ | XQA Learn