In a pawn class that inherits from the cocos2d Sprite class, I used this->getBoundingBox() in it's update function. This caused an "Access violation at reading location" error. Then, I swapped "this" with "GAME::PLAYER", a variable in a namespace that references the player and it worked. Why does this->getBoundingBox() cause an error when GAME::PLAYER->getBoundingBox() works perfectly fine? Aren't they supposed to be the same thing? Just to note, "this->" works with any other function but getBoundingBox. Is it something I'm doing wrong? I'm not THAT good with C++
Here's pawn.h
#include <cocos2d.h>
#ifndef PLAYER_CONTROLLER
#define PLAYER_CONTROLLER GAME::PLAYER
class pawn : public cocos2d::Sprite {
public:
pawn();
~pawn();
static pawn* create();
static pawn* create(bool default_moving);
bool moving;
bool right;
int speed;
cocos2d::Rect getBounds();
void step();
void initOptions();
void update(float dt) override;
void move(cocos2d::Vec2 vec);
void moveX(int x);
void moveY(int y);
virtual bool touchBegan(cocos2d::Touch*, cocos2d::Event*);
virtual void touchEnded(cocos2d::Touch*, cocos2d::Event*);
};
namespace GAME {
static pawn* PLAYER;
};
#endif
Here's pawn.cpp
#include "player.h"
#include <cocos2d.h>
pawn::pawn() {
}
pawn::~pawn() {
}
bool pawn::touchBegan(cocos2d::Touch* touch, cocos2d::Event* event) {
this->move(cocos2d::Vec2(5, 0));
this->moving = false;
return true;
}
void pawn::touchEnded(cocos2d::Touch* touch, cocos2d::Event* event) {
this->moving = true;
}
void pawn::step() {
if (this->moving) {
if (this->right) {
this->move(cocos2d::Vec2(this->speed, 0));
}
else {
this->move(cocos2d::Vec2(-this->speed, 0));
}
if (this->getPositionX() < 0) {
this->right = true;
CCLOG("Going right V4");
}
else {
if (this->getPositionX() + this->getContentSize().width > cocos2d::Director::getInstance()->getWinSizeInPixels().width + cocos2d::Director::getInstance()->getVisibleOrigin().x){
this->right = false;
CCLOG("Going left V4");
}
}
}
}
void pawn::move(cocos2d::Vec2 vec) {
PLAYER_CONTROLLER->setPosition(cocos2d::Vec2(PLAYER_CONTROLLER->getPositionX() + vec.x, PLAYER_CONTROLLER->getPositionY() + vec.y));
}
void pawn::moveX(int x) {
}
void pawn::moveY(int y) {
}
void pawn::update(float dt) {
//cocos2d::Rect act = this->getBoundingBox();
this->getPosition();
this->step();
}
cocos2d::Rect pawn::getBounds() {
if (!PLAYER_CONTROLLER) {
CCLOG("Is this the problem?");
}
return PLAYER_CONTROLLER->getBoundingBox();
}
pawn* pawn::create() {
auto character = new pawn();
character->moving = true;
character->right = false;
character->speed = 5;
character->setPositionY(50);
if (PLAYER_CONTROLLER == NULL) {
CCLOG("There is no player, yet.");
CCLOG("Adding player");
PLAYER_CONTROLLER = character;
}
else {
CCLOG("There's already a player");
return NULL;
}
//character->setPositionX(40);
if (character->initWithFile("Base.jpg")){
return character;
}
CC_SAFE_DELETE(character);
return NULL;
}
pawn* pawn::create(bool default_moving) {
pawn* character = new pawn();
character->moving = default_moving;
character->setPositionX(40);
if (character->initWithFile("Base.jpg")){
return character;
}
CC_SAFE_DELETE(character);
return NULL;
}
Is it maybe because I call a pawn method from another class? I use a Collider class to call functions in pawn
Collider.cpp
#include "Collider.h"
#include "player.h"
Collider::Collider() : CollideMode(OVERLAP) {
}
Collider::~Collider() {
}
Collider* Collider::create() {
Collider* col = new Collider;
if (col->initWithFile("Base.jpg")){
col->setAnchorPoint(cocos2d::Vec2(0, 0));
col->setContentSize(cocos2d::Size(100, 100));
return col;
}
CC_SAFE_DELETE(col);
return NULL;
}
void Collider::collision(cocos2d::Vec2 intersect) {
CCLOG("IT IS COLLIDING");
if (intersect.x < intersect.y) {
PLAYER_CONTROLLER->move(cocos2d::Vec2(-intersect.x, 0));
CCLOG("X");
}
else if (intersect.x > intersect.y) {
PLAYER_CONTROLLER->move(cocos2d::Vec2(0, -intersect.y));
CCLOG("Y");
}
}
void Collider::update(float dt) {
//cocos2d::Rect col = this->getBoundingBox();
auto act = PLAYER_CONTROLLER->getBounds();
if (PLAYER_CONTROLLER) {
if (!PLAYER_CONTROLLER) {
CCLOG("There is no player?");
}
}
else {
CCLOG("Not colliding");
}
}
I don't seems any problem with this->getBoundingBox() inside update(float dt) function.
I've created small test :
declaration inside .h file
class MySprite: public Sprite {
public:
bool init() override;
void update(float) override;
CREATE_FUNC(MySprite);
};
Now method definition inside .cpp file
bool MySprite::init(){
if(!Sprite::init())
return false;
scheduleUpdate();
return true;
}
void MySprite::update(float dt){
auto rect=this->getBoundingBox();
CCLOG("Inside Update method of MySprite Bounding rect Width %f & Height %f",rect.size.width,rect.size.height);
}
Then I created an autoreleased object of MySprite and add to parent.
auto mysprite=MySprite::create();
mysprite->setContentSize(Size(10,10));
addChild(mysprite);
Run, Expected result on output console.
I see my mistake. It was the fact that I redefined the namespace "GAME" and it's variable, "GAME::PLAYER" every time I included pawn.h, the other source files that I called the pawn functions from didn't know what GAME::PLAYER or PLAYER_CONTROLLER ( just a macro for GAME::PLAYER ) was, as I had only defined PLAYER_CONTROLLER in pawn.cpp. That's why when you called PLAYER_CONTROLLER->method() in another file, it passed in NULL as "this", and also why PLAYER_CONTROLLER was referring to a different PLAYER_CONTROLLER than the one passed in as "this".
I solved it by using the extern keyword that makes the variables global between all files, which was my original intention.
pawn.h
#include <cocos2d.h>
#ifndef PLAYER_CONTROLLER
#define PLAYER_CONTROLLER GAME::PLAYER
#define INITIALIZE_PLAYER pawn* GAME::PLAYER = NULL
class pawn : public cocos2d::Sprite {
public:
pawn();
~pawn();
static pawn* create();
static pawn* getController();
static pawn* create(bool default_moving);
bool moving;
bool right;
int speed;
cocos2d::Rect getBounds();
void step();
void initOptions();
void update(float dt) override;
void move(cocos2d::Vec2 vec);
void moveX(int x);
void moveY(int y);
virtual bool touchBegan(cocos2d::Touch*, cocos2d::Event*);
virtual void touchEnded(cocos2d::Touch*, cocos2d::Event*);
};
namespace GAME {
extern pawn* PLAYER;
};
#endif
This is why I said I wasn't that good at C++.
Related
I took the design of a game from one person and now I have a problem.
Below there is an implementation of the class Base, in which all the initial parameters of the window are defined, and all its fields are brought into the global scope.
Base.h :
#pragma once
#include "Define.h"
#include "Audio.h"
#include "Font.h"
#include "Texture.h"
#include "GameObject.h"
class Base {
public:
static sf::RenderWindow wnd;
static Texture texture;
static Font font;
static Audio audio;
static sf::Event event;
static int scr_w;
static int scr_h;
static v2f cur_p;
static v2f cur_p_wnd;
static vector<unique_ptr<GameObject>> objects;
static float time;
static void SystemUpdate() {
time = float(clock.getElapsedTime().asMicroseconds()) / 1000.f, clock.restart();
cur_p = wnd.mapPixelToCoords(sf::Mouse::getPosition(wnd));
cur_p_wnd = v2f(sf::Mouse::getPosition(wnd));
}
static void CloseEvent() {
if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == Keyboard::Escape)) wnd.close();
}
static bool IsKeyPressed(const sf::Keyboard::Key& code) {
if (event.type == sf::Event::KeyPressed)
if (event.key.code == code) return true;
return false;
}
static bool IsKeyReleased(const sf::Keyboard::Key& code) {
if (event.type == sf::Event::KeyReleased)
if (event.key.code == code) return true;
return false;
}
template <class T>
static void new_object(T* obj) {
B::objects.push_back(unique_ptr<GameObject>(obj));
}
template <class T>
static void delete_object(T* obj) {
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
if ((*itr)->ID == obj->ID) {
B::objects.erase(itr);
return;
}
}
}
static bool IsMouseInRect(Shape& s) {
return s.getGlobalBounds().contains(B::wnd.mapPixelToCoords(sf::Mouse::getPosition(B::wnd)));
}
Base(string init) {
if (init == "init") {
scr_w = sf::VideoMode::getDesktopMode().width;
scr_h = sf::VideoMode::getDesktopMode().height;
font = Font();
texture = Texture();
audio = Audio();
wnd.create(sf::VideoMode(scr_w, scr_h), "Sea Battle", sf::Style::Close, sf::ContextSettings(0, 0, 8));
cur_p = v2f(0, 0);
cur_p_wnd = v2f(0, 0);
wnd.setMouseCursorVisible(true);
wnd.setFramerateLimit(30);
srand(::time(0));
}
}
Base(void) {}
private:
static sf::Clock clock;
};
sf::RenderWindow B::wnd;
Texture B::texture;
Font B::font;
Audio B::audio;
sf::Event B::event;
int B::scr_w;
int B::scr_h;
v2f B::cur_p;
v2f B::cur_p_wnd;
float B::time;
sf::Clock B::clock;
vector<unique_ptr<GameObject>> B::objects;
It is included to the class Game, in which its constructor is called.
Game.cpp :
#include "Game.h"
Game::Game() {
B("init");
game_state = GameState::Planning;
GameObject* background = new GameObject(v2f(5000, 5000));
background->getRect().setFillColor(Color(255, 255, 255));
new_object(background);
new_object(new Field());
}
void Game::Update() {
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Update();
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
UpdateUI();
}
void Game::Action() {
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Action();
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
ActionUI();
}
void Game::Draw() {
wnd.clear();
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Draw(wnd);
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
DrawUI();
wnd.display();
}
void Game::UpdateUI() {}
void Game::ActionUI() {}
void Game::DrawUI() {}
void Game::Play() {
while (wnd.isOpen()) {
SystemUpdate();
Update();
while (wnd.pollEvent(event)) {
CloseEvent();
Action();
}
Draw();
}
}
Game::~Game(){}
int main() {
Game game;
game.Play();
return 0;
}
The main game loop is in the class Game, and this class must call the Draw, Action, Update methods for each object contained in the object vector.(objects vector is in the Base.h)
But if I create any new object class and try to implement its Draw, Action, Update methods in it, then I will not be able to use in them all the variables in Base.h, brought to the global scope, and without them I cannot properly implement my objects, how can I change the architecture so that everything works?
///
For example, if I want the position of my object to change to the current position of the cursor, then I need a variable from Base.h, which I will refer to as B::cur_p, but the compiler simply gives an error: "unknown authenticator".
The answer to globals is NO globals (just like optimization, only use them if you know its worth the cost). It is bad for maintainability and bad for testing! (and in large projects bad for component dependency and bad for build times) If you want to learn about architecture, now is a good time te read up on dependency injection. e.g. https://en.wikipedia.org/wiki/Dependency_injection
Here is an example in code for your game :
#include <vector>
//-----------------------------------------------------------------------------
// just some structs to be able to use names from your Game to recognize.
struct RenderWindow {};
struct Texture {};
struct Font {};
struct Audio {};
struct GameObject
{
void update() {};
};
// etc
//-----------------------------------------------------------------------------
// global_data_itf.h
// model of a screen, those values belong together
struct screen_size_t
{
unsigned int width{ 3440 };
unsigned int height{ 1440 };
};
// Now define an interface for accessing your data.
// This also allows you
// to create mocks (e.g. with different screen sizes) for testing
// not something you can do with static or real global variables
//
// Note you should really make functions const and return const&
// if data is readonly!
//
class global_data_itf
{
public:
virtual RenderWindow& render_window() = 0;
virtual Texture& texture() = 0;
virtual Font& font() = 0;
virtual Audio& audio() = 0;
virtual const screen_size_t& screen_size() = 0;
// unique_ptr probably not needed. Give GameObject a move constructor
// and emplace gameobjects
virtual std::vector<GameObject>& objects() = 0;
virtual float& time() = 0;
virtual ~global_data_itf() = default;
protected:
global_data_itf() = default;
};
//-----------------------------------------------------------------------------
// global_data.h
// #include "global_data_itf.h"
// For the game you need a default implementation of the global_data_itf
// this is it.
class global_data final :
public global_data_itf
{
// todo constructor with proper initialization
virtual RenderWindow& render_window() override
{
return m_render_window;
}
virtual Texture& texture() override
{
return m_texture;
}
virtual Font& font() override
{
return m_font;
}
virtual Audio& audio() override
{
return m_audio;
}
virtual const screen_size_t& screen_size() override
{
return m_screen_size;
}
virtual std::vector<GameObject>& objects() override
{
return m_objects;
}
virtual float& time() override
{
return m_time;
}
private:
RenderWindow m_render_window;
Texture m_texture;
Font m_font;
Audio m_audio;
screen_size_t m_screen_size;
std::vector<GameObject> m_objects;
float m_time;
};
//-----------------------------------------------------------------------------
// Game.h
// #include "global_data_itf.h"
class Game
{
public:
explicit Game(global_data_itf& data);
void Update();
private:
global_data_itf& m_data;
};
//-----------------------------------------------------------------------------
// Game.cpp
// include "game.h"
Game::Game(global_data_itf& data) :
m_data{ data }
{
}
void Game::Update()
{
for (auto& object : m_data.objects())
{
object.update();
}
}
//-----------------------------------------------------------------------------
// main.cpp
// #include "global_data.h"
// #include "game.h"
int main()
{
global_data data;
Game game(data); // inject the dependency on data into game.
// if you have other classes needing access to global data
// you can inject global_data into those classes as well.
return 0;
}
Well at first, if you want to access your variables from wherever, you can make them real global variables, put them in a header, etc. If I got you right, this fixes the whole problem as variables and methods go separately.
I am trying to access a function of a pointer and it does not work and it gives me an LoadStoreAlignmentCause Exception. Furthermore I want to check if the pointer does exist, but it always returns true for that.
LedFunction.h
#include "Led/LedStates.h"
class LedStates;
class LedFunction {
public:
LedStates *state;
virtual bool init();
bool loadValues();
virtual void render() = 0;
};
LedFunction.cpp
#include "Led/LedFunction.h"
bool LedFunction::init() {
return false;
}
RainbowFunction.h
class RainbowFunction: public LedFunction {
public:
RainbowFunction() {
Serial.println("Rainbow Constructor.");
}
void render() {
Serial.println("From Rainbow...");
}
}
};
LedStates.h
#include "Handlers/LedHandler.h"
#include "Led/LedFunction.h"
class LedHandler;
class LedFunction;
class LedStates {
public:
uint8_t (*values)[3];
int count = 0;
bool dirty = false;
LedHandler* ledHandler;
LedFunction* function = 0;
LedStates(LedHandler* handler);
void setFunction(LedFunction *newFunction);
void setRgb(int i, uint8_t r, uint8_t g, uint8_t b);
void render(); //TODO check virtual key
void setValues(LedStates &to);
void commit();
void fade(LedStates &to, long f0, long f1);
};
LedStates.cpp
#include "Led/LedStates.h"
#include "Led/Animations/RainbowFunction.h"
LedStates::LedStates(LedHandler* handler) {
this->ledHandler = handler;
count = ledHandler->getLength();
values = new uint8_t[count][3];
this->setFunction(new RainbowFunction());
}
void LedStates::setFunction(LedFunction* newFunction) {
Serial.println("SETTING FUNCTION");
if(function)
delete function; //TODO check virtual destructor
function = newFunction;
if(!function)
return;
function->state = this;
Serial.println("-----Setting Done-----");
}
void LedStates::render() {
Serial.println(2);
Serial.println("B:" + (String) (function != 0));
Serial.println("B:" + (String) (function != false));
if(function == nullptr) { //This is the check that is not working properly
Serial.println(22222);
//delay(1000);
//function->render();
} else {
Serial.println(33333);
function->render();
}
Serial.println(3);
}
LedHandler.h
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <FastLED.h>
//#include "Led/LedFunction.h"
#include "Led/LedStates.h"
#include "Led/Fading.h"
class LedStates;
class LedHandler {
public:
LedHandler(int length, uint16_t pin);
void clear();
void show();
void setColor(int s, int r, int g, int b);
void loop();
Adafruit_NeoPixel getStrip();
int getLength();
private:
LedStates* currentState;
LedStates* targetState;
Fader<LedStates> *ledFader;
int length;
Adafruit_NeoPixel strip;
CRGB* leds;
};
LedHandler.cpp
#include "Handlers/LedHandler.h"
LedHandler::LedHandler(int length, uint16_t pin) {
Serial.begin(115200);
this->length = length;
this->strip = Adafruit_NeoPixel(length, pin);
this->strip.begin();
CRGB* arr = new CRGB[length];
this->leds = arr;
FastLED.addLeds<WS2812B, 6, RGB>(leds, 60).setCorrection(TypicalLEDStrip);
//Serial.println("-----Creating States-----");
LedStates currentLedStates = LedStates(this);
LedStates targetLedStates = LedStates(this);
Fader<LedStates> ledFader = Fader<LedStates>(currentLedStates, targetLedStates);
//Serial.println("-----Created States-----");
this->currentState = ¤tLedStates;
this->targetState = &targetLedStates;
this->ledFader = &ledFader;
}
void LedHandler::loop() {
Serial.println("--::--::--::--::--::--::--");
currentState->render();
Serial.println(99);
Serial.println(6);
currentState->commit();
Serial.println("-------------------------");
delay(10000);
}
The Serialmonitor output:
SETTING FUNCTION
-----Setting Done-----
Rainbow Constructor.
SETTING FUNCTION
-----Setting Done-----
--::--::--::--::--::--::--
2
B:1
B:1
33333
Exception (9):
epc1=0x40202a92 epc2=0x00000000 epc3=0x00000000 excvaddr=0x4020d32d depc=0x00000000
These lines define local variables inside the function LedHandler::LedHandler(int length, uint16_t pin):
LedStates currentLedStates = LedStates(this);
LedStates targetLedStates = LedStates(this);
These lines remember the address of the local variables:
this->currentState = ¤tLedStates;
this->targetState = &targetLedStates;
This line deletes the local variables so the memory can be used for something else:
}
and this line calls the something else (nobody knows what it will be):
currentState->render();
I'm recently learning to make a 2d game in SFML using a tutorial series on youtube by Suraj Sharma(Video 57):
https://www.youtube.com/watch?v=kwd_AVCkvXE&list=PL6xSOsbVA1ebkU66okpi-KViAO8_9DJKg&index=57
His Source Code:
https://github.com/Headturna/SFML_RPG
Right now he's teaching me to symplify the game's menu system by making a mini class 'StateData' in the parent class 'State' so any inherited class can access 'State' parameters via 'StateData'(Ex:MainMenu(StData* Stdata){}).
After debugging the game seems to runs fine.But everytime i click on something on the menu(start,settings,etc...) a 'Read access violation:Stdata was nullptr' occurs in the 'State' class constructor.
Here's the code:
State.h:
#pragma once
#ifndef STATE_H
#define STATE_H
#include "Player.h"
#include "GrphSettings.h"
class Player;
class GrphSettings;
class State;
class StData {
public:
StData(){}
//Vars
float GridSize;
sf::RenderWindow* Window;
GrphSettings* GSettings;
std::map<std::string, int>* SupportedKeys;
std::stack<State*>* states;
};
class State
{
private:
protected:
StData* Stdata;
std::stack<State*>* states;
sf::RenderWindow* window;
std::map<std::string, int>* SupportedKeys ;
std::map<std::string, int> Keybinds;
bool quit;
bool pause;
float keyTime;
float keyTimeMax;
float GridSize;
sf::Vector2i MousePosScr;
sf::Vector2i MousePosWind;
sf::Vector2f MousePosView;
//Resources
std::map<std::string,sf::Texture> texture;
//Funcs
virtual void InitKeybinds() = 0;
public:
State(StData* Stdata);
virtual~State();
//Access
const bool getKeytime();
const bool& getquit()const;
//Funcs
void Endstate();
void PauseSt();
void UnPauseSt();
virtual void UpdateInput(const float& dt) = 0;
virtual void UpdateMousePos();
virtual void UpdateKeyTime(const float& dt);
virtual void Update(const float& dt) = 0;
virtual void Render(sf::RenderTarget* target = nullptr) = 0;
};
#endif // !1
State.cpp:
#include "pch.h"
#include "State.h"
State::State(StData* Stdata)
{
this->Stdata = Stdata;
this->window = Stdata->Window;
this->SupportedKeys = Stdata->SupportedKeys;
this->states = Stdata->states;
this->quit = false;
this->pause = false;
this->keyTime = 0.f;
this->keyTimeMax = 10.f;
this->GridSize = Stdata->GridSize;
}
State::~State()
{
}
//Access
const bool State::getKeytime()
{
if (this->keyTime >= this->keyTimeMax) {
this->keyTime = 0.f;
return true;
}
return false;
}
const bool& State::getquit() const
{
// TODO: insert return statement here
return this->quit;
}
//Funcs
void State::Endstate()
{
this->quit = true;
}
void State::PauseSt()
{
this->pause = true;
}
void State::UnPauseSt()
{
this->pause = false;
}
void State::UpdateMousePos()
{
this->MousePosScr = sf::Mouse::getPosition();
this->MousePosWind = sf::Mouse::getPosition(*this->window);
this->MousePosView = this->window->mapPixelToCoords(sf::Mouse::getPosition(*this->window));
}
void State::UpdateKeyTime(const float& dt)
{
if (this->keyTime < this->keyTimeMax)
this->keyTime += 100.f * dt;
}
Every button one the menu(start,exit...)is an inherited state from 'State' class.This is,for example,the 'Edit' state.
Edit.h:
#pragma once
#ifndef EDIT_H
#define EDIT_H
#include "State.h"
#include "Gui.h"
#include "PauseMenu.h"
#include "TileMap.h"
class State;
class Gui;
class PauseMenu;
class TileMap;
class Edit:public State
{
private:
//Vars
sf::Font Fnt;
PauseMenu* PMenu;
std::map<std::string, gui::Button*> buttons;
TileMap Map;
//Functions
void InitVars();
void InitBackGrnd();
void InitFonts();
void InitKeybinds();
void InitPauseMenu();
void InitBtn();
public:
Edit(StData* Stdata);
virtual~Edit();
//Functions
void UpdateInput(const float& dt);
void Update(const float& dt);
void UpdatePButtons();
void UpdateBtn();
void Endstate();
void RenderBtn(sf::RenderTarget& target);
void Render(sf::RenderTarget* target = nullptr);
};
#endif // ! EDIT_H
Edit.cpp:
#include "pch.h"
#include "Edit.h"
void Edit::InitVars()
{
}
void Edit::InitBackGrnd()
{
}
void Edit::InitFonts()
{
if (!this->Fnt.loadFromFile("Fonts/SPACEMAN.ttf")) {
throw("Error::Edit::Couldn't load font");
}
}
void Edit::InitKeybinds()
{
std::ifstream ifs("Config/EditKeys.ini");
if (ifs.is_open()) {
std::string key = "";
std::string key2 = "";
int keyval = 0;
while (ifs >> key >> key2)
{
this->Keybinds[key] = this->SupportedKeys->at(key2);
}
}
ifs.close();
this->Keybinds["Close"] = this->SupportedKeys->at("ESC");
this->Keybinds["Left"] = this->SupportedKeys->at("A");
this->Keybinds["Right"] = this->SupportedKeys->at("D");
this->Keybinds["Up"] = this->SupportedKeys->at("W");
this->Keybinds["Down"] = this->SupportedKeys->at("S");
}
void Edit::InitPauseMenu()
{
this->PMenu = new PauseMenu(*this->window, this->Fnt);
this->PMenu->addButtons("Quit", 800.f, "Quit");
}
void Edit::InitBtn()
{
}
Edit::Edit(StData* Stdata)
:State(Stdata)
{
this->InitVars();
this->InitBackGrnd();
this->InitFonts();
this->InitKeybinds();
this->InitPauseMenu();
this->InitBtn();
}
Edit::~Edit()
{
auto it = this->buttons.begin();
for (it = this->buttons.begin(); it != this->buttons.end(); ++it) {
delete it->second;
}
delete this->PMenu;
}
//Funcs
void Edit::UpdateInput(const float& dt)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key(this->Keybinds.at("Close")))
&& this->getKeytime()) {
if (!this->pause)
this->PauseSt();
else this->UnPauseSt();
}
}
void Edit::Update(const float& dt)
{
this->UpdateMousePos();
this->UpdateKeyTime(dt);
this->UpdateInput(dt);
if (!this->pause) {//Unpaused
this->UpdateBtn();
}
else {//Paused
this->PMenu->Update(this->MousePosView);
this->UpdatePButtons();
}
this->UpdateBtn();
std::cout << this->MousePosView.x << " " << this->MousePosView.y << "\r";
}
void Edit::UpdatePButtons()
{
if (this->PMenu->isPressed("Quit"))
this->Endstate();
}
void Edit::UpdateBtn()
{ //Update buttons and handle their functions
for (auto& it : this->buttons) {
it.second->Update(MousePosView);
}
}
void Edit::Endstate()
{
std::cout << "Ending State" << "\n";
}
void Edit::RenderBtn(sf::RenderTarget& target)
{
for (auto& it : this->buttons) {
it.second->Render(target);
}
}
void Edit::Render(sf::RenderTarget* target)
{
if (!target)
target = this->window;
this->RenderBtn(*target);
this->Map.Render(*target);
if (this->pause) {//Pause Menu
this->PMenu->Render(*target);
}
sf::Text MText;
MText.setPosition(this->MousePosView);
MText.setFont(this->Fnt);
MText.setCharacterSize(12);
std::stringstream ss;
ss << this->MousePosView.x << ' ' << this->MousePosView.y;
MText.setString(ss.str());
target->draw(MText);
}
Can anyone help me ?
I'm having trouble drawing a custom sf::Drawable derived object.
//Textbox.h
#pragma once
#include "Header.h"
#ifndef TEXTBOX_H
#define TEXTBOX_H
class Textbox : public Drawable {
public:
Textbox(int max_chars, bool numeric);
Textbox(int max_chars);
Textbox(bool numeric);
Textbox();
void setTextColor(Color color);
void setPosition(float x, float y);
Vector2f getPosition() {
return m_gshape.getPosition();
}
Vector2f getSize();
String getString();
void setFocus(bool value);
bool isFocused();
void input(Uint32 text_char);
void clear();
private:
virtual void Textbox::draw(sf::RenderTarget& target, sf::RenderStates states) const {
target.draw(m_gshape, states);
target.draw(m_textbox, states);
}
unsigned int max_length;
int min_ascii = 32;
int max_ascii = 127;
bool focus;
string content;
Text m_textbox;
RectangleShape m_gshape;
};
#endif // !TEXTBOX_H
And
//Textbox.cpp
#pragma once
#include "Textbox.h"
Textbox::Textbox(int max_chars, bool numeric) {
max_length = max_chars;
m_gshape.setSize(Vector2f(6 + 15 * max_length, 30));
m_gshape.setFillColor(Color::White);
m_gshape.setOutlineThickness(2);
m_gshape.setOutlineColor(Color(60, 60, 60));
m_gshape.setPosition(0, 0);
m_textbox.setFont(default_font);
m_textbox.setCharacterSize(25);
m_textbox.setFillColor(Color::White);
if (max_chars > 1)
m_textbox.setString(to_string((int)pow(10, max_chars - 1)));
else
m_textbox.setString("0");
if (numeric) {
min_ascii = 47;
max_ascii = 58;
}
}
Textbox::Textbox(int max_chars) : Textbox(max_chars, false) {}
Textbox::Textbox(bool numeric) : Textbox(2, numeric) {}
Textbox::Textbox() : Textbox(2, false) {}
void Textbox::setTextColor(Color color) {
m_textbox.setFillColor(color);
}
void Textbox::setPosition(float x, float y) {
FloatRect textbox_bounds = m_textbox.getGlobalBounds();
m_gshape.setPosition(x, y);
m_textbox.setPosition(m_gshape.getPosition().x + (m_gshape.getSize().x - textbox_bounds.width) / 2 - textbox_bounds.left,
m_gshape.getPosition().y + (m_gshape.getSize().y - textbox_bounds.height) / 2 - textbox_bounds.top);
}
Vector2f Textbox::getSize() {
return m_gshape.getSize();
}
String Textbox::getString() {
return m_textbox.getString();
}
void Textbox::setFocus(bool value) {
focus = true;
}
bool Textbox::isFocused() {
return focus;
}
void Textbox::input(Uint32 text_char) {
content = m_textbox.getString().toAnsiString();
if (text_char == 13) {
focus = false;
return;
}
if (m_textbox.getString().getSize() < max_length) {
if (text_char > min_ascii && text_char < max_ascii) {
m_textbox.setString(m_textbox.getString() + text_char);
}
}
if (text_char == 8 && m_textbox.getString().getSize() > 0) {
content.resize(m_textbox.getString().getSize() - 1);
m_textbox.setString(content);
}
}
void Textbox::clear() {
m_textbox.setString("");
}
Everything works except for the drawing part: while g_shape gets drawn and rendered m_textbox doesn't. I'm sure of this because I can still edit the text, however it's not displayed.
I must admit I didn't fully understand the sf::Drawable inheritance and consequently I'm not sure I overrid draw() correctly.
Thanks to #AlexMeuer I found the solution.
In my header file I had my global font set as extern sf::Font default_font however in my main.cpp I never declared it.
I have a class that inherits from CCNode. HcharacterDrawnode contains a group of StrokeDrawnode which is another custom CCNode. Now I add (m_HDrawnode)HcharacterDrawnode to a layer and runAction.
CCAction* place = CCMoveTo::create(2.0,ccp(0,0));
m_HDrawnode->runAction(place);
But nothing happened. I have checked some webpage. Someone said it may related to m_bRunning , however I cannot find a place to set this variable.
HcharacterDrawnode.h
class HcharacterDrawnode : public CCNode
{
public:
HcharacterDrawnode();
~HcharacterDrawnode();
CREATE_FUNC(HcharacterDrawnode);
virtual bool init();
virtual void onEnter();
virtual void onExit();
virtual void draw();
void addPoint(CCPoint point);
void addStroke(Stroke s);
void removeLastStroke();
CC_SYNTHESIZE_RETAIN(CCArray*,strokeDrawlist,StrokeDrawnodeList);
private:
};
HcharacterDrawnode.cpp
#include "HcharacterDrawnode.h"
HcharacterDrawnode::HcharacterDrawnode():strokeDrawlist(NULL)
{
}
HcharacterDrawnode::~HcharacterDrawnode()
{
CC_SAFE_RELEASE(strokeDrawlist);
}
void HcharacterDrawnode::onEnter(){
CCNode::onEnter();
}
void HcharacterDrawnode::onExit(){
CCNode::onExit();
}
bool HcharacterDrawnode::init(){
this->setStrokeDrawnodeList(CCArray::create());
return true;
}
void HcharacterDrawnode::draw(){
CCObject* ob;
CCARRAY_FOREACH(strokeDrawlist,ob){
((StrokeDrawnode*)(ob))->draw();
}
}
void HcharacterDrawnode::addPoint(CCPoint point){
StrokeDrawnode* t = (StrokeDrawnode*)(strokeDrawlist->objectAtIndex(strokeDrawlist->count()-1));
t->addPoint(point);
}
void HcharacterDrawnode::addStroke(Stroke s){
strokeDrawlist->addObject(StrokeDrawnode::create(s));
}
void HcharacterDrawnode::removeLastStroke(){
strokeDrawlist->removeLastObject();
}
StrokeDrawnode.h
class StrokeDrawnode : public CCNode
{
public:
StrokeDrawnode();
StrokeDrawnode(Stroke stro);
~StrokeDrawnode();
static StrokeDrawnode* create(Stroke stro);
Stroke stroke;
ccColor4B mcolor;
virtual void onEnter();
virtual void onExit();
virtual void draw();
int visibleIndex;
void addPoint(CCPoint point);
private:
};
StrokeDrawnode.cpp
#include "StrokeDrawnode.h"
StrokeDrawnode::StrokeDrawnode()
{
}
StrokeDrawnode::StrokeDrawnode(Stroke stro){
this->stroke = stro;
}
void StrokeDrawnode::onEnter(){
CCNode::onEnter();
}
void StrokeDrawnode::onExit(){
CCNode::onExit();
}
StrokeDrawnode* StrokeDrawnode::create(Stroke stro){
StrokeDrawnode* pRet = new StrokeDrawnode(stro);
if (pRet && pRet->init())
{
pRet->autorelease();
return pRet;
}else{
delete pRet;
pRet = NULL;
return NULL;
}
}
StrokeDrawnode::~StrokeDrawnode()
{
}
void StrokeDrawnode::draw(){
//CCLog("StrokeDrawnode::draw");
glLineWidth(6.0f);
ccDrawColor4F(0,0,0,1);
// glEnable(GL_LINE_SMOOTH);
CCPoint pre = stroke.pointList[0];
for (int i = 1; i< stroke.pointCount; i++)
{
ccDrawLine(pre,stroke.pointList[i]);
pre = stroke.pointList[i];
}
// glDisable(GL_LINE_SMOOTH);
}
void StrokeDrawnode::addPoint(CCPoint point){
this->stroke.addPoint(point);
}
You draw your StrokeNodes but you forgot to call the CCNode::draw() function for HcharacterDrawnode:
void HcharacterDrawnode::draw(){
CCNode::draw();
CCObject* ob;
CCARRAY_FOREACH(strokeDrawlist,ob){
((StrokeDrawnode*)(ob))->draw();
}
}
Also if you overwrite init() in your class you should call init of your parent:
bool HcharacterDrawnode::init(){
if(!CCNode::init())
return false;
this->setStrokeDrawnodeList(CCArray::create());
return true;
}