Revamped animation system

Smoother reloading
Added idle atlas for player
This commit is contained in:
Ethan Adams 2024-08-17 18:06:13 -04:00
parent 90a843946a
commit 846f85f1f6
28 changed files with 836 additions and 302 deletions

View file

@ -0,0 +1,6 @@
<animations>
<animation name="bubble_idle_anim" type="idle">
<FPS>2</FPS>
<sprite path="sprites/bubbleoAtlas64.png" frameSize="64.0"/>
</animation>
</animations>

View file

@ -0,0 +1,10 @@
<animations>
<animation name="machine_gun_idle_anim" type="idle">
<FPS>4</FPS>
<sprite path="sprites/machineGunAtlas256.png" frameSize="256.0"/>
</animation>
<animation name="machine_gun_reload_anim" type="reload">
<FPS>2</FPS>
<sprite path="sprites/pistolAtlas256.png" frameSize="256.0"/>
</animation>
</animations>

View file

@ -0,0 +1,6 @@
<animations>
<animation name="pistol_idle_anim" type="idle">
<FPS>4</FPS>
<sprite path="sprites/pistolAtlas256.png" frameSize="256.0"/>
</animation>
</animations>

View file

@ -0,0 +1,11 @@
<animations directional="true">
<animation name="player_move_anim" type="move">
<FPS>7</FPS>
<sprite path="sprites/player3AtlasMove64.png" frameSize="64.0"/>
</animation>
<animation name="player_idle_anim" type="idle">
<FPS>5</FPS>
<sprite path="sprites/player3AtlasIdle64.png" frameSize="64.0"/>
</animation>
</animations>

View file

@ -0,0 +1,6 @@
<animations>
<animation name="tmp_enemy_move_anim" type="move">
<FPS>7</FPS>
<sprite path="sprites/player2Atlas.png" frameSize="128.0"/>
</animation>
</animations>

View file

@ -35,18 +35,18 @@
</map>
<entities>
<player x="7" y="5" weapon="shotGun">
<sprite file="sprites/player3Atlas.png" framesize="64.0" directional="true"/>
<animation name="player_anim"/>
</player>
<entity x="10" y="3" weapon="pistolGun">
<sprite file="sprites/player3Atlas.png" frameSize="64.0" directional="true"/>
<animation name="player_anim"/>
<script file="scripts/ai/grunt_behaviour.lua"/>
</entity>
<entity x="6" y="3" weapon="pistolGun">
<sprite file="sprites/player2Atlas.png" frameSize="128.0"/>
<animation name="tmp_enemy_anim"/>
<script file="scripts/ai/grunt_behaviour.lua"/>
</entity>
<entity x="5" y="3" weapon="pistolGun">
<sprite file="sprites/player2Atlas.png" frameSize="128.0"/>
<animation name="tmp_enemy_anim"/>
<script file="scripts/ai/grunt_behaviour.lua"/>
</entity>
</entities>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<weapons>
<weapon name="bubbleGun" fireSpeed="100.0">
<bullet sprite="sprites/bubbleoAtlas64.png" animated="true" frameSize="64.0">
<weapon name="bubbleGun" fireSpeed="100.0" maxAmmo="40000" clipSize="1000">
<bullet anim="bubble_anim">
<spread>60</spread>
<speed>20.0</speed>
<drop>250.0</drop>
@ -11,13 +11,13 @@
</bullet>
</weapon>
<weapon name="shotGun" fireSpeed="750.0">
<weapon name="shotGun" fireSpeed="750.0" maxAmmo="64" clipSize="4">
<script file="scripts/weapons/shotgun_script.lua"/>
<sprite file="sprites/machineGunAtlas256.png" animated="true" frameSize="256.0">
<animation name="machine_gun_anim">
<size x="55.0" y="55.0"/>
<offset x="-30.0" y="0.0"/>
</sprite>
<bullet sprite="sprites/bullet.png" animated="false" frameSize="64.0">
</animation>
<bullet sprite="sprites/bullet.png">
<spread>50</spread>
<speed>60.0</speed>
<drop>550.0</drop>
@ -26,12 +26,12 @@
</bullet>
</weapon>
<weapon name="machineGun" fireSpeed="50.0">
<sprite file="sprites/machineGunAtlas256.png" animated="true" frameSize="256.0">
<weapon name="machineGun" fireSpeed="50.0" maxAmmo="512" clipSize="64">
<animation name="machine_gun_anim">
<size x="55.0" y="55.0"/>
<offset x="-30.0" y="0.0"/>
</sprite>
<bullet sprite="sprites/bullet.png" animated="false" frameSize="64.0">
</animation>
<bullet sprite="sprites/bullet.png">
<spread>20</spread>
<speed>60.0</speed>
<drop>950.0</drop>
@ -40,12 +40,12 @@
</bullet>
</weapon>
<weapon name="pistolGun" fireSpeed="750.0">
<sprite file="sprites/pistolAtlas256.png" animated="true" frameSize="256.0">
<weapon name="pistolGun" fireSpeed="750.0" maxAmmo="512" clipSize="21">
<animation name="pistol_anim">
<size x="30.0" y="30.0"/>
<offset x="0.0" y="0.0"/>
</sprite>
<bullet sprite="sprites/bullet.png" animated="false" frameSize="64.0">
</animation>
<bullet sprite="sprites/bullet.png">
<spread>5.0</spread>
<speed>50.0</speed>
<drop>500.0</drop>

View file

@ -67,7 +67,7 @@ add_executable (YuppleMayham
"include/utility/raycaster.h"
"include/utility/ftfont.h"
"include/utility/direction.h"
)
"include/graphics/animation.h" "src/graphics/animation.cpp")
add_custom_command(TARGET YuppleMayham PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/Resources/ $<TARGET_FILE_DIR:YuppleMayham>)

View file

@ -4,6 +4,7 @@
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <SDL_timer.h>
#include "graphics/shader.h"
@ -19,7 +20,9 @@ public:
position(glm::vec3(0.0f)),
scale(glm::vec3(1.0f)),
rotation(0.0f),
speed(20.0f) {};
speed(20.0f),
entityid(SDL_GetTicks())
{};
virtual ~Entity() {};
void setPosition(const glm::vec3& position);
@ -39,6 +42,7 @@ public:
const glm::vec2 getFacingDir() const { return glm::vec2(cos(glm::radians(rotation)), sin(glm::radians(rotation))); }
const glm::vec3 getCenter() const { return glm::vec3(position.x + (0.5f * scale.x), position.y + (0.5f * scale.y), 0.0f); }
const bool getIsMoving() const { return isMoving; }
const int getEntityID() const { return entityid; }
const std::shared_ptr<PhysicsComponent> getPhysicsComponent() const { return physics; }
// TODO: right now there is no default behavior, but eventually the Entity class will be expanded to handle physics
@ -53,9 +57,12 @@ protected:
float rotation;
float speed;
int entityid;
std::shared_ptr<PhysicsComponent> physics;
bool isMoving = false;
bool wasMoving = false;
bool isRotatable = true;
bool flipped = false;

View file

@ -19,7 +19,7 @@ class EventManager;
class GameActor : public Entity
{
public:
GameActor(const std::shared_ptr<Shader>& shader) : Entity(shader) { actorid = SDL_GetTicks(); }
GameActor(const std::shared_ptr<Shader>& shader) : Entity(shader) {}
~GameActor();
void addComponent(std::shared_ptr<Component> component);
@ -28,8 +28,7 @@ public:
void update(float deltaTime) override;
void render(const std::shared_ptr<Camera>& camera) override;
const std::optional<std::shared_ptr<Weapon>>& getHeldWeapon() const;
const int getActorID() const { return actorid; }
const std::optional<std::shared_ptr<Weapon>> getHeldWeapon() const;
void setRotation(const float& rotation) override;
@ -52,7 +51,6 @@ private:
using component_vector_t = std::vector<std::shared_ptr<Component>>;
using weapon_vector_t = std::vector<std::shared_ptr<Weapon>>;
int actorid;
component_vector_t components;
weapon_vector_t weapons;
size_t currentWeaponIndex = 0;

View file

@ -5,6 +5,7 @@
#include <glm/glm.hpp>
#include <vector>
class Component;
class AnimationSet;
class Camera;
class Bullet : public Entity

View file

@ -30,6 +30,7 @@ public:
Weapon(const WeaponData* data, const std::shared_ptr<Shader>& weaponShader, const std::shared_ptr<Shader>& bulletShader, ResourceManager* resourceManager);
void setWielder(const std::shared_ptr<GameActor>& wielder) { this->wielder = wielder; }
void toggleInfiniteAmmo() { infiniteAmmo = !infiniteAmmo; }
void wield() { wielded = true; }
void putaway() { wielded = false; }
@ -37,16 +38,17 @@ public:
void attachScript(const std::shared_ptr<WeaponScript>& script);
void hookEventManager(const std::shared_ptr<EventManager>& eventManager);
void shoot();
void reload();
void update(float deltaTime);
void render(const std::shared_ptr<Camera>& camera);
struct BulletData {
glm::vec3 origin;
glm::vec2 direction;
float mass;
float sizeMod;
float speedMod;
float dropMod;
float mass = 1.f;
float sizeMod = 1.f;
float speedMod = 1.f;
float dropMod = 1.f;
};
void onHitCallback(std::shared_ptr<GameActor> target, std::shared_ptr<PhysicsComponent> bullet, const glm::vec2& normal);
@ -66,6 +68,12 @@ private:
glm::vec2 weaponSize;
glm::vec2 weaponOffset;
// TODO: Add reloading
// weaponMag will be unused for now, but we will do a reload animation and reload the weapon eventually
Uint16 weaponMag, weaponMagSize, weaponAmmo;
bool reloading = false;
bool wasReloading = false;
bool infiniteAmmo = false;
float bulletSpeed;
float bulletDrop;

View file

@ -0,0 +1,102 @@
#ifndef _H_ANIMATION_H
#define _H_ANIMATION_H
#include <memory>
#include <SDL_timer.h>
#include <string>
#include <unordered_map>
#include "utility/direction.h"
class SpriteAtlas;
class ResourceManager;
class EventManager;
// Each entity will contain animation data,
// this data will hold:
// name of the animation,
// type of animation ie. idle animation, directional, etc.
// directory of the animation atlas containing the frames
struct AnimationData;
class Animation
{
public:
Animation(const std::shared_ptr<AnimationData>& animData, ResourceManager* resourceManager);
std::string getName() const { return animName; }
std::string getType() const { return animType; }
void bind();
void draw();
void draw(Direction dir);
void play() { isPlaying = true; }
void stop() { isPlaying = false; }
const bool getPlaying() const { return isPlaying; }
const bool getDirectional() const { return isDirectional; }
const int getCycles() const { return cycles; }
private:
std::string animName;
std::string animType;
std::shared_ptr<SpriteAtlas> spriteAtlas;
Uint32 elapsedTime = 0;
Uint32 lastFrameTick = 0;
float FPS;
int currentFrame;
int cycles;
bool isDirectional;
bool isPlaying;
// this ticks our frame forward according to ticks passed
void frameTick();
void singleDraw();
void directionalDraw(Direction dir);
};
// We will load our animation component with every loaded animation,
// this will be the handler for every animation an entity uses
class AnimationSet
{
public:
AnimationSet(const int& entityid);
AnimationSet(const int& entityid, ResourceManager* resourceManager, std::vector<std::shared_ptr<AnimationData>> animSet);
AnimationSet(const int& entityid, const std::unordered_map<std::string, std::shared_ptr<Animation>>& animations);
std::shared_ptr<Animation>& operator [](std::string animType) { return anims[animType]; }
void addAnimation(const std::shared_ptr<Animation> anim) { anims.try_emplace(anim->getType(), anim); }
void setAnimation(const std::string& animType) { curAnim = anims[animType]; }
const int getEntityID() const { return entityid; }
const bool getDirectional() const { return isDirectional; }
void bind();
void draw();
void play() { curAnim->play(); }
void stop() { curAnim->stop(); }
void setFacingDir(Direction& dir) { facing = dir; }
void attachEventManager(const std::shared_ptr<EventManager>& e);
private:
int entityid;
bool isDirectional;
Direction facing = Direction::Down;
std::unordered_map<std::string, std::shared_ptr<Animation>> anims;
std::shared_ptr<Animation> curAnim;
std::shared_ptr<EventManager> eventManager;
};
#endif // _H_ANIMATION_H

View file

@ -23,8 +23,6 @@ public:
void bind();
virtual void draw() = 0;
virtual void play() = 0;
virtual void idle() = 0;
virtual std::shared_ptr<Sprite> clone() const = 0;
protected:
@ -42,8 +40,6 @@ public:
~SpriteStatic();
void draw() override;
void play() override { /*unused*/ }
void idle() override { /*unused*/ }
std::shared_ptr<Sprite> clone() const override {
return std::make_shared<SpriteStatic>(*this);
@ -66,73 +62,54 @@ private:
};
class SpriteAnimated : public Sprite
class SpriteAtlas : public Sprite
{
public:
// let's keep texture grids squares, saves everyone time...
SpriteAnimated(const char* textureAtlasPath, float frameSize);
~SpriteAnimated();
void draw() override;
void play() override;
void idle() override;
std::shared_ptr<Sprite> clone() const override {
return std::make_shared<SpriteAnimated>(*this);
}
bool isPlaying = false;
private:
void SetupAnimated(float frameSize);
struct VertexIDs {
unsigned VAO, VBO;
};
unsigned EBO;
// Vertex array buffer IDs
std::vector<VertexIDs> ids;
int currentFrame = 0;
float FPS = 7.5f;
Uint32 lastFrameTick = 0;
};
class SpriteDirectionalAnimated : public Sprite
{
public:
SpriteDirectionalAnimated(const char* textureAtlasPath, float frameSize);
~SpriteDirectionalAnimated();
// let's keep texture grids squares, saves everyone time...
SpriteAtlas(const char* textureAtlasPath, float frameSize, bool isDirectional = false);
~SpriteAtlas();
void bindFrame(VertexIDs* f);
// draw current frame
void draw() override;
void play() override;
void idle() override;
std::shared_ptr<Sprite> clone() const override {
return std::make_shared<SpriteDirectionalAnimated>(*this);
VertexIDs frame(int index, Direction dir = Direction::None) {
if (dir == Direction::None) return singleFrame(index);
return dirFrames(dir, index);
}
Direction direction = Direction::Down;
void setDirection(Direction& dir) { direction = dir; }
bool isPlaying = false;
size_t size() { return (isDirectional) ? directionalIDs.size() : singleIDs.size(); }
std::shared_ptr<Sprite> clone() const override {
return std::make_shared<SpriteAtlas>(*this);
}
private:
void Setup(float frameSize);
void SetupDirectional(float frameSize);
struct VertexIDs {
unsigned VAO, VBO;
};
unsigned EBO;
bool isDirectional;
std::unordered_map<Direction, VertexIDs> idleIDs;
std::unordered_map<Direction, std::vector<VertexIDs>> walkingIDs;
VertexIDs* curFrame;
int currentFrame = 0;
float FPS = 7.5f;
Uint32 lastFrameTick = 0;
VertexIDs singleFrame(int index) const {
return (index < 0 || index >= singleIDs.size()) ? singleIDs[0] : singleIDs[index];
}
// return the frame in the direction at the selected index
VertexIDs dirFrames(Direction dir, int index) const {
return (index < 0 || index >= directionalIDs.at(dir).size()) ? directionalIDs.at(dir)[0] : directionalIDs.at(dir)[index];
}
// Vertex array buffer IDs, if the sprite atlas is non-directional
std::vector<VertexIDs> singleIDs;
// Vertex ids if the sprite atlas is directional
std::unordered_map<Direction, std::vector<VertexIDs>> directionalIDs;
};
#endif // _H_SPRITE_H

View file

@ -4,6 +4,7 @@
#include "graphics/mesh.h"
#include "graphics/sprite.h"
#include "graphics/shader.h"
#include "graphics/animation.h"
#include "gameplay/ai.h"
#include "events.h"
@ -21,6 +22,7 @@ public:
virtual void idle() = 0;
int ownerid = 0;
private:
std::shared_ptr<EventManager> eventManager;
};
@ -36,6 +38,7 @@ public:
}
void play() override { /*unused*/ }
void idle() override { /*unused*/ }
~MeshComponent() { mesh->~Mesh(); }
@ -48,33 +51,16 @@ class SpriteComponent : public Component
{
public:
SpriteComponent(std::shared_ptr<Sprite> sprite) : sprite(sprite), Component(nullptr) {}
SpriteComponent(std::shared_ptr<Sprite> sprite, const std::shared_ptr<EventManager>& eventManager) :
Component(eventManager), sprite(sprite) {
auto directionalSprite = std::dynamic_pointer_cast<SpriteDirectionalAnimated>(sprite);
if (directionalSprite)
{
eventManager->subscribe("OnDirectionChange", [sprite, this](std::shared_ptr<Event> e) {
auto directionEvent = std::static_pointer_cast<DirectionChangeEvent>(e);
auto directionSprite = std::static_pointer_cast<SpriteDirectionalAnimated>(sprite);
if (directionEvent->actorid == ownerid)
directionSprite->setDirection(directionEvent->direction);
});
}
};
void bind() override {
if (sprite) sprite->bind();
}
void update() override {}
void update() override { /*unused*/ }
void render() override {
if (sprite) sprite->draw();
}
void play() override {
if (sprite) sprite->play();
}
void idle() override {
if (sprite) sprite->idle();
}
void play() override { /*unused*/ }
void idle() override { /*unused*/ }
std::shared_ptr<Sprite>& getSprite() { return sprite; }
@ -84,6 +70,42 @@ private:
std::shared_ptr<Sprite> sprite;
};
class AnimationComponent : public Component
{
public:
AnimationComponent(std::shared_ptr<AnimationSet> animSet) : animSet(animSet), Component(nullptr) {};
AnimationComponent(std::shared_ptr<AnimationSet> animSet, const std::shared_ptr<EventManager>& eventManager) :
animSet(animSet), Component(nullptr)
{
this->animSet->attachEventManager(eventManager);
}
void bind() override {
if (animSet)
animSet->bind();
}
void update() override {/*unused*/}
void render() override {
if (animSet)
animSet->draw();
}
void play() override {
if (animSet)
animSet->play();
}
void idle() override {
if (animSet)
animSet->stop();
}
std::shared_ptr<AnimationSet>& getAnimationSet() { return animSet; }
~AnimationComponent() { }
private:
std::shared_ptr<AnimationSet> animSet;
};
class ShaderComponent : public Component
{
public:

View file

@ -4,7 +4,8 @@
#include <cmath>
enum class Direction {
None = 0,
Down = 1,
Right = 2,
Left = 3,

View file

@ -44,10 +44,46 @@ public:
class DirectionChangeEvent : public Event {
public:
DirectionChangeEvent(const int actorid, Direction direction) : actorid(actorid), direction(direction) {}
DirectionChangeEvent(const int entityid, Direction direction) : entityid(entityid), direction(direction) {}
std::string getType() const override { return "OnDirectionChange"; }
Direction direction;
int actorid;
int entityid;
};
class EntityMoveEvent : public Event {
public:
EntityMoveEvent(const int entityid) : entityid(entityid) {}
std::string getType() const override { return "OnEntityMove"; }
int entityid;
};
class EntityStopEvent : public Event {
public:
EntityStopEvent(const int entityid) : entityid(entityid) {}
std::string getType() const override { return "OnEntityStop"; }
int entityid;
};
class EntityReloadEvent : public Event {
public:
EntityReloadEvent(const int entityid) : entityid(entityid) {}
std::string getType() const override { return "OnEntityReload"; }
int entityid;
};
class EntityFinishReloadEvent : public Event {
public:
EntityFinishReloadEvent(const int entityid) : entityid(entityid) {}
std::string getType() const override { return "OnEntityFinishReload"; }
int entityid;
};
class AnimationFinishedEvent : public Event {
public:
AnimationFinishedEvent(const int entityid, const std::string animType) : entityid(entityid), animType(animType) {}
std::string getType() const override { return "OnAnimationFinished"; }
std::string animType;
int entityid;
};
class EventManager {

View file

@ -9,9 +9,11 @@
#include <cassert>
class Sprite;
class SpriteAtlas;
class Shader;
class Weapon;
class Script;
class AnimationSet;
class AIScript;
class WeaponScript;
class TileSet;
@ -25,18 +27,19 @@ public:
{
xmlLoader->loadWeapons("weapons");
xmlLoader->loadScenes("scenes");
xmlLoader->loadAnimations("animations");
};
std::shared_ptr<Sprite> loadSpriteAnimated (const std::string& path, float frameSize);
std::shared_ptr<Sprite> loadSpriteDirAnimated (const std::string& path, float frameSize);
std::shared_ptr<SpriteAtlas> loadSpriteAtlas (const std::string& path, float frameSize, bool isDirectional = false);
std::shared_ptr<Sprite> loadSpriteStatic (const std::string& path);
std::shared_ptr<AIScript> loadAIScript (const std::string& path);
std::shared_ptr<WeaponScript> loadWeaponScript (const std::string& path);
std::shared_ptr<TileSet> loadTileSet (const std::string& path, float frameSize);
std::shared_ptr<Shader> loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath);
std::shared_ptr<Weapon> loadWeapon (const std::string& name, std::shared_ptr<Shader> weaponShader, std::shared_ptr<Shader> bulletShader);
std::shared_ptr<SceneData> loadScene (const std::string& id);
std::shared_ptr<Shader> loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath);
std::shared_ptr<Weapon> loadWeapon (const std::string& name, std::shared_ptr<Shader> weaponShader, std::shared_ptr<Shader> bulletShader);
std::shared_ptr<SceneData> loadScene (const std::string& id);
std::shared_ptr<AnimationSet> loadAnimationSet(const std::string& name, int entityid = 0);
void clearResources();

View file

@ -10,12 +10,11 @@ struct Tile;
struct EntityData {
bool isPlayer;
bool animated;
int x = 0, y = 0;
std::string sprite;
float frameSize = 0.f;
std::string graphic;
std::string weapon = "pistolGun";
std::string script;
bool isDirectional = false;
};
struct MapData {
@ -37,26 +36,44 @@ struct SceneData {
struct WeaponData {
std::string name;
float fireSpeed = 250.0f;
int clipSize = 21;
int maxAmmo = 512;
std::string script = "";
std::string sprite;
std::string graphic;
std::string animSet;
bool animated = false;
float frameSize;
float sizeX = 50.f, sizeY = 50.f;
float offsetX = 0.f, offsetY = 0.f;
float bulletFrameSize;
float bulletSizeX = 50.f, bulletSizeY = 50.f;
std::string bulletSprite;
std::string bulletGraphic;
bool bulletAnimated = false;
float bulletSpread = 1.0f, bulletSpeed = 3.0f, bulletDrop = 500.f;
float modMin = 0.5f, modMax = 1.0f;
};
struct AnimationData {
std::string name;
std::string type;
std::string spriteAtlas;
// Each entity will have a set of animations,
// this animation set is meant to keep each group
// of animations within their set.
// The actual set name is based on the file the animation
// is held in.
std::string animSet;
bool directional = false;
bool oneShot;
float FPS = 1.f;
float frameSize;
};
class XMLLoader
{
public:
XMLLoader() {}
bool loadScenes(const char* sceneFolder);
bool loadWeapons(const char* weaponFolder);
bool loadAnimations(const char* animationFolder);
const std::shared_ptr<SceneData> getSceneData(const std::string& id) const {
try {
@ -66,15 +83,39 @@ public:
return nullptr;
}
}
const std::shared_ptr<AnimationData> getAnimationData(const std::string& name) const {
try {
return animations.at(name);
}
catch (std::exception&) {
return nullptr;
}
}
// return a full set of animations, may need further optimization.
// one idea is when loading animations we create a seperate map that holds each set by their reference, so we can just do a simple,
// hash table lookup.
std::vector<std::shared_ptr<AnimationData>> getAnimationSet(const std::string& set) const {
std::vector<std::shared_ptr<AnimationData>> animSet;
animSet.reserve(animations.size());
for (const auto& [name, anim] : animations) {
if (anim->animSet == set) animSet.push_back(anim);
}
animSet.shrink_to_fit();
return animSet;
}
const WeaponData* getWeaponDataByName(const char* name) const;
void clearData() { scenes.clear(); weaponData.clear(); }
void clearData() { scenes.clear(); weaponData.clear(); animations.clear(); }
protected:
bool loadXmlScene(const char* xmlFile, SceneData* out);
bool loadMap(const char* xmlFile, SceneData* out);
bool loadEntityData(const char* xmlFile, SceneData* out);
private:
std::unordered_map<std::string, std::shared_ptr<SceneData>> scenes;
std::unordered_map<std::string, std::shared_ptr<AnimationData>> animations;
std::vector<WeaponData> weaponData;
};

View file

@ -10,11 +10,11 @@ GameActor::~GameActor() { }
void GameActor::addComponent(std::shared_ptr<Component> component)
{
component->ownerid = actorid;
component->ownerid = entityid;
components.push_back(component);
}
const std::optional<std::shared_ptr<Weapon>>& GameActor::getHeldWeapon() const
const std::optional<std::shared_ptr<Weapon>> GameActor::getHeldWeapon() const
{
return (weapons.empty() || currentWeaponIndex >= weapons.size()) ? std::nullopt : std::make_optional(weapons[currentWeaponIndex]);
}
@ -23,6 +23,7 @@ void GameActor::pickupWeapon(std::shared_ptr<Weapon> weapon)
{
weapon->setWielder(std::shared_ptr<GameActor>(this));
weapons.push_back(weapon);
// wield the newly picked up weapon.
getHeldWeapon().value()->putaway();
currentWeaponIndex = weapons.size() - 1;
getHeldWeapon().value()->wield();
@ -40,7 +41,7 @@ void GameActor::setRotation(const float& rotation)
if (!isRotatable && eventManager) {
Direction newDir = getDirectionFromRotation(rotation);
if (getDirectionFromRotation(this->rotation) != newDir)
eventManager->notify(std::make_shared<DirectionChangeEvent>(actorid, newDir));
eventManager->notify(std::make_shared<DirectionChangeEvent>(entityid, newDir));
}
this->rotation = rotation;
updateModelMatrix();
@ -54,24 +55,36 @@ void GameActor::update(float deltaTime)
component->update();
for (auto& weapon : weapons)
weapon->update(deltaTime);
if (eventManager)
{
if (isMoving && !wasMoving)
{
eventManager->notify(std::make_shared<EntityMoveEvent>(entityid));
wasMoving = true;
}
else if (!isMoving && wasMoving)
{
eventManager->notify(std::make_shared<EntityStopEvent>(entityid));
wasMoving = false;
}
}
isMoving = false;
}
void GameActor::render(const std::shared_ptr<Camera>& camera)
{
Entity::render(camera);
// regular loop through components, but if the component returns true to kill, we erase it.
// Components are always assumed to be smart pointers!
for (auto& component : components)
{
if (isMoving)
component->play();
else
component->idle();
component->bind();
component->render();
}
for (auto& weapon : weapons)
weapon->render(camera);
isMoving = false;
}
void GameActor::moveUp() { if (physics) physics->rigidBody.applyForce(glm::vec3( 0.f,-1.f, 0.f), 1500.25f); isMoving = true; }
@ -108,7 +121,7 @@ void GameActor::followMouse(const MouseState& mouse_state)
float newRotation = glm::degrees(glm::atan(direction.y, direction.x));
if (getDirectionFromRotation(rotation) != getDirectionFromRotation(newRotation))
if (eventManager)
eventManager->notify(std::make_shared<DirectionChangeEvent>(actorid, getDirectionFromRotation(newRotation)));
eventManager->notify(std::make_shared<DirectionChangeEvent>(entityid, getDirectionFromRotation(newRotation)));
//setRotation(glm::degrees(glm::atan(direction.y, direction.x)));
this->rotation = newRotation;
}

View file

@ -8,6 +8,7 @@
#include "graphics/tile.h"
#include "graphics/sprite.h"
#include "graphics/animation.h"
#include "utility/component.h"
#include "utility/xmlloader.h"
@ -57,22 +58,25 @@ void Scene::loadDebugShooterScene()
for (EntityData entityData : sceneData->entities)
{
auto entity = std::make_shared<GameActor>(playerShader);
std::shared_ptr<Sprite> entitySprite;
// Directional is the kind of sprite sheet we are using, in this case for directional, I have the sprite sheet handle the rotations
// instead of just rotating the object, this makes it look quite a bit better from the end user perspective.
if (entityData.isDirectional)
if (entityData.animated)
{
entitySprite = resourceManager->loadSpriteDirAnimated(entityData.sprite, entityData.frameSize);
auto entityAnimation = resourceManager->loadAnimationSet(entityData.graphic, entity->getEntityID());
// because we don't want to have the engine rotate the object based on the entities rotation,
// we set the this value to false so we no longer rotate the object.
entity->setRotatable(false);
if (entityAnimation->getDirectional())
entity->setRotatable(false);
entity->addComponent(std::make_shared<AnimationComponent>(entityAnimation, eventManager));
}
else
entitySprite = resourceManager->loadSpriteAnimated(entityData.sprite, entityData.frameSize);
{
auto entitySprite = resourceManager->loadSpriteStatic(entityData.graphic);
entity->addComponent(std::make_shared<SpriteComponent>(entitySprite));
}
auto defaultWeapon = resourceManager->loadWeapon("pistolGun", weaponShader, bubbleShader);
auto entityWeapon = resourceManager->loadWeapon(entityData.weapon, weaponShader, bubbleShader);
entity->addComponent(std::make_shared<SpriteComponent>(entitySprite, eventManager));
entity->pickupWeapon(defaultWeapon);
entity->pickupWeapon(entityWeapon);
entity->hookEventManager(eventManager);
@ -80,7 +84,7 @@ void Scene::loadDebugShooterScene()
entity->setScale(glm::vec3(mapData.tileSize, mapData.tileSize, 1.f));
entity->addPhysicsComponent(
physicsEngine->createObject(entity->getActorID(),
physicsEngine->createObject(entity->getEntityID(),
entity->getPosition(),
49.0,
PhysicsComponent::Collider::Shape::Circle,
@ -107,7 +111,7 @@ void Scene::loadDebugShooterScene()
entity->addComponent(std::make_shared<AIComponent>(ai));
}
}
entities.emplace(entity->getActorID(), entity);
entities.emplace(entity->getEntityID(), entity);
}
physicsEngine->loadCollisionMap(map->getCollisionMap(), sceneData->map.tileSize);

View file

@ -12,8 +12,6 @@
#include "utility/script.h"
// TODO: Regular clean up, make this mess readable!
// TODO: Attach script to weapons, create custom shoot scripts, allowing the addition of shotguns and other whacky things
// TODO: Finally just allow the XMLLoader to read the weaponscript supplied and load it into the weapon
Weapon::Weapon(const WeaponData* data, const std::shared_ptr<Shader>& weaponShader, const std::shared_ptr<Shader>& bulletShader, ResourceManager* resourceManager)
:
@ -21,25 +19,28 @@ Weapon::Weapon(const WeaponData* data, const std::shared_ptr<Shader>& weaponShad
bulletShader (bulletShader),
weaponSize (glm::vec2(data->sizeX, data->sizeY)),
weaponOffset (glm::vec2(data->offsetX, data->offsetY)),
weaponMagSize (data->clipSize),
weaponMag (data->clipSize),
weaponAmmo (data->maxAmmo),
bulletDrop (data->bulletDrop),
bulletSpeed (data->bulletSpeed),
bulletSize (glm::vec2(data->bulletSizeX, data->bulletSizeY)),
bulletSpread (std::make_shared<UTIL::RandomGenerator>(-data->bulletSpread, data->bulletSpread)),
bulletModifer (std::make_shared<UTIL::RandomGenerator>(data->modMin, data->modMax)),
fireSpeed (data->fireSpeed),
bulletManager (std::make_shared<BulletManager>()),
bulletSprite (data->bulletAnimated ?
std::make_shared<SpriteComponent>(resourceManager->loadSpriteAnimated(data->bulletSprite, data->bulletFrameSize))
: std::make_shared<SpriteComponent>(resourceManager->loadSpriteStatic(data->bulletSprite)))
bulletManager (std::make_shared<BulletManager>())
{
if (data->bulletAnimated)
bulletSprite = std::make_shared<AnimationComponent>(resourceManager->loadAnimationSet(data->bulletGraphic, entityid));
else
bulletSprite = std::make_shared<SpriteComponent>(resourceManager->loadSpriteStatic(data->bulletGraphic));
if (data->animated)
{
addComponent(std::make_shared<SpriteComponent>(resourceManager->loadSpriteAnimated(data->sprite, data->frameSize)));
for (auto& c : components)
c->play();
addComponent(std::make_shared<AnimationComponent>(resourceManager->loadAnimationSet(data->graphic, entityid)));
}
else
addComponent(std::make_shared<SpriteComponent>(resourceManager->loadSpriteStatic(data->sprite)));
addComponent(std::make_shared<SpriteComponent>(resourceManager->loadSpriteStatic(data->graphic)));
this->setScale(glm::vec3(weaponSize.x, weaponSize.y, 1.0f));
};
@ -47,18 +48,38 @@ void Weapon::addComponent(const std::shared_ptr<Component>& comp) {
components.push_back(comp);
}
void Weapon::reload()
{
// TODO: Create reload event that will be captured by the gun animation set, to start the reloading animation
if (eventManager)
{
eventManager->notify(std::make_shared<EntityReloadEvent>(entityid));
reloading = true;
if (weaponAmmo < weaponMagSize) {
weaponMag = weaponAmmo;
weaponAmmo = 0;
}
else {
weaponMag = weaponMagSize;
weaponAmmo -= weaponMagSize;
}
}
}
void Weapon::shoot()
{
if (wielder)
{
Uint32 currentTime = SDL_GetTicks();
if (currentTime - lastFireTime >= fireSpeed)
if (weaponMag <= 0 && !reloading) reload();
if (currentTime - lastFireTime >= fireSpeed && !reloading && weaponMag > 0)
{
if (!weaponScript || !weaponScript->lua["onShoot"].valid())
{
// create bullet using this generated data
BulletData b = genBulletData();
createBullet(b);
weaponMag -= 1;
}
else {
auto result = weaponScript->lua["onShoot"]();
@ -67,6 +88,7 @@ void Weapon::shoot()
sol::error err = result;
std::cerr << "lua error: " << err.what() << std::endl;
}
weaponMag -= 1;
}
lastFireTime = currentTime;
}
@ -76,6 +98,28 @@ void Weapon::shoot()
void Weapon::hookEventManager(const std::shared_ptr<EventManager>& eventManager)
{
this->eventManager = eventManager;
for (auto& component : components)
{
auto animComponent = std::dynamic_pointer_cast<AnimationComponent>(component);
if (animComponent != nullptr)
{
animComponent->getAnimationSet()->attachEventManager(eventManager);
}
}
this->eventManager->subscribe("OnAnimationFinished", [&, this](std::shared_ptr<Event> e) {
auto animFinished = std::static_pointer_cast<AnimationFinishedEvent>(e);
if (animFinished->entityid == entityid && animFinished->animType == "reload")
{
if (reloading)
{
reloading = false;
wasReloading = true;
}
}
});
bulletManager->hookEventManager(eventManager);
}
@ -110,6 +154,11 @@ void Weapon::update(float deltaTime)
for (auto& component : components)
component->update();
if (eventManager && wasReloading)
{
wasReloading = false;
eventManager->notify(std::make_shared<EntityFinishReloadEvent>(entityid));
}
}
bulletManager->update(deltaTime);
}
@ -121,6 +170,7 @@ void Weapon::render(const std::shared_ptr<Camera>& camera)
{
for (auto& component : components)
{
component->play();
component->bind();
component->render();
}
@ -185,9 +235,9 @@ Weapon::BulletData Weapon::genBulletData()
void Weapon::createBullet(const Weapon::BulletData& data)
{
auto bullet = std::make_shared<Bullet>(wielder->getActorID(), bulletShader, data.origin, data.direction, bulletSpeed, bulletDrop, bulletSize);
auto bullet = std::make_shared<Bullet>(wielder->getEntityID(), bulletShader, data.origin, data.direction, bulletSpeed, bulletDrop, bulletSize);
bullet->addComponent(bulletSprite);
bullet->addPhysicsComponent(std::make_shared<PhysicsComponent>(PhysicsComponentFactory::makeBullet(wielder->getActorID(), data.origin, data.mass, bulletSize.x / 2)));
bullet->addPhysicsComponent(std::make_shared<PhysicsComponent>(PhysicsComponentFactory::makeBullet(wielder->getEntityID(), data.origin, data.mass, bulletSize.x / 2)));
bullet->getPhysicsComponent()->rigidBody.velocity += bulletSpeed * glm::vec3(data.direction.x, data.direction.y, 0.f) / data.mass;
if (eventManager)
@ -195,3 +245,22 @@ void Weapon::createBullet(const Weapon::BulletData& data)
bulletManager->addBullet(bullet);
}
/*
!| SLATED FOR REMOVAL |!
// Swap the reload animation and the regular weapon animation
void Weapon::swapSprites()
{
//std::swap(weaponSprites.first, weaponSprites.second);
}
void Weapon::checkAndFinishReload()
{
if (weaponSprites.first->kill() && reloading) {
weaponSprites.first->idle();
weaponSprites.first->reset();
std::swap(weaponSprites.first, weaponSprites.second);
reloading = false;
}
}
*/

View file

@ -0,0 +1,181 @@
#include "graphics/animation.h"
#include "graphics/sprite.h"
#include "utility/xmlloader.h"
#include "utility/resourcemanager.h"
#include "utility/events.h"
Animation::Animation(const std::shared_ptr<AnimationData>& animData, ResourceManager* resourceManager) :
animName(animData->name),
animType(animData->type),
FPS(animData->FPS),
currentFrame(0),
cycles(0),
isDirectional(animData->directional),
isPlaying(isDirectional),
spriteAtlas(resourceManager->loadSpriteAtlas(animData->spriteAtlas, animData->frameSize, animData->directional))
{}
void Animation::bind()
{
spriteAtlas->bind();
}
void Animation::draw()
{
singleDraw();
}
void Animation::draw(Direction dir)
{
directionalDraw(dir);
}
void Animation::singleDraw()
{
if (isPlaying)
{
frameTick();
auto frame = spriteAtlas->frame(currentFrame);
spriteAtlas->bindFrame(&frame);
spriteAtlas->draw();
}
else
{
auto frame = spriteAtlas->frame(0);
spriteAtlas->bindFrame(&frame);
spriteAtlas->draw();
}
}
void Animation::directionalDraw(Direction dir)
{
if (isPlaying)
{
frameTick();
}
auto frame = spriteAtlas->frame(currentFrame, dir);
spriteAtlas->bindFrame(&frame);
spriteAtlas->draw();
}
void Animation::frameTick()
{
Uint32 currentTime = SDL_GetTicks();
elapsedTime = currentTime - lastFrameTick;
if (elapsedTime >= 1000.0f / FPS)
{
if (++currentFrame > spriteAtlas->size() - 1)
{
currentFrame = 0;
cycles += 1;
}
lastFrameTick = currentTime;
}
}
AnimationSet::AnimationSet(const int& entityid) : entityid(entityid), isDirectional(false) {};
AnimationSet::AnimationSet(const int& entityid, ResourceManager* resourceManager, std::vector<std::shared_ptr<AnimationData>> animSet) : entityid(entityid)
{
for (const auto& anim : animSet)
{
anims.try_emplace(anim->type, std::make_shared<Animation>(anim, resourceManager));
if (anim->type == "idle")
curAnim = anims[anim->type];
}
// if we don't have an idle animation, we are just gonna set the current animation to the top of the set
if (curAnim == nullptr)
curAnim = anims[animSet[0]->type];
isDirectional = curAnim->getDirectional();
}
AnimationSet::AnimationSet(const int& entityid, const std::unordered_map<std::string, std::shared_ptr<Animation>>& animations) : entityid(entityid), anims(animations)
{
for (const auto& [type, anim] : animations)
{
curAnim = anim;
if (type == "idle")
{
break;
}
}
isDirectional = curAnim->getDirectional();
}
void AnimationSet::attachEventManager(const std::shared_ptr<EventManager>& e)
{
eventManager = e;
eventManager->subscribe("OnDirectionChange", [this](std::shared_ptr<Event> e) {
auto directionEvent = std::static_pointer_cast<DirectionChangeEvent>(e);
if (directionEvent->entityid == entityid)
setFacingDir(directionEvent->direction);
});
eventManager->subscribe("OnEntityMove", [this](std::shared_ptr<Event> e) {
auto moveEvent = std::static_pointer_cast<EntityMoveEvent>(e);
if (moveEvent->entityid == entityid)
{
if (isDirectional)
{
if (anims["move"] != NULL)
curAnim = anims["move"];
}
else
play();
}
});
eventManager->subscribe("OnEntityStop", [this](std::shared_ptr<Event> e) {
auto stopEvent = std::static_pointer_cast<EntityStopEvent>(e);
if (stopEvent->entityid == entityid)
{
if (isDirectional)
{
if (anims["idle"] != NULL)
curAnim = anims["idle"];
}
else
stop();
}
});
eventManager->subscribe("OnEntityReload", [this](std::shared_ptr<Event> e) {
auto reloadEvent = std::static_pointer_cast<EntityReloadEvent>(e);
if (reloadEvent->entityid == entityid)
{
if (anims["reload"] != NULL)
curAnim = anims["reload"];
}
});
eventManager->subscribe("OnEntityFinishReload", [this](std::shared_ptr<Event> e) {
auto reloadEvent = std::static_pointer_cast<EntityFinishReloadEvent>(e);
if (reloadEvent->entityid == entityid)
{
if (anims["idle"] != NULL)
curAnim = anims["idle"];
}
});
}
void AnimationSet::bind()
{
curAnim->bind();
}
void AnimationSet::draw()
{
int lastCycle = curAnim->getCycles();
if (curAnim->getDirectional())
curAnim->draw(facing);
else
curAnim->draw();
// Sending animation events
if (eventManager)
{
// If the animation has cycled, we send this event after every cycle
if ((curAnim->getCycles() - lastCycle) > 0)
eventManager->notify(std::make_shared<AnimationFinishedEvent>(entityid, curAnim->getType()));
}
}

View file

@ -59,16 +59,21 @@ SpriteStatic::~SpriteStatic()
glDeleteVertexArrays(1, &VAO);
}
SpriteAnimated::SpriteAnimated(const char* textureAtlasPath, float frameSize)
SpriteAtlas::SpriteAtlas(const char* textureAtlasPath, float frameSize, bool isDirectional) : isDirectional(isDirectional), curFrame(nullptr)
{
EBO = 0;
texture = new Texture();
texture->loadTexture(textureAtlasPath);
if (texture)
SetupAnimated(frameSize);
{
if (!isDirectional)
Setup(frameSize);
else
SetupDirectional(frameSize);
}
}
void SpriteAnimated::SetupAnimated(float frameSize)
void SpriteAtlas::Setup(float frameSize)
{
int width = texture->getWidth();
int height = texture->getHeight();
@ -110,57 +115,44 @@ void SpriteAnimated::SetupAnimated(float frameSize)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
ids.push_back({ VAO, VBO });
singleIDs.push_back({ VAO, VBO });
}
}
}
void SpriteAnimated::draw()
void SpriteAtlas::bindFrame(VertexIDs* f)
{
if (isPlaying)
{
Uint32 currentTime = SDL_GetTicks();
if (currentTime - lastFrameTick >= 1000.f / FPS)
{
if (++currentFrame > ids.size() - 1)
currentFrame = 0;
lastFrameTick = currentTime;
}
glBindVertexArray(ids[currentFrame].VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
else
{
glBindVertexArray(ids[0].VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
curFrame = f;
}
void SpriteAnimated::play() { isPlaying = true; }
void SpriteAnimated::idle() { isPlaying = false; }
void SpriteAtlas::draw()
{
glBindVertexArray(curFrame->VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
SpriteAnimated::~SpriteAnimated()
SpriteAtlas::~SpriteAtlas()
{
delete texture;
glDeleteBuffers(1, &EBO);
for (VertexIDs idSet : ids)
for (VertexIDs idSet : singleIDs)
{
glDeleteBuffers(1, &idSet.VBO);
glDeleteVertexArrays(1, &idSet.VAO);
}
auto deleteBufferList = [](std::pair <Direction, std::vector<VertexIDs>> items) {
for (VertexIDs v : items.second) {
glDeleteBuffers(1, &v.VBO);
glDeleteVertexArrays(1, &v.VAO);
}
};
std::for_each(directionalIDs.begin(), directionalIDs.end(), deleteBufferList);
singleIDs.clear();
directionalIDs.clear();
}
SpriteDirectionalAnimated::SpriteDirectionalAnimated(const char* textureAtlas, float frameSize)
{
texture = new Texture();
texture->loadTexture(textureAtlas);
if (texture)
Setup(frameSize);
}
void SpriteDirectionalAnimated::Setup(float frameSize)
void SpriteAtlas::SetupDirectional(float frameSize)
{
int width = texture->getWidth();
int height = texture->getHeight();
@ -174,10 +166,10 @@ void SpriteDirectionalAnimated::Setup(float frameSize)
for (int col = 0; col < frameCols; col++)
{
Direction dir;
// top of the spritesheet is the entities idle directions
// then each row is a 4 frame walk cycle of each direction
if (row == 0) dir = (Direction)(col + 1);
else dir = (Direction)(row);
// Directional spritesheets are stored, with an animation for each row,
// on the first row we are looking down, then right etc...
// refer to utility/direction.h to each index
dir = (Direction)(row + 1); // note we add one, since dir: 0 is None, this is for non-directional sprites
float left = (col) * (frameSize / width);
float right = (col + 1) * (frameSize / width);
@ -209,56 +201,7 @@ void SpriteDirectionalAnimated::Setup(float frameSize)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
if (row == 0)
idleIDs.emplace(std::pair<Direction, VertexIDs>(dir, { VAO, VBO }));
else
walkingIDs[dir].push_back({ VAO, VBO });
directionalIDs[dir].push_back({ VAO, VBO });
}
}
}
void SpriteDirectionalAnimated::draw()
{
if (isPlaying)
{
Uint32 currentTime = SDL_GetTicks();
if (currentTime - lastFrameTick >= 1000.f / FPS)
{
if (++currentFrame > walkingIDs[direction].size() - 1)
currentFrame = 0;
lastFrameTick = currentTime;
}
glBindVertexArray(walkingIDs[direction][currentFrame].VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
else
{
glBindVertexArray(idleIDs[direction].VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
}
void SpriteDirectionalAnimated::play() { isPlaying = true; }
void SpriteDirectionalAnimated::idle() { isPlaying = false; }
SpriteDirectionalAnimated::~SpriteDirectionalAnimated()
{
delete texture;
glDeleteBuffers(1, &EBO);
auto deleteBuffers = [](std::pair<Direction, VertexIDs> item) {
glDeleteBuffers(1, &item.second.VBO);
glDeleteVertexArrays(1, &item.second.VAO);
};
auto deleteBufferList = [](std::pair <Direction, std::vector<VertexIDs>> items) {
for (VertexIDs v : items.second) {
glDeleteBuffers(1, &v.VBO);
glDeleteVertexArrays(1, &v.VAO);
}
};
std::for_each(idleIDs.begin(), idleIDs.end(), deleteBuffers);
std::for_each(walkingIDs.begin(), walkingIDs.end(), deleteBufferList);
idleIDs.clear();
walkingIDs.clear();
}

View file

@ -5,23 +5,19 @@
#include "utility/script.h"
#include "graphics/tiletype.h"
#include "graphics/shader.h"
#include "graphics/animation.h"
#include "gameplay/weapons/weapons.h"
std::shared_ptr<Sprite> ResourceManager::loadSpriteAnimated(const std::string& path, float frameSize)
std::shared_ptr<SpriteAtlas> ResourceManager::loadSpriteAtlas(const std::string& path, float frameSize, bool isDirectional)
{
auto iterator = sprites.find(path);
if (iterator != sprites.end())
return iterator->second;
auto sprite = std::make_shared<SpriteAnimated>(path.c_str(), frameSize);
return std::dynamic_pointer_cast<SpriteAtlas>(iterator->second);
auto sprite = std::make_shared<SpriteAtlas>(path.c_str(), frameSize, isDirectional);
sprites[path] = sprite;
return sprite;
}
std::shared_ptr<Sprite> ResourceManager::loadSpriteDirAnimated(const std::string& path, float frameSize)
{
return std::make_shared<SpriteDirectionalAnimated>(path.c_str(), frameSize);
}
std::shared_ptr<Sprite> ResourceManager::loadSpriteStatic(const std::string& path)
{
auto iterator = sprites.find(path);
@ -78,6 +74,12 @@ std::shared_ptr<SceneData> ResourceManager::loadScene(const std::string& id)
return xmlLoader->getSceneData(id);
}
std::shared_ptr<AnimationSet> ResourceManager::loadAnimationSet(const std::string& name, int entityid)
{
auto animSetData = xmlLoader->getAnimationSet(name);
return std::make_shared<AnimationSet>(entityid, this, animSetData);
}
// this will need some work, but for now where there is only one weapon type we can work with it
// TODO: Allow different weapon types!
/*

View file

@ -127,50 +127,72 @@ bool XMLLoader::loadEntityData(const char* xmlFile, SceneData* out)
tinyxml2::XMLElement* playerElement = entities->FirstChildElement("player");
if (playerElement == NULL)
return false;
int x, y;
float frameSize = 64.f;
bool isDirectional = false;
const char* spritePath, * weaponName = "pistolGun";
if (playerElement->QueryIntAttribute("x", &x) != tinyxml2::XML_SUCCESS ||
playerElement->QueryIntAttribute("y", &y) != tinyxml2::XML_SUCCESS)
EntityData playData;
const char *graphic, *weaponName = "pistolGun";
if (playerElement->QueryIntAttribute("x", &playData.x) != tinyxml2::XML_SUCCESS ||
playerElement->QueryIntAttribute("y", &playData.y) != tinyxml2::XML_SUCCESS)
return false;
playerElement->QueryStringAttribute("weapon", &weaponName);
tinyxml2::XMLElement* sprite = playerElement->FirstChildElement("sprite");
if (sprite == NULL)
return false;
if (sprite->QueryStringAttribute("file", &spritePath) != tinyxml2::XML_SUCCESS)
return false;
sprite->QueryFloatAttribute("framesize", &frameSize);
sprite->QueryBoolAttribute("directional", &isDirectional);
out->entities.push_back({ true, x, y, spritePath, frameSize, weaponName, "", isDirectional});
tinyxml2::XMLElement* anim = playerElement->FirstChildElement("animation");
if (anim == NULL)
{
playData.animated = false;
tinyxml2::XMLElement* sprite = playerElement->FirstChildElement("sprite");
if (sprite == NULL)
return false;
if (sprite->QueryStringAttribute("file", &graphic) != tinyxml2::XML_SUCCESS)
return false;
}
else
{
playData.animated = true;
if (anim->QueryStringAttribute("name", &graphic) != tinyxml2::XML_SUCCESS)
return false;
}
playData.isPlayer = true;
playData.graphic = graphic;
playData.weapon = weaponName;
out->entities.push_back(playData);
// Adding every other entity...
// TODO: Add npcs to game and enable their use with XMLLoader
for (tinyxml2::XMLElement* e = entities->FirstChildElement("entity"); e != NULL; e = e->NextSiblingElement("entity"))
{
int x, y;
float frameSize = 64.f;
const char* spritePath, * weaponName = "pistolGun", * scriptPath;
bool isDirectional = false;
if (e->QueryIntAttribute("x", &x) != tinyxml2::XML_SUCCESS ||
e->QueryIntAttribute("y", &y) != tinyxml2::XML_SUCCESS)
EntityData data;
const char *graphic, *weaponName = "pistolGun", *scriptPath;
if (e->QueryIntAttribute("x", &data.x) != tinyxml2::XML_SUCCESS ||
e->QueryIntAttribute("y", &data.y) != tinyxml2::XML_SUCCESS)
return false;
e->QueryStringAttribute("weapon", &weaponName);
tinyxml2::XMLElement* sprite = e->FirstChildElement("sprite");
if (sprite == NULL)
return false;
if (sprite->QueryStringAttribute("file", &spritePath) != tinyxml2::XML_SUCCESS)
return false;
sprite->QueryFloatAttribute("frameSize", &frameSize);
sprite->QueryBoolAttribute("directional", &isDirectional);
tinyxml2::XMLElement* script = e->FirstChildElement("script");
if (script == NULL || script->QueryStringAttribute("file", &scriptPath))
tinyxml2::XMLElement* anim = e->FirstChildElement("animation");
if (anim == NULL)
{
out->entities.push_back({ false, x, y, spritePath, frameSize, weaponName, "", isDirectional });
continue;
data.animated = false;
tinyxml2::XMLElement* sprite = e->FirstChildElement("sprite");
if (sprite == NULL)
continue;
if (sprite->QueryStringAttribute("file", &graphic) != tinyxml2::XML_SUCCESS)
continue;
}
out->entities.push_back({ false, x, y, spritePath, frameSize, weaponName, scriptPath, isDirectional });
else
{
data.animated = true;
if (anim->QueryStringAttribute("name", &graphic) != tinyxml2::XML_SUCCESS)
continue;
}
tinyxml2::XMLElement* script = e->FirstChildElement("script");
script->QueryStringAttribute("file", &scriptPath);
data.isPlayer = false;
data.graphic = graphic;
data.script = scriptPath;
data.weapon = weaponName;
out->entities.push_back(data);
}
return true;
@ -207,11 +229,13 @@ bool XMLLoader::loadWeapons(const char* weaponFolder)
{
// populate weapon data into this temp buffer, then push it into our list of weapons
WeaponData data;
const char* name, * sprite, * bulletSprite, * script;
const char *name, *graphic, *bulletGraphic, *script;
// Getting top level weapon data, attribs in the weapon Node
if (weapon->QueryStringAttribute("name", &name) != tinyxml2::XML_SUCCESS)
continue;
weapon->QueryFloatAttribute("fireSpeed", &data.fireSpeed);
weapon->QueryIntAttribute("maxAmmo", &data.maxAmmo);
weapon->QueryIntAttribute("clipSize", &data.clipSize);
// Getting script file if Node exists
tinyxml2::XMLElement* wepScript = weapon->FirstChildElement("script");
if (wepScript)
@ -219,13 +243,31 @@ bool XMLLoader::loadWeapons(const char* weaponFolder)
else
script = "";
// Getting weapon sprite information, held in the sprite node
tinyxml2::XMLElement* wepSprite = weapon->FirstChildElement("sprite");
if (wepSprite)
tinyxml2::XMLElement* anim = weapon->FirstChildElement("animation");
if (anim)
{
wepSprite->QueryStringAttribute("file", &sprite);
wepSprite->QueryBoolAttribute("animated", &data.animated);
if (data.animated)
wepSprite->QueryFloatAttribute("frameSize", &data.frameSize);
data.animated = true;
if (anim->QueryStringAttribute("name", &graphic) != tinyxml2::XML_SUCCESS)
continue;
if (anim->ChildElementCount() != 0)
{
tinyxml2::XMLElement* size = anim->FirstChildElement("size");
size->QueryFloatAttribute("x", &data.sizeX);
size->QueryFloatAttribute("y", &data.sizeY);
tinyxml2::XMLElement* offset = anim->FirstChildElement("offset");
offset->QueryFloatAttribute("x", &data.offsetX);
offset->QueryFloatAttribute("y", &data.offsetY);
}
}
else
{
tinyxml2::XMLElement* wepSprite = weapon->FirstChildElement("sprite");
if (wepSprite == NULL)
continue;
wepSprite->QueryStringAttribute("file", &graphic);
data.animated = false;
if (wepSprite->ChildElementCount() != 0)
{
tinyxml2::XMLElement* size = wepSprite->FirstChildElement("size");
@ -236,31 +278,32 @@ bool XMLLoader::loadWeapons(const char* weaponFolder)
offset->QueryFloatAttribute("y", &data.offsetY);
}
}
else {
// If we don't have a sprite element, default to pistol
sprite = "sprites/pistolAtlas256.png";
data.animated = true;
data.frameSize = 256.f;
data.sizeX = 30.f;
data.sizeY = 30.f;
}
// getting bullet information held in the bullet node and each child node held therein
tinyxml2::XMLElement* bullet = weapon->FirstChildElement("bullet");
if (bullet->QueryStringAttribute("sprite", &bulletSprite) != tinyxml2::XML_SUCCESS ||
bullet->QueryBoolAttribute( "animated", &data.bulletAnimated) != tinyxml2::XML_SUCCESS ||
bullet->FirstChildElement("size")->QueryFloatAttribute("x", &data.bulletSizeX) != tinyxml2::XML_SUCCESS ||
if (bullet->FindAttribute("anim"))
{
data.bulletAnimated = true;
if (bullet->QueryStringAttribute("anim", &bulletGraphic) != tinyxml2::XML_SUCCESS)
continue;
}
else
{
data.bulletAnimated = false;
if (bullet->QueryStringAttribute("sprite", &bulletGraphic) != tinyxml2::XML_SUCCESS)
continue;
}
if (bullet->FirstChildElement("size")->QueryFloatAttribute("x", &data.bulletSizeX) != tinyxml2::XML_SUCCESS ||
bullet->FirstChildElement("size")->QueryFloatAttribute("y", &data.bulletSizeY) != tinyxml2::XML_SUCCESS)
continue;
data.name = name;
data.script = script;
data.sprite = sprite;
data.bulletSprite = bulletSprite;
data.graphic = graphic;
data.bulletGraphic = bulletGraphic;
if (data.animated)
if (bullet->QueryFloatAttribute("frameSize", &data.bulletFrameSize) != tinyxml2::XML_SUCCESS)
continue;
tinyxml2::XMLElement* spread = bullet->FirstChildElement("spread");
tinyxml2::XMLElement* speed = bullet->FirstChildElement("speed");
tinyxml2::XMLElement* drop = bullet->FirstChildElement("drop");
@ -281,4 +324,48 @@ bool XMLLoader::loadWeapons(const char* weaponFolder)
}
}
return (!weaponData.empty());
}
bool XMLLoader::loadAnimations(const char* animationFolder)
{
std::filesystem::path folder(animationFolder);
if (!std::filesystem::exists(folder) || !std::filesystem::is_directory(folder))
return false;
for (auto& file : std::filesystem::directory_iterator(folder))
{
if (!file.path().has_extension() || !file.path().has_filename() || !file.exists() || file.is_directory())
continue;
tinyxml2::XMLDocument doc;
if (doc.LoadFile(file.path().generic_string().c_str()) != tinyxml2::XML_SUCCESS)
continue;
tinyxml2::XMLElement* anims = doc.FirstChildElement("animations");
bool directional = false;
anims->QueryBoolAttribute("directional", &directional);
if (anims == NULL)
continue;
for (tinyxml2::XMLElement* e = anims->FirstChildElement("animation"); e != NULL; e = e->NextSiblingElement("animation"))
{
AnimationData animData;
const char *name, *type, *spriteAtlas;
if (e->QueryStringAttribute("name", &name) != tinyxml2::XML_SUCCESS ||
e->QueryStringAttribute("type", &type) != tinyxml2::XML_SUCCESS)
continue;
tinyxml2::XMLElement* fps = e->FirstChildElement("FPS");
fps->QueryFloatText(&animData.FPS);
tinyxml2::XMLElement* sprite = e->FirstChildElement("sprite");
if (sprite->QueryStringAttribute("path", &spriteAtlas) != tinyxml2::XML_SUCCESS ||
sprite->QueryFloatAttribute("frameSize", &animData.frameSize) != tinyxml2::XML_SUCCESS)
continue;
animData.name = name;
animData.type = type;
animData.spriteAtlas = spriteAtlas;
animData.animSet = file.path().stem().string();
animData.directional = directional;
animations.try_emplace(animData.name, std::make_shared<AnimationData>(animData));
}
}
return true;
}