I've fixed the bugs with the animations. (And I've updated them on the got-hub)
For the second one it wasn't a bug I've just forgot to replace the animation in the gridmap when the caracter moved.
But now everything is fine :
Here is an example of source code which moves a caracter accross the map.
#ifndef MY_APPLI
#define MY_APPLI
#include "ODFAEG/Core/application.h"
#include "ODFAEG/Graphics/2D/tile.h"
#include "ODFAEG/Graphics/2D/map.h"
#include "ODFAEG/Graphics/2D/world.h"
#include "ODFAEG/Graphics/2D/decor.h"
#include "ODFAEG/Graphics/2D/anim.h"
#include "ODFAEG/Graphics/2D/ambientLight.h"
#include "ODFAEG/Core/actionMap.h"
#include "ODFAEG/Core/system.h"
#include "ODFAEG/Core/entitiesUpdater.h"
#include "ODFAEG/Core/animationUpdater.h"
#include "caracter.h"
using namespace odfaeg;
using namespace sf;
class MyAppli : public Application {
private :
const int speed = 50;
Map* theMap;
Clock realTime;
EntitiesUpdater *eu;
RenderTexture *renderTextShadows, *renderTextLights;
RectangleShape rect;
AnimUpdater *au;
bool running;
ActionMap *closedAction;
ActionMap *moveAction;
Listener listener;
TileGround *tg;
Wall *w;
Caracter* caracter;
sf::Keyboard::Key actualKey, previousKey;
enum TEXTURES {
GRASS, WALLS, HOUSE, FIRE1, FIRE2, FIRE3
};
public :
MyAppli(sf::RenderWindow &window) : Application (window) {
running = false;
actualKey = sf::Keyboard::Unknown;
previousKey = sf::Keyboard::Unknown;
}
void close () {
getWindow().close();
}
void keyHeldDown (sf::Keyboard::Key key, sf::Time elapsedTime) {
sf::View view = getWindow().getView();
float t = speed * elapsedTime.asSeconds();
if (actualKey != sf::Keyboard::Key::Unknown && key == sf::Keyboard::Key::Z) {
view.move (0, -t);
if (!caracter->isMoving()) {
if (actualKey != previousKey) {
Vec2f dir(0, -1);
caracter->setDir(dir);
}
caracter->setMoving(true);
}
theMap->moveEntity(caracter, caracter->getDir().x * t, caracter->getDir().y * t);
} else if (actualKey != sf::Keyboard::Key::Unknown && key == sf::Keyboard::Key::Q) {
view.move (-t, 0);
if (!caracter->isMoving()) {
if (actualKey != previousKey) {
Vec2f dir(-1, 0);
caracter->setDir(dir);
}
caracter->setMoving(true);
}
theMap->moveEntity(caracter, caracter->getDir().x * t, caracter->getDir().y * t);
} else if (actualKey != sf::Keyboard::Key::Unknown && key == sf::Keyboard::Key::S) {
view.move (0, t);
if (!caracter->isMoving()) {
if (actualKey != previousKey) {
Vec2f dir(0, 1);
caracter->setDir(dir);
}
caracter->setMoving(true);
}
theMap->moveEntity(caracter, caracter->getDir().x * t, caracter->getDir().y * t);
} else if (actualKey != sf::Keyboard::Key::Unknown && key == sf::Keyboard::Key::D) {
view.move (t, 0);
if (!caracter->isMoving()) {
if (actualKey != previousKey) {
Vec2f dir(1, 0);
caracter->setDir(dir);
}
caracter->setMoving(true);
}
theMap->moveEntity(caracter, caracter->getDir().x * t, caracter->getDir().y * t);
}
getWindow().setView(view);
renderTextShadows->setView(view);
renderTextLights->setView(view);
BoundingRectangle br (view.getCenter().x - view.getSize().x * 0.5f, view.getCenter().y - view.getSize().y * 0.5f, view.getSize().x, view.getSize().y);
eu->setViewRect(br);
au->setViewRect(br);
eu->update();
}
bool mouseInside (Vector2f mousePos) {
BoundingRectangle br (0, 0, 100, 100);
if (br.isPointInside(Vec2f(mousePos.x, mousePos.y))) {
return true;
}
return false;
}
void onMouseInside (Vector2f mousePos) {
std::cout<<"Mouse inside : "<<mousePos.x<<" "<<mousePos.y<<std::endl;
}
void load() {
ResourceManager<sf::Texture, TEXTURES>* tm = new ResourceManager<sf::Texture, TEXTURES>();
tm->fromFile("tilesets/herbe.png", GRASS);
tm->fromFile("tilesets/murs.png", WALLS);
tm->fromFile("tilesets/maison.png", HOUSE);
tm->fromFile("tilesets/flemmes1.png", FIRE1);
tm->fromFile("tilesets/flemmes2.png", FIRE2);
tm->fromFile("tilesets/flemmes3.png", FIRE3);
getCache().addResourceManager(*tm, "TextureManager");
}
void init () {
addClock(realTime, "RealTime");
TextureManager<TEXTURES> &tm = getCache().resourceManager<sf::Texture, TEXTURES>("TextureManager");
View view = getWindow().getDefaultView();
view.setCenter(0, 0);
Vec2f pos (view.getCenter().x - view.getSize().x * 0.5f, view.getCenter().y - view.getSize().y * 0.5f);
BoundingRectangle rect (pos.x, pos.y, view.getSize().x, view.getSize().y);
theMap = new Map (100, 50, 30, "Map test");
eu = new EntitiesUpdater(theMap, rect);
eu->launch();
au = new AnimUpdater(theMap, rect);
au->setInterval(seconds(0.01f));
au->start();
std::vector<Tile*> tiles;
std::vector<Tile*> walls;
tiles.push_back(new Tile(tm.getResourceByAlias(GRASS), Vec2f(0, 0), Vec2f(120, 60),IntRect(0, 0, 100, 50), 0));
walls.push_back(new Tile(tm.getResourceByAlias(WALLS), Vec2f(0, 0), Vec2f(100, 100), IntRect(100, 0, 100, 100), 1));
walls.push_back(new Tile(tm.getResourceByAlias(WALLS), Vec2f(0, 0), Vec2f(100, 100), IntRect(100, 100, 100, 100), 1));
walls.push_back(new Tile(tm.getResourceByAlias(WALLS), Vec2f(0, 0), Vec2f(100, 100), IntRect(100, 200, 100, 100), 1));
walls.push_back(new Tile(tm.getResourceByAlias(WALLS), Vec2f(0, 0), Vec2f(100, 100), IntRect(100, 300, 100, 100), 1));
walls.push_back(new Tile(tm.getResourceByAlias(WALLS), Vec2f(0, 0), Vec2f(100, 100), IntRect(100, 400, 100, 100), 1));
walls.push_back(new Tile(tm.getResourceByAlias(WALLS), Vec2f(0, 0), Vec2f(100, 100), IntRect(100, 500, 100, 100), 1));
BoundingRectangle mapZone(0, 0, 1000, 1000);
theMap->generate_map<TEXTURES>(tiles, walls, mapZone);
Decor* decor = new Decor(new Tile(tm.getResourceByAlias(HOUSE), Vec2f(0, 0), Vec2f(250, 300), IntRect(0, 0, 250, 300), 2), AmbientLight::getAmbientLight());
decor->setPosition(Vec2f(100, 100));
decor->setShadowCenter(Vec2f(0, 60));
decor->changeGravityCenter(Vec2f(50, 50));
theMap->addEntity<TEXTURES>(decor);
PonctualLight* light = new PonctualLight(Vec2f(50, 150),100,50,0,200,sf::Color(255,255,0),16,0);
theMap->addEntity<TEXTURES>(light);
Anim* fire = new Anim(0.1f, Vec2f(0, 100), Vec2f(100, 100), 2);
Decor *fire1 = new Decor(new Tile(tm.getResourceByAlias(FIRE1), Vec2f(0, 100), Vec2f(100, 100), IntRect(0, 0, 150, 200), 2), AmbientLight::getAmbientLight());
Decor *fire2 = new Decor(new Tile(tm.getResourceByAlias(FIRE2), Vec2f(0, 100), Vec2f(100, 100), IntRect(0, 0, 150, 200), 2), AmbientLight::getAmbientLight());
Decor *fire3 = new Decor(new Tile(tm.getResourceByAlias(FIRE3), Vec2f(0, 100), Vec2f(100, 100), IntRect(0, 0, 150, 200), 2), AmbientLight::getAmbientLight());
fire1->setShadowCenter(Vec2f(80, 100));
fire2->setShadowCenter(Vec2f(80, 100));
fire3->setShadowCenter(Vec2f(80, 100));
fire1->changeGravityCenter(Vec2f(50, 50));
fire2->changeGravityCenter(Vec2f(50, 50));
fire3->changeGravityCenter(Vec2f(50, 50));
fire->addEntity(fire1);
fire->addEntity(fire2);
fire->addEntity(fire3);
fire->play(true);
theMap->addEntity<TEXTURES>(fire);
au->addAnim(fire);
Tile *t = new Tile(walls[3]->getTexture(), walls[3]->getPosition(), walls[3]->getSize(), walls[3]->getTextureRect(),3);
w = new Wall(3, 80, t,AmbientLight::getAmbientLight());
w->setPosition (Vec2f (0, 130));
theMap->addEntity<TEXTURES>(w);
caracter = new Caracter("Sorrok", "Nagi", "M", "Map test", "Brain", "Green", "White","Normal","Novice", 1);
std::string path = "tilesets/tilesetnovice.png";
getCache().resourceManager<sf::Texture, TEXTURES>("TextureManager").fromFile(path);
const Texture *text = getCache().resourceManager<sf::Texture, TEXTURES>("TextureManager").getResourceByPath(path);
int textRectX = 0, textRectY = 0, textRectWidth = 50, textRectHeight = 100;
int textWidth = text->getSize().x;
int textHeight = text->getSize().y;
for (unsigned int i = 0; i < 24; i+=3) {
Anim* animation = new Anim(0.1f, Vec2f(0, 0), Vec2f(50, 100), 0);
for (unsigned int j = 0; j < 3; j++) {
IntRect textRect (textRectX, textRectY, textRectWidth, textRectHeight);
Tile *tile = new Tile(text, Vec2f(0, 0), Vec2f(textRectWidth, textRectHeight), textRect, 0);
Decor *decor = new Decor(tile, odfaeg::AmbientLight::getAmbientLight());
decor->setShadowCenter(Vec2f(80, 130));
decor->changeGravityCenter(Vec2f(50, 50));
textRectX += textRectWidth;
if (textRectX + textRectWidth > textWidth) {
textRectX = 0;
textRectY += textRectHeight;
}
animation->addEntity(decor);
}
caracter->addAnimation(animation);
au->addAnim(animation);
}
theMap->addEntity<TEXTURES>(caracter);
theMap->computeIntersectionsWithWalls();
renderTextShadows = new RenderTexture();
renderTextShadows->create(view.getSize().x, view.getSize().y);
renderTextLights = new RenderTexture();
renderTextLights->create(view.getSize().x, view.getSize().y);
renderTextShadows->setView(view);
renderTextLights->setView(view);
MemberFunction<void(MyAppli*)> f1 (&MyAppli::close);
closedAction = new ActionMap(f1, this);
Action *a = new Action(Action::EVENT_TYPE::CLOSED);
closedAction->addAction(a);
MemberFunction<void(MyAppli*, sf::Keyboard::Key, sf::Time)> b2 (&MyAppli::keyHeldDown);
moveAction = new ActionMap(b2, this, sf::Keyboard::Key::Unknown, realTime.restart());
Action *a1 = new Action (Action::EVENT_TYPE::KEY_HELD_DOWN, sf::Keyboard::Key::Z);
Action *a2 = new Action (Action::EVENT_TYPE::KEY_HELD_DOWN, sf::Keyboard::Key::Q);
Action *a3 = new Action (Action::EVENT_TYPE::KEY_HELD_DOWN, sf::Keyboard::Key::S);
Action *a4 = new Action (Action::EVENT_TYPE::KEY_HELD_DOWN, sf::Keyboard::Key::D);
Action *combined = new Action(*a1 || *a2 || *a3 || *a4);
moveAction->addAction(combined);
MemberFunction<bool(MyAppli*, sf::Vector2f)> trigFunc (&MyAppli::mouseInside);
MemberFunction<void(MyAppli*, sf::Vector2f)> onTrigFunc(&MyAppli::onMouseInside);
listener.connect("MouseInside", this, trigFunc, this, onTrigFunc, Vector2f(-1, -1));
listener.connectAction("CloseAction", closedAction);
listener.connectAction("MoveAction", moveAction);
listener.listen();
getWindow().setView(view);
eu->update();
}
void render() {
if (getWindow().isOpen())
{
// clear the window with black color
getWindow().clear(sf::Color::Black);
std::vector<Entity*> entities = theMap->getVisibleEntities("E_TILE");// draw everything here...
for (unsigned int i = 0; i < entities.size(); i++) {
getWindow().draw(*entities[i]);
}
entities = theMap->getVisibleEntities("E_WALL+E_DECOR+E_ANIMATION+E_CARACTER");
renderTextShadows->clear(Color::White);
for (unsigned int i = 0; i < entities.size(); i++) {
if (entities[i]->getType() == "E_SHADOW_WALL" || entities[i]->getType() == "E_SHADOW_TILE") {
renderTextShadows->draw(*entities[i]);
}
}
View view = getWindow().getView();
Vector2f v2 (view.getCenter().x - view.getSize().x * 0.5f, view.getCenter().y - view.getSize().y * 0.5f);
rect.setPosition(Vector2f(v2.x, v2.y));
rect.setFillColor((Color(100, 100, 100, 128)));
rect.setSize(Vector2f (view.getSize().x, view.getSize().y));
renderTextShadows->draw(rect, RenderStates(BlendAdd));
renderTextShadows->display();
Sprite shadows (renderTextShadows->getTexture());
shadows.setPosition(v2.x, v2.y);
getWindow().draw (shadows, RenderStates(BlendMultiply));
for (unsigned int i = 0; i < entities.size(); i++) {
if (entities[i]->getType() == "E_TILE")
getWindow().draw(*entities[i]);
}
for (int n = 0; n < w->getSegments().size(); n++) {
Segment *s = w->getSegments()[n];
VertexArray line(Lines, 2);
Vertex origin, ext;
origin.position = Vector2f (s->getOrig().x, s->getOrig().y);
ext.position = Vector2f(s->getExt().x, s->getExt().y);
origin.color = Color(255,0,0);
ext.color = Color(255, 0, 0);
line.append(origin);
line.append(ext);
getWindow().draw(line);
}
AmbientLight::getAmbientLight()->setColor(Color(255, 255, 255));
Color ambientColor = AmbientLight::getAmbientLight()->getColor();
renderTextLights->clear(ambientColor);
entities = theMap->getVisibleEntities("E_PONCTUAL_LIGHT");
for (unsigned int i = 0; i < entities.size(); i++) {
renderTextLights->draw(*entities[i], BlendAdd);
}
renderTextLights->display();
Sprite lights (renderTextLights->getTexture());
lights.setPosition(v2.x, v2.y);
getWindow().draw(lights, BlendMultiply);
// end the current frame
getWindow().display();
}
realTime.restart();
}
void update () {
// check all the window's events that were triggered since the last iteration of the loop
sf::Event event;
if (getWindow().pollEvent(event))
{
listener.pushEvent(event);
if (event.type == sf::Event::Closed) {
running =false;
}
if (event.type == sf::Event::KeyPressed) {
previousKey = actualKey;
actualKey = event.key.code;
listener.setActionParams<MyAppli, sf::Keyboard::Key, sf::Time>("MoveAction", this, event.key.code,realTime.restart());
}
if (event.type == sf::Event::KeyReleased) {
caracter->setMoving(false);
previousKey = event.key.code;
actualKey = sf::Keyboard::Key::Unknown;
}
if (event.type == sf::Event::MouseMoved) {
Vector2f mousePos = Vector2f(event.mouseMove.x, event.mouseMove.y);
listener.setParams<MyAppli, MyAppli, Vector2f> ("MouseInside", this, this, mousePos);
}
}
}
void stop() {
running = false;
}
int exec () {
load();
init();
running = true;
while (running) {
render();
update();
}
return EXIT_SUCCESS;
}
~MyAppli () {
}
};
#endif // MY_APPLI
And the carater which is a custom entity :
#include "caracter.h"
using namespace std;
using namespace sf;
Caracter::Caracter (string factionName, string pseudo, string sex, string currentMapName, string hairColor,
string eyesColor, string skinColor, string faceType, string classs, int level) : AnimatedEntity (Vec2f(-50, -25), Vec2f (100, 50), Vec2f(50, 25), 0, "E_CARACTER") {
currentAnimIndex = 0;
this->factionName = factionName;
this->pseudo = pseudo;
this->sex = sex;
this->currentMapName = currentMapName;
this->hairColor = hairColor;
this->eyesColor = eyesColor;
this->faceType = faceType;
this->skinColor = skinColor;
this->classs = classs;
this->level = level;
currentPointIndex = 0;
speed = 100;
moving = false;
dir = Vec2f(0, 1);
this->life = 100;
this->maxLife = 100;
range = 50;
attackSpeed = 1.f;
attack = 10;
fightingMode = attacking = false;
alive = true;
xp = 0;
xpReqForNextLevel = 1500;
regenHpSpeed = 1.f;
regenHpAmount = 1;
}
void Caracter::onMove(Vec2f& d) {
for (unsigned int i = 0; i < anims.size(); i++) {
anims[i]->move(d);
}
}
float Caracter::getRegenHpSpeed () {
return regenHpSpeed;
}
void Caracter::setRegenHpSpeed(float regenHpSpeed) {
this->regenHpSpeed = regenHpSpeed;
}
Time Caracter::getTimeOfLastHpRegen() {
return clockRegenHp.getElapsedTime();
}
void Caracter::setLevel(int level) {
this->level = level;
}
void Caracter::setCurrentXp(int xp) {
this->xp = xp;
}
void Caracter::setXpReqForNextLevel(int xpReqForNextLevel) {
this->xpReqForNextLevel = xpReqForNextLevel;
}
void Caracter::up (int xp) {
this->xp += xp;
if (this->xp >= xpReqForNextLevel) {
level++;
this->xp = this->xp - xpReqForNextLevel;
xpReqForNextLevel *= 2;
}
}
int Caracter::getCurrentXp () {
return xp;
}
int Caracter::getXpReqForNextLevel () {
return xpReqForNextLevel;
}
void Caracter::setSpeed(int speed) {
this->speed;
}
int Caracter::getSpeed() {
return speed;
}
int Caracter::getRegenHpAmount() {
return regenHpAmount;
}
void Caracter::setRegenHpAmount(int regenHpAmount) {
this->regenHpAmount = regenHpAmount;
}
void Caracter::setLife(int life) {
this->life = life;
clockRegenHp.restart();
}
int Caracter::getLife() {
return life;
}
void Caracter::setRange(int range) {
this->range = range;
}
int Caracter::getRange() {
return range;
}
void Caracter::setAttackSpeed (float attackSpeed) {
this->attackSpeed = attackSpeed;
}
float Caracter::getAttackSpeed () {
return attackSpeed;
}
void Caracter::setAttacking(bool b) {
this->attacking = b;
}
void Caracter::setAlive(bool b) {
alive = b;
}
bool Caracter::isAlive () {
return alive;
}
bool Caracter::isAttacking() {
return attacking;
}
void Caracter::setFightingMode(bool b) {
this->fightingMode = b;
}
bool Caracter::operator== (Entity &other) {
if (getType() != other.getType())
return false;
Caracter& caracter = static_cast<Caracter&>(other);
if (anims.size() != caracter.anims.size())
return false;
for (unsigned int i = 0; i < anims.size(); i++) {
if (anims[i] != caracter.anims[i])
return false;
}
return true;
}
bool Caracter::isInFightingMode() {
return fightingMode;
}
void Caracter::setAttack(int attack) {
this->attack = attack;
}
int Caracter::getAttack() {
return attack;
}
Time Caracter::getTimeOfLastAttack() {
return clockAtkSpeed.getElapsedTime();
}
void Caracter::setDir (Vec2f dir) {
anims[currentAnimIndex]->setCurrentTile(0);
float angleRadians = const_cast<Vec2f&>(Vec2f::yAxis).getAngleBetween(dir);
int angle = Math::toDegrees(angleRadians);
//Sud
if (angle >= -10 && angle <= 10)
currentAnimIndex = 0;
//Sud ouest
else if (angle > -80 && angle < -10)
currentAnimIndex = 3;
//Ouest
else if (angle >= -100 && angle <= -80)
currentAnimIndex = 6;
//Nord ouest
else if (angle > -170 && angle < -100)
currentAnimIndex = 1;
//Nors est
else if (angle > 100 && angle < 170)
currentAnimIndex = 7;
//Est
else if (angle >= 80 && angle <= 100)
currentAnimIndex = 2;
//Sud est
else if (angle > 10 && angle < 80)
currentAnimIndex = 5;
else
currentAnimIndex = 4;
if (attacking)
currentAnimIndex += 8;
this->dir = dir;
}
Vec2f Caracter::getDir () {
return dir;
}
void Caracter::setMoving (bool b) {
this->moving = b;
if (moving) {
anims[currentAnimIndex]->play(true);
} else {
anims[currentAnimIndex]->stop();
anims[currentAnimIndex]->setCurrentTile(0);
}
}
bool Caracter::isMoving () {
return moving;
}
Vec2f Caracter::getPosition () {
return anims[currentAnimIndex]->getPosition();
}
void Caracter::setPath(vector<Vec2f> path) {
this->path = path;
}
vector<Vec2f> Caracter::getPath() {
return path;
}
void Caracter::addAnimation (Anim *anim) {
anims.push_back(anim);
}
Anim* Caracter::getAnimation(int index) {
if (index >= 0 && index < anims.size())
return anims[index];
return NULL;
}
unsigned int Caracter::getCurrentPathIndex() {
return currentPointIndex;
}
void Caracter::setCurrentPathIndex (unsigned int currentPointIndex) {
this->currentPointIndex = currentPointIndex;
}
void Caracter::setMaxLife(int life) {
this->maxLife = maxLife;
}
int Caracter::getMaxLife() {
return maxLife;
}
int Caracter::getLevel() {
return level;
}
string Caracter::getClass () {
return classs;
}
void Caracter::onDraw(sf::RenderTarget &target, sf::RenderStates states) const {
target.draw(*getCurrentEntity(), states);
}
Entity* Caracter::getCurrentEntity() {
return anims[currentAnimIndex]->getCurrentEntity();
}
Caracter::~Caracter() {
}
In the main we just have to do this :
#include <iostream>
#include "myApplication.h"
using namespace odfaeg;
using namespace sf;
int main () {
sf::RenderWindow window (sf::VideoMode(800, 600), "ODFAEG DEMO");
MyAppli appli (window);
return appli.exec();
}
Screenshot and movies are coming! (I'll include some tilesets and a demo on the git-hub repository as soon as possible)