I have some experience with OOP but I'm very new to game programming. For a school assignment, here's my first attempt at making a "real" game containing Entities, a game engine, game loop and the likes. I wanted to know if I used those concepts correctly, and would love to have some tips on things I could've done better.
Here's the Entities
header, from which everything is derived (players, enemies, asteroids...):
#ifndef A_ENTITIES_HPP
# define A_ENTITIES_HPP
#include <iostream>
#include <ncurses.h>
class AEntities
{
private:
AEntities(void);
protected:
WINDOW const * _win;
int _x, _y;
int _winwidth, _winheight;
char _char;
AEntities * _missile;
public:
AEntities(WINDOW const * win, int x, int y, char caract);
virtual ~AEntities(void);
AEntities(AEntities const &src);
AEntities &operator=(AEntities const & rhs);
virtual void movement() = 0;
virtual void update(void) = 0;
void draw(void);
void del(void);
bool impact(AEntities * entities) const;
int getX(void) const;
int getY(void) const;
char getChar(void) const;
AEntities * getMissile(void) const;
static int loopCount;
};
#endif
And here's the GameEngine
which contains a array of pointers to Entities
, for their management:
AEntities * _entities[ENTITIES_MAX];
Code:
#include "GameEngine.hpp"
GameEngine::GameEngine(void)
{
initscr();
nodelay(stdscr, TRUE);
keypad(stdscr, TRUE);
noecho();
curs_set(0);
getmaxyx(stdscr, _winheight, _winwidth);
_p1 = new Player(stdscr, 0, _winheight / 2);
_win = stdscr;
for (int i = 0; i < ENTITIES_MAX; i++)
_entities[i] = 0;
}
GameEngine::~GameEngine(void)
{
delete _p1;
endwin();
}
GameEngine::GameEngine(GameEngine const &src) :
_win(src._win), _winheight(src._winheight), _winwidth(src._winwidth)
{
}
GameEngine &GameEngine::operator=(GameEngine const &src)
{
if (this != &src)
_win = src._win;
return (*this);
}
bool GameEngine::render(void)
{
usleep(15000);
mvprintw(0, 0, "Score: %d", _score);
if (AEntities::loopCount == 101)
AEntities::loopCount = 0;
AEntities::loopCount++;
if (rand() % GameEngine::i < 5)
{
int enY = rand() % _winheight;
addEntity(new Enemy(_win, _winwidth, (enY == 0) ? 1 : enY));
if (GameEngine::i == EASY)
{
int asY = rand() % _winheight;
addEntity(new Asteroids(_win, _winwidth, (asY == 0) ? 1 : asY));
}
}
if (!updateEntities())
return (false);
drawEntities();
refresh();
return (true);
}
AEntities * GameEngine::getEntity(int n) const
{
return (_entities[n]);
}
void GameEngine::drawEntities(void)
{
_p1->draw();
for (int i = 0; i < ENTITIES_MAX; i++)
{
if (_entities[i] != 0)
_entities[i]->draw();
}
}
void GameEngine::addEntity(AEntities * entity)
{
for (int i = 0; i < ENTITIES_MAX; i++)
{
if (_entities[i] == 0)
{
_entities[i] = entity;
return ;
}
}
}
bool GameEngine::updateEntities(void)
{
_p1->del();
_p1->update();
addEntity(_p1->getMissile());
// _p1->draw();
for (int i = 0; i < ENTITIES_MAX; i++)
{
if (_entities[i])
{
_entities[i]->del();
_entities[i]->update();
// _entities[i]->draw();
addEntity(_entities[i]->getMissile());
}
}
if (playerColision())
return (false);
entityColision();
return (true);
}
void GameEngine::deleteEntity(AEntities * entity)
{
for (int i = 0; i < ENTITIES_MAX; i++)
{
if ( _entities[i] == entity)
{
if (_entities[i])
delete _entities[i];
_entities[i] = 0;
return ;
}
}
}
bool GameEngine::playerColision(void)
{
for (int i = 0; i < ENTITIES_MAX; i++)
{
if (_entities[i] && _p1->impact(_entities[i]))
return (true);
}
return (false);
}
void GameEngine::entityColision(void)
{
for (int i = 0; i < ENTITIES_MAX; i++)
{
for (int j = i + 1; j < ENTITIES_MAX; j++)
{
if (_entities[i] && _entities[j] && _entities[i]->impact(_entities[j]))
{
_score += 50;
deleteEntity(_entities[i]);
deleteEntity(_entities[j]);
}
}
if (_entities[i] && (_entities[i]->getX() < 0 || _entities[i]->getX() > 3000))
{
_score -= 10;
deleteEntity(_entities[i]);
}
}
}
WINDOW const * GameEngine::getWindow(void) const
{
return (_win);
}
int GameEngine::i = EASY;
int GameEngine::_score = 0;
PS: The graphical part is handled with ncurses, but really, all there is to it is mvaddch(y, x, char)
, which draws a char
at the specified position (yes, y first). Also, the GameEngine::i static
thing for managing difficulty wasn't made by me, and it could indeed be improved by at least passing it to a GameEngine
constructor.