Open_Pong: On Click Debugging
Written on October 9th, 2021 by AsianMario
Cleaning Up
Before I dive into making the on click debugging I needed to cleanup and sort out how my game objects are stored, because right now doing g->gameObject[42]
isn’t great. I used a C++ map which would have a name that would store a pointer to a game object. Since I wanted it to be accessed globally I did need to make a class for it which wasn’t much of a hassle.
// GOList Class
class GOList {
public:
map<std::string, GameObject*> GOList;
};
Very simple. Next, I created a new cpp vector in game.h
to store the map called OBJList
, even though there was only one game object map this just made everything a little bit more neat. Then, I made a object in main.cpp
called ObjectList (doesn’t really matter what I called it) then did g.OBJList.push_back(&ObjectList);
to store it. To add GameObjects to the map I simply did this: ObjectList.GOList.insert(pair<std::string, GameObject*>(paddle1.name, &paddle1));
and… done! Although, I did need to add a new variable called name
in GameObject.h
.
Bounding Boxes & Co-ordinate Confusion
Since I need to show the object properties when the mouse clicks on the GameObject I need to make a bounding box for every important mesh (except particles & powerup boxes). Of course, it’s not ideal to manually create a bounding box for each mesh I made a function called createBoundingBox();
in GameObject.h
. I plan to make AABB (Axis-Aligned Bounding Boxes) which means they don’t rotate and in theory much simpler. Ideally, the ends of the AABB would match the most extreme vertices from the mesh, I did something a little bit simpler:
glm::vec4 GameObject::createBoundingBox() {
glm::vec4 boundingBox = glm::vec4(1.0f);
//RIGHT
boundingBox.x = position.x + scale.x;
//LEFT
boundingBox.y = position.x - scale.x;
//UP
boundingBox.z = position.y + scale.y;
//DOWN
boundingBox.w = position.y - scale.y;
return boundingBox;
}
Even though this would create bounding boxes that are a little bit bigger than the mesh it wouldn’t be soo inaccurate it would cause a problem. Then I create another variable in GameObject.h
called BoundingBox
, its a vec4 variable which, you guessed it! Stores the mesh’s bounding box. You’d think we’re ready to implement the on click debugging now but there’s a couple more stuff to do before we go on and do that. Next up, I have to translate mouse/screen co-ordinates to world co-ordinates. Since I decided to scale everything up early on due to mathematical errors the world co-ordinates are now 0-100 instead of 0-1, not a big issue though. I couldn’t find the equation to translate screen co-ordinates to world co-ordinates until I stumbled on a 3D ray picker article which I modified a bit to make it work for 2D. Looks like this:
void Screen::screenToWorldCord() {
float x = ((2.0f * xpos) / 1920 - 1.0f) * 100.0;
float y = (1.0f - (2.0f * ypos) / 1080) * 100.0;
xpos = x;
ypos = y;
}
Finally, we can move onto on click debugging.
On Click Debugging
Finally, we’re here! On click debugging! A bit complicated but i’ll explain the best I can. All the way back in my blog post called ‘GUI Update’ I explained how I used IMGUI, probably best if you read it before reading the rest of this. Instead of creating debug windows one by one in my game scene I made it spawn everytime the mouse hovers over a mesh which will then show its properties which you can edit. I had to edit the createDebugMenu();
function by changing its parameters from a GameObject to an OBJList so it make’s it easier to implement. It looks like this now:
void GUI::createDebugMenu( GOList* obj, string name, glm::vec3 orgPos, glm::vec3 orgRot, glm::vec3 orgScale) { //name has to match the same name in the GOList map
{
GameObject* objDebug = obj->GOList.at(name);
ImGui::Begin(name.c_str());
bool resetScale = false;
bool resetPos = false;
bool resetRot = false;
ImGui::Text("Mesh Controls");
ImGui::Dummy(ImVec2(0.0f, 5.0f));
ImGui::SliderFloat("Scale X", &objDebug->scale.x, 0.0f, 200.0f);
ImGui::SliderFloat("Scale Y", &objDebug->scale.y, 0.0f, 200.0f);
ImGui::SliderFloat("Scale Z", &objDebug->scale.z, 0.0f, 200.0f);
if (ImGui::Button("Reset Scale"))
resetScale = true;
if (resetScale == true) {
objDebug->scale = orgScale;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f));
ImGui::SliderFloat("Translation X", &objDebug->position.x, -200.0f, 200.0f);
ImGui::SliderFloat("Translation Y", &objDebug->position.y, -200.0f, 200.0f);
ImGui::SliderFloat("Translation Z", &objDebug->position.z, -200.0f, 200.0f);
if (ImGui::Button("Reset Position"))
resetPos = true;
if (resetPos == true) {
objDebug->position = orgPos;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f));
ImGui::SliderFloat("Rotation X", &objDebug->rotation.x, -360.0f, 360.0f);
ImGui::SliderFloat("Rotation Y", &objDebug->rotation.y, -360.0f, 360.0f);
ImGui::SliderFloat("Rotation Z", &objDebug->rotation.z, -360.0f, 360.0f);
if (ImGui::Button("Reset Rotation"))
resetRot = true;
if (resetRot == true) {
objDebug->rotation = orgRot;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f));
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
}
}
Then, I made a new function called onClickDebug
, this function will be later called in the main game scene. First, I check what scene i’m on, on click debugging only works in the Game scene. Then I make a new mouse state to check what i’m pressing on my mouse. Then I get the mouse co-ordinates then convert them to world co-ordinates. I then check if the mouse has been pressed, if it has then it will run a check on all game objects and see which mesh the mouse clicked on. Function to check if mouse is in a mesh bounding box:
bool Screen::collisionBB(Game* g, GameObject* GO) {
if (xpos <= GO->boundingBox.x && xpos >= GO->boundingBox.y && ypos <= GO->boundingBox.z && ypos >= GO->boundingBox.w) {
return true;
}
else {
return false;
}
}
Finally, I render the debug window of the mesh. I need to make a few more improvements such as permanently rendering debug screens until the mouse clicks on something else. Current changes will be pushed to github and a video devlog will come along soon. Thanks for reading!
(also here’s the function for the on click debug function)
void GUI::onClickDebug(Game* g) {
if (g->ScreenHandler[0]->screen == ScreenHandler::SCREENTYPE::GAME) {
int state = glfwGetMouseButton(g->gameWindow, GLFW_MOUSE_BUTTON_LEFT);
g->ScreenObject[0]->getCursorPosition(g);
g->ScreenObject[0]->screenToWorldCord();
cout << g->paddles[0]->boundingBox.x << endl;
if (state == GLFW_PRESS) {
for (GameObject* o : g->gameObjects) {
bool mouseIntersect = g->ScreenObject[0]->collisionBB(g, o);
if (o->name != "") {
if (mouseIntersect == true) {
g->debugGUI[0]->createDebugMenu(g->OBJList[0], o->name, glm::vec3(-0.75f, 0.0f, 0.0f), VEC3_ZERO, glm::vec3(0.015f, 0.2f, 1.0f));
}
}
}
}
}
}