SDL/OpenGL Double Buffering Memory Leak - c++

So I've been trying to make my own mini-engine for a game using SDL and OpenGL, with the main game logic contained inside of a singleton class. But for the life of me I can't figure out how I'm getting a memory leak with this sample portion of the engine, although I'm pretty certain that it occurs whenever I call SDL_GL_SwapBuffers. The leak is about 4 K every few two or three seconds. I'm using SDL 1.3; please help me find the leak, it's been driving me crazy for the past week!
MainModule.h
#ifndef MAINMODULE_H
#define MAINMODULE_H
/// Includes
#include "SDL/SDL.h"
#include "SDL/SDL_opengl.h"
#include <string>
/// Define Statements (Screen elements)
#define MAINMODULE_WIDTH 800
#define MAINMODULE_HEIGHT 600
#define MAINMODULE_CAPTION "MainModule"
/// Define Statements (OpenGL memory usage)
#define MAINMODULE_RED_SIZE 8
#define MAINMODULE_GREEN_SIZE 8
#define MAINMODULE_BLUE_SIZE 8
#define MAINMODULE_ALPHA_SIZE 8
#define MAINMODULE_BUFFER_SIZE 32
#define MAINMODULE_DEPTH_SIZE 16
#define MAINMODULE_DOUBLEBUFFER 1 // 1 to Enable
#define MAINMODULE_FLAGS SDL_OPENGL
/// Define Statements (OpenGL elements)
#define MAINMODULE_CLEARCOLOR 1.0f, 1.0f, 1.0f, 1.0f
#define MAINMODULE_SHADEMODEL GL_SMOOTH
#define MAINMODULE_DEPTH_TEST 0 // 1 to Enable
class MainModule {
private: // Constructor/Deconsctuctor
MainModule();
~MainModule();
private: // Class Variables
static MainModule* _Instance; // Singleton instance of the module.
static int _Width; // Width of the game screen.
static int _Height; // Height of the game screen.
static std::string _Caption; // Game screen caption/title.
static SDL_Surface* _ScreenSurface; // Game screen as represented by the window.
static SDL_Event* _Event; // Events such as mouse/key input.
static bool _IsInitialized; // Has the engine been initialized?
static bool _IsRunning; // Is the engine running?
public: // Get/Set Functions
static inline int Width() { return _Width; }
static inline int Height() { return _Height; }
static inline std::string Caption() { return _Caption; }
static inline bool IsInitialized() { return _IsInitialized; }
static inline bool IsRunning() { return _IsRunning; }
static void SetCaption(std::string caption);
public: // Class Functions
static void ConstructInstance();
static void DeconstructInstance();
static void InitializeModule();
static void RunGameLogic(); // Updates and renders game information.
};
#endif // MAINMODULE_H
MainModule.ccp
/// Includes
#include "MainModule.h"
#include <iostream>
// Static Variable Declarations
MainModule* MainModule::_Instance = 0;
int MainModule::_Width = MAINMODULE_WIDTH;
int MainModule::_Height = MAINMODULE_HEIGHT;
std::string MainModule::_Caption = MAINMODULE_CAPTION;
SDL_Surface* MainModule::_ScreenSurface = 0;
SDL_Event* MainModule::_Event = 0;
bool MainModule::_IsInitialized = false;
bool MainModule::_IsRunning = false;
/// Constructor/Deconstructor
MainModule::MainModule() { }
MainModule::~MainModule() {
if (_Event != 0) delete _Event;
if (_ScreenSurface != 0) SDL_FreeSurface(_ScreenSurface);
SDL_Quit();
}
/// Set Functions
void MainModule::SetCaption(std::string caption)
{ _Caption = caption; if (_IsInitialized) SDL_WM_SetCaption(_Caption.c_str(), 0); }
/// Class Functions
void MainModule::ConstructInstance()
{ if (_Instance == 0) _Instance = new MainModule(); }
void MainModule::DeconstructInstance()
{ if (_Instance != 0) { delete _Instance; _Instance = 0; } }
void MainModule::InitializeModule() {
ConstructInstance(); // Create an instance if the ConstructInstance function wasn't created before.
if (_Instance == 0) { printf("MainModule instance not created properly./n"); return; }
// Initialize SDL.
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { printf("SDL Initialization error: %s/n", SDL_GetError()); return; }
// Set OpenGL memory usage.
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, MAINMODULE_RED_SIZE);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, MAINMODULE_GREEN_SIZE);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, MAINMODULE_BLUE_SIZE);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, MAINMODULE_ALPHA_SIZE);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, MAINMODULE_BUFFER_SIZE);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, MAINMODULE_DEPTH_SIZE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, MAINMODULE_DOUBLEBUFFER);
// Creates the screen and window.
_ScreenSurface = SDL_SetVideoMode(MAINMODULE_WIDTH, MAINMODULE_HEIGHT, MAINMODULE_BUFFER_SIZE, MAINMODULE_FLAGS);
if (_ScreenSurface == 0) { printf("ScreenSurface not created properly./n"); return; }
SDL_WM_SetCaption(_Caption.c_str(), 0);
(MAINMODULE_DEPTH_TEST == 1) ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
glClearColor(MAINMODULE_CLEARCOLOR);
glShadeModel(GL_SMOOTH);
_IsInitialized = true;
_IsRunning = true;
_Event = new SDL_Event();
}
void MainModule::RunGameLogic() {
while (SDL_PollEvent(_Event)) { // Event handling loop
switch (_Event->type) {
case SDL_QUIT: // Exits out of game
{ _IsRunning = false; break; }
case SDL_ACTIVEEVENT:
{ break; }
case SDL_KEYDOWN: // Keyboard press down
{ break; }
case SDL_KEYUP: // Keyboard press up
{ break; }
case SDL_MOUSEMOTION: // Mouse movement
{ break; }
case SDL_MOUSEBUTTONDOWN: // Mouse button down
{ break; }
case SDL_MOUSEBUTTONUP: // Mouse button up
{ break; }
}
}
// Rendering logic
(MAINMODULE_DEPTH_TEST == 1) ? glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) : glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapBuffers();
}
/// Entry point for the program
int main(int argc, char** argv) {
MainModule::InitializeModule();
if (MainModule::IsInitialized()) { // If the modules initialized sucessfully
while (MainModule::IsRunning()) { MainModule::RunGameLogic(); }
}
MainModule::DeconstructInstance();
return 0;
}

Generally that sort of issue relates to internal issues with the underlying OpenGL libraries, the compiler, or something of that sort - graphics code like that is notorious for being a bit buggy and finicky.
If you give a few more details I may be able to give some more help - what operating system, compiler, graphics driver/version, specific SDL version down to build, etc.
In the mean time, consider compiling under another operating system and see what happens, or switch out SDL version.
But yeah, almost certain you're not doing anything wrong...

Related

"x is not a class or namespace name" when trying to use functions between namespaces

I am new to C++ and am currently trying to implement namespaces.
Here, you can see, I have a namespace, GameEngine_CORE, where all the behind-the-scenes work is done. I have the namespace SandBox, which is where the user actually would write their game.
I need SandBox to be able to access some of the variables in GameEngine_CORE, which seems to work fine (I have put a list of using GameEngine_CORE::... to save the user's time).
However, I also need GameEngine_CORE to be able to call the start() and update() function in SandBox, but upon compilation I get an error every time I try and call something from SandBox, eg: SandBox::start();
'SandBox' is not a class or namespace name
I am coding in Visual Studio, and compiling in Debug mode for Windows x64.
#pragma once
#include "SDL.h"
#undef main
#include <iostream>
#include <math.h>
#include <vector>
#include <stdio.h> // Temp
#include "Vec2.h"
#include "Log.h"
#include "Time.h"
#include "Boundary.h"
#include "Ray.h"
#include "Particle.h"
#include "Polygon.h"
#include "Path.h"
#include "Circle.h"
namespace GameEngine_CORE {
// Renderer
SDL_Renderer* renderer;
SDL_Window* window;
bool isRunning;
bool fullscreen;
// Mouse
int mouseX;
int mouseY;
Uint32 mouse;
// Keyboard
Vec2 arrowInp;
// Collision Stack
std::vector<Boundary> collisionStack;
// Log
Log logger;
// Physics
Time timer;
Time clockTime;
float deltaTime;
Vec2 gravity = Vec2(0, -9.8f);
void handleEvents();
void render();
void createWindow();
void destroyWindow();
// For the SandBox Program
void SandBox::start();
void SandBox::update();
int main() { // Entry Point
double m_LastClock = 0.0;
clockTime.StartTimer(); // Start Counter
SandBox::start(); // For user
while (isRunning) {
double m_CurrentClock;
m_CurrentClock = clockTime.GetTimer(); // Get Counter
deltaTime = (float)(m_CurrentClock - m_LastClock);
if (deltaTime > 0.15f) {
deltaTime = 0.15f;
}
handleEvents();
render();
m_LastClock = m_CurrentClock;
}
destroyWindow();
return 0;
}
// Initialise the window to draw to
void createWindow() {
// Set window size and type
fullscreen = true;
Uint32 flags = 0;
flags = SDL_WINDOW_RESIZABLE;
if (fullscreen) {
flags = flags | SDL_WINDOW_MAXIMIZED;
}
if (SDL_Init(SDL_INIT_EVERYTHING) == 0) {
logger.Message("Subsystems Initialised");
window = SDL_CreateWindow("2D Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 300, 300, flags);
if (window) {
logger.Message("Window Created");
// Minimum window size
SDL_SetWindowMinimumSize(window, 1000, 1000);
}
// Create Renderer for window
renderer = SDL_CreateRenderer(window, -1, 0);
if (renderer) {
SDL_SetRenderDrawColor(renderer, 121, 121, 121, 255);
logger.Message("Renderer Created");
// Set how to blend alphas and colours
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
isRunning = true;
}
}
}
void destroyWindow() {
// Frees memory associated with renderer and window
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window); //error here
SDL_Quit();
}
// Handles SDL events
void handleEvents() {
// Check for quit
SDL_Event event;
SDL_PollEvent(&event);
// Check we have latest inputs
SDL_PumpEvents();
// Reset inputs
arrowInp.x = 0; arrowInp.y = 0;
// If we get quit event, stop running and free up memory
switch (event.type) {
case SDL_QUIT:
isRunning = false;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_LEFT: arrowInp.x = -1; break;
case SDLK_RIGHT: arrowInp.x = 1; break;
}
switch (event.key.keysym.sym)
{
case SDLK_UP: arrowInp.y = -1; break;
case SDLK_DOWN: arrowInp.y = 1; break;
}
break;
default:
break;
}
}
// Render Function
void render() {
// Set back ground colour and clear renderer every frame
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SandBox::update();
SDL_RenderPresent(renderer); // Draw to the screen
}
}
using GameEngine_CORE::createWindow;
using GameEngine_CORE::deltaTime;
using GameEngine_CORE::mouse;
using GameEngine_CORE::mouseX;
using GameEngine_CORE::mouseY;
using GameEngine_CORE::renderer;
using GameEngine_CORE::arrowInp;
using GameEngine_CORE::collisionStack;
namespace SandBox { // Where the user of the engine would write code
// Drawing Vars
Particle particle = Particle(50, 359.99f, Vec2(900, 700));
Boundary boundaries[] = { Boundary(Vec2(800, 0), Vec2(800, 1000)), Boundary(Vec2(300, 300), Vec2(400, 700)), Boundary(Vec2(300, 700), Vec2(800, 800)) };
GameEngine::Polygon square = GameEngine::Polygon(Vec2(800, 400), 45, Vec2(400, 200), 4);
GameEngine::Polygon poly1 = GameEngine::Polygon(Vec2(200, 400), Vec2(200, 200), 6);
GameEngine::Polygon ellipse = GameEngine::Polygon(Vec2(1200, 600), Vec2(200, 400), 64);
Path path = Path(Vec2(500, 100));
Circle circle = Circle(100, Vec2(800, 500));
GameEngine::Polygon player = GameEngine::Polygon(Vec2(700, 400), 0, Vec2(60, 60), 16);
float moveSpeed;
Vec2 velocity;
float rotator = 0;
// Functions
static void collision();
static void start() { // Called when the program starts
createWindow();
moveSpeed = 1000.0f;
path.addPoints(std::vector<Vec2> { Vec2(200, 100), Vec2(200, 800), Vec2(350, 800), Vec2(700, 650), Vec2(400, 400), Vec2(200, 100)});
}
static void update() { // Repeats every frame
printf("%f secs \n", deltaTime);
// Get the mouse' current state
mouse = SDL_GetMouseState(&mouseX, &mouseY);
particle.setPos(Vec2((float)mouseX, (float)mouseY));
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); // Draw Boundaries
rotator += 0.01f;
poly1.setRot(rotator);
// Player
player.Show(renderer);
square.Show(renderer);
poly1.Show(renderer);
ellipse.Show(renderer);
path.Show(renderer);
collision();
}
static void collision() {
// Clears stack
collisionStack.clear();
// Adds colliders to stack
square.makeCollider(collisionStack);
poly1.makeCollider(collisionStack);
ellipse.makeCollider(collisionStack);
path.makeCollider(collisionStack);
player.makeCollider(collisionStack);
// Draw Particles
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 100);
particle.collide(renderer, collisionStack);
}
static void movement() {
Vec2 velocity = arrowInp * moveSpeed;
}
}
I don't imagine anyone needs to see the header files, but I can provide them if requested. I also declare my start() and update() functions in GameEngine_CORE, which I'm not sure is correct, however I was getting complaints from Visual Studio that it could not find the function definitions otherwise.

Gtkmm : Drawing with cairo

Using Gtkmm and Cairo, I want to be able to draw different shapes on photos. In the header bar of my window, I have two buttons representing shapes to draw (circle and rectangle). When you click one of them, you can draw its associated shape. Here is mt code:
MyWindow.cpp
#include "MyWindow.h"
MyWindow::MyWindow()
: circleButton("circle"),
rectangleButton("rectangle ") {
set_default_size(700, 700);
set_position(Gtk::WIN_POS_CENTER);
header.set_show_close_button(true);
header.pack_start(rectangleButton);
header.pack_start(circleButton);;
set_titlebar(header);
// Dwg is an instance of Drawing class
circleButton.signal_clicked().connect([&] {
Dwg.switch_to_circle();
});
rectangleButton.signal_clicked().connect([&] {
Dwg.switch_to_rectangle();
});
add(Dwg);
show_all();
}
Drawing.h
#ifndef DRAWING_H
#define DRAWING_H
#include <gtkmm.h>
#include <cairo/cairo.h>
class MyDrawing : public Gtk::Layout {
public:
MyDrawing();
~MyDrawing();
void switch_to_circle();
void switch_to_rectangle();
protected:
virtual bool draw_image(const Cairo::RefPtr<::Cairo::Context> &cr);
virtual bool draw_rectangle(const Cairo::RefPtr<::Cairo::Context> &cr);
virtual bool draw_circle(const Cairo::RefPtr<::Cairo::Context> &cr);
private:
Glib::RefPtr<Gdk::Pixbuf> pix;
double beginPoint_x, beginPoint_y, endPoint_x, endPoint_y, lineWidth,width,height;
bool isDrawRectangle;
};
#endif // DRAWING_H
Drawing.cpp
#include <iostream>
#include "MyDrawing.h"
#include <cairomm/context.h>
#include <cairomm/surface.h>
MyDrawing::MyDrawing()
: isDrawRectangle(true),
width(20),
height(20) {
pix = Gdk::Pixbuf::create_from_file("file.svg", 500, 500);
if (pix) {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_image));
}
add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON_PRESS_MASK);
signal_button_press_event().connect([&](GdkEventButton *e) {
this->beginPoint_x = e->x;
this->beginPoint_y = e->y;
if(isDrawRectangle) {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_rectangle));
queue_draw();
}
else {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_circle));
queue_draw();
}
return true;
});
signal_motion_notify_event().connect([&](GdkEventMotion *e) {
this->endPoint_x = e->x;
this->endPoint_y = e->y;
width = endPoint_x - beginPoint_x;
height = endPoint_y - beginPoint_y;
if(isDrawRectangle) {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_rectangle));
queue_draw();
}
else {
this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_circle));
queue_draw();
}
return true;
});
}
MyDrawing::~MyDrawing() = default;
bool MyDrawing::draw_image(const Cairo::RefPtr<::Cairo::Context> &cr) {
std::cout << "signal img" << std::endl;
if (pix) {
cr->save();
Gdk::Cairo::set_source_pixbuf(cr, pix, 100, 100);
cr->rectangle(0, 0, get_width(), get_height());
cr->fill();
cr->restore();
}
return false;
}
bool MyDrawing::draw_rectangle(const Cairo::RefPtr<::Cairo::Context> &cr) {
std::cout << "signal square" << std::endl;
cr->save();
cr->set_line_width(10);
cr->set_source_rgba(0., 0., 1., 1.);
cr->rectangle(beginPoint_x, beginPoint_y, width, height);
cr->stroke();
cr->save();
cr->restore();
return false;
}
bool MyDrawing::draw_circle(const Cairo::RefPtr<::Cairo::Context> &cr) {
std::cout << "signal square" << std::endl;
cr->save();
cr->set_line_width(10);
cr->set_source_rgba(0., 0., 1., 1.);
cr->arc(beginPoint_x, beginPoint_y, width, 0, 2 * M_PI);
cr->stroke();
cr->restore();
return false;
}
void MyDrawing::switch_to_circle() {
isDrawRectangle = false;
}
void MyDrawing::switch_to_rectangle() {
isDrawRectangle = true;
}
When I click another shape, the previous shape keeps being displayed on the drawing area and the new shape is drawn on it. On the other hand, when the signal is disconnected, the corresponding shape also disappears from the screen. How could I make sure the shapes keep being displayed?
I am not sure exactly what made you inherit from Gtk::Layout instead of using a standard Gtk::DrawingArea, but I created a simplified (and working) example using a design similar to yours.
The basic idea is that when the user is done drawing a shape (stops the drag and releases the mouse button), the following happens:
The current state of the window (in terms of what is drawn on it) is saved to a Gtk::Pixbuf.
That Gtk::PixBuf is painted on the window.
This means that in 1., the last drawn shaped is also saved in the buffer. When 2. happens, is repainted on the window and hence does not go away. Here is the code, which you will need to adapt a bit to your case. First, a draw helper:
class DrawHelper : public Gtk::Layout
{
public:
DrawHelper();
~DrawHelper();
private:
bool draw_image(const Cairo::RefPtr<::Cairo::Context>& p_context);
bool draw_rectangle(const Cairo::RefPtr<::Cairo::Context>& p_context);
bool add_current_shape(const Cairo::RefPtr<::Cairo::Context>& p_context);
Glib::RefPtr<Gdk::Pixbuf> m_buffer;
double m_startX;
double m_startY;
double m_endX;
double m_endY;
double m_width;
double m_height;
sigc::connection m_drawConnection;
};
which is responsible to do the actual drawing and handle connections. It is implemented like so:
DrawHelper::DrawHelper()
{
// Create a pixel buffer containing the background image:
m_buffer = Gdk::Pixbuf::create_from_file("file.svg", DEFAULT_WIDTH, DEFAULT_HEIGHT);
signal_draw().connect(sigc::mem_fun(*this, &DrawHelper::draw_image));
// Enable signals:
add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
// Save initial pointer position when clicked:
signal_button_press_event().connect(
[this](GdkEventButton* p_event)
{
m_startX = p_event->x;
m_startY = p_event->y;
return true;
});
// Update rectangle when mouse is dragged:
signal_motion_notify_event().connect(
[this](GdkEventMotion* p_event)
{
m_endX = p_event->x;
m_endY = p_event->y;
m_width = m_endX - m_startX;
m_height = m_endY - m_startY;
signal_draw().connect(sigc::mem_fun(*this, &DrawHelper::draw_rectangle));
queue_draw();
return true;
});
// Change background so it includes the shape just drawn by
// the user:
signal_button_release_event().connect(
[this](GdkEventButton* p_event)
{
// Notice we save to connection to later disconnect it:
m_drawConnection = signal_draw().connect(sigc::mem_fun(*this, &DrawHelper::add_current_shape));
return true;
});
}
DrawHelper::~DrawHelper() = default;
bool DrawHelper::draw_image(const Cairo::RefPtr<::Cairo::Context>& p_context)
{
Gdk::Cairo::set_source_pixbuf(p_context, m_buffer, 0, 0);
p_context->paint();
return false;
}
bool DrawHelper::draw_rectangle(const Cairo::RefPtr<::Cairo::Context>& p_context)
{
p_context->save();
p_context->set_line_width(2);
p_context->rectangle(m_startX, m_startY, m_width, m_height);
p_context->stroke();
p_context->restore();
return false;
}
bool DrawHelper::add_current_shape(const Cairo::RefPtr<::Cairo::Context>& p_context)
{
// Save the current drawing, including the last drawn
// shape. This will become the new background (which will
// visually preserve the last drawn shape).
m_buffer = Gdk::Pixbuf::create(p_context->get_target(), 0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT);
Gdk::Cairo::set_source_pixbuf(p_context, m_buffer, 0, 0);
p_context->paint();
// We disconnect the signal because we do not want it
// to keep getting called:
m_drawConnection.disconnect();
return false;
}
Then, a window to hold this helper and display it to the user:
class MyWindow : public Gtk::Window
{
public:
MyWindow();
private:
DrawHelper m_drawHelper;
};
MyWindow::MyWindow()
{
set_default_size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Add draw helper:
add(m_drawHelper);
// Show all widgets:
show_all();
}
Then, the main to run it:
#include <gtkmm.h>
#include <cairo/cairo.h>
#include <cairomm/context.h>
#include <cairomm/surface.h>
constexpr int DEFAULT_WIDTH = 500;
constexpr int DEFAULT_HEIGHT = 500;
// DrawHelper here ...
// MyWindow here ...
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
MyWindow window;
return app->run(window);
}
That being said, I would recommend you use a classic Gtk::DrawingArea instead and overload the on_draw signal handler. This would make all of this easier to understand, and the online documentation would be of more help to you.
If you are still interested, I have another solution for you. Instead of saving the already drawn shape on the background image, you could save their parameters directly and redraw them. I have written an example program that does just this:
#include <memory>
#include <vector>
#include <gtkmm.h>
#include <cairo/cairo.h>
#include <cairomm/context.h>
#include <cairomm/surface.h>
constexpr int DEFAULT_WIDTH = 500;
constexpr int DEFAULT_HEIGHT = 500;
constexpr double LINE_WIDTH = 2.0;
// Free functions for drawing shapes:
namespace
{
void DrawRectangle(const Cairo::RefPtr<Cairo::Context>& p_context,
double p_startX,
double p_startY,
double p_width,
double p_height)
{
p_context->save();
p_context->set_line_width(LINE_WIDTH);
p_context->set_source_rgba(0, 0, 1, 1);
p_context->rectangle(p_startX, p_startY, p_width, p_height);
p_context->stroke();
p_context->restore();
}
void DrawCircle(const Cairo::RefPtr<Cairo::Context>& p_context,
double p_startX,
double p_startY,
double p_width)
{
p_context->save();
p_context->set_line_width(LINE_WIDTH);
p_context->set_source_rgba(0, 0, 1, 1);
p_context->arc(p_startX, p_startY, p_width, 0, 2 * M_PI);
p_context->stroke();
p_context->restore();
}
}
// Shape interface:
//
// A shape represents a 2D geometric shape a user can draw on the
// Drawing area. All shapes implement a 'Draw' method which is where
// the drawing logic resides.
class IShape
{
public:
virtual ~IShape() = default;
virtual void Draw(const Cairo::RefPtr<Cairo::Context>& p_context) = 0;
};
// Rectangle shape:
class Rectangle : public IShape
{
public:
Rectangle(double p_left, double p_up, double p_width, double p_height)
: m_left{p_left}
, m_up{p_up}
, m_width{p_width}
, m_height{p_height}
{}
void Draw(const Cairo::RefPtr<Cairo::Context>& p_context) override
{
DrawRectangle(p_context, m_left, m_up, m_width, m_height);
}
private:
double m_up;
double m_left;
double m_width;
double m_height;
};
// Circle shape:
class Circle : public IShape
{
public:
Circle(double p_cX, double p_cY, double p_radius)
: m_cX{p_cX}
, m_cY{p_cY}
, m_radius{p_radius}
{}
void Draw(const Cairo::RefPtr<Cairo::Context>& p_context) override
{
DrawCircle(p_context, m_cX, m_cY, m_radius);
}
private:
double m_cX;
double m_cY;
double m_radius;
};
// Draw helper:
//
// This class represents the widget onto which the user can drawn. Under
// the hood, this is a Gtk::Drawing area with some signal handlers defined
// to draw shapes on user action.
//
// All drawing occurs in the 'on_draw' method, and all signal handlers to
// is to handle the data (e.g positions, dimentsions, etc) for the 'on_draw'
// method to work appropriately.
//
// The 'SetCurrentShape' method can be used to tell the helper which shape
// to draw.
class DrawHelper : public Gtk::DrawingArea
{
public:
enum class Shape
{
None,
Rectangle,
Circle,
};
DrawHelper()
{
add_events(Gdk::BUTTON1_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
// Click, drag and release signal handlers:
signal_button_press_event().connect( [this](GdkEventButton* p_event){return OnButtonPressed(p_event);} );
signal_motion_notify_event().connect( [this](GdkEventMotion* p_event){return OnMouseMotion(p_event);} );
signal_button_release_event().connect([this](GdkEventButton* p_event){return OnButtonReleased(p_event);});
}
void SetCurrentShape(Shape p_shape)
{
m_currentShape = p_shape;
}
private:
// All drawing occurs here and only here:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& p_context) override
{
// Draw background:
if(!m_buffer)
{
m_buffer = Gdk::Pixbuf::create_from_file("file.svg", DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
Gdk::Cairo::set_source_pixbuf(p_context, m_buffer, 0, 0);
p_context->paint();
// Draw previously drawn shapes:
for(const auto& shape : m_alreadyDrawn)
{
shape->Draw(p_context);
}
// Draw current shape:
if(m_currentShape == Shape::Rectangle)
{
DrawRectangle(p_context, m_startX, m_startY, m_width, m_height);
}
if(m_currentShape == Shape::Circle)
{
DrawCircle(p_context, m_startX, m_startY, m_width);
}
return false;
}
bool OnButtonPressed(GdkEventButton* p_event)
{
m_startX = p_event->x;
m_startY = p_event->y;
return true;
}
bool OnMouseMotion(GdkEventMotion* p_event)
{
m_endX = p_event->x;
m_endY = p_event->y;
m_width = m_endX - m_startX;
m_height = m_endY - m_startY;
queue_draw();
return true;
}
bool OnButtonReleased(GdkEventButton* p_event)
{
if(m_currentShape == Shape::Rectangle)
{
m_alreadyDrawn.push_back(std::make_unique<Rectangle>(m_startX, m_startY, m_width, m_height));
}
if(m_currentShape == Shape::Circle)
{
m_alreadyDrawn.push_back(std::make_unique<Circle>(m_startX, m_startY, m_width));
}
return true;
}
Shape m_currentShape = Shape::None;
Glib::RefPtr<Gdk::Pixbuf> m_buffer;
double m_startX;
double m_startY;
double m_endX;
double m_endY;
double m_width;
double m_height;
std::vector<std::unique_ptr<IShape>> m_alreadyDrawn;
};
// Main window:
//
// This window holds all widgets. Through it, the user can pick a shape
// to draw and use the mouse to draw it.
class MyWindow : public Gtk::Window
{
public:
MyWindow()
: m_drawRectangleBtn{"Rectangle"}
, m_drawCircleBtn{"Circle"}
{
set_default_size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
m_headerBar.set_show_close_button(true);
m_headerBar.pack_start(m_drawRectangleBtn);
m_headerBar.pack_start(m_drawCircleBtn);;
set_titlebar(m_headerBar);
add(m_drawArea);
m_drawRectangleBtn.signal_clicked().connect([this](){OnRectangleBtnClicked();});
m_drawCircleBtn.signal_clicked().connect([this](){OnCircleBtnClicked();});
show_all();
}
private:
Gtk::HeaderBar m_headerBar;
Gtk::Button m_drawRectangleBtn;
Gtk::Button m_drawCircleBtn;
DrawHelper m_drawArea;
void OnRectangleBtnClicked()
{
m_drawArea.SetCurrentShape(DrawHelper::Shape::Rectangle);
}
void OnCircleBtnClicked()
{
m_drawArea.SetCurrentShape(DrawHelper::Shape::Circle);
}
};
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
MyWindow window;
return app->run(window);
}
Each time the user releases the mouse button, the drawn shape is saved (with the parameters at the time of the release) into an std::vector as an IShape, which has a Draw, method. This method can later be called to redraw the shape. Then, in the on_draw handler, all previously drawn shapes are redrawn, leaving them on the screen. Note that I have used a Gtk::DrawingArea here, which is more typical than your approach. I wanted to show you that alternative which, in my opinion, makes cleaner code (no messing around with the handler callbacks).
On a final note, possible enhancements are possible with this (there are more, theses are just some I was thinking about while writing this):
You could reduce performance costs by caching some stuff instead of redrawing everything every time.
You could reduce performance costs by using parameters in the calls to queue_draw so that the whole widget is not constantly redrawn (only the part that changed).
You could use a factory to create the shapes. This would decouple the shape creation from the rest of the code, which would only know the IShape interface. It would also make you program easier to maintain if you ever want to add shapes.

SFML sf::Mouse::getPosition method cannot write the correct argument

My problem is that I have written this code inside Game::HandleInput() method but I cannot make the sf::Mouse::getPosition() method to get the mouse coordinates relative to window. Without argument, I don't get an error. However, ship doesn't rotate properly. I have tried getPosition(m_window) and getPosition(&m_window). I am getting this error:
no instance of overloaded function "sf::Mouse::getPosition" matches the argument list
EDIT: UPDATED THE WINDOW.H
Window.h:
class Window{
//Constructers
public:
Window();
Window(const std::string& l_title, const sf::Vector2u& l_size);
...
private:
sf::RenderWindow m_window;
...
}
EDIT: ADDED THE FULL CODE OF WINDOW.CPP:
Window.cpp:
#include "Window.h"
Window::Window() {
Setup("Window", sf::Vector2u(640, 480));
}
Window::Window(const std::string& l_title, const sf::Vector2u& l_size) {
Setup(l_title, l_size);
}
Window::~Window() {
Destroy();
}
void Window::Setup(const std::string& l_title,
const sf::Vector2u& l_size)
{
m_windowTitle = l_title;
m_windowSize = l_size;
m_isFullscreen = false;
m_isDone = false;
Create();
}
void Window::Create() {
auto style = (m_isFullscreen ? sf::Style::Fullscreen
: sf::Style::Default);
m_window.create({ m_windowSize.x, m_windowSize.y, 32 },
m_windowTitle, style);
}
void Window::Destroy() {
m_window.close();
}
void Window::Update() {
sf::Event event;
while (m_window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
m_isDone = true;
}
else if (event.type == sf::Event::KeyPressed &&
event.key.code == sf::Keyboard::F5)
{
ToggleFullscreen();
}
}
}
void Window::ToggleFullscreen() {
m_isFullscreen = !m_isFullscreen;
Destroy();
Create();
}
void Window::BeginDraw() { m_window.clear(sf::Color::Black); }
void Window::EndDraw() { m_window.display(); }
bool Window::IsDone() { return m_isDone; }
bool Window::IsFullscreen() { return m_isFullscreen; }
sf::Vector2u Window::GetWindowSize() { return m_windowSize; }
void Window::Draw(sf::Drawable& l_drawable){
m_window.draw(l_drawable);
}
EDIT: UPDATED THE GAME.H
Game.h:
class Game{
public:
Game();
~Game();
void HandleInput();
void Update();
void Render();
Window* GetWindow();
private:
...
Window m_window;
...
}
EDIT: UPDATED THE GAME.CPP
Game.cpp:
Game::Game() : m_window("Multiplayer Space Shooter Game", sf::Vector2u(800, 600)) {
// Setting up class members.
m_shipText.loadFromFile("C:\\Users\\AliTeo\\Desktop\\Piksel çalışmaları\\ship_pixel2.png");
m_ship.setTexture(m_shipText);
m_ship.setOrigin(m_shipText.getSize().x / 2, m_shipText.getSize().y / 2);
m_ship.setPosition(320, 240);
}
void Game::HandleInput() {
...
//Get the angle between ship and mouse.
//Error if there is an argument in getPosition()
m_angle = atan2(sf::Mouse::getPosition().y - m_ship.getPosition().y, sf::Mouse::getPosition().x - m_ship.getPosition().x); //TO DO: getPosition(&Relative To)
m_angle *= 180 / m_PI;
...
}
Window* Game::GetWindow() { return &m_window; }
EDIT: ADDED THE MAIN.CPP
Main.cpp
int main() {
Game game;
while (!game.GetWindow()->IsDone()) {
game.HandleInput();
game.Update();
game.Render();
}
}
First let me give you what I assume is a minimal example reproducing your problem:
#include <SFML/Graphics.hpp>
class MyWindow {
public:
MyWindow()
: m_window({800, 600, 32}, "my window title") {}
private:
sf::RenderWindow m_window;
};
int main() {
MyWindow window;
sf::Mouse::getPosition(window);
}
This code doesn't compile (and admittedly wouldn't do anything interesting when compiled, but that's not the point). I suspect it'd give you the same error that you're currently having if you tried to compile it.
Please note that this is what we expect when we talk about a MCVE: this code is short, simple, exhibits the error and would compile if not because of it.
Besides, it makes the error painfully clear, and if you tried to come up with a MCVE yourself, you may have solved your problem without having to post a question here, which would certainly save you time.
Contrast with your code:
m_angle = atan2(sf::Mouse::getPosition().y - m_ship.getPosition().y
,sf::Mouse::getPosition().x - m_ship.getPosition().x
);
//TO DO: getPosition(Relative To)
This code is legal, but you explained that it is incorrect and you wanted to turn it into something along those lines:
m_angle = atan2(sf::Mouse::getPosition(m_window).y - m_ship.getPosition().y
,sf::Mouse::getPosition(m_window).x - m_ship.getPosition().x
);
//TO DO: getPosition(Relative To)
... which doesn't compile.
However, in this scope m_window is a Window not a sf::RenderWindow!
The problem is that you're passing a reference to an object (MyWindow in my example, Window in your case) that encapsulates a sf::RenderWindow, but isn't convertible to sf::Window& itself.
Therefore, you can't pass it to sf::Mouse::getPosition which expects either nothing or a sf::Window&, but certainly not a Window& or a MyWindow&.
There are a lot of ways of fixing this. Two of which are presented below:
#include <SFML/Graphics.hpp>
class MyWindow {
public:
MyWindow()
: m_window({800, 600, 32}, "my window title") {}
// you could add an accessor
const sf::Window& getSfmlWindow() const { return m_window; }
// you may also expose a method to get the mouse position
// relatively to this window
const sf::Vector2i getMousePosition() const {
return sf::Mouse::getPosition(m_window);
}
private:
sf::RenderWindow m_window;
};
int main() {
MyWindow window;
sf::Vector2i mouse_position;
// this won't work! window isn't convertible to sf::Window&
// mouse_position = sf::Mouse::getPosition(window);
// using the accessor
mouse_position = sf::Mouse::getPosition(window.getSfmlWindow());
// or the exposed method
mouse_position = window.getMousePosition();
}

SFML Sprite isn't showing on the window but it isn't giving me any errors

I'm using SFML for creating a small game for practice, a replica of the old Tanks game I played when I was young on some type of console.
I've encountered a strage thing when I'm trying to draw the player. I'm saying strange because at first it was showing, so I don't think something is wrong with my function, but after I made a derived class for the enemy tanks and made a new file for that I had some nesting problems with linking all the files togheter(5 files, 1 cpp and 4 headers). After I figured that out, I ran into this problem and can't find any solution to it. It was working before that problem and now it doesn't anymore.
I checked the texture, if it is loading, the position I set it to, they are ok.
Here is the class for the player tank
class tank{
protected:
sf::FloatRect boundingBox;
sf::Sprite tankSprite;
bullet *mBullet;
public :
tank(); // constructor
bullet * get_ptr(){return mBullet;}
sf::FloatRect get_boundingBox(){return boundingBox;}
void setRotation(float);
void show(){game.draw(tankSprite);}
void update();
void shoot(){mBullet = new bullet(tankSprite);}
};
And this is the code in my main where I update the world and draw on my window
if(!stop)
Me.update();
if(Me.get_ptr()->isFired()==true)
Me.get_ptr()->update();
// Output on the window
game.clear();
Me.show();
if(Me.get_ptr()->isFired()==true)
Me.get_ptr()->show();
game.display();
Side note : game is the render window and is globally declared( I know it's bad practice, I'm slowing getting to ditch this bad habit)
main.cpp
#include <SFML/Graphics.hpp>
#include "Tank.h"
int main()
{
tank Me;
init();
while (game.isOpen())
{
sf::Event event;
while (game.pollEvent(event))
{
if (event.type == sf::Event::Closed)
game.close();
else if (event.type == sf::Event::KeyPressed)
{
stop=false;
switch(event.key.code)
{
case sf::Keyboard::W:
{
Me.setRotation(0);
break;
}
case sf::Keyboard::D:
{
Me.setRotation(90);
break;
}
case sf::Keyboard::S:
{
Me.setRotation(180);
break;
}
case sf::Keyboard::A:
{
Me.setRotation(270);
break;
}
case sf::Keyboard::Escape:
{
game.close();
break;
}
case sf::Keyboard::Space:
{
if(Me.get_ptr()->isFired()==false)Me.shoot();
break;
}
case sf::Keyboard::LControl:
{
stop=true;
break;
}
default:
break;
}
}
}
if(!stop)
Me.update();
if(Me.get_ptr()->isFired()==true)
Me.get_ptr()->update();
// Output on the window
game.clear();
Me.show();
if(Me.get_ptr()->isFired()==true)
Me.get_ptr()->show();
game.display();
}
return 0;
}
init.h
#include<iostream>
bool stop;
sf::RenderWindow game(sf::VideoMode(400, 400), "SFML");
sf::Texture myTankTexture;
sf::Texture bulletTexture;
void init(){
srand(time(NULL));
stop = true;
game.setVerticalSyncEnabled(true);
game.setFramerateLimit(60);
if(!myTankTexture.loadFromFile("tank.jpg"))
{
std::cout<<"eroare la textura tank"<<std::endl;
}
myTankTexture.setSmooth(true);
if(!bulletTexture.loadFromFile("bullet.jpg"))
{
std::cout<<"bullet texture error"<<std::endl;
}
bulletTexture.setSmooth(true);
}
sf::Vector2f rotationToDirection(int rotation){
sf::Vector2f dir;
switch (rotation)
{
case 0:
{
dir.x=0.0;
dir.y=-1.0;
break;
}
case 90:
{
dir.x=1.0;
dir.y=0.0;
break;
}
case 180:
{
dir.x=0.0;
dir.y=1.0;
break;
}
case 270:
{
dir.x=-1.0;
dir.y=0.0;
break;
}
}
return dir;
}
bullet.h
#include "init.h"
class bullet{
protected:
sf::Sprite bulletSprite;
sf::FloatRect boundingBox;
bool isBFired = false;
public:
bullet(sf::Sprite); // constructor
~bullet(){isBFired=false;}
sf::FloatRect get_boundingBox(){return boundingBox;}
bool isFired(){if(isBFired)return true;else return false;}
int collision();
void del(){delete this;}
void update();
void show(){game.draw(bulletSprite);}
};
bullet::bullet(sf::Sprite sprite){
isBFired = true;
bulletSprite.setTexture(bulletTexture);
bulletSprite.setOrigin(2.5,5.0);
bulletSprite.setRotation(sprite.getRotation());
bulletSprite.setPosition(sprite.getPosition().x+rotationToDirection(bulletSprite.getRotation()).x*5.0,sprite.getPosition().y+rotationToDirection(bulletSprite.getRotation()).y*5.0);
boundingBox = bulletSprite.getLocalBounds();
}
int bullet::collision(){
if(bulletSprite.getPosition().x < 0
|| bulletSprite.getPosition().x > 400
|| bulletSprite.getPosition().y > 400
|| bulletSprite.getPosition().y < 0 )return 1;
else
return 0;
}
void bullet::update(){
bulletSprite.move(rotationToDirection(bulletSprite.getRotation()).x*6.0,rotationToDirection(bulletSprite.getRotation()).y*6.0);
if(collision()==1)
delete this;
else
boundingBox = bulletSprite.getLocalBounds();
}
tank.h
#include "bullet.h"
class tank{
protected:
sf::FloatRect boundingBox;
sf::Sprite tankSprite;
bullet *mBullet;
public :
tank(); // constructor
bullet * get_ptr(){return mBullet;}
sf::FloatRect get_boundingBox(){return boundingBox;}
void setRotation(float);
void show(){game.draw(tankSprite);}
void update();
void shoot(){mBullet = new bullet(tankSprite);}
};
tank::tank(){
tankSprite.setTexture(myTankTexture);
tankSprite.setOrigin(20,20);
tankSprite.setRotation(0);
tankSprite.setPosition(200,200);
boundingBox = tankSprite.getLocalBounds();
}
void tank::update(){
tankSprite.move(rotationToDirection(tankSprite.getRotation()).x*3,rotationToDirection(tankSprite.getRotation()).y*3);
boundingBox = tankSprite.getLocalBounds();
}
void tank::setRotation(float rotation){
tankSprite.setRotation(rotation);
}
You are not setting the texture rectangle as far as I see, see if this helps you. When you set your tank sprite try :
tankSprite.setTexture(myTankTexture, true);
Passing true will reset your texture rectangle to the size of the sprite, see the SFML docs for more information.
Also, you are creating the Tank class before calling init(), myTankTexture will be empty so the tank constructor uses that, and your texture is loaded later.
As they say in your language, "multa bafta" :)

C++ Polymorphism with Lists issue

First of all thank you for your time and apologize for the long of this post but i couldn't find any other way to make it shorter and also for me english! if you don't understand something, just ask ^^. Hope you can find the error because is driving me crazy.
I'm currently learning DirectX 11 and i'm making This Little Game from this website but applying OOP and DirectX 11 instead of 9 just taking certain things from that project.
Ok, now that you have a little context here is the problem.
I made an abstract class called GameObject which encapsulates all the functionalities concerning to rendering, like storing the image(s), animation, transition between frames, etc. This GameObject class is used to define every other object that will interact in my game.
GameObject Class
////////////////////////////////////////////////////////////////////////////////
// Filename: GameObject.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GAME_OBJECT_H_
#define _GAME_OBJECT_H_
///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "Sprite.h"
#include "InputHandler.h"
#include "Timer.h"
class GameObject
{
public:
GameObject();
GameObject(const GameObject& other);
~GameObject();
virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) = 0;
bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen, WCHAR* spriteFileName, Bitmap::DimensionType bitmap, Bitmap::DimensionType sprite, int numberOfFramesAcross, int initialFrame, bool useTimer);
virtual void Shutdown();
virtual bool Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix);
void Move();
void Move(const D3DXVECTOR2 vector);
virtual void Frame(const InputHandler::ControlsType& controls);
void SortFrameArray(const int* framesOrder, int size);
void SetPosition(const POINT& position);
const POINT GetPosition();
void SetVelocity(const D3DXVECTOR2& velocity);
const D3DXVECTOR2 GetVelocity();
void SetStatus(const bool status);
bool GetStatus();
float GetMovementDelayTime();
void ResetMovementDelayTime();
float GetAnimationDelayTime();
void ResetAnimationDelayTime();
//Both of this objects i think i'll remove them from this class. I don't think they belong here.
ID3D11Device* GetDevice();
HWND GetHWND();
Sprite* GetSprite();
protected:
ID3D11Device* m_device;
HWND m_hwnd;
Sprite* m_Sprite;
Timer* m_Timer;
POINT m_position;
D3DXVECTOR2 m_velocity;
bool m_active;
float m_movementDelay;
float m_animationDelay;
};
#endif
Cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: GameObject.cpp
////////////////////////////////////////////////////////////////////////////////
#include "GameObject.h"
GameObject::GameObject()
{
this->m_Sprite = nullptr;
this->m_Timer = nullptr;
this->m_movementDelay = 0.0f;
this->m_animationDelay = 0.0f;
}
GameObject::GameObject(const GameObject& other)
{
}
GameObject::~GameObject()
{
}
bool GameObject::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen, WCHAR* spriteFileName, Bitmap::DimensionType bitmap, Bitmap::DimensionType sprite, int numberOfFramesAcross, int initialFrame, bool useTimer)
{
bool result;
this->m_device = device;
this->m_hwnd = hwnd;
this->m_Sprite = new Sprite();
if (!this->m_Sprite)
{
return false;
}
result = this->m_Sprite->Initialize(device, hwnd, screen, spriteFileName, bitmap, sprite, numberOfFramesAcross, initialFrame);
if (!result)
{
return false;
}
if (useTimer)
{
this->m_Timer = new Timer();
if (!this->m_Timer)
{
return false;
}
result = this->m_Timer->Initialize();
if (!result)
{
return false;
}
}
return true;
}
void GameObject::Shutdown()
{
SAFE_SHUTDOWN(this->m_Sprite);
SAFE_DELETE(this->m_Timer);
}
bool GameObject::Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
return this->m_Sprite->Render(deviceContext, this->m_position, wordMatrix, viewMatrix, projectionMatrix);
}
void GameObject::Move()
{
this->m_position.x += this->m_velocity.x;
this->m_position.y += this->m_velocity.y;
}
void GameObject::Move(const D3DXVECTOR2 vector)
{
this->m_position.x += vector.x;
this->m_position.y += vector.y;
}
void GameObject::Frame(const InputHandler::ControlsType& controls)
{
if (this->m_Timer)
{
this->m_Timer->Frame();
this->m_movementDelay += this->m_Timer->GetTime();
this->m_animationDelay += this->m_Timer->GetTime();
}
}
void GameObject::SortFrameArray(const int* framesOrder, int size)
{
this->m_Sprite->SortFrameArray(framesOrder, size);
}
void GameObject::SetPosition(const POINT& position)
{
this->m_position = position;
}
const POINT GameObject::GetPosition()
{
return this->m_position;
}
void GameObject::SetVelocity(const D3DXVECTOR2& velocity)
{
this->m_velocity = velocity;
}
const D3DXVECTOR2 GameObject::GetVelocity()
{
return this->m_velocity;
}
void GameObject::SetStatus(const bool status)
{
this->m_active = status;
}
bool GameObject::GetStatus()
{
return this->m_active;
}
Sprite* GameObject::GetSprite()
{
return this->m_Sprite;
}
float GameObject::GetAnimationDelayTime()
{
return this->m_animationDelay;
}
void GameObject::ResetMovementDelayTime()
{
this->m_movementDelay = 0.0f;
}
float GameObject::GetMovementDelayTime()
{
return this->m_animationDelay;
}
void GameObject::ResetAnimationDelayTime()
{
this->m_animationDelay = 0.0f;
}
ID3D11Device* GameObject::GetDevice()
{
return this->m_device;
}
HWND GameObject::GetHWND()
{
return this->m_hwnd;
}
And i made the derived class Fighter which represents my Spaceship and has a FighterFlame which i think is not relevant to the problem, and a list of pointers to pointers of Bullet (m_Bullets) which will be the bullets coming out from the Ship.
////////////////////////////////////////////////////////////////////////////////
// Filename: Fighter.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _FIGHTER_H_
#define _FIGHTER_H_
//////////////
// INCLUDES //
//////////////
#include <list>
///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "GameObject.h"
#include "Bullet.h"
#include "FighterFlame.h"
class Fighter : public GameObject
{
public:
Fighter();
Fighter(const Fighter& other);
~Fighter();
virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) override;
virtual void Shutdown();
virtual bool Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix) override;
virtual void Frame(const InputHandler::ControlsType& controls) override;
private:
void GenerateTriBullet();
void ValidateBulletsBounds();
private:
int m_life;
int m_lives;
FighterFlame* m_FighterFlame;
std::list<Bullet**> m_Bullets;
const int SHIP_SPEED = 3;
const float MOVEMENT_DELAY = 16.0f;
const float ANIMATION_DELAY = 20.0f;
const float SHOOT_DELAY = 30.0f;
};
#endif
Cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: Fighter.cpp
////////////////////////////////////////////////////////////////////////////////
#include "Fighter.h"
Fighter::Fighter() : GameObject()
{
this->m_life = 100;
this->m_lives = 3;
this->m_FighterFlame = nullptr;
}
Fighter::Fighter(const Fighter& other)
{
}
Fighter::~Fighter()
{
}
bool Fighter::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen)
{
bool result;
this->m_life = 100;
this->m_lives = 3;
result = GameObject::Initialize(device, hwnd, screen, L"Fighter.dds", Bitmap::DimensionType{ 1152, 216 }, Bitmap::DimensionType{ 144, 108 }, 8, 7, true);
if (!result)
{
MessageBox(hwnd, L"Could not initialize Fighter", L"Error", MB_OK);
return false;
}
this->m_position = POINT{ 0, 0 };
int order[16] = { 7, 6, 5, 4, 3, 2, 1, 0, 8, 9, 10, 11, 12, 13, 14, 15 };
GameObject::SortFrameArray(order, 16);
this->m_FighterFlame = new FighterFlame();
if (!this->m_FighterFlame)
{
return false;
}
result = this->m_FighterFlame->Initialize(device, hwnd, screen);
if (!result)
{
MessageBox(hwnd, L"Could not initialize FighterFlame", L"Error", MB_OK);
return false;
}
return true;
}
bool Fighter::Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
bool result;
result = GameObject::Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
if (!result)
{
return false;
}
result = this->m_FighterFlame->Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
if (!result)
{
return false;
}
for (Bullet** bullet : this->m_Bullets)
{
if (bullet)
{
result = (*bullet)->Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
if (!result)
{
return false;
}
}
}
return true;
}
void Fighter::Shutdown()
{
GameObject::Shutdown();
SAFE_SHUTDOWN(this->m_FighterFlame);
for (Bullet** bullet : this->m_Bullets)
{
SAFE_SHUTDOWN(*bullet);
}
this->m_Bullets.clear();
}
void Fighter::Frame(const InputHandler::ControlsType& controls)
{
GameObject::Frame(controls);
this->m_FighterFlame->SetPosition(POINT{ this->m_position.x - 26, this->m_position.y + 47});
this->m_FighterFlame->Frame(controls);
for (Bullet** bullet : this->m_Bullets)
{
(*bullet)->Frame(controls);
}
if (GameObject::GetMovementDelayTime() > MOVEMENT_DELAY)
{
if (controls.up ^ controls.down)
{
if (controls.up)
{
if (GameObject::GetPosition().y > 0)
{
GameObject::Move(D3DXVECTOR2(0, -SHIP_SPEED));
}
if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
{
GameObject::GetSprite()->IncrementFrame();
GameObject::ResetAnimationDelayTime();
}
}
else if (controls.down)
{
if (GameObject::GetPosition().y < (GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().height - GameObject::GetSprite()->GetBitmap()->GetBitmapDimensions().height))
{
GameObject::Move(D3DXVECTOR2(0, SHIP_SPEED));
}
if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
{
GameObject::GetSprite()->DecrementFrame();
GameObject::ResetAnimationDelayTime();
}
}
}
else
{
if (GameObject::GetSprite()->GetCurrentFrame() > (GameObject::GetSprite()->GetAmountOfFrames() / 2))
{
if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
{
GameObject::GetSprite()->DecrementFrame();
GameObject::ResetAnimationDelayTime();
}
}
if (GameObject::GetSprite()->GetCurrentFrame() < (GameObject::GetSprite()->GetAmountOfFrames() / 2))
{
if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
{
GameObject::GetSprite()->IncrementFrame();
GameObject::ResetAnimationDelayTime();
}
}
}
if (controls.right ^ controls.left)
{
if (controls.right)
{
if (GameObject::GetPosition().x < (GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().width - GameObject::GetSprite()->GetBitmap()->GetBitmapDimensions().width))
{
GameObject::Move(D3DXVECTOR2(SHIP_SPEED, 0));
}
}
else if (controls.left)
{
if (GameObject::GetPosition().x > 0)
{
GameObject::Move(D3DXVECTOR2(-SHIP_SPEED, 0));
}
}
}
GameObject::ResetMovementDelayTime();
}
if (controls.spaceBar)
{
Fighter::GenerateTriBullet();
}
Fighter::ValidateBulletsBounds();
}
void Fighter::GenerateTriBullet()
{
Bullet* upBullet = new Bullet();
upBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
upBullet->SetVelocity(D3DXVECTOR2(20, 2));
upBullet->SetPosition(GameObject::GetPosition());
upBullet->Move();
this->m_Bullets.push_back(&upBullet);
Bullet* middleBullet = new Bullet();
middleBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
middleBullet->SetVelocity(D3DXVECTOR2(20, 0));
middleBullet->SetPosition(GameObject::GetPosition());
middleBullet->Move();
this->m_Bullets.push_back(&middleBullet);
Bullet* downBullet = new Bullet();
downBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
downBullet->SetVelocity(D3DXVECTOR2(20, -2));
downBullet->SetPosition(GameObject::GetPosition());
downBullet->Move();
this->m_Bullets.push_back(&downBullet);
}
void Fighter::ValidateBulletsBounds()
{
for (std::list<Bullet**>::iterator it = this->m_Bullets.begin(); it != this->m_Bullets.end(); it++)
{
if ((*(*(&(it)._Ptr->_Myval)))->GetPosition().x > GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().width)
{
SAFE_SHUTDOWN(**it);
this->m_Bullets.erase(it);
}
}
}
And finally the problematic one, The Bullet class who is also derived from GameObject and will represent the bullets that the spaceship can shoot.
////////////////////////////////////////////////////////////////////////////////
// Filename: Bullet.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _BULLET_H_
#define _BULLET_H_
///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "GameObject.h"
class Bullet : public GameObject
{
public:
Bullet();
Bullet(const Bullet& other);
~Bullet();
virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) override;
virtual void Frame(const InputHandler::ControlsType& controls) override;
private:
const float MOVEMENT_DELAY = 16.0f;
};
#endif
Cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: Bullet.cpp
////////////////////////////////////////////////////////////////////////////////
#include "Bullet.h"
Bullet::Bullet() : GameObject()
{
}
Bullet::Bullet(const Bullet& other)
{
}
Bullet::~Bullet()
{
}
bool Bullet::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen)
{
bool result;
result = GameObject::Initialize(device, hwnd, screen, L"Bullet.dds", Bitmap::DimensionType{ 18, 3 }, Bitmap::DimensionType{ 18, 3 }, 1, 0, true);
if (!result)
{
return false;
}
return true;
}
void Bullet::Frame(const InputHandler::ControlsType& controls)
{
GameObject::Frame(controls);
if (GameObject::GetMovementDelayTime() > MOVEMENT_DELAY)
{
GameObject::Move();
}
}
And the problem:
When the Gameloop is running and i press space bar, this occurs
// this if is from Fighter::Frame
if (controls.spaceBar)
{
Fighter::GenerateTriBullet();
}
Fighter::ValidateBulletsBounds();
It enters to the GenerateTriBullet method, which stores 3 bullets on the m_Bullets list.
void Fighter::GenerateTriBullet()
{
Bullet* upBullet = new Bullet();
upBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
upBullet->SetVelocity(D3DXVECTOR2(20, 2));
upBullet->SetPosition(GameObject::GetPosition());
upBullet->Move();
this->m_Bullets.push_back(&upBullet);
Bullet* middleBullet = new Bullet();
middleBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
middleBullet->SetVelocity(D3DXVECTOR2(20, 0));
middleBullet->SetPosition(GameObject::GetPosition());
middleBullet->Move();
this->m_Bullets.push_back(&middleBullet);
Bullet* downBullet = new Bullet();
downBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
downBullet->SetVelocity(D3DXVECTOR2(20, -2));
downBullet->SetPosition(GameObject::GetPosition());
downBullet->Move();
this->m_Bullets.push_back(&downBullet);
}
When it leaves the method, i check the list and the bullets are still there as well as before entering the ValidateBulletsBound, but as soon as it enters the method and before doing ANYTHING, the bullets on the list are simply gone, and with this i mean, the m_Bullets list still has three objects, but happens that they are all NULL.
To explain myself a little better, what i want to do is that every time i press space-bar 3 bullets appears on the screen, and I'm trying to do that by asking if the space-bar value is true, add 3 Bullets to the m_Bullet list, then validate that the bullets in the list are still between the screen space, otherwise remove it.
But as you can see, i successfully store the bullets on the list and as soon as i enter to validate, they are gone... poof!
I don't know why any of this is happening, they are different instance of a class who doesn't share anything between them (memory-wise speaking), there's no static method or pointers shared by then, and even though they would it shouldn't be a problem given that they are just entering in another method and no operation has been done in the middle of that "entering the another method" part, or whatsoever. They even are in the same class, same context, no complex operation or leak (that i know of). I really don't know what's going on!
I want to finish by acknowledging that there are some serious design problems like the one's on GenerateTriBullet, and the fact that i'm not using matrices to move the objects. I'm just trying to finish it first (this is the first game i make on DirectX, really exited btw!!! ), then when i can see the big picture, start to put everything where it belongs. Also, how do i get the value from the list iterator, i read that it was (*it) for simple values, but i have a pointer to a pointer, so i thought it would be **it, but it always resolves to nullptr.
I really hope you can help me.
Here's the project, if you feel like you didn't understand and want to go a little further. You just have to run the project, a ship will appear in a little black window, put a breakpoint on line 182 of the Fighter class then press spacebar on the game window, then from there see what happens with m_Bullets when it leaves GenerateTriBullet and Enters ValidateBulletsBounds.
THANK YOU!
One clear problem:
m_Bullets is a list<Bullet**>. When you add to it you are adding the address of a local variable, e.g.,
Bullet* upBullet = new Bullet();
...
this->m_Bullets.push_back(&upBullet);
Went this method returns, the address stored from the &upBullet expression is no longer valid, as that variable no longer exits.
I think you mean to have m_Bullets as list<Bullet*>, and to add to it:
Bullet* upBullet = new Bullet();
...
this->m_Bullets.push_back(upBullet);
I think the best solution is to let list deal with the memory management and just have m_Bullets as list<Bullet>, then:
this->m_Bullets.emplace_back();
However this will probably require rethinking some of the polymorphism.