I am using an SFML view integrated inside a wxWidgets frame. I used the code sample on the SFML website (which is quite old but I got it to work making a few tweaks) to do this. And then started fleshing out my project from that base class. However, I am at the stage now where I need to create and delete many SFML+wxWidgets windows based on user actions, however SFML crashes whenever I close its parent wxWidgets window.
I get the following error:
Cannot close SFML area when SFML is integrated in a NSView.
All the SFML+wxWidgets examples on the web I found face this error when I run it after closing. This error should not be an issue if the user only needs to close the window once, but I am managing many windows over the user session, so if it crashes once, it brings down the whole app with it.
Here is the section of the header file code for the base class combining wxWidgets and sfml, everything else is specific to my app, and not to the error:
class ChessWidgetBase : public wxControl, public sf::RenderWindow {
public:
ChessWidgetBase(wxWindow* parent, wxSize size);
virtual ~ChessWidgetBase() {};
private:
DECLARE_EVENT_TABLE()
virtual void HandleLeftDown(wxMouseEvent&) {}
virtual void HandleLeftUp(wxMouseEvent&) {}
virtual void OnUpdate() {};
void OnIdle(wxIdleEvent&);
void OnPaint(wxPaintEvent&);
void OnEraseBackground(wxEraseEvent&);
};
This code is based off this minimum reproducable example I used from the internet to make the above class:
#include <iostream>
#include <wx/wx.h>
#include <memory>
#include <SFML/Graphics.hpp>
#ifdef __WXGTK__
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#endif
using namespace std;
static const int kDefaultWindowWidth = 1280;
static const int kDefaultWindowHeight = 720;
static const int kCanvasMargin = 50;
struct wxSfmlCanvas : public wxControl, public sf::RenderWindow
{
wxSfmlCanvas(
wxWindow *parent = nullptr,
wxWindowID windowId = -1,
const wxPoint &position = wxDefaultPosition,
const wxSize &size = wxDefaultSize,
long style = 0) :
wxControl(parent, windowId, position, size, style)
{
createRenderWindow();
}
virtual void onUpdate(){};
void onIdle(wxIdleEvent& event)
{
// Send a paint message when control is idle, to ensure max framerate
Refresh();
}
void onPaint(wxPaintEvent& event)
{
wxPaintDC dc(this); // Prepare control to be repainted
onUpdate(); // Tick update
display(); // Draw
}
// Explicitly overriding prevents wxWidgets from drawing, which could result in flicker
void onEraseBackground(wxEraseEvent& event){}
void createRenderWindow()
{
#ifdef __WXGTK__
gtk_widget_realize(m_wxwindow);
gtk_widget_set_double_buffered(m_wxwindow, false);
GdkWindow *gdkWindow = gtk_widget_get_window((GtkWidget*)GetHandle());
XFlush(GDK_WINDOW_XDISPLAY(gdkWindow));
sf::RenderWindow::create(GDK_WINDOW_XWINDOW(gdkWindow));
#else
sf::RenderWindow::create(GetHandle());
#endif
}
void setwxWindowSize(const wxSize& size)
{
this->SetSize(size);
}
void setRenderWindowSize(const sf::Vector2u& size)
{
this->setSize(size);
}
virtual ~wxSfmlCanvas(){};
wxDECLARE_EVENT_TABLE();
};
struct Canvas : public wxSfmlCanvas
{
Canvas(
wxWindow* parent,
wxWindowID id,
wxPoint position,
wxSize size,
long style = 0) :
wxSfmlCanvas(parent, id, position, size, style)
{
}
virtual void onUpdate()
{
clear(sf::Color::Yellow);
// TODO: Do some sprite drawing or whatever
}
void onResize(wxSizeEvent& event)
{
auto size = event.GetSize();
auto newCanvasWidth = size.x - (2 * kCanvasMargin);
auto newCanvasHeight = size.y - (2 * kCanvasMargin);
// Resize Canvas window
this->setwxWindowSize({newCanvasWidth, newCanvasHeight});
this->setRenderWindowSize({(unsigned int)newCanvasWidth, (unsigned int)newCanvasHeight});
}
};
struct AppFrame : public wxFrame
{
AppFrame(const wxString& title, const wxPoint& pos, const wxSize& size) :
wxFrame(NULL, wxID_ANY, title, pos, size),
_panel(new wxPanel(this)),
_canvas(new Canvas(
_panel.get(),
wxID_ANY,
wxPoint(kCanvasMargin, kCanvasMargin),
wxSize(kDefaultWindowWidth - (2 * kCanvasMargin), kDefaultWindowHeight - (2 * kCanvasMargin))
))
{
_panel->SetBackgroundColour(*wxCYAN);
////////////////////////////////////////////////////////////////////////////////
// Probably due to some RTTI, IDE is getting confused by this dynamic call
// and doesn't understand the correct Bind overload. Causes non sequitur errors
// to display in the inspector. Suppress.
//
// Dynamic binding is cleanest here, since we don't want to hook up our resize
// handler until our dependencies (Canvas namely) have finished their initialization
////////////////////////////////////////////////////////////////////////////////
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wint-conversion"
Bind(wxEVT_SIZE, &AppFrame::onResize, this);
#pragma clang diagnostic pop
////////////////////////////////////////////////////////////////////////////////
}
void onResize(wxSizeEvent& event)
{
_canvas->onResize(event);
event.Skip();
}
unique_ptr<wxPanel> _panel;
unique_ptr<Canvas> _canvas;
};
struct App : public wxApp
{
virtual bool OnInit()
{
auto frame = new AppFrame("SFML Canvas Demo", wxPoint(100, 100),
wxSize(kDefaultWindowWidth, kDefaultWindowHeight));
frame->Show(true);
return true;
}
};
wxBEGIN_EVENT_TABLE(wxSfmlCanvas, wxControl)
EVT_IDLE(wxSfmlCanvas::onIdle)
EVT_PAINT(wxSfmlCanvas::onPaint)
EVT_ERASE_BACKGROUND(wxSfmlCanvas::onEraseBackground)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(App);
(if you want to run it you might have to install sfml+wxwidgets)
Any ways to handle closing a window that prevents a crash with wxWidgets+SFML? Just need some ideas and a couple lines of code to show them, no need for complete examples...
To fix this error upgrade SFML to version >= 2.6.0. You cannot find this in the downloads site for SFML as it hasn't been released yet, so you must install from the Github directly over here and build it from source: https://github.com/SFML/SFML/tree/2.6.x.
Related
I am at a loss for why this isn't working. I've tried passing a function by value, by reference, and still when I go to call the function from within SetFromState, it is null.
My class:
StateIndicator::StateIndicator(QWidget *parent) {
StateIndicator(parent, StateIndicator::DefaultSelectionStrategy);
}
StateIndicator::StateIndicator(QWidget *parent,
const std::function<std::string (State)>& selectionStrategy)
:QWidget(parent),
selection_strategy_(selectionStrategy)
{
wrapped = new QLabel(this);
}
void StateIndicator::SetFromState(State state)
{
std::string resourcePath = selection_strategy_(state);
wrapped->setTextFormat(Qt::TextFormat::RichText);
char* buffer = new char[StateIndicator::label_format.length() + resourcePath .length()];
sprintf(buffer, label_format.c_str(), resourcePath .c_str());
wrapped->setText(buffer);
}
std::string TruckStateIndicator::DefaultSelectionStrategy(State state){
return ":img/Disconnected";
}
call:
StateIndicator* state = new StateIndicator(this);
ui->leftPaneLayout->insertWidget(1,state);
state->SetFromState(State::Connected);
When I use the debugger, in my constructor it shows that my selection strategy is set. But when I get to the call to SetFromState the private member selection_strategy_ is null.
However, my code works if within the SetFromState function I instead change it to:
std::function<std::string(TruckState)> strategy = &DefaultSelectionStrategy;
std::string path = strategy(state);
and assign the variable right there. It feels like something is going out of scope and causing my function pointer to become null, but I'm pretty much stuck.
Edited:
Here is the header file. I will also try to make the example minimal by removing the inheritance and trying to reproduce
#ifndef STATEINDICATOR_H
#define STATEINDICATOR_H
#include <QLabel>
#include "src/models/state.h"
class StateIndicator : public QWidget
{
public:
explicit StateIndicator(QWidget* parent);
explicit StateIndicator(QWidget* parent,
const std::function<std::string (State)>& selectionStrategy);
void SetFromState(State state);
private:
static std::string DefaultSelectionStrategy(State state);
QLabel* wrapped;
const std::function<std::string(State)> image_selection_strategy_;
inline const static std::string label_format = "<html><head/><body><p><"
"img src="%s"/>"
"</p></body></html>";
};
#endif // TRUCKSTATEINDICATOR_H
The problem was with how I was calling the overloaded constructor it seems:
StateIndicator::StateIndicator(QWidget *parent) {
StateIndicator(parent, StateIndicator::DefaultSelectionStrategy);
}
should be
StateIndicator::StateIndicator(QWidget *parent) :
StateIndicator(parent, StateIndicator::DefaultSelectionStrategy)
{
}
I am using SFML to create a space invaders clone with C++. I am quite new to C++ and I'm learning it as I'm working on this project.
I am getting the following error when trying to create a new enemy using the constructor and array to set settings for each created enemy:
1>enemy.cpp
1>C:\Users\dzamm\source\repos\SFML\enemy.cpp(35,1): error C2280: 'Enemy &Enemy::operator =(const Enemy &)': attempting to reference a deleted function
1>C:\Users\dzamm\source\repos\SFML\enemy.h(55): message : compiler has generated 'Enemy::operator =' here
1>C:\Users\dzamm\source\repos\SFML\enemy.h(55,1): message : 'Enemy &Enemy::operator =(const Enemy &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'sf::RenderWindow &sf::RenderWindow::operator =(const sf::RenderWindow &)'
Enemy Constructor:
Enemy alienArray[NUMBER_OF_ALIENS];
//constructor sets ID number, loads sprite
Enemy::Enemy(const int id, float sp)
{
//set alive
mIsAlive = true;
//set speed
enemySpeed = sp;
// Load an enemy texture
if (!mEnemyTexture.loadFromFile("Media/Textures/PixelSpaceships/red_01.png")) {
// Handle loading error
std::cout << "Oh No! File not loaded." << std::endl;
}
//scale sprite and set texture so we know size
enemySprite.setTexture(mEnemyTexture);
}
Enemy loader method
void Enemy::loader(int noOfAliens) { // might add xPosition;yPosition
for (int i = 0; i < noOfAliens; i++)
{
Enemy alien(i, 10.f); // speed issue
alien.setLocation(i * 100.f + 50.f, alien.getSprite().getGlobalBounds().height / 2);
line 35 alienArray[i] = alien;
//alienArray.push_back(alien);
}
}
1>C:\Users\dzamm\source\repos\SFML\enemy.cpp(35,1): error C2280: 'Enemy &Enemy::operator =(const Enemy &)': attempting to reference a deleted function
Enemy Header file:
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Network.hpp>
#pragma once
#define NUMBER_OF_ALIENS 4
//class Game;
class Enemy
{
public:
//constructor sets ID number, loads sprite
Enemy() {};
Enemy(const int, float);
sf::Sprite& getSprite();
void kill();
bool isAlive();
void load();
void loader(const int noOfAliens);
void setLocation(float xPosition, float yPosition);
void enemyInstantiator(int noOfEnemies, float xPosition, float yPosition);
void update();
void render(sf::RenderWindow& window);
private:
void enemyBehaviour(std::vector<sf::Sprite>& enemyList);
void enemyMovement(std::vector<Enemy>& enemyList);
private:
sf::Texture mEnemyTexture;
sf::Sprite enemySprite;
//std::vector<Enemy> alienArray;
//std::vector<sf::Sprite> mEnemies;
sf::RectangleShape mEnemyBounds;
sf::RenderWindow mWindow;
bool mIsShooting;
bool mIsAlive;
float enemySpeed;
};
I read on forums that issue is related to working with a copy of the object instead of the original object, however I am not sure how to handle this to reference the original object I'm modifying.
To reproduce create a constructor for Enemy.cpp class, create an entity Enemy alien using for loop in method as shown in Enemy::loader and store the created entity in the Enemy alienArray as shown by index.
The issue was related to an sf::RenderWindow reference that was not being even utilised in the code.
sf::RenderWindow &sf::RenderWindow::operator =(const sf::RenderWindow &)
Once I removed this all errors disappeared.
I'm new to C++, in order to learn it better (I learn from doing) I have rewritten the code from an example C++ game I found over on GitHub, I intend to alter pieces of code to expand on it and change the graphics once I have it working in order to know exactly what does what. Rather than simply copying and pasting the existing code I made sure to write it all myself, I believe re-writing code gives a better understanding than simply copy&paste.
I'm trying to load images for a small game as a C++ test, when trying to execute the code from the IDE it keeps failing near if(iter == _images.end()) I have tried everything I myself can think of like changing the type of _images, changing how filename gets made, adding checks, removing checks, I have spent the last 3 days on Google and StackOverflow trying every fix I came accross that mentioned a similar problem. So far nothing has helped.
I will post the code sections I believe relevant to this here, please have a look and point me to where or how to fix this.
First things first, the error it shows me:
Exception thrown: read access violation.
std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,sf::Texture,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,sf::Texture> >,0> >::_Root(...) returned 0x4.
If there is a handler for this exception, the program may be safely continued.
From what I have gathered this has to do with the memory it is trying to read/write to.
Call Stack
FileName.exe!std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,sf::Texture,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,sf::Texture> >,0> >::_Lbound<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & _Keyval) Line 2060 C++
FileName.exe!std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,sf::Texture,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,sf::Texture> >,0> >::lower_bound(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & _Keyval) Line 1538 C++
FileName.exe!std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,sf::Texture,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,sf::Texture> >,0> >::find(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & _Keyval) Line 1481 C++
FileName.exe!VisibleGameObject::Load(std::basic_string<char,std::char_traits<char>,std::allocator<char> > filename) Line 17 C++
FileName.exe!Player::Player() Line 7 C++
FileName.exe!`dynamic initializer for 'speler''() Line 30 C++
[External Code]
VisibleGameObject.cpp
void VisibleGameObject::Load(std::string filename) {
filename = "textures/" + filename;
//Basic algorithm for images
auto iter = _images.find(filename);
if (iter == _images.end()) {
sf::Texture texture;
if (!texture.loadFromFile(filename)) {
std::cout << "[ERROR] Image failed to load!" << std::endl;
assert(false);
}
auto img = _images.insert(std::pair<std::string, sf::Texture>(filename, texture)).first;
std::cout << "[DEBUG] " + filename + ": Loaded and added to cache." << std::endl;
_sprite.setTexture(img->second);
_isLoaded = true;
}
else {
_sprite.setTexture(iter->second);
_isLoaded = true;
}
_imageKey = filename;
}
VisibleGameObject.h
#pragma once
#include "stdafx.h"
class VisibleGameObject {
public:
VisibleGameObject();
virtual ~VisibleGameObject();
virtual void Load(std::string filename);
virtual void Draw(sf::RenderWindow & window);
virtual void Update(float elapsedTime) = 0;
virtual void SetPosition(float x, float y);
virtual sf::Vector2f GetPosition() const;
virtual float GetWidth() const;
virtual float GetHeight() const;
virtual sf::Rect<float> GetBoundingRect() const;
virtual bool IsLoaded() const;
virtual bool ShouldPersist() const;
virtual void ToggleVisibility(bool isVisible);
virtual bool ShouldDraw() const;
protected:
sf::Sprite& GetSprite();
private:
sf::Sprite _sprite;
std::string _imageKey;
bool _isLoaded;
bool _shouldDraw;
static std::map<std::string, sf::Texture> _images;
};
stdafx.h
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/Audio.hpp>
#include <map>
#include <iostream>
#include <cassert>
The call to VisibleGameObject.cpp happens from Game.cpp
void Game::Start() {
_mainWindow.create(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Window PlaceHolder Title");
Background *background1 = new Background(1);
background1->SetPosition(0.f, -768.f);
_gameObjectManager.Add("bg1", background1);
Background *background2 = new Background(2);
background2->SetPosition(0.f, -768.f);
_gameObjectManager.Add("bg2", background2);
Background *background4 = new Background(4);
background4->SetPosition(0.f, -768.f);
_gameObjectManager.Add("bg4", background4);
Player *player = new Player();
player->ResetStartingPosition();
_gameObjectManager.Add("player", player);
MainHUD *hud = new MainHUD();
_gameObjectManager.Add("hud", hud);
GameOverHUD *gameOver = new GameOverHUD();
_gameObjectManager.Add("gameover", gameOver);
while (!IsExiting()) {
GameLoop();
}
_mainWindow.close();
}
If anything is missing or I have added things that can be exluded, let me know and I will edit the question.
I'm a game designer coming from a Basic-like programming language and moving to C++.
For a new game I'd like to have a gui programmed using OOP.
My gui currently consists of a gui-class (the outer wrapper), a g_element-class (the middle wrapper containing all the common attributes) and a button-class (overriding and extending the g_element-class).
The problem is I'm getting the following error:
Severity Code Description Project File Line Suppression State
Error LNK2001 unresolved external symbol "public: static class
std::list,class std::allocator > > gui
::el_stack"(?el_stack#?$gui#V?$g_element#Vbutton######2V?$list#V?$g_element#Vbutton####V?$allocator#V?$g_element#Vbutton#####std###std##A)
I really think that this error is not the only problem - I might have a completely wrong approach for this whole thing. I'm also not really sure about that template-thing - I thought I'd be able to expand my g_element-class with gui-elements later (i.e. buttons, sliders, windows...) - but give me a hint if there's something that I can optimise.
Here is my gui.cpp file (so far):
using namespace std;
template <class GUI_ELEMENT> class gui {
protected:
void add_to_stack(GUI_ELEMENT elem) {
// The error comes from here..
// I wanted a list of all my g_elements (buttons)
el_stack.push_back(elem);
printf("size now: %d", el_stack.size());
}
unsigned int get_stack_size() {
return el_stack.size();
}
public:
static list<GUI_ELEMENT> el_stack; // The elements list
void render() {
// here, I'd like to iterate over all i.e. buttons to draw them
}
};
template <class GUI_ELEMENT> class g_element : public gui<g_element<GUI_ELEMENT> >{
private:
float x;
float y;
float w;
float h;
public:
void set_width(float width) {
this->w = width;
}
float get_width() {
return this->w;
}
};
class button : public g_element<button> {
protected:
char* caption;
public:
button(float x, float y, float w, float h, char* caption) {
this->set_width(w);
this->set_caption(caption);
this->add_to_stack(*this);
}
void set_caption(char* caption) {
this->caption = caption;
}
char* get_caption() {
return this->caption;
}
};
I'd like to use my gui like so:
// Create a few test buttons
button b1(50, 50, 100, 50, "Test");
b1.set_width(150);
float s = b1.get_width();
printf("size w: %f", s);
printf("\ncaption: %s", b1.get_caption());
button b2(50, 50, 100, 50, "Test");
button b3(50, 50, 100, 50, "Test2");
button b4(50, 50, 100, 50, "Test3");
// Rendering currently (all buttons at once)
gui<button> G;
G.render();
// but this would be nicer:
gui::render_buttons()
// or
gui<button>::render()
Is there somebody who might help me? Really big thanks in advance!
All static fields should be defined outside of the class definition. In your case you have to add these lines of code after a class definition:
template<class GUI_ELEMENT>
list<GUI_ELEMENT> gui<GUI_ELEMENT>::el_stack;
I am trying to create a generic menu button class that is templated to a class, so I can make this button in any class. I want to create a void function pointer to different functions in that class, so when you click on the New Game button, it will call the NewGame() function etc.
I'm still a little new to the idea of creating function pointers and would like some advice.
I get a crazy link error everytime I try to compile my code now with this Menubutton.
here is the error:
Error 1 error LNK2019: unresolved
external symbol "public: void
__thiscall MENUBUTTON::Draw(void)"
(?Draw#?$MENUBUTTON#VTitleScreen####QAEXXZ)
referenced in function "public:
virtual void __thiscall
TitleScreen::Draw(void)"
(?Draw#TitleScreen##UAEXXZ) TitleScreen.obj
MenuButton.h
template <class t>
struct MENUBUTTON
{
SPRITE Normal; // Sprite to display when not hovered over or pressed down
SPRITE Hover; // Sprite to display when hovered over
RECTANGLE HoverBounds; // The Rectangle that activates the hover flag
t* pClass; // Pointer to the templated class
void (t::*ClickFunction)(); // Pointer to void function
void SetButton(int xPos, int yPos, int width, int height, int hPadLeft, int hPadTop, int hWidth, int hHeight, LPCTSTR normalFilePath, LPCTSTR hoverFilePath, t* objectClass, void (t::*ClickFunction)());
bool IsMouseHover();
void CheckPressed();
void Draw();
};
MenuButton.cpp
#include "Global.h"
template <class t>
void MENUBUTTON<t>::SetButton(int xPos, int yPos, int width, int height, int hPadLeft, int hPadTop, int hWidth, int hHeight, LPCTSTR normalFilePath, LPCTSTR hoverFilePath, t* objectClass, void (t::*ClickFunction)())
{
// Position
Hover.position.x = Normal.position.x = xPos;
Hover.position.y = Normal.position.y = yPos;
Hover.position.z = Normal.position.z = 0;
// Width / Height
Hover.width = Normal.width = width;
Hover.height = Normal.height = height;
// Hover RECTANGLE
HoverBounds.x = xPos + hPadLeft;
HoverBounds.y = yPos + hPadTop;
HoverBounds.width = hWidth;
HoverBounds.height = hHeight;
// Load the Sprites
LoadSprite(&Normal, normalFilePath, width, height, 1, 1);
LoadSprite(&Hover, hoverFilePath, width, height, 1, 1);
// Set the Click function pointer
this->pClass = objectClass;
this->ClickFunction = ClickFunction;
}
template <class t>
void MENUBUTTON<t>::Draw()
{
if(IsMouseHover())
{
DrawSprite(&Hover, 0, Hover.position.x, Hover.position.y, Hover.position.z);
}
else
{
DrawSprite(&Normal, 0, Normal.position.x, Normal.position.y, Normal.position.z);
}
}
template <class t>
bool MENUBUTTON<t>::IsMouseHover()
{
return (((InputData.MousePosition.x >= HoverBounds.x) && (InputData.MousePosition.x <= (HoverBounds.x + HoverBounds.width))) &&
((InputData.MousePosition.y >= HoverBounds.y) && (InputData.MousePosition.y <= (HoverBounds.y + HoverBounds.height)))) ? true : false;
}
Here is my Title Screen which is using the menu button.
TitleScreen.h
class TitleScreen : public BaseState
{
// SPRITES
SPRITE titleScreenBG;
// MENU BUTTONS
MENUBUTTON<TitleScreen> playButton;
MENUBUTTON<TitleScreen> quitButton;
public:
TitleScreen();
virtual void Initialize();
virtual void End();
virtual void Update(float dt, INPUTDATA* input);
virtual void Draw();
void QuitGame();
void NewGame();
};
TitleScreen.cpp
#include "Global.h"
// Constructors
TitleScreen::TitleScreen()
{
}
// Virtual Voids
void TitleScreen::End()
{
}
void TitleScreen::Initialize()
{
this->Enabled = true;
this->Visible = true;
// Initialize sprites
ZeroMemory(&titleScreenBG, sizeof(SPRITE));
LoadSprite(&titleScreenBG, TEXT("../../PNG/TitleScreenBG.png"), 1440, 900, 1, 1);
titleScreenBG.position.x = titleScreenBG.position.y = titleScreenBG.position.z = 0;
// Initialize buttons
ZeroMemory(&playButton, sizeof(MENUBUTTON<TitleScreen>));
playButton.SetButton(55, 170, // x , y
512, 128, // width, height
10, 10, // Left, Top Padding
400, 70, // Hover width, Hover height
TEXT("../../PNG/NewGame.png"), TEXT("../../PNG/NewGameH.png"),
this, &TitleScreen::NewGame);
ZeroMemory(&quitButton, sizeof(MENUBUTTON<TitleScreen>));
quitButton.SetButton(55, 240, // x , y
512, 128, // width, height
10, 10, // Left, Top Padding
190, 70, // Hover width, Hover height
TEXT("../../PNG/QuitButton.png"), TEXT("../../PNG/QuitButtonH.png"),
this, &TitleScreen::QuitGame);
}
void TitleScreen::Update(float dt, INPUTDATA* input)
{
}
void TitleScreen::Draw()
{
StartRender();
DrawSprite(&titleScreenBG, 0, titleScreenBG.position.x, titleScreenBG.position.y, titleScreenBG.position.z);
playButton.Draw();
quitButton.Draw();
EndRender();
}
// Public Methods
void TitleScreen::QuitGame()
{
CloseDirect3D();
}
void TitleScreen::NewGame()
{
CloseDirect3D();
}
If anyone has any advice on how I can make the buttons dynamic for any case in a different way, or know what this problem is, please help! :)
To get rid of the link error, move all method definitions starting with template from the .cpp file to the .h file. In your code, move these:
template <class t> void MENUBUTTON<t>::SetButton ... { ... }
template <class t> void MENUBUTTON<t>::Draw ... { ... }
The reason why it doesn't work with the .cpp file is that the compiler treats MENUBUTTON<Foo> and MENUBUTTON<Bar> etc. as different classes, and it generates and compiles such a class whenever it is used. So if you use MENUBUTTON<TitleScreen> when compiling titlescreen.cpp, then the MENUBUTTON<TitleScreen> code will be compiled into the object file titlescreen.o. But the methods SetButton and Draw will be missing at link time, because they are not defined in titlescreen.cpp or any of the .h files it includes. MenuButton.o won't contain it either, because MenuButton.cpp does not need MENUBUTTON<TitleScreen>.
Just to clarify the first answer, you can't have a template class that is declared in a header and then implemented in a separated compiled translation unit (i.e. .cpp file). You've got to put all the code in the header. You can either put it all in the initial class declaration, or using "inline" declare the class first with the functions, then implement the specific functions further down in the header.
Speaking of a better way ... why do you need a template here?
Plain old virtual function or pointer to member would do.
This would be a perfect application of the Command Design Pattern.
Templates give you compile-time polymorphism, while you are probably looking for run-time polymorphism here.