I've looked at a lot of other similar questions but none of them have seemed to help.
I've used virtual functions before but this time they just don't seem to be working.
Header for parent class:
#ifndef OBJECT_OBJECT
#define OBJECT_OBJECT
#include <raylib.h>
#include <raymath.h>
class Object {
public:
Object() {}
Object(Rectangle rect, Color color);
Object(int x, int y, int width, int height, Color color);
virtual void Update(float deltaTime) {}
Rectangle rect;
Color color;
};
#endif /* OBJECT_OBJECT */
Source for parent class:
#include "Object.hpp"
Object::Object(Rectangle rect, Color color) {
this->rect = rect;
this->color = color;
}
Object::Object(int x, int y, int width, int height, Color color) {
this->rect = { (float)x, (float)y, (float)width, (float)height };
this->color = color;
}
Header for child:
#ifndef OBJECT_OBJECTS_PLAYER
#define OBJECT_OBJECTS_PLAYER
#include <stdio.h>
#include "../Object.hpp"
class Player : public Object {
public:
Player() {}
Player(Rectangle rect, Color color) : Object(rect, color) {}
Player(int x, int y, int width, int height, Color color) : Object(x, y, width, height, color) {}
void Update(float deltaTime) override;
int speed = 10;
};
#endif /* OBJECT_OBJECTS_PLAYER */
Source for child:
#include "Player.hpp"
void Player::Update(float deltaTime) {
if (IsKeyPressed(KEY_W | KEY_UP)) this->rect.y -= speed * deltaTime;
if (IsKeyPressed(KEY_S | KEY_DOWN)) this->rect.y += speed * deltaTime;
if (IsKeyPressed(KEY_A | KEY_LEFT)) this->rect.x -= speed * deltaTime;
if (IsKeyPressed(KEY_D | KEY_RIGHT)) this->rect.x += speed * deltaTime;
}
Full main.cpp file:
#include <raylib.h>
#include <raymath.h>
#include <vector>
#include <string>
#include "Object/Object.hpp"
#include "Object/Objects/Player.hpp"
void NewObject(Rectangle rect, Color color);
void NewObject(Object obj);
std::vector<Object> objects;
bool shouldClose;
int screenWidth;
int screenHeight;
int main(void) {
InitWindow(0, 0, "2D Platformer");
screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight();
SetExitKey(-1);
ToggleFullscreen();
Player player({ 0, 0, 20, 20 }, (Color){0, 255, 0, 255});
NewObject(player);
Camera2D camera = { 0 };
camera.target = (Vector2){0, 0};
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;
while (!WindowShouldClose()) {
float deltaTime = GetFrameTime();
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode2D(camera);
for (int i = 0; i < objects.size(); i++) {
objects[i].Update(deltaTime);
DrawRectangleRec(objects[i].rect, objects[i].color);
}
EndMode2D();
EndDrawing();
}
CloseWindow();
return 0;
}
void NewObject(Rectangle rect, Color color) {
objects.push_back(*(new Object(rect, color)));
}
void NewObject(Object obj) {
objects.push_back(obj);
}
CMakeLists.txt files:
cmake_minimum_required(VERSION 3.11)
project(safcal)
cmake_policy(SET CMP0076 NEW)
find_package(raylib 3.0 QUIET)
if (NOT raylib_FOUND)
include(FetchContent)
FetchContent_Declare(
raylib
URL https://github.com/raysan5/raylib/archive/master.tar.gz
)
FetchContent_GetProperties(raylib)
if (NOT raylib_POPULATED)
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR})
endif()
endif()
add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PRIVATE main.cpp)
add_subdirectory(Object)
target_link_libraries(${PROJECT_NAME} raylib)
target_sources(${PROJECT_NAME} PRIVATE
"Object.cpp"
)
target_sources(safcal PRIVATE
"Player.cpp"
)
The problem is with the Update function. Commenting anything to do with it being overridden stops the error.
EDIT:
The full error
[build] FAILED: safcal
[build] : && /bin/x86_64-linux-gnu-g++-10 -g CMakeFiles/safcal.dir/main.cpp.o CMakeFiles/safcal.dir/Object/Object.cpp.o -o safcal _deps/raylib-build/raylib/libraylib.a -lm -lpthread /usr/lib/x86_64-linux-gnu/libGL.so /usr/lib/x86_64-linux-gnu/libGLU.so _deps/raylib-build/raylib/external/glfw/src/libglfw3.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so -lm -ldl && :
[build] /usr/bin/ld: CMakeFiles/safcal.dir/main.cpp.o: warning: relocation against `_ZTV6Player' in read-only section `.text._ZN6PlayerC2E9Rectangle5Color[_ZN6PlayerC5E9Rectangle5Color]'
[build] /usr/bin/ld: CMakeFiles/safcal.dir/main.cpp.o: in function `Player::Player(Rectangle, Color)':
[build] /home/callum/Documents/Programming/C++/SafCal/build/../Object/Objects/Player.hpp:9: undefined reference to `vtable for Player'
[build] /usr/bin/ld: warning: creating DT_TEXTREL in a PIE
I've used virtual functions before but this time they just don't seem to be working.
Object slicing occurred in your code, So you can't see the desired result。
Player player({ 0, 0, 20, 20 }, (Color){0, 255, 0, 255});
NewObject(player);
You can try to make the following changes:
std::vector<std::shared_ptr<Object>> objects;
...
void NewObject(std::shared_ptr<Object> obj) {
objects.push_back(obj);
}
...
NewObject(new Player());
In addition, the destructor of the base class needs to be declared as a virtual function。
Related
I have a problem with updating the value of a class variable on each frame of gameloop.
I am using wxWidgets for creating a cross-platform window and for graphics as well as gameloop. This is my main Window class which implements rendering and the gameloop.
#include <wx/wx.h>
#include "window.h"
#include "../entity/entity.h"
#include "../../configuration.h"
Window::Window(const wxString & title, const wxPoint & position, const wxSize & size): wxFrame(nullptr, wxID_ANY, title, position, size) {
timer = new wxTimer(this, 1);
Entity entity((width / 2) - 50, 100, 100, 100, wxColour(255, 0, 0));
AddEntity(entity);
Connect(wxEVT_PAINT, wxPaintEventHandler(Window::OnPaint));
Connect(wxEVT_TIMER, wxCommandEventHandler(Window::OnTimer));
Start();
}
void Window::Render(wxPaintEvent & event) {
wxPaintDC dc(this);
for (Entity entity : entities) {
entity.Render(dc);
}
}
void Window::Update(wxCommandEvent & event) {
for (Entity entity : entities) {
entity.Update();
}
}
void Window::AddEntity(Entity & entity) {
entities.push_back(entity);
}
void Window::OnTimer(wxCommandEvent & event) {
Update(event);
}
void Window::OnPaint(wxPaintEvent & event) {
Render(event);
}
void Window::Start() {
isStarted = true;
timer->Start(10);
}
void Window::Stop() {
isPaused = !isPaused;
if (isPaused) {
timer->Stop();
} else {
timer->Start(10);
}
}
Here is the Entity class which represent a rectangle that can be drawn onto the window.
#include <wx/wx.h>
#include <stdlib.h>
#include "entity.h"
#include "../gravity/gravity.h"
Entity::Entity(int _x, int _y, int _width, int _height, wxColour _color) : x( _x ), y( _y ), width( _width ), height( _height ), color( _color ) {
}
void Entity::Render(wxPaintDC & dc) {
wxPen pen(color);
dc.SetPen(pen);
dc.DrawLine(x, y, x + width, y);
dc.DrawLine(x, y + height, x + width, y + height);
dc.DrawLine(x, y, x, y + height);
dc.DrawLine(x + width, y, x + width, y + height);
}
void Entity::Update() {
y += 1;
std::cout << y << std::endl;
}
On each call of the Entity::Update() method, I want to increment the y position and rerender the entity. However, the value of y gets incremented only once and stays the same for the rest of the application lifetime and I can't seem to understand why. I'll be thankful for any help.
When you loop over your entities like this (in Window::Render, Window::Update):
for (Entity entity : entities) {
in each iteration entity will get a copy of the element in entities.
In order to operate on your actual entities via a reference you need to change it to:
//----------v---------------------
for (Entity & entity : entities) {
Another option is to use auto & (auto by itself does not include "reference-ness" and so will force again a copy as in your code):
//---vvvvvv---------------------
for (auto & entity : entities) {
Note that even if you only read data from your elements (and don't modify it) you can change your loop to use const & to save the copies.
I'm doing a school project and I'm stuck here. I'm trying to make my hexagon gradually change its color from yellow to blue. This is my hexagon.hh:
#ifndef HEXAGON_HH
#define HEXAGON_HH
#include <QGraphicsPolygonItem>
#include <QPropertyAnimation>
#include "gamecontroller.hh"
class GameController;
class Hexagon : public QGraphicsPolygonItem
{
public:
Hexagon(QGraphicsItem *parent = 0);
~Hexagon();
GameController* _controller;
Common::CubeCoordinate _coord;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent* event);
};
#endif // HEXAGON_HH
I tried to use QPropertyAnimation like this:
QPropertyAnimation* animation = new QPropertyAnimation(_Tilemap.at(tileCoord)->hexagon_, "brush");
animation->setDuration(10000);
animation->setStartValue(QBrush(Qt::yellow()));
animation->setEndValue(QBrush(Qt::blue()));
animation->start();
But it couldn't be used on the hexagon class so it didn't work. How could I make the hexagon change its color so that there's an animation?
e: heres the error I get when I tried to use QPropertyAnimation:
/home/litmanen/test/UI/gamecontroller.cpp:256: error: no matching function for call to ?QPropertyAnimation::QPropertyAnimation(Hexagon*&, const char [6])?
QPropertyAnimation* animation = new QPropertyAnimation(_Tilemap.at(tileCoord)->hexagon_, "brush");
The error is caused because QPropertyAnimation only apply to QObject, in your case QGraphicsPolygonItem is not. So a possible solution is to inherit from QObject:
*.h
#ifndef HEXAGON_H
#define HEXAGON_H
#include <QBrush>
#include <QGraphicsPolygonItem>
#include <QObject>
class Hexagon : public QObject, public QGraphicsPolygonItem
{
Q_OBJECT
Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
public:
explicit Hexagon(QObject *parent=nullptr);
GameController* _controller;
Common::CubeCoordinate _coord;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent* event);
};
#endif // HEXAGON_H
*.cpp
#include "hexagon.h"
Hexagon::Hexagon(QObject *parent):
QObject(parent)
{
/*another code*/
}
void Hexagon::mousePressEvent(QGraphicsSceneMouseEvent* event){
/*another code*/
}
On the other hand it still does not work since there is no interpolator for QBrush, so the solution is to implement an interpolator (use the interpolator of this solution)
static QVariant brushInterpolator(const QBrush &start, const QBrush &end, qreal progress)
{
QColor cstart = start.color();
QColor cend = end.color();
int sh = cstart.hsvHue();
int eh = cend.hsvHue();
int ss = cstart.hsvSaturation();
int es = cend.hsvSaturation();
int sv = cstart.value();
int ev = cend.value();
int hr = qAbs( sh - eh );
int sr = qAbs( ss - es );
int vr = qAbs( sv - ev );
int dirh = sh > eh ? -1 : 1;
int dirs = ss > es ? -1 : 1;
int dirv = sv > ev ? -1 : 1;
return QBrush(QColor::fromHsv( sh + dirh * progress * hr,
ss + dirs * progress * sr,
sv + dirv * progress * vr), progress > 0.5 ? Qt::SolidPattern : Qt::Dense6Pattern );
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qRegisterAnimationInterpolator<QBrush>(brushInterpolator);
// ...
Another solution is to implement the same logic with QColor that does not need an interpolator:
*.h
#ifndef HEXAGON_H
#define HEXAGON_H
#include <QGraphicsPolygonItem>
#include <QObject>
class Hexagon : public QObject, public QGraphicsPolygonItem
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor)
public:
explicit Hexagon(QObject *parent=nullptr);
QColor color() const;
void setColor(const QColor &color);
GameController* _controller;
Common::CubeCoordinate _coord;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent* event);
};
#endif // HEXAGON_H
*.cpp
#include "hexagon.h"
#include <QBrush>
Hexagon::Hexagon(QObject *parent):
QObject(parent)
{
QBrush b = brush();
b.setStyle(Qt::SolidPattern);
setBrush(b);
/*another code*/
}
QColor Hexagon::color() const
{
return brush().color();
}
void Hexagon::setColor(const QColor &color)
{
QBrush b = brush();
b.setColor(color);
setBrush(b);
}
void Hexagon::mousePressEvent(QGraphicsSceneMouseEvent* event){
/*another code*/
}
Then you use "color" instead of "brush":
QPropertyAnimation* animation = new QPropertyAnimation(_Tilemap.at(tileCoord)->hexagon_, "color");
animation->setDuration(10000);
animation->setStartValue(QColor(Qt::yellow));
animation->setEndValue(QColor(Qt::blue));
animation->start();
Another simpler solution is to use QVariantAnimation:
auto it = _Tilemap.at(tileCoord)->hexagon_;
QVariantAnimation *animation = new QVariantAnimation;
QObject::connect(animation, &QVariantAnimation::valueChanged, [it](const QVariant & v){
it->setBrush(QBrush(v.value<QColor>()));
});
animation->setDuration(10000);
animation->setStartValue(QColor(Qt::yellow));
animation->setEndValue(QColor(Qt::blue));
animation->start();
I just got back into coding. Trying to make a simple breakout game, I did start of just making a simple 'pong' game but found it quite easy so I am trying to expand it to a breakout game (image attached for those who do not know what it is).
To handle the blocks at the top of the screen I have used a vector of blocks, from which right now I am trying to draw them onto the screen. I am unable to do this as I am getting an error:
error C2664: 'void sf::RenderTarget::draw(const sf::Vertex
*,size_t,sf::PrimitiveType,const sf::RenderStates &)' : cannot convert argument 1 from 'Block' to 'const sf::Drawable &'
which is inside the block.cpp file
Here is the relevant code, there are more functions but they do not apply to this. Sorry for any bad code in there :)
block.cpp
Block::Block(float startX, float startY)
{
position.x = startX;
position.y = startY;
colour = sf::Color::White;
block.setSize(sf::Vector2f(width, height));
block.setFillColor(colour);
block.setPosition(position);
}
void Block::draw(Block block, sf::RenderWindow& window)
{
window.draw(block);
}
blockContainer.cpp
#pragma once
class ContainerOfBlocks
{
public:
ContainerOfBlocks(int useless);
~ContainerOfBlocks();
std::vector<Block> getContainer();
void drawContainer(sf::RenderWindow& window);
private:
std::vector<Block> blockContainer;
};
blockContainer.h
#pragma once
class ContainerOfBlocks
{
public:
ContainerOfBlocks(int useless);
~ContainerOfBlocks();
std::vector<Block> getContainer();
void drawContainer(sf::RenderWindow& window);
private:
std::vector<Block> blockContainer;
};
Thank you for any help :)
(I'll have to put it here because I don't have enough reputation yet to comment)
some things
I don't understand why you have this code void Block::draw(Block block, sf::RenderWindow &window). It should be void Block::draw(sf::RenderWindow &window) and then just draw block (which is a class member) OR pass block by reference if you want to draw the block from somewhere else
In any case, Block should inherit from sf::Drawable and use its function to draw. I think that's what the error message is saying. For example class Block : public sf::Drawable { ... }; and the function to draw would be virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const; in the header and void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const { renderTarget.draw(block); } in your .cpp. Then you can iterate over the vector of blocks you already have and draw each block
the function std::vector<Block> getContainer() should return a reference to the vector (std::vector<Block> &getContainer())
it's not an error but I prefer using #ifndef ... #define... #endif header guard instead of #pragma once
edit (regarding your replies below):
I made a quick project that uses most of your code.
(Also make sure to read my notes below the code)
Here's a picture of what it looks like compiled:
Code:
block.h
#ifndef BLOCK_H_INCLUDED
#define BLOCK_H_INCLUDED
#include <SFML/Graphics.hpp>
class Block : public sf::Drawable {
public:
Block();
Block::Block(float startX, float startY);
virtual ~Block();
private:
virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const;
sf::RectangleShape block;
sf::Vector2f position;
float width;
float height;
sf::Color colour;
};
#endif
block.cpp
#include "block.h"
Block::Block() :
position(sf::Vector2f()),
width(40.0f),
height(20.0f),
colour(sf::Color())
{
}
Block::Block(float startX, float startY) :
width(40.0f),
height(20.0f)
{
position.x = startX;
position.y = startY;
colour = sf::Color::White;
block.setSize(sf::Vector2f(width, height));
block.setFillColor(colour);
block.setPosition(position);
}
Block::~Block() {
}
void Block::draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const {
renderTarget.draw(block);
}
blockContainer.h
#ifndef BLOCKCONTAINER_H_INCLUDED
#define BLOCKCONTAINER_H_INCLUDED
#include "block.h"
class ContainerOfBlocks : public sf::Drawable {
public:
ContainerOfBlocks();
ContainerOfBlocks(int useless, const sf::Vector2f pos);
~ContainerOfBlocks();
std::vector<Block> &getContainer();
void drawContainer(sf::RenderWindow &window);
private:
virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const;
std::vector<Block> blockContainer;
};
#endif
blockContainer.cpp
#include "blockContainer.h"
ContainerOfBlocks::ContainerOfBlocks() {
}
ContainerOfBlocks::ContainerOfBlocks(int useless, const sf::Vector2f pos) {
if (useless > 0) {
float x = pos.x;
float y = pos.y;
for (std::size_t i = 0; i < static_cast<std::size_t>(useless); ++i) {
blockContainer.push_back(Block(x, y));
x += 50.0f;
}
}
}
ContainerOfBlocks::~ContainerOfBlocks() {
}
std::vector<Block> &ContainerOfBlocks::getContainer() {
return blockContainer;
}
void ContainerOfBlocks::drawContainer(sf::RenderWindow &window) {
for (std::size_t i = 0; i < blockContainer.size(); ++i) {
window.draw(blockContainer[i]);
}
}
void ContainerOfBlocks::draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const {
for (std::size_t i = 0; i < blockContainer.size(); ++i) {
renderTarget.draw(blockContainer[i]);
}
}
main.cpp
#include <SFML/Graphics.hpp>
#include "block.h"
#include "blockContainer.h"
int main() {
sf::RenderWindow window(sf::VideoMode(400, 200), "SFML works!");
window.setFramerateLimit(30);
window.setVerticalSyncEnabled(false);
// create container with 5 blocks in it, starting at pos 10/10
// this container will be drawn using ContainerOfBlocks' void drawContainer(sf::RenderWindow &window)
ContainerOfBlocks testBlocks(5, sf::Vector2f(10.0f, 10.0f));
// create another container, starting at pos 10/50
// this one will be drawn using sf::Drawable's function to draw
ContainerOfBlocks testBlocks2(5, sf::Vector2f(10.0f, 50.0f));
while (window.isOpen()) {
sf::Event evt;
while (window.pollEvent(evt)) {
if (evt.type == sf::Event::Closed) {
window.close();
}
}
window.clear();
testBlocks.drawContainer(window);
window.draw(testBlocks2);
window.display();
}
return 0;
}
As you can see Block now inherits from sf::Drawable and can be drawn with xxx.draw(block).
BlockContainer now has two different functions to draw its contents (this is only for demonstrating purposes, you only need one function to draw depending on what you like better). If you want to keep your own drawing function, you can remove the : public sf::Drawable from BlockContainer.
In main() two block containers are created, one (testBlocks) will be drawn using BlockContainer's void drawContainer(sf::RenderWindow &window) draw function from your original code, the other (testBlocks2) using sf::Drawable's.
Also note how &getContainer() now returns a reference to the vector of blocks. If you don't return a reference the original vector won't be affected by whatever you want to do to it from the outside.
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.
Apologize if this is too long of a post. I'm merely trying to get SDL to work in an object oriented fashion - there's no point in me moving on until I get past this point. Been spending a fair amount of time compiling this and getting errors. I'll post my header files and sources, along with my makefile and an output to see what's going on.
Here's render.h:
#ifndef RENDER_H
#define RENDER_H
#include <string>
#include <SDL/SDL.h>
using std::string;
class Render
{
public:
Render(string filename, int x, int y, SDL_Surface * destination);
~Render();
private:
SDL_Surface * m_optimizedImage;
void load_image(string filename);
void apply_surface(int x, int y, SDL_Surface * source, SDL_Surface * destination);
};
#endif
...and render.cpp:
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <string>
#include "render.h"
using std::string;
Render::Render(string filename, int x, int y, SDL_Surface * destination)
{
this->m_optimizedImage = NULL;
load_image(filename);
apply_surface(x, y, m_optimizedImage, destination);
}
Render::~Render()
{
delete m_optimizedImage;
}
void Render::load_image(string filename)
{
SDL_Surface * loadedImage = IMG_Load(filename.c_str());
if (loadedImage != NULL)
{
m_optimizedImage = SDL_DisplayFormat(loadedImage);
SDL_FreeSurface(loadedImage);
}
}
void Render::apply_surface(int x, int y, SDL_Surface * source, SDL_Surface * destination)
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface(source, NULL, destination, &offset);
}
...and screenwriter.h:
#include <iostream>
#include <SDL/SDL.h>
#ifndef SCREENWRITER_H
#define SCREENWRITER_H
class ScreenWriter
{
public:
~ScreenWriter();
bool flip_screen();
void delay_screen(int milliseconds);
bool get_screen_state() const;
static ScreenWriter& get_instance();
SDL_Surface * get_screen() const;
private:
ScreenWriter();
void initialize();
bool m_screenFailure;
SDL_Surface * m_screen;
};
#endif
...and screenwriter.cpp:
#include <SDL/SDL.h>
#include "screenwriter.h"
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define SCREEN_BPP 32
ScreenWriter::ScreenWriter()
{
this->m_screenFailure = false;
initialize();
}
ScreenWriter::~ScreenWriter()
{
SDL_Quit();
}
ScreenWriter& ScreenWriter::get_instance()
{
static ScreenWriter instance;
return instance;
}
SDL_Surface * ScreenWriter::get_screen() const
{
return m_screen;
}
bool ScreenWriter::get_screen_state() const
{
return this->m_screenFailure;
}
void ScreenWriter::delay_screen(int milliseconds)
{
SDL_Delay(milliseconds);
}
int ScreenWriter::flip_screen()
{
if (SDL_Flip(m_screen) == -1)
{
return 1;
}
else
{
SDL_Flip(m_screen);
return 0;
}
}
void ScreenWriter::initialize()
{
if (SDL_Init(SDL_INIT_EVERYTHING == -1))
{
std::cout << "SDL_Init has failed";
}
else
{
SDL_Init(SDL_INIT_EVERYTHING);
//initialize screen
this->m_screen = SDL_SetVideoMode(SCREEN_WIDTH,
SCREEN_HEIGHT,
SCREEN_BPP,
SDL_SWSURFACE);
if (m_screen == NULL)
{
this->m_screenFailure = true;
}
else
{
this->m_screenFailure = false;
}
//set caption header
SDL_WM_SetCaption("Hello WOrld", NULL);
}
}
...and of course main.cpp:
#include <iostream>
#include <SDL/SDL.h>
#include "screenwriter.h"
#include "render.h"
int main(int argc, char * args[])
{
std::cout << "hello world!" << std::endl;
ScreenWriter * instance = ScreenWriter::get_instance();
instance->flip_screen();
Render render = new Render("look.png", 0, 0, instance->get_screen());
delete instance();
return 0;
}
...my output:
g++ -c main.cpp render.h screenwriter.h -lSDL -lSDL_image
main.cpp: In function ‘int main(int, char**)’:
main.cpp:10:55: error: cannot convert ‘ScreenWriter’ to ‘ScreenWriter*’ in initialization
main.cpp:12:69: error: conversion from ‘Render*’ to non-scalar type ‘Render’ requested
make: *** [main.o] Error 1
...my makefile
program : main.o render.o screenwriter.o
g++ -o program main.o render.o screenwriter.o -lSDL -lSDL_image
main.o : main.cpp render.h screenwriter.h
g++ -c main.cpp render.h screenwriter.h -lSDL -lSDL_image
render.o : render.h render.cpp
g++ -c render.h render.cpp -lSDL
screenwriter.o : screenwriter.h screenwriter.cpp
g++ -c screenwriter.h screenwriter.cpp -lSDL -lSDL_image
clean:
rm program main.o render.o screenwriter.o
the nitty gritty:
My goal with this is to have ScreenWriter implemented as a singleton to setup allegro and flag everything as needed through initialization. The second objective relies on render to just render by specifying x and y coordinates, along with a path to the file to render to load on the map. This is easy to do procedurally, but I'm ready to experiment with OO design on this.
So, any thoughts?
You have two syntax errors which are clear from your errors:
ScreenWriter * instance = ScreenWriter::get_instance();
should be
ScreenWriter & instance = ScreenWriter::get_instance();
because get_instance returns a reference, not a pointer, and
Render render = new Render("look.png", 0, 0, instance->get_screen());
should be
Render * render = new Render("look.png", 0, 0, instance->get_screen());
because new returns a pointer, not an object or reference.
Also,
delete instance();
should be
delete render;
because not only is the former completely wrong, but it's not being used on anything allocated by new. render is, however. So you have to delete it to avoid a memory leak.
I don't understand the "any thoughts" part of the question because it doesn't say about what. English isn't my first language though, so forgive me if I missed something.
Flip the screen after rendering the image. When you blit something, it's blitted on a buffer. SDL_Flip() just swaps those buffers so that your o/p can be seen. So you should be replacing those two lines, I guess.