🫥

0から作るゲーム開発の工夫したところ

2024/12/08に公開

僕はGLFW、GLAD、GLM、OpenGLを使ってMinecraftのようなゲームを作りました。その中で特に苦労したのが 物理判定と移動 です。

たとえば、キーボード入力で「W」キーを押したとします。この入力に応じて、プレイヤーの位置を単純に「x座標を-10」するような処理をしてしまうと問題が発生します。なぜなら、この方法ではプレイヤーがブロックに「めり込んだ」場合、そのめり込みがどの方向から起きたのかを判別できないからです。つまり、上から落ちてきてめり込んだのか、それとも横や下からなのかがわからなくなります。

この問題を解決するために、以下のような方法を取りました:
1. 各フレームの最後に、プレイヤーの位置を検証する処理を行います。
2. プレイヤーの位置を更新する際、forループなどを使って位置を少しずつ動かします。
3. プレイヤーが特定の方向に「めり込んだ」場合、その方向に補正を加えることで、跳ね返るような挙動を作りました(たとえば、x方向にめり込んだら、xを減少させた分だけ補正する)。

こうすることで、めり込みを防ぎ、プレイヤーが自然に動けるようにしました。

実際のコードの一部

#ifndef LINK_HPP
#define LINK_HPP

#include <cstdlib>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
#include <sstream>
#include "imgui.h"                      // Dear ImGui本体
#include "backends/imgui_impl_glfw.h"    // GLFWバックエンド
#include "backends/imgui_impl_opengl3.h" // OpenGLバックエンド

//変数
const unsigned int WINDOW_WIDTH = 1440;
const unsigned int WINDOW_HEIGHT = 810;
glm::vec3 cameraPosition(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp(0.0f, 1.0f, 0.0f);
float lastX = WINDOW_WIDTH / 2.0f;
float lastY = WINDOW_HEIGHT / 2.0f;
float yaw = -90.0f, pitch = 0.0f;
bool firstMouse = true;
float deltaTime = 0.0f, lastFrame = 0.0f;
float moveSpeed = 5.0f;
float sensitivity = 0.05f;
glm::vec3 playerPosition(100.0f, 30.0f, 250.0f); // 初期プレイヤー位置
glm::vec3 playerVelocity(0.0f, 0.0f, 0.0f); // 初期プレイヤー速度
const float gravity = -12.0f; // 重力加速度;
const float jumpStrength = 6.0f;
const float playerHeight = 1.8f; // プレイヤーの高さ
const float playerWidth = 0.6f; // プレイヤーの幅
bool onGround = false;

struct CubeData {
    glm::vec3 position;
    glm::vec3 color;
    bool drawWireframe;
    bool isSolid;
};

class CubeWorld {
public:
CubeWorld(const std::vector<CubeData>& initialCubes = {}) {
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return;
    }
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Cube World", nullptr, nullptr);
    if (window == nullptr) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return;
    }
    glfwMakeContextCurrent(window);
    glfwSetWindowUserPointer(window, this);
    glfwSetKeyCallback(window, keyHandlerWrapper);
    glfwSetMouseButtonCallback(window, mouseButtonCallbackWrapper);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cerr << "Failed to initialize GLAD" << std::endl;
        return;
    }

    glEnable(GL_DEPTH_TEST);
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glfwSetCursorPosCallback(window, mouseCallback);

    shaderProgram = createShaderProgram();

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    viewLoc = glGetUniformLocation(shaderProgram, "view");
    projectionLoc = glGetUniformLocation(shaderProgram, "projection");
    modelLoc = glGetUniformLocation(shaderProgram, "model");

    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;

    ImGui_ImplGlfw_InitForOpenGL(window, true);
    if (window == nullptr) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return;
    }
    ImGui_ImplOpenGL3_Init("#version 330 core");

    std::cout << "Display Size: " << io.DisplaySize.x << ", " << io.DisplaySize.y << std::endl;
    glClearColor(0.53f, 0.81f, 0.92f, 1.0f);
    for (const CubeData& cube : initialCubes) {
        cubes.push_back({cube.position, cube.color, false, true});
    }
}

~CubeWorld() {
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteProgram(shaderProgram);
    glfwDestroyWindow(window);
    glfwTerminate();

    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
}

    void AddCube(const glm::vec3& position, const glm::vec3& color, bool drawWireframe) {
        cubes.push_back({position, color, drawWireframe, true});
        std::cout << "Added cube at: (" << position.x << ", " << position.y << ", " << position.z << ")" << std::endl;
    }

    int jumpSpeed = 4;
    void Update(float deltaTime) {
        // カメラの前方向ベクトル(Y成分は無視して平面方向で計算)
        glm::vec3 forwardDirection = glm::normalize(glm::vec3(cameraFront.x, 0.0f, cameraFront.z));  // Y成分を無視
        glm::vec3 rightDirection = glm::normalize(glm::cross(forwardDirection, cameraUp));  // 右方向ベクトル

        // プレイヤーの移動処理:WASDによる入力
        glm::vec3 desiredVelocity(0.0f);

        float run = 1.5f;
        // WASDによる移動入力処理
        if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
            if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
                desiredVelocity += forwardDirection * moveSpeed * run;
            } else {
                desiredVelocity += forwardDirection * moveSpeed;
            }
        }
        if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
            if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
                desiredVelocity -= forwardDirection * moveSpeed * run;
            } else {
                desiredVelocity -= forwardDirection * moveSpeed;
            }
        }
        if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
            if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
                desiredVelocity -= rightDirection * moveSpeed * run;
            } else {
                desiredVelocity -= rightDirection * moveSpeed;
            }
        }
        if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS){
            if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
                desiredVelocity += rightDirection * moveSpeed * run;
            } else {
                desiredVelocity += rightDirection * moveSpeed;
            }
        }

        // 水平移動処理:XとZ軸方向の移動
        glm::vec3 movement = desiredVelocity * deltaTime;

        // プレイヤーの位置更新(移動)
        glm::vec3 newPosition = playerPosition + movement;

        // 衝突チェック(水平移動)
        if (!checkHorizontalCollision(newPosition)) {
            playerPosition = newPosition;  // 衝突しなければ位置を更新
        }

        // 垂直方向の移動(重力適用とジャンプ)
        if (!onGround) {
            playerVelocity.y += gravity * deltaTime;  // 重力を加算
        }

        // 垂直移動処理
        newPosition = playerPosition + glm::vec3(0.0f, playerVelocity.y * deltaTime, 0.0f);

        // 垂直移動の衝突チェック
        if (!checkVerticalCollision(newPosition)) {
            playerPosition = newPosition;  // 衝突しなければ位置を更新
        } else {
            // 衝突した場合、速度をゼロにして地面に着地
            playerVelocity.y = 0.0f;
            onGround = true;  // 着地したので地面にいる
        }

        // 水平移動後に地面チェック
        if (checkGroundStatus(playerPosition)) {
            onGround = true;
        } else {
            onGround = false;
        }

        // プレイヤーの目標位置を少し上にオフセット(カメラ位置)
        glm::vec3 targetCameraPosition = playerPosition + glm::vec3(0.0f, 1.5f, 0.0f);

        // 補間を使ってカメラを滑らかに動かす
        float smoothFactor = 0.7f;  // 0.0f~1.0fでスムーズさを調整
        cameraPosition = glm::mix(cameraPosition, targetCameraPosition, smoothFactor);

        // 最終的なカメラ位置を更新
    }

    bool checkGroundStatus(const glm::vec3& position) {
        // 地面との衝突判定を行い、地面にいるかどうかをチェック
        // ここでは簡単にY成分をチェックする例を示す
        glm::vec3 checkPosition = position - glm::vec3(0.0f, 0.1f, 0.0f);  // 少し下の位置をチェック

        if (checkVerticalCollision(checkPosition)) {
            return true;  // 地面にいる
        }
        return false;  // 空中にいる
    }

    // レイとキューブの交差をデバッグ表示する関数
    void debugRayIntersection(const glm::vec3& rayOrigin, const glm::vec3& rayDir, const CubeData& cube) {
        float tMin = 0.0f;
        if (rayIntersectsCube(rayOrigin, rayDir, cube, tMin)) {
            glm::vec3 hitPosition = rayOrigin + rayDir * tMin;
        }
    }

    void renderCrosshair(float size, ImVec4 color) {
        // ウィンドウサイズの中央を取得
        ImGuiIO& io = ImGui::GetIO();
        float centerX = io.DisplaySize.x / 2.0f;
        float centerY = io.DisplaySize.y / 2.0f;

        // クロスヘアの描画
        ImDrawList* drawList = ImGui::GetForegroundDrawList();
        drawList->AddLine(ImVec2(centerX - size, centerY), ImVec2(centerX + size, centerY), ImGui::ColorConvertFloat4ToU32(color), 1.0f);
        drawList->AddLine(ImVec2(centerX, centerY - size), ImVec2(centerX, centerY + size), ImGui::ColorConvertFloat4ToU32(color), 1.0f);
    }
    // 変更されたImGuiのデバッグ情報表示部分
    void Render() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glm::mat4 view = glm::lookAt(cameraPosition, cameraPosition + cameraFront, cameraUp);
        glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 100.0f);

        glUseProgram(shaderProgram);
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));

        for (const CubeData& cube : cubes) {
            // カメラとキューブの距離を計算
            float distance = glm::length(cameraPosition - cube.position);

            // 距離が50.0f以内のキューブだけ描画
            if (distance <= 50.0f) {
                glm::mat4 model = glm::mat4(1.0f);
                model = glm::translate(model, cube.position);
                glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

                // 色をシェーダーに送信
                glUniform3f(glGetUniformLocation(shaderProgram, "objectColor"), cube.color.r, cube.color.g, cube.color.b);

                glBindVertexArray(VAO);
                glPolygonMode(GL_FRONT_AND_BACK, cube.drawWireframe ? GL_LINE : GL_FILL);
                glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
            }
        }
    }

    bool checkVerticalCollision(const glm::vec3& position) {
        for (const CubeData& cube : cubes) {
            if (!cube.isSolid) continue;

            // プレイヤーからの距離をチェック(5.0f以内)
            float distance = glm::distance(position, cube.position);
            if (distance > 5.0f) continue;

            // キューブのAABB(境界ボックス)
            glm::vec3 cubeMin = cube.position - glm::vec3(0.5f);
            glm::vec3 cubeMax = cube.position + glm::vec3(0.5f);

            // プレイヤーのAABB(縦長の立方体、少し小さめ)
            glm::vec3 playerMin = position - glm::vec3(playerWidth / 2.0f - 0.15f, playerHeight / 2.0f - 0.15f, playerWidth / 2.0f - 0.15f);
            glm::vec3 playerMax = position + glm::vec3(playerWidth / 2.0f - 0.15f, playerHeight / 2.0f - 0.15f, playerWidth / 2.0f - 0.15f);

            // AABBの衝突判定(X, Y, Z軸で重なっているかチェック)
            if (playerMax.x > cubeMin.x && playerMin.x < cubeMax.x &&
                playerMax.y > cubeMin.y && playerMin.y < cubeMax.y &&
                playerMax.z > cubeMin.z && playerMin.z < cubeMax.z) {
                return true; // 衝突発生
            }
        }
        return false; // 衝突なし
    }

    bool checkHorizontalCollision(const glm::vec3& position) {
        for (const CubeData& cube : cubes) {
            if (!cube.isSolid) continue;

            // プレイヤーからの距離をチェック(5.0f以内)
            float distance = glm::distance(position, cube.position);
            if (distance > 5.0f) continue;

            // キューブのAABB(境界ボックス)
            glm::vec3 cubeMin = cube.position - glm::vec3(0.5f);
            glm::vec3 cubeMax = cube.position + glm::vec3(0.5f);

            // プレイヤーのAABB(縦長の立方体、少し小さめ)
            glm::vec3 playerMin = position - glm::vec3(playerWidth / 2.0f - 0.15f, playerHeight / 2.0f - 0.15f, playerWidth / 2.0f - 0.15f);
            glm::vec3 playerMax = position + glm::vec3(playerWidth / 2.0f - 0.15f, playerHeight / 2.0f - 0.15f, playerWidth / 2.0f - 0.15f);

            // AABBの衝突判定(X, Y, Z軸で重なっているかチェック)
            if (playerMax.x > cubeMin.x && playerMin.x < cubeMax.x &&
                playerMax.y > cubeMin.y && playerMin.y < cubeMax.y &&
                playerMax.z > cubeMin.z && playerMin.z < cubeMax.z) {
                return true; // 衝突発生
            }
        }
        return false; // 衝突なし
    }

    GLFWwindow* getWindow() const { return window; }

    static void keyHandlerWrapper(GLFWwindow* window, int key, int scancode, int action, int mods) {
        CubeWorld* world = reinterpret_cast<CubeWorld*>(glfwGetWindowUserPointer(window));
        if (world) {
            world->keyHandler(window, key, scancode, action, mods);
        }
    }

    void keyHandler(GLFWwindow* window, int key, int scancode, int action, int mods) {
        float cameraSpeed = moveSpeed * deltaTime * 2;
        if (action == GLFW_PRESS || action == GLFW_REPEAT) {
            glm::vec3 oldPosition = playerPosition;
            if (key == GLFW_KEY_E)
                exit(0);
            if (key == GLFW_KEY_SPACE && onGround) {
                playerVelocity.y = jumpStrength;
                onGround = false;
            }
            if (checkHorizontalCollision(playerPosition)) {
                playerPosition = oldPosition; // 衝突が発生した場合は元の位置に戻す
            }
        }
    }

    static void mouseButtonCallbackWrapper(GLFWwindow* window, int button, int action, int mods) {
        CubeWorld* world = reinterpret_cast<CubeWorld*>(glfwGetWindowUserPointer(window));
        if (world) {
            world->mouseButtonCallback(window, button, action, mods);
        }
    }

    void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
                glm::vec3 rayOrigin = cameraPosition;
                glm::vec3 rayDirection = glm::normalize(cameraFront);

                float maxDistance = 10.0f; // レイの最大距離
                glm::vec3 hitPosition;
                bool hit = false;
                float tMinClosest = maxDistance;

                int hitIndex = -1; // 交差したキューブのインデックス

                // すべてのキューブに対して交差判定
                for (size_t i = 0; i < cubes.size(); ++i) {
                    float tMin;
                    if (rayIntersectsCube(rayOrigin, rayDirection, cubes[i], tMin)) {
                        if (tMin < tMinClosest && tMin >= 0) {
                            tMinClosest = tMin;
                            hitPosition = rayOrigin + rayDirection * tMin;
                            hit = true;
                            hitIndex = i; // 交差したキューブのインデックスを記録
                        }
                    }
                }

                // 交差が検出された場合
                if (hit && hitIndex != -1) {
                    // 交差したキューブを削除
                    std::cout << "Destroyed cube at: ("
                              << cubes[hitIndex].position.x << ", "
                              << cubes[hitIndex].position.y << ", "
                              << cubes[hitIndex].position.z << ")" << std::endl;
                    cubes.erase(cubes.begin() + hitIndex); // キューブを削除
                    onGround = false;
                }
            }
        if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
            glm::vec3 rayOrigin = cameraPosition;
            glm::vec3 rayDirection = glm::normalize(cameraFront);

            float maxDistance = 10.0f;
            glm::vec3 hitPosition;
            bool hit = false;
            float tMinClosest = maxDistance;

            for (const CubeData& cube : cubes) {
                float tMin;
                if (rayIntersectsCube(rayOrigin, rayDirection, cube, tMin)) {
                    if (tMin < tMinClosest && tMin >= 0) {
                        tMinClosest = tMin;
                        hitPosition = rayOrigin + rayDirection * tMin;
                        hit = true;
                    }
                }
            }

            if (hit) {
                glm::vec3 placePosition = hitPosition - rayDirection * 0.5f;
                placePosition.x = round(placePosition.x);
                placePosition.y = round(placePosition.y);
                placePosition.z = round(placePosition.z);
                AddCube(placePosition, glm::vec3(255.0f, 0.0f, 0.0f), false);
            } else {
                std::cout << "No block detected within max distance." << std::endl;
            }
        }
    }

private:
    GLFWwindow* window;
    unsigned int shaderProgram;
    unsigned int VAO, VBO, EBO;
    int viewLoc, projectionLoc, modelLoc;

    std::vector<CubeData> cubes;

    float vertices[216] = {
        // 頂点データ: 頂点座標と色
        -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
        0.5f,  0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 1.0f,
        0.5f, -0.5f,  0.5f, 0.0f, 1.0f, 1.0f,
        0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f, 0.5f, 0.5f, 0.5f
    };

    unsigned int indices[36] = {
        0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4,
        0, 4, 7, 7, 3, 0, 1, 5, 6, 6, 2, 1,
        3, 2, 6, 6, 7, 3, 0, 1, 5, 5, 4, 0
    };


    std::string loadShaderSource(const char* filepath) {
        std::ifstream file(filepath);
        if (!file.is_open()) {
            std::cerr << "ERROR::SHADER::FILE_NOT_READABLE: " << filepath << std::endl;
            return "";
        }
        std::stringstream buffer;
        buffer << file.rdbuf();
        return buffer.str();
    }

    unsigned int compileShader(unsigned int type, const char* source) {
        unsigned int shader = glCreateShader(type);
        glShaderSource(shader, 1, &source, nullptr);
        glCompileShader(shader);
        int success;
        char infoLog[512];
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (!success) {
            glGetShaderInfoLog(shader, 512, nullptr, infoLog);
            std::cerr << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog << std::endl;
        }
        return shader;
    }

    unsigned int createShaderProgram() {
        // シェーダーソースをファイルから読み込む
        std::string vertexShaderSource = loadShaderSource("../Shader/vertex.glsl");
        std::string fragmentShaderSource = loadShaderSource("../Shader/fragment.glsl");

        unsigned int vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource.c_str());
        unsigned int fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource.c_str());

        unsigned int shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);

        int success;
        char infoLog[512];
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
            std::cerr << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
        }

        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        return shaderProgram;
    }

    static void mouseCallback(GLFWwindow* window, double xpos, double ypos) {
        static float lastX = WINDOW_WIDTH / 2.0f;
        static float lastY = WINDOW_HEIGHT / 2.0f;
        static bool firstMouse = true;

        if (firstMouse) {
            lastX = xpos;
            lastY = ypos;
            firstMouse = false;
        }

        float xOffset = xpos - lastX;
        float yOffset = lastY - ypos;  // Y座標は上下逆
        lastX = xpos;
        lastY = ypos;

        xOffset *= sensitivity;
        yOffset *= sensitivity;

        yaw += xOffset;
        pitch += yOffset;

        if (pitch > 89.0f) pitch = 89.0f;
        if (pitch < -89.0f) pitch = -89.0f;

        glm::vec3 front;
        front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
        front.y = sin(glm::radians(pitch));
        front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
        cameraFront = glm::normalize(front);
    }

    bool rayIntersectsCube(const glm::vec3& rayOrigin, const glm::vec3& rayDir, const CubeData& cube, float& tMin) {
        glm::vec3 cubeMin = cube.position - glm::vec3(0.5f);
        glm::vec3 cubeMax = cube.position + glm::vec3(0.5f);

        float t1 = (cubeMin.x - rayOrigin.x) / rayDir.x;
        float t2 = (cubeMax.x - rayOrigin.x) / rayDir.x;
        float t3 = (cubeMin.y - rayOrigin.y) / rayDir.y;
        float t4 = (cubeMax.y - rayOrigin.y) / rayDir.y;
        float t5 = (cubeMin.z - rayOrigin.z) / rayDir.z;
        float t6 = (cubeMax.z - rayOrigin.z) / rayDir.z;

        float tmin = std::fmax(std::fmax(std::fmin(t1, t2), std::fmin(t3, t4)), std::fmin(t5, t6));
        float tmax = std::fmin(std::fmin(std::fmax(t1, t2), std::fmax(t3, t4)), std::fmax(t5, t6));

        if (tmax < 0 || tmin > tmax) {
            return false; // No intersection
        }

        tMin = tmin;
        return true; // Intersection detected
    }

    bool isPointIntersectingWithScene(const glm::vec3& point) {
        float tMin;
        for (const CubeData& cube : cubes) {
            if (rayIntersectsCube(point, glm::vec3(0.0f, -1.0f, 0.0f), cube, tMin)) {
                if (tMin <= playerHeight / 2.0f) {
                    return true;
                }
            }
        }
        return false;
    }
};
#endif

Discussion