Related
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.
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();
}
So, after debugging the program in Qt I realized that the debugger thinks I did not initialized the variables; however, I got the variables in and out from the private class, made the class a pointer and seems like nothing happens. Please let me know what am I missing, I have the same problem in other program but I don't know if is just me or the program.
The code is as follows:
main:
#include "selectionarea.h"
#include <QApplication>
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// QMainWindow *win = new QMainWindow();
QSize winsize(500,500);
SelectionArea area;
area.setStartingLocX(0);
area.setStartingLocY(0);
area.setLength(300);
area.setWidth(300);
area.resize(winsize);
area.show();
return app.exec();
}
selectionarea.cpp
#include "selectionarea.h"
#include <QPainter>
#include <QDebug>
#include <QLabel>
SelectionArea::SelectionArea()
{
}
void SelectionArea::paintEvent(QPaintEvent *)
{
QRect rectangle(getStartingLocx(), getStartingLocy(),
getWidth(), getLength());
/*QRegion Constructs a paint event object with the
* region that needs to be updated. The region is
* specified by paintRegion.*/
QPainter painter(this);
painter.setPen(QPen(Qt::SolidPattern,
2.0,
Qt::SolidLine,
Qt::FlatCap,
Qt::MiterJoin));
painter.drawRect(rectangle);
}
void SelectionArea::setStartingLocX(int x)
{
x=StartingLocX;
qDebug() <<x<<" "<<StartingLocX;
}
int SelectionArea::getStartingLocx()
{
return StartingLocX;
}
void SelectionArea::setStartingLocY(int y)
{
y=StartingLocY;
qDebug() <<y<<" "<<StartingLocY;
}
int SelectionArea::getStartingLocy()
{
return StartingLocY;
}
void SelectionArea::setWidth(int w)
{
w=Width;
qDebug() <<w<<" "<<Width;
}
int SelectionArea::getWidth()
{
return Width;
}
void SelectionArea::setLength(int l)
{
l=Length;
}
int SelectionArea::getLength()
{
return Length;
}
and selectionarea.h
#ifndef SELECTIONAREA_H
#define SELECTIONAREA_H
#include <QPixmap>
#include <QLabel>
#include <QRect>
class SelectionArea : public QLabel
{
int StartingLocX;
int StartingLocY;
int Length;
int Width;
public:
SelectionArea();
~SelectionArea()
{
}
void setStartingLocX(int);
void setStartingLocY(int);
void setLength(int);
void setWidth(int);
int getStartingLocx();
int getStartingLocy();
int getWidth();
int getLength();
virtual void paintEvent(QPaintEvent *);
};
#endif // SELECTIONAREA_H
forgive my ambiguity.
UPDATE:
the application output is
0 0
0 0
0 0
and the window is displayed.
Making my comment the answer
Your setter function should actually look like:
void SelectionArea::setStartingLocX(int x)
{
StartingLocX = x;
}
because you initialize class member variable StartingLocX with the value of x (same for other setter functions). In your version of the function you do the opposite, so that your class member variables remain uninitialized.
You don't initialize the member variables when creating an instance of SelectionArea. You only set them afterwards via the setter functions.
You should initialize them in your constructor using an initializer list.
This might in this case be obsolete, yet it will prevent you from running into strange error cases.
This is very much non-idiomatic C++:
SelectionArea area;
area.setStartingLocX(0);
area.setStartingLocY(0);
area.setLength(300);
area.setWidth(300);
You should refactor the code to read:
SelectionArea area{0, 0, 300, 300};
or even
SelectionArea area{300, 300};
Furthermore, you really should make it a true QObject, not a half-made one. And don't shadow QWidget's own members (like witdth() etc.) with your own of similar names. Getters don't have to have a get prefix, it only adds to visual clutter and is not helpful.
The properties I've added below are likely overkill, just add the ones you're reasonably likely to use. It's still a good idea to simply keep the entire selection as a QRect - that's what it really is.
class SelectionArea : public QLabel
{
Q_OBJECT
Q_PROPERTY(QRect selection READ selection WRITE setSelection USER true)
Q_PROPERTY(QPoint selTopLeft READ selTopLeft WRITE setSelTopLeft STORED false)
Q_PROPERTY(int selX READ selX WRITE setSelX STORED false)
Q_PROPERTY(int selY READ sely WRITE setSelY STORED false)
Q_PROPERTY(QSize selSize READ selSize WRITE setSelSize STORED false)
Q_PROPERTY(int selWidth READ selWidth WRITE setSelWidth STORED false)
Q_PROPERTY(int selHeight READ selHeight WRITE setSelHeight STORED false)
QRect m_selection;
public:
SelectionArea(QWidget * parent = 0) : QLabel(parent) {}
SelectionArea(const QSize & size, QWidget * parent = 0) :
QLabel(parent), m_selection(QPoint(), size) {}
SelectionArea(const QPoint & startingLoc, const QSize & size,
QWidget * parent = 0) :
QLabel(parent), m_selection(startingLoc, size) {}
SelectionArea(int width, int height, QWidget * parent = 0) :
QLabel(parent), m_selection(0, 0, width, height) {}
SelectionArea(int x, int y, int width, int height, QWidget * parent = 0) :
QLabel(parent), m_selection(x, y, width, height) {}
void setSelection(const QRect & r) {
if (m_selection == r) return;
m_selection = r;
update();
}
void setSelTopLeft(const QPoint & p) {
if (m_selection.topLeft() == p) return;
m_selection.moveTo(p);
update();
}
void setSelX(int x) {
if (m_selection.x() == x) return;
m_selection.moveTo(x, m_selection.y());
update();
}
void setSelY(int y) {
if (m_selection.y() == y) return;
m_selection.moveTo(m_selection.x(), y);
update();
}
void setSelSize(const QSize & s) {
if (m_selection.size() == s) return;
m_selection.setSize(s);
update();
}
// etc.
QRect selection() const { return m_selection; }
QPoint selTopLeft() const { return m_selection.topLeft(); }
int selX() const { return m_selection.x(); }
int selY() const { return m_selection.y(); }
QSize selSize() const { return m_selection.size(); }
// etc.
protected:
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
};
It's also questionable whether you really want to derive from QLabel. It seems that you override the painting, and you don't really care for the text property directly. You'd be better off inheriting straight from a QFrame instead.
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.
I am somewhat new to C++ programming and to Cocos2d-x and I am having some trouble with my first Cocos2d-x game. I am literally just trying to simulate gravity and update the player's position based on that. I am running Xcode 4.6.1 and for some reason I keep getting a BAD_ACCESS exception after one run (if I restart it seems to work). It happens at libobjc.A.dylib`objc_release. I have tried doing an allexceptions breakpoint and it breaks at a memory address in the dylib. This only happened after I added a velocity variable to my Player class, so maybe I am not properly allocating pointers and the such? Here are the relevant classes I believe.
#include "cocos2d.h"
using namespace cocos2d;
class Player : public Sprite{
private:
Point velocity;
public:
static Player* create(const char *filename);
void update(float delta);
void setVelocity(const Point &v);
const Point& getVelocity() const;
~Player();
};
#include "Player.h"
USING_NS_CC;
using namespace cocos2d;
Player::~Player(){
}
Player* Player::create(const char *filename){
Player* self = (Player *) Sprite::create(filename);
self->setVelocity(Point::ZERO);
return self;
}
void Player::update(float delta){
log("In player update");
Point gravity = Point(0.0, -450.0);
Point gravityStep = gravity * delta;
this->setVelocity(this->getVelocity() + gravityStep);
log("Velocity after setting: %f", getVelocity().y);
Point stepVelocity = this->getVelocity() * delta;
this->setPosition(this->getPosition() + stepVelocity);
log("Position after setting: %f, %f", getPosition().x, getPosition().y);
}
void Player::setVelocity(const Point &v){
this->velocity = v;
}
const Point& Player::getVelocity() const
{
return this->velocity;
}
#include "GameLayer.h"
USING_NS_CC;
Scene* GameLayer::scene()
{
// 'scene' is an autorelease object
Scene *scene = Scene::create();
// 'layer' is an autorelease object
GameLayer *layer = GameLayer::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
GameLayer::~GameLayer(){
CC_SAFE_RELEASE(player);
CC_SAFE_RELEASE(map);
}
bool GameLayer::init()
{
if ( !Layer::init() )
{
return false;
}
LayerColor *blueSky = LayerColor::create(*new Color4B(100, 100, 250, 255));
this->addChild(blueSky);
map = TMXTiledMap::create("level1.tmx");
this->addChild(map);
player = Player::create("koalio_stand.png");
player->setPosition(Point(100,50));
map->addChild(player, 15);
setKeyboardEnabled(true);
this->scheduleUpdate();
return true;
}
void GameLayer::update(float dt){
player->update(dt);
}
EDIT: So I figured out that the constructor of player was the problem. However, I'm wondering if it is kosher to do the following (since Player inherits from Sprite) to copy the Sprite function:
Sprite* Sprite::create(const char *filename)
{
Sprite *sprite = new Sprite();
if (sprite && sprite->initWithFile(filename))
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return NULL;
}
and have the Player function be:
Player* Player::create(const char *filename){
Player *sprite = new Player();
if (sprite && sprite->initWithFile(filename))
{
sprite->setVelocity(Point::ZERO);
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return NULL;
}
Or perhaps is there a cleaner way to call the create of Sprite, but to create a Player object which inherits and sets the velocity?