Question: Use the BoxDragger project as your starting point. In it, there are four rectangles you can click and drag with the left mouse button. You
Use the BoxDragger project as your starting point. In it, there are four rectangles you can click and drag with the left mouse button. You can also use the right mouse button to change to a random color. Pressing SPACE resets the boxes to their original positions because you can 'suck up' other boxes and drag them along because I am not preventing that.
Criteria for exercise:
- Instead of having starting locations for the boxes hard-coded in, FIRST, create an XML file with an element for each Box object and have, as attributes, the following:
- x, y, for each rectangle data (w and h don't have to be saved - they can stay 100x100)
- r, g, b, for each rectangle's color data - you can keep the 'a' as 255 for alpha
- Don't change the four corner default starting locations, keep those hard-coded into the class. You'll see what I mean.
- Upon construction of each Box, load in the XML attribute data like my TinyXML example.
- Before the program exit, save the rectangle data back into the DOM elements and save the XML file.
- It'll be up to you how to store the DOM elements but you can take hints from my XML example.
- When you run the program again, it should load in the rectangle locations from before.
- You can modify the program, e.g. the Box constructor as you see fit to get the job done.
- Have an XML file with the five pieces of data required
- Properly load the data from the XML file into the Box attributes
- Save the data back to the XML file properly
Box.cpp
#include "Box.h" #include "Engine.h" #include
void Box::Update() { int mx = Engine::Instance().GetMouseX(); int my = Engine::Instance().GetMouseY(); if (mx < (m_rDst.x + m_rDst.w) && mx > m_rDst.x && // If cursor is within X bounds my < (m_rDst.y + m_rDst.h) && my > m_rDst.y) // And cursor is within Y bounds { if (Engine::Instance().GetMouseBtn(0)) { m_rDst.x = mx - m_rDst.w/2; m_rDst.y = my - m_rDst.h/2; } if (Engine::Instance().GetMouseBtn(1)) // What can we do to "slow down" the color set? { m_cColor.r = rand() % 256; m_cColor.g = rand() % 256; m_cColor.b = rand() % 256; } } }
void Box::Render() { SDL_SetRenderDrawColor(Engine::Instance().GetRenderer(), m_cColor.r, m_cColor.g, m_cColor.b, m_cColor.a); SDL_RenderFillRect(Engine::Instance().GetRenderer(), &m_rDst); }
Box.h
#pragma once #include "SDL.h"
class Box { private: SDL_Rect m_rDst; SDL_Rect m_rStartPos; SDL_Color m_cColor; public: Box(SDL_Rect r) { m_rDst = m_rStartPos = r; m_cColor = { 255, 255, 255, 255 }; } void Update(); void Render(); void Reset() { m_rDst = m_rStartPos; } };
Engine.cpp
#include "Engine.h" #include
Engine::Engine():m_bRunning(false){ cout << "Engine class constructed!" << endl; } Engine::~Engine(){}
bool Engine::Init(const char* title, int xpos, int ypos, int width, int height, int flags) { srand((unsigned)time(NULL)); cout << "Initializing game..." << endl; // Attempt to initialize SDL. if (SDL_Init(SDL_INIT_EVERYTHING) == 0) { // Create the window. m_pWindow = SDL_CreateWindow(title, xpos, ypos, width, height, flags); if (m_pWindow != nullptr) // Window init success. { m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0); if (m_pRenderer != nullptr) // Renderer init success. {
} else return false; // Renderer init fail. } else return false; // Window init fail. } else return false; // SDL init fail. m_fps = (Uint32)round((1 / (double)FPS) * 1000); // Sets FPS in milliseconds and rounds. m_iKeystates = SDL_GetKeyboardState(nullptr); m_vBoxes.reserve(4); m_vBoxes.push_back(new Box({ 100, 100, 100, 100 })); m_vBoxes.push_back(new Box({ 800, 100, 100, 100 })); m_vBoxes.push_back(new Box({ 100, 500, 100, 100 })); m_vBoxes.push_back(new Box({ 800, 500, 100, 100 })); m_bRunning = true; // Everything is okay, start the engine. cout << "Init success!" << endl; return true; }
void Engine::Wake() { m_start = SDL_GetTicks(); }
void Engine::Sleep() { m_end = SDL_GetTicks(); m_delta = m_end - m_start; if (m_delta < m_fps) // Engine has to sleep. SDL_Delay(m_fps - m_delta); }
void Engine::HandleEvents() { SDL_Event event;
while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: // User pressed window's 'x' button. m_bRunning = false; break; case SDL_KEYDOWN: // Try SDL_KEYUP instead. if (event.key.keysym.sym == SDLK_ESCAPE) m_bRunning = false; break; case SDL_KEYUP: if (event.key.keysym.sym == ' ') { for (int i = 0; i < (int)m_vBoxes.size(); i++) m_vBoxes[i]->Reset(); } break; case SDL_MOUSEBUTTONDOWN: if (event.button.button == SDL_BUTTON_LEFT) m_bMouseBtn[0] = true; if (event.button.button == SDL_BUTTON_RIGHT) m_bMouseBtn[1] = true; break; case SDL_MOUSEBUTTONUP: if (event.button.button == SDL_BUTTON_LEFT) m_bMouseBtn[0] = false; if (event.button.button == SDL_BUTTON_RIGHT) m_bMouseBtn[1] = false; break; case SDL_MOUSEMOTION: SDL_GetMouseState(&m_iMouseX, &m_iMouseY); break; } } }
// Keyboard utility function. bool Engine::KeyDown(SDL_Scancode c) { if (m_iKeystates != nullptr) { if (m_iKeystates[c] == 1) return true; else return false; } return false; }
void Engine::Update() { for (int i = 0; i < (int)m_vBoxes.size(); i++) m_vBoxes[i]->Update(); }
void Engine::Render() { SDL_SetRenderDrawColor(m_pRenderer, 0, 0, 0, 255); SDL_RenderClear(m_pRenderer); // Clear the screen with the draw color. // Render stuff. for (int i = 0; i < (int)m_vBoxes.size(); i++) m_vBoxes[i]->Render(); // Draw anew. SDL_RenderPresent(m_pRenderer); }
void Engine::Clean() { cout << "Cleaning game." << endl; for (int i = 0; i < (int)m_vBoxes.size(); i++) { delete m_vBoxes[i]; m_vBoxes[i] = nullptr; } m_vBoxes.clear(); SDL_DestroyRenderer(m_pRenderer); SDL_DestroyWindow(m_pWindow); SDL_Quit(); }
int Engine::Run() { if (m_bRunning) // What does this do and what can it prevent? return -1; if (Init("GAME1017 Box Dragger", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, 0) == false) return 1; while (m_bRunning) // Main engine loop. { Wake(); HandleEvents(); Update(); Render(); if (m_bRunning) Sleep(); } Clean(); return 0; }
Engine& Engine::Instance() { static Engine instance; // C++11 will prevent this line from running more than once. Magic statics. return instance; }
SDL_Renderer* Engine::GetRenderer() { return m_pRenderer; } int Engine::GetMouseX() { return m_iMouseX; } int Engine::GetMouseY() { return m_iMouseY; } bool Engine::GetMouseBtn(int i) { return m_bMouseBtn[i]; }
Engine.h
#pragma once #include
// Do not include any macros, initialized properties or full function definitions in this header.
class Engine { private: // Private properties. bool m_bRunning; // Loop control flag. bool m_bMouseBtn[2] = { false, false }; bool m_bColorSet = false; int m_iMouseX, m_iMouseY; // Variables to hold mouse positions. Maybe better as array? const Uint8* m_iKeystates; // Keyboard state container. Uint32 m_start, m_end, m_delta, m_fps; // Fixed timestep variables. SDL_Window* m_pWindow; // This represents the SDL window. SDL_Renderer* m_pRenderer; // This represents the buffer to draw to.
vector
private: // Private methods. bool Init(const char* title, int xpos, int ypos, int width, int height, int flags); void Wake(); void Sleep(); void HandleEvents(); void Update(); void Render(); void Clean(); public: // Public methods. Engine(); ~Engine(); int Run(); static Engine& Instance(); // This static method creates the static instance that can be accessed 'globally' bool KeyDown(SDL_Scancode c); SDL_Renderer* GetRenderer(); int GetMouseX(); int GetMouseY(); bool GetMouseBtn(int i); };
main.cpp
#include "Engine.h"
int main(int argc, char* args[]) // Main MUST have these parameters for SDL. { return Engine::Instance().Run(); // Invokes Run() of the engine and stays in the function until engine is done. }
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
