I'm trying to make a library to simplify the ncurses use to display colors. I'm doing it object-oriented so it'll easy to handle changes in the future. But the problem is that I can't get to work this code.
#include <ncurses.h>
#include <string.h>
#include <string>
#include <unistd.h>
#include <iostream>
using namespace std;
class ColorWindow {
private:
bool canColor;
WINDOW* container;
int height, width, startx, starty;
public:
ColorWindow() {
if(has_colors()) {
canColor=true;
}
this->height = 20;
this->width = 84;
this->starty = (LINES - height) / 2; /* Calculating for a center placement */
this->startx = (COLS - width) / 2;
}
bool writeStringWithColor(int x, int y, const char* message) {
if(!canColor) {
writeString(3, 5, "Sorry, your term can't show colors.");
return false;
}
init_pair(1, COLOR_RED, COLOR_BLACK);
writeString(0, 10, "aaaaaaaaa");
wattron(getContainer(), COLOR_PAIR(1));
writeString(x, y, message);
wattroff(getContainer(), COLOR_PAIR(1));
}
void writeString(int x, int y, const char* message) {
mvwprintw(getContainer(), x, y, message);
}
WINDOW* createNewContainer() {
this->container = newwin(height, width, starty, startx);
wrefresh(this->container); /* Show that box */
return getContainer();
}
WINDOW* getContainer() {
return this->container;
}
void refreshContainer() {
refresh();
wrefresh(this->container); /* Show that box */
}
};
int main() {
ColorWindow cw = ColorWindow();
initscr(); /* Start curses mode */
cbreak(); /* Line buffering disabled, Pass on
* everything to me */
keypad(stdscr, TRUE);
start_color();
cw.createNewContainer();
bool success = cw.writeStringWithColor(0, 10, "Hello everyone in color!!");
if(!success)
cw.writeString(0, 10, "Write with color failed :(");
cw.refreshContainer();
sleep(2);
endwin();
return 0;
}
Thanks in Advance.
There are a couple of bugs in your code:
You don't initialize canColor and container, so copying the fields into cw in main has undefined behavior. Fixed by:
ColorWindow() : canColor(false), container(nullptr) {
writeStringWithColor is missing a return statement at the end, also leading to undefined behavior in main. Fixed by:
return true;
at the end of writeStringWithColor.
Your x and y arguments are swapped in the call to mvwprintw in writeString. Fixed by:
mvwprintw(getContainer(), y, x, message);
LINES and COLS are only valid after ncurses is initialized, so your starty and startx values are garbage. Fixed by moving the initialization of cw after the ncurses init code in main:
initscr(); /* Start curses mode */
cbreak(); /* Line buffering disabled, Pass on
* everything to me */
keypad(stdscr, TRUE);
start_color();
ColorWindow cw = ColorWindow();
Full program:
#include <ncurses.h>
#include <string.h>
#include <string>
#include <unistd.h>
#include <iostream>
using namespace std;
class ColorWindow {
private:
bool canColor;
WINDOW* container;
int height, width, startx, starty;
public:
ColorWindow() : canColor(false), container(nullptr) {
if(has_colors()) {
canColor=true;
}
this->height = 20;
this->width = 84;
this->starty = (LINES - height) / 2; /* Calculating for a center placement */
this->startx = (COLS - width) / 2;
}
bool writeStringWithColor(int x, int y, const char* message) {
if(!canColor) {
writeString(3, 5, "Sorry, your term can't show colors.");
return false;
}
init_pair(1, COLOR_RED, COLOR_BLACK);
writeString(0, 10, "aaaaaaaaa");
wattron(getContainer(), COLOR_PAIR(1));
writeString(x, y, message);
wattroff(getContainer(), COLOR_PAIR(1));
return true;
}
void writeString(int x, int y, const char* message) {
mvwprintw(getContainer(), y, x, message);
}
WINDOW* createNewContainer() {
this->container = newwin(height, width, starty, startx);
wrefresh(this->container); /* Show that box */
return getContainer();
}
WINDOW* getContainer() {
return this->container;
}
void refreshContainer() {
refresh();
wrefresh(this->container); /* Show that box */
}
};
int main() {
initscr(); /* Start curses mode */
cbreak(); /* Line buffering disabled, Pass on
* everything to me */
keypad(stdscr, TRUE);
start_color();
ColorWindow cw = ColorWindow();
cw.createNewContainer();
bool success = cw.writeStringWithColor(0, 10, "Hello everyone in color!!");
if(!success)
cw.writeString(0, 10, "Write with color failed :(");
cw.refreshContainer();
sleep(2);
endwin();
return 0;
}
Related
I am coding a terminal based game in c++ using the ncurses library, and I would like the enemy class to be able to access the players location which I can access from within my main function using p->yLoc and p->xLoc, however, I cannot access these variables from within my enemy::move function, as it just returns the error p was not declared in this scope.
Here is my code:
enemy.h:
#ifndef _ENEMY_H_
#define _ENEMY_H_
#include "player.h"
#include <unistd.h>
void Enemy::move(){
int playerY, playerX;
int yMv, xMv;
while (1){
playerY = p->yLoc;
playerX = p->xLoc;
if (playerY > yLoc) {
mvdown();
} else if (playerY < yLoc) {
mvup();
}
if (playerX > xLoc){
mvright();
} else if (playerX < xLoc) {
mvleft();
}
sleep(1);
}
}
player.h
#ifndef _PLAYER_H_
#define _PLAYER_H_
class Player {
public:
Player(WINDOW * win, int y, int x, char c);
void mvup();
void mvdown();
void mvleft();
void mvright();
int getmv();
void display();
int xLoc, yLoc, xMax, yMax;
private:
char character;
WINDOW * curwin;
};
Player::Player(WINDOW * win, int y, int x, char c){
curwin = win;
yLoc = y;
xLoc = x;
getmaxyx(curwin, yMax, xMax);
keypad(curwin, true);
character = c;
}
main.cpp
#include <ncurses.h>
#include "player.h"
#include <string>
#include <ctime>
#include <cstdlib>
#include "enemy.h"
#include <thread>
using namespace std; // I understand this is bad practice
int main(){
// starts ncurses
initscr();
noecho();
cbreak();
int yMax, xMax;
getmaxyx(stdscr, yMax, xMax);
WINDOW * playwin = newwin(20, 50, (yMax/2)-10, 10);
box(playwin, 0, 0);
refresh();
wrefresh(playwin);
if (nodelay (playwin, 1) == ERR) {
// an error has occurred
}
Player * p = new Player(playwin, 1, 1, '#'); // params: (window to display in, starting y, starting x, character to display)
Enemy * e = new Enemy(playwin, 10, 10, 'x'); // same here
thread enemyLoop(&Enemy::move, e); // start the movement for the enemy in a different thread
while(p->getmv() != 'x'){ // leave the game with the key x
// updates the location of the two sprites, and refreshes the window.
p->display();
e->display();
wrefresh(playwin);
}
endwin();
return 0;
}
If Enemy::move is supposed to know about the other players location then you have to pass the player as argument:
void Enemy::move(const Player &p)
e->move(*p);
Then the function can access the players position.
Note: why are you allocating the player and enemy on the heap. there really is no need for that.
I'm making a basic terminal-app engine with ncurses. I have made a basic window class, which stores the window you are working with, and allows you to write on it. Now, I'm doing a ColorWindow class, which inherits from BasicWindow, and should allow you to write with colors. Per abstraction, I'm initializing the color pairs I need in one of the methods of the class, here is the example code:
EDIT: I'm writing here all code
#include <ncurses.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <iomanip>
#include <locale>
#include <sstream>
#include <string>
class ColorWindow : public BasicWindow {
private:
int npairs;
bool canColor;
void initializePairs() {
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
init_pair(4, COLOR_BLUE, COLOR_BLACK);
init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
init_pair(6, COLOR_CYAN, COLOR_BLACK);
init_pair(7, COLOR_WHITE, COLOR_BLACK);
npairs = 8;
}
public:
ColorWindow() {
if(has_colors()) {
initializePairs();
this->canColor = true;
}
else {
this->canColor = false;
}
}
ColorWindow(int he, int wi, int stx, int sty) : BasicWindow(he, wi, stx, sty) {
if(has_colors()) {
initializePairs();
this->canColor = true;
}
else {
this->canColor = false;
}
}
int addColorPair(int foreground, int background) {
if(foreground < 0 || foreground > 7
|| background < 0 || background > 7)
return -1;
init_pair(npairs, foreground, background);
npairs++;
return npairs-1;
}
bool ColorWindow::writeStringWithColor(int x, int y,const char* message, int pair) {
if(!isInLimits(x, y))
return false;
if(pair >= npairs)
return false;
if(canColor)
return false;
wattron(getContainer(), COLOR_PAIR(pair));
mvwprintw(getContainer(), x, y, message);
wattroff(getContainer(), COLOR_PAIR(7));
}
bool changeColor(int color, int r, int g, int b) {
if(!can_change_color())
return false;
if(color < 0 || color > 7 || r < 0 || r > 1000
|| g < 0 || g > 1000 || b < 0 || b > 1000)
return false;
init_color(color, r, g, b);
return true;
}
};
class BasicWindow {
private:
WINDOW* container;
int startx, starty, height, width;
public:
BasicWindow() {
this->height = 20;
this->width = 84;
this->starty = (LINES - height) / 2; /* Calculating for a center placement */
this->startx = (COLS - width) / 2;
}
BasicWindow(int he, int wi, int stx, int sty) {
this->height = he;
this->width = wi;
this->starty = sty;
this->startx = stx;
}
WINDOW* createNewContainer() {
this->container = newwin(height, width, starty, startx);
wrefresh(this->container); /* Show that box */
return getContainer();
}
WINDOW* getContainer() {
return this->container;
}
bool writeString(int x, int y, const char* message) {
if(!isInLimits(x, y))
return false;
mvwprintw(this->container, x, y, message);
}
void refreshContainer() {
refresh();
wrefresh(this->container); /* Show that box */
}
void destroyWindow() {
wborder(this->container, ' ', ' ', ' ',' ',' ',' ',' ',' ');
wrefresh(this->container);
delwin(this->container);
}
bool isInLimits(int x, int y) {
if(x < 0 || x >= this->width-1) {
return false;
}
if(y < 0 || y >= this->height-1) {
return false;
}
return true;
};
And here is the main:
#include <iostream>
#include <unistd.h>
#include <ncurses.h>
#include "windows.h"
int main() {
ColorWindow cw = ColorWindow(20, 80, 0, 0);
initscr(); /* Start curses mode */
cbreak(); /* Line buffering disabled, Pass on
* everty thing to me */
keypad(stdscr, TRUE);
start_color();
WINDOW* container;
container = cw.createNewContainer();
cw.writeStringWithColor(0, 10, "Hello everyone in color!!", 2);
cw.refreshContainer();
sleep(2);
endwin();
return 0;
}
It initializes the color mode, but shows nothing, as if the pair selected was the BLACK BLACK.
Running the code (and tracing it using ncurses' debug-trace feature), there is only one call to init_pair, using a pair number which appears to come from an uninitialized value, e.g., 7978 in this part of the listing:
called {init_pair(0x193e250,7978,5,7)
return }0
and a quick scan of the sources shows that corresponds to this method:
/* PUBLIC METHODS */
int ColorWindow::addColorPair(int foreground, int background) {
if(foreground < 0 || foreground > 7
|| background < 0 || background > 7)
return -1;
init_pair(npairs, foreground, background);
npairs++;
return npairs-1;
}
and npairs is never set to zero in the constructor.
You might find valgrind useful for seeing this type of problem.
#include "std_lib_facilities_4.h"
#include "Window.h"
#include "Graph.h"
#include "GUI.h"
#include <FL/Fl_Image.H>
using namespace Graph_lib;
using namespace std;
struct Lines_window:Graph_lib::Window{
Lines_window(Point xy, int w, int h, const string& title);
Button button_1;
Button button_2;
static void cb_change_color(Address, Address);
static void cb_change_picture(Address, Address);
};
Lines_window::Lines_window(Point xy, int w, int h, const string& title) :
Window(xy, w, h, title),
button_1(Point(x_max()/2, y_max()/2), 200, 100, "Button 1", cb_change_color),
button_2(Point(x_max()/3, y_max()/3), 200, 100, "Button 2", cb_change_picture)
{
attach(button_1);
attach(button_2);
}
void Lines_window::cb_change_color(Address, Address pw)
{
}
void Lines_window::cb_change_picture(Address, Address pw)
{
}
int main()
try {
if(H112 != 201401L)error("Error: incorrect std_lib_facilities_4.h version ", H112);
using namespace Graph_lib;
Lines_window win(Point(100,100),600,400,"Buttons");
return gui_main();
return 0;
}
catch(exception& e) {
cerr << "exception: " << e.what() << '\n';
return 1;
}
catch (...) {
cerr << "Some exception\n";
return 2;
}
This is my button code. I am trying to make two buttons, one that changes color when you press it and another that puts an image on the button when you press it. I haven't made a callback yet because this won't compile. Errors are:
GUI.cpp:16:6: error: prototype for ‘void Graph_lib::Button::attach(Graph_lib::Window&, Fl_Color)’ does not match any in class ‘Graph_lib::Button’
void Button::attach(Window& win, Fl_Color color)
^
In file included from GUI.cpp:10:0:
GUI.h:66:14: error: candidate is: virtual void Graph_lib::Button::attach(Graph_lib::Window&)
void attach(Window&);
^
Do I need callbacks to compile? I am basing this code off of my professor's code here. I took everything out except for the buttons so I could make them. The GUI.cpp and GUI.h were given to us. What am I doing wrong?
GUI.cpp
//
// This is a GUI support code to the chapters 12-16 of the book
// "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
//
#include <FL/Fl.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Output.H>
#include "GUI.h"
namespace Graph_lib {
//------------------------------------------------------------------------------
void Button::attach(Window& win, Fl_Color color)
{
pw = new Fl_Button(loc.x, loc.y, width, height, label.c_str());
pw->color(BLUE);
pw->callback(reinterpret_cast<Fl_Callback*>(do_it), &win); // pass the window
own = &win;
}
//------------------------------------------------------------------------------
int In_box::get_int()
{
Fl_Input& pi = reference_to<Fl_Input>(pw);
// return atoi(pi.value());
const char* p = pi.value();
if (!isdigit(p[0])) return -999999;
return atoi(p);
}
//------------------------------------------------------------------------------
string In_box::get_string()
{
Fl_Input& pi = reference_to<Fl_Input>(pw);
return string(pi.value());
}
//------------------------------------------------------------------------------
void In_box::attach(Window& win)
{
pw = new Fl_Input(loc.x, loc.y, width, height, label.c_str());
own = &win;
}
//------------------------------------------------------------------------------
void Out_box::put(const string& s)
{
reference_to<Fl_Output>(pw).value(s.c_str());
}
//------------------------------------------------------------------------------
void Out_box::attach(Window& win)
{
pw = new Fl_Output(loc.x, loc.y, width, height, label.c_str());
own = &win;
}
//------------------------------------------------------------------------------
int Menu::attach(Button& b)
{
b.width = width;
b.height = height;
switch(k) {
case horizontal:
b.loc = Point(loc.x+offset,loc.y);
offset+=b.width;
break;
case vertical:
b.loc = Point(loc.x,loc.y+offset);
offset+=b.height;
break;
}
selection.push_back(b); // b is NOT OWNED: pass by reference
return int(selection.size()-1);
}
//------------------------------------------------------------------------------
int Menu::attach(Button* p)
{
Button& b = *p;
b.width = width;
b.height = height;
switch(k) {
case horizontal:
b.loc = Point(loc.x+offset,loc.y);
offset+=b.width;
break;
case vertical:
b.loc = Point(loc.x,loc.y+offset);
offset+=b.height;
break;
}
selection.push_back(&b); // b is OWNED: pass by pointer
return int(selection.size()-1);
}
//------------------------------------------------------------------------------
} // of namespace Graph_lib
GUI.h
//
// This is a GUI support code to the chapters 12-16 of the book
// "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
//
#ifndef GUI_GUARD
#define GUI_GUARD
#include "Window.h"
#include "Graph.h"
namespace Graph_lib {
//------------------------------------------------------------------------------
typedef void* Address; // Address is a synonym for void*
typedef void(*Callback)(Address, Address); // FLTK's required function type for all callbacks
//------------------------------------------------------------------------------
template<class W> W& reference_to(Address pw)
// treat an address as a reference to a W
{
return *static_cast<W*>(pw);
}
//------------------------------------------------------------------------------
class Widget {
// Widget is a handle to an Fl_widget - it is *not* an Fl_widget
// We try to keep our interface classes at arm's length from FLTK
public:
Widget(Point xy, int w, int h, const string& s, Callback cb)
: loc(xy), width(w), height(h), label(s), do_it(cb)
{}
virtual void move(int dx,int dy) { hide(); pw->position(loc.x+=dx, loc.y+=dy); show(); }
virtual void hide() { pw->hide(); }
virtual void show() { pw->show(); }
virtual void attach(Window&) = 0;
Point loc;
int width;
int height;
string label;
Callback do_it;
virtual ~Widget() { }
protected:
Window* own; // every Widget belongs to a Window
Fl_Widget* pw; // connection to the FLTK Widget
private:
Widget& operator=(const Widget&); // don't copy Widgets
Widget(const Widget&);
};
//------------------------------------------------------------------------------
struct Button : Widget {
Button(Point xy, int w, int h, const string& label, Callback cb)
: Widget(xy,w,h,label,cb)
{}
void attach(Window&);
};
//------------------------------------------------------------------------------
struct In_box : Widget {
In_box(Point xy, int w, int h, const string& s)
:Widget(xy,w,h,s,0) { }
int get_int();
string get_string();
void attach(Window& win);
};
//------------------------------------------------------------------------------
struct Out_box : Widget {
Out_box(Point xy, int w, int h, const string& s)
:Widget(xy,w,h,s,0) { }
void put(int);
void put(const string&);
void attach(Window& win);
};
//------------------------------------------------------------------------------
struct Menu : Widget {
enum Kind { horizontal, vertical };
Menu(Point xy, int w, int h, Kind kk, const string& label)
: Widget(xy,w,h,label,0), k(kk), offset(0)
{}
Vector_ref<Button> selection;
Kind k;
int offset;
int attach(Button& b); // Menu does not delete &b
int attach(Button* p); // Menu deletes p
void show() // show all buttons
{
for (unsigned int i = 0; i<selection.size(); ++i)
selection[i].show();
}
void hide() // hide all buttons
{
for (unsigned int i = 0; i<selection.size(); ++i)
selection[i].hide();
}
void move(int dx, int dy) // move all buttons
{
for (unsigned int i = 0; i<selection.size(); ++i)
selection[i].move(dx,dy);
}
void attach(Window& win) // attach all buttons
{
for (int i=0; i<selection.size(); ++i) win.attach(selection[i]);
own = &win;
}
};
//------------------------------------------------------------------------------
} // of namespace Graph_lib
#endif // GUI_GUARD
I found the answer. It wouldn't compile because I added an argument to be passed to button (the Fl_Color). I will have to figure out a way to pass a value to the button to change the color.
so i have just started playing with sdl and i have it working fine in a single class but for some reason when i separate things into seperate classes the display opens a insta closes. an ideas ?
Main Class Header
#pragma once
#include <SDL.h>
#include <glew.h>
#include <iostream>
#include "Input.h"
#include "Display.h"
#include "RenderingEngine.h"
#include "PhysicsEngine.h"
class Main
{
public:
Main();
~Main();
/* Engine settings & Engine Controlls*/
void start();
void stop();
void pause(bool value);
void run();
private:
/* Loop Controllers */
bool running;
bool paused;
/* Engine Initialisation */
void initSDL();
RenderingEngine render_core = RenderingEngine(4, 2);
PhysicsEngine physics_core = PhysicsEngine();
Display display = Display("game engine", 900, 900, 900, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
Input input;
};
Main Class
#include "Main.h"
Main::Main()
{
initSDL();
start();
}
Main::~Main()
{
}
void Main::initSDL()
{
SDL_Init(SDL_INIT_EVERYTHING);
}
void Main::start()
{
if (running) return;
running = true;
run();
}
void Main::stop()
{
if (!running) return;
running = false;
exit(0);
}
void Main::pause(bool value)
{
paused = value;
}
void Main::run()
{
while (running)
{
if (!paused)
{
}
render_core.render();
display.swapBackBuffer();
input.update();
}
}
int main(int argc, char *argv[])
{
Main engine;
return 0;
}
Display Header
#pragma once
#include <iostream>
#include <SDL.h>
class Display
{
public:
Display(const char* name, int x, int y, int w, int h, Uint32 flags);
~Display();
void swapBackBuffer();
private:
int x;
int y;
int w;
int h;
const char* name;
Uint32 flags;
SDL_Window *window;
SDL_GLContext opengl;
};
Display Class
#include "Display.h"
Display::Display(const char* n, int x, int y, int w, int h, Uint32 f)
{
this->x = x;
this->y = y;
this->w = w;
this->h = h;
this->name = name;
this->flags = flags;
this->window = SDL_CreateWindow(n, x, y, w, h, f);
this->opengl = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, opengl);
printf("Display: initialised\n\n");
}
Display::~Display()
{
SDL_GL_DeleteContext(opengl);
SDL_DestroyWindow(window);
printf("Display: destroyed\n\n");
}
void Display::swapBackBuffer()
{
SDL_GL_SwapWindow(window);
}
Render Engine class.... there isn't anything important in the header
#include "RenderingEngine.h"
RenderingEngine::RenderingEngine(int major_version, int minor_version)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major_version);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor_version);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
setClearColour(0, 0, 0, 1);
printf("Rendering Engine:: initialised\n\n");
}
RenderingEngine::~RenderingEngine()
{
printf("Rendering Engine:: destroyed\n");
}
void RenderingEngine::setClearColour(float r, float g, float b, float a)
{
glClearColor(r, g, b, a);
}
void RenderingEngine::clearScreen()
{
glClear(GL_COLOR_BUFFER_BIT || GL_DEPTH_BUFFER_BIT);
}
void RenderingEngine::render()
{
clearScreen();
}
input class
#include "Input.h"
Input::Input()
{
printf("Input:: initialised\n");
}
Input::~Input()
{
printf("Input:: destroyed\n");
}
void Input::setMouseVisabilityTo(bool value)
{
if (value) SDL_ShowCursor(1);
else SDL_ShowCursor(0);
}
int Input::getMouseX()
{
return mouseX;
}
int Input::getMouseY()
{
return mouseY;
}
void Input::update()
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
break;
case SDL_KEYDOWN:
keyboard[event.key.keysym.sym] = true;
break;
case SDL_KEYUP:
keyboard[event.key.keysym.sym] = false;
break;
case SDL_MOUSEBUTTONDOWN:
mouse[event.button.button] = true;
break;
case SDL_MOUSEBUTTONUP:
mouse[event.button.button] = false;
break;
case SDL_MOUSEWHEEL:
break;
case SDL_MOUSEMOTION:
mouseX = event.button.x;
mouseY = event.button.y;
break;
}
}
}
i know there are a lot a files so the help will be greatly appreciated, this has been bugging me for a while now
my edited main.h file
#include "Display.h"
#include "RenderingEngine.h"
#include "PhysicsEngine.h"
class Main
{
public:
Main() :
render_core(4, 2),
display("game engine", 900, 900, 900, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL),
physics_core(),
input()
{
running = false;
paused = false;
initSDL();
start();
}
~Main();
/* Engine settings & Engine Controlls*/
void start();
void stop();
void pause(bool value);
void run();
private:
/* Loop Controllers */
bool running;
bool paused;
/* Engine Initialisation */
void initSDL();
RenderingEngine render_core;
PhysicsEngine physics_core;
Display display;
Input input;
Is this some weird version of C++ (e.g. C++11) where you can declare and initialize a non-static member variable all in the same statement?
You should not be doing that kind of thing, the order in which your render context is constructed and initialized in relation to the rest of your software is extremely important. This is what constructors are for.
Assuming you actually are intentionally (ab)using C++11 here, your window closes immediately because this statement:
Display display = Display("game engine", 900, 900, 900, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
Constructs a Display object, and then makes a copy of that object and assigns it to display. After this statement "returns", the original object is destroyed.
And look at what the destructor for Display does:
Display::~Display()
{
SDL_GL_DeleteContext(opengl);
SDL_DestroyWindow(window);
printf("Display: destroyed\n\n");
}
Long story short, do not initialize your members this way. Constructors were perfectly fine before C++11 came along and made life more difficult.
Consider something like the following constructor instead:
Main () : render_core (4, 2),
display ("game engine", 900, 900, 900, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL),
physics_core ()
{
initSDL ();
start ();
}
...
private:
RenderingEngine render_core;
PhysicsEngine physics_core;
Display display;
This solution initializes all of the members of Main when it is constructed, without any copy-assignment and is compatible with a much wider range of C++ compilers. No temporary Display object in this code means that your window is not going to be created and then immediately destroyed.
I am making a program that will draw balls bouncing around the screen. I am currently working on the part that draws the balls.
My code consists of the following:
BallEngine Class - Manage all the Allegro functions/objects
BallManager Class - Manages all of the balls
Ball Class - Hold information about a ball
main.cpp - Game loop, etc.
BallEngine.h:
#pragma once
#include <allegro5\allegro.h>
#include <allegro5\allegro_font.h>
#include <allegro5\allegro_ttf.h>
#include "BallManager.h"
ALLEGRO_DISPLAY *display;
class BallEngine
{
private:
bool fullScreen;
int fps;
bool running;
public:
BallManager BManager;
bool getFullScreen();
bool getIsRunning();
void updateFullScreen();
void setFullScreen(bool value);
void setIsRunning(bool value);
int getFPS();
//Allegro Objects
ALLEGRO_FONT *deafault_font_12;
ALLEGRO_EVENT_QUEUE *event_queue;
ALLEGRO_TIMER *timer;
//Colors
ALLEGRO_COLOR RED;
ALLEGRO_COLOR GREEN;
ALLEGRO_COLOR BLUE;
ALLEGRO_COLOR YELLOW;
ALLEGRO_COLOR PINK;
ALLEGRO_COLOR LIGHT_BLUE;
ALLEGRO_COLOR WHITE;
ALLEGRO_COLOR BLACK;
//Debug
bool showDebug;
void drawBallInfo(int x, int y, int id); //Draws information about a certain ball
BallEngine(int width, int height);
~BallEngine(void);
};
BallEngine.cpp:
#include "BallEngine.h"
BallEngine::BallEngine(int width, int height)
{
running = true;
showDebug = false;
fps = 60;
al_init();
if(!al_init())
{
printf("Failed to initalize Allegro \n");
}
al_install_keyboard();
if(!al_install_keyboard())
{
printf("Failed to initalize keyboard \n");
}
al_init_font_addon();
al_init_ttf_addon();
fullScreen = false;
updateFullScreen();
deafault_font_12 = al_load_font("arial.ttf", 12, 0);
event_queue = al_create_event_queue();
al_register_event_source(event_queue, al_get_keyboard_event_source());
timer = al_create_timer(1.0/fps);
al_register_event_source(event_queue, al_get_timer_event_source(timer));
display = al_create_display(width, height);
//Define engine colors
RED = al_map_rgb(255,0,0);
GREEN = al_map_rgb(0,255,0);
BLUE = al_map_rgb(0,0,255);
YELLOW = al_map_rgb(255,255,0);
PINK = al_map_rgb(255,0,255);
LIGHT_BLUE = al_map_rgb(255,255,0);
WHITE = al_map_rgb(255,255,255);
BLACK = al_map_rgb(0,0,0);
}
BallEngine::~BallEngine(void)
{
}
bool BallEngine::getFullScreen()
{
return fullScreen;
}
bool BallEngine::getIsRunning()
{
return running;
}
void BallEngine::updateFullScreen()
{
if ( fullScreen == true )
{
al_set_new_display_flags(ALLEGRO_FULLSCREEN);
}
else
{
al_set_new_display_flags(ALLEGRO_WINDOWED);
}
}
void BallEngine::setFullScreen(bool value)
{
fullScreen = value;
}
int BallEngine::getFPS()
{
return fps;
}
void BallEngine::drawBallInfo(int x, int y, int id)
{
if(BManager.isBallExist(id))
{
al_draw_textf(deafault_font_12, RED, x, y, 0, "X: %i Y: %i Velocity: %i Angle: %i Radius: %i Color %ALLEGRO_COLOR ", BManager.getBall_X(id), BManager.getBall_Y(id), BManager.getBall_Velocity(id), BManager.getBall_Angle(id), BManager.getBall_Radius(id), BManager.getBall_Color(id));
}
else
{
printf("Failed to draw ball %i information: Ball selceted out of range \n", id);
}
}
BallManager.h:
#pragma once
#include <iostream>
#include <vector>
#include "Ball.h"
#include <allegro5\allegro.h>
class BallManager
{
private:
std::vector<Ball*> List;
public:
//Get functions
int getBall_X(int id);
int getBall_Y(int id);
int getBall_Velocity(int id);
int getBall_Angle(int id);
int getBall_Radius(int id);
ALLEGRO_COLOR getBall_Color(int id);
//Set Functions
void setBall_X(int id, int value);
void setBall_Y(int id, int value);
void setBall_Velocity(int id, int value);
void setBall_Angle(int id, int value);
void setBall_Radius(int id, int value);
void setBall_Color(int id, ALLEGRO_COLOR value);
bool isBallExist(int id); //Returns true if a ball at index id exists. Else returns false.
void CreateBall(int x, int y, int velocity, int angle, int radius, ALLEGRO_COLOR color);
void DeleteBall(int id);
void drawBall(int id);
void drawBalls();
void updateBalls(); //NOT YET DONE
BallManager(void);
~BallManager(void);
};
BallManager.cpp:
#include "BallManager.h"
BallManager::BallManager(void)
{
}
BallManager::~BallManager(void)
{
}
//Get Functions:
int BallManager::getBall_X(int id)
{
return List[id]->getPos_X();
}
int BallManager::getBall_Y(int id)
{
return List[id]->getPos_Y();
}
int BallManager::getBall_Velocity(int id)
{
return List[id]->getVelocity();
}
int BallManager::getBall_Angle(int id)
{
return List[id]->getAngle();
}
int BallManager::getBall_Radius(int id)
{
return List[id]->getRadius();
}
ALLEGRO_COLOR BallManager::getBall_Color(int id)
{
return List[id]->getColor();
}
//Set functions:
void BallManager::setBall_X(int id, int value)
{
List[id]->setPos_X(value);
}
void BallManager::setBall_Y(int id, int value)
{
List[id]->setPos_Y(value);
}
void BallManager::setBall_Velocity(int id, int value)
{
List[id]->setVelocity(value);
}
void BallManager::setBall_Angle(int id, int value)
{
List[id]->setAngle(value);
}
void BallManager::setBall_Radius(int id, int value)
{
List[id]->setRadius(value);
}
void BallManager::setBall_Color(int id, ALLEGRO_COLOR value)
{
List[id]->setColor(value);
}
void BallManager::CreateBall(int x, int y, int velocity, int angle, int radius, ALLEGRO_COLOR color)
{
Ball* ball = new Ball(x, y, velocity, angle, radius, color);
List.push_back(ball);
}
void BallManager::DeleteBall(int id)
{
if(isBallExist(id))
{
delete List[id];
List.erase(List.begin()+id);
}
else
{
printf("Failed to delete ball %i information: Ball selceted out of range \n", id);
}
}
bool BallManager::isBallExist(int id)
{
if((id+1) > List.size() || id < 0)
{
return false;
}
return true;
}
void BallManager::drawBall(int id)
{
List[id]->Draw();
}
void BallManager::drawBalls()
{
int total = List.size();
for(int index = 0; index < total; index++)
{
List[index]->Draw();
}
}
void updateBalls()
{
//TODO
}
Ball.h:
#pragma once
#include <allegro5\allegro.h>
#include <allegro5\allegro_primitives.h>
class Ball
{
private:
int x;
int y;
int velocity; //Positive is left side of screen, Negitive is right side of screen
int angle; // Angle derived from the positive vertical
int radius;
ALLEGRO_COLOR color;
public:
//Get Functions
int getPos_X();
int getPos_Y();
int getVelocity();
int getAngle();
int getRadius();
ALLEGRO_COLOR getColor();
//Set Functions
void setPos_X(int value);
void setPos_Y(int value);
void setVelocity(int value);
void setAngle(int value);
void setRadius(int value);
void setColor(ALLEGRO_COLOR value);
//Draws to screen
void Draw();
//Constructor
Ball(int Start_X, int Start_Y, int Start_Velocity, int Start_Angle, int Start_Radius, ALLEGRO_COLOR Start_Color);
//Desctructor
~Ball(void);
};
Ball.cpp:
#include "Ball.h"
Ball::Ball(int Start_X, int Start_Y, int Start_Velocity, int Start_Angle, int Start_Radius, ALLEGRO_COLOR Start_Color)
{
x = Start_X;
y = Start_Y;
velocity = Start_Velocity;
angle = Start_Angle;
radius = Start_Radius;
color = Start_Color;
}
Ball::~Ball(void)
{
}
//Get functions
int Ball::getPos_X()
{
return x;
}
int Ball::getPos_Y()
{
return y;
}
int Ball::getVelocity()
{
return velocity;
}
int Ball::getAngle()
{
return angle;
}
int Ball::getRadius()
{
return radius;
}
ALLEGRO_COLOR Ball::getColor()
{
return color;
}
//Set functions
void Ball::setPos_X(int value)
{
x = value;
}
void Ball::setPos_Y(int value)
{
y = value;
}
void Ball::setVelocity(int value)
{
velocity = value;
}
void Ball::setAngle(int value)
{
angle = value;
}
void Ball::setRadius(int value)
{
radius = value;
}
void Ball::setColor(ALLEGRO_COLOR value)
{
color = value;
}
void Ball::Draw()
{
al_draw_filled_circle(x, y, radius, color);
}
main.cpp:
#include <allegro5\allegro.h>
#include "BallEngine.h"
int ScreenWidth = 620;
int ScreenHeight = 480;
int main()
{
BallEngine Engine(ScreenWidth, ScreenHeight);
//Test balls
Engine.BManager.CreateBall(10, 20, 0, 0, 5, al_map_rgb(0,0,255));
Engine.BManager.CreateBall(11, 21, 1, 1, 5, al_map_rgb(0,0,255));
Engine.BManager.CreateBall(12, 22, 2, 2, 5, al_map_rgb(0,0,255));
Engine.BManager.CreateBall(13, 23, 3, 3, 5, al_map_rgb(0,0,255));
ALLEGRO_EVENT events;
int selected = 0; //Used to show which ball is selected
al_start_timer(Engine.timer);
while(Engine.getIsRunning())
{
al_wait_for_event(Engine.event_queue, &events);
if(events.type == ALLEGRO_EVENT_KEY_DOWN)
{
//Keyboard Input
switch(events.keyboard.keycode)
{
case ALLEGRO_KEY_ESCAPE:
Engine.setIsRunning(false);
break;
case ALLEGRO_KEY_RIGHT:
Engine.showDebug = !Engine.showDebug; //Toggles the selected balls info
break;
case ALLEGRO_KEY_UP:
selected++;
break;
case ALLEGRO_KEY_DOWN:
selected--;
break;
case ALLEGRO_KEY_DELETE:
Engine.BManager.DeleteBall(selected); //Deletes a certain ball
break;
}
}
else if(events.type == ALLEGRO_EVENT_TIMER)
{
//Update
}
//Draw
Engine.BManager.drawBalls();
//Show debug
if(Engine.showDebug == true)
{
Engine.drawBallInfo(10, 10, selected);
}
al_flip_display();
al_clear_to_color(al_map_rgb(0,0,0));
}
return 0;
}
I am having trouble drawing the balls. In allegro 4, you would pass a buffer on which to draw to and then draw the buffer to the screen. With the code that I have above I am getting an error at the draw() function in the Ball class.
The error reads:
Debug Error!
R6010 -abort() has been called
I also get some information the command prompt:
Assertion failed: addon_initialized, file allegro-git\addons\primitives\primitives.c, line 79
I think I am getting an error because the draw function doesn't have anywhere to draw to because the display was created in the BallEngine Class, but how do I fix it?
Assertion failed: addon_initialized, file allegro-git\addons\primitives\primitives.c, line 79
That's precisely the problem. You haven't initialized the primitives addon.
al_init_primitives_addon()
Also, you should use forward slashes as part of paths (e.g., <allegro5/allegro.h>) because it is cross platform.