diff --git a/Resources/animations/bubble_anim.xml b/Resources/animations/bubble_anim.xml
new file mode 100644
index 0000000..d5c71bb
--- /dev/null
+++ b/Resources/animations/bubble_anim.xml
@@ -0,0 +1,6 @@
+
+
+ 2
+
+
+
\ No newline at end of file
diff --git a/Resources/animations/machine_gun_anim.xml b/Resources/animations/machine_gun_anim.xml
new file mode 100644
index 0000000..d308c6f
--- /dev/null
+++ b/Resources/animations/machine_gun_anim.xml
@@ -0,0 +1,10 @@
+
+
+ 4
+
+
+
+ 2
+
+
+
diff --git a/Resources/animations/pistol_anim.xml b/Resources/animations/pistol_anim.xml
new file mode 100644
index 0000000..d2638ab
--- /dev/null
+++ b/Resources/animations/pistol_anim.xml
@@ -0,0 +1,6 @@
+
+
+ 4
+
+
+
diff --git a/Resources/animations/player_anim.xml b/Resources/animations/player_anim.xml
new file mode 100644
index 0000000..0b375f9
--- /dev/null
+++ b/Resources/animations/player_anim.xml
@@ -0,0 +1,11 @@
+
+
+ 7
+
+
+
+
+ 5
+
+
+
diff --git a/Resources/animations/tmp_enemy_anim.xml b/Resources/animations/tmp_enemy_anim.xml
new file mode 100644
index 0000000..68500ec
--- /dev/null
+++ b/Resources/animations/tmp_enemy_anim.xml
@@ -0,0 +1,6 @@
+
+
+ 7
+
+
+
diff --git a/Resources/scenes/debugScene.xml b/Resources/scenes/debugScene.xml
index 62da31c..11d39e8 100644
--- a/Resources/scenes/debugScene.xml
+++ b/Resources/scenes/debugScene.xml
@@ -35,18 +35,18 @@
-
+
-
+
-
+
-
+
diff --git a/Resources/sprites/player3AtlasIdle64.png b/Resources/sprites/player3AtlasIdle64.png
new file mode 100644
index 0000000..541c7de
Binary files /dev/null and b/Resources/sprites/player3AtlasIdle64.png differ
diff --git a/Resources/sprites/player3AtlasMove64.png b/Resources/sprites/player3AtlasMove64.png
new file mode 100644
index 0000000..9e46f29
Binary files /dev/null and b/Resources/sprites/player3AtlasMove64.png differ
diff --git a/Resources/weapons/bubblegun.xml b/Resources/weapons/bubblegun.xml
index ff0536d..b6ee9b2 100644
--- a/Resources/weapons/bubblegun.xml
+++ b/Resources/weapons/bubblegun.xml
@@ -1,8 +1,8 @@
-
-
+
+
60
20.0
250.0
@@ -11,13 +11,13 @@
-
+
-
+
-
-
+
+
50
60.0
550.0
@@ -26,12 +26,12 @@
-
-
+
+
-
-
+
+
20
60.0
950.0
@@ -40,12 +40,12 @@
-
-
+
+
-
-
+
+
5.0
50.0
500.0
diff --git a/YuppleMayham/CMakeLists.txt b/YuppleMayham/CMakeLists.txt
index 818b00e..77a6d5e 100644
--- a/YuppleMayham/CMakeLists.txt
+++ b/YuppleMayham/CMakeLists.txt
@@ -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/ $)
diff --git a/YuppleMayham/include/gameplay/entity.h b/YuppleMayham/include/gameplay/entity.h
index 4741c54..cbaa5ec 100644
--- a/YuppleMayham/include/gameplay/entity.h
+++ b/YuppleMayham/include/gameplay/entity.h
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#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 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 physics;
bool isMoving = false;
+ bool wasMoving = false;
bool isRotatable = true;
bool flipped = false;
diff --git a/YuppleMayham/include/gameplay/gameactor.h b/YuppleMayham/include/gameplay/gameactor.h
index 1f52643..835b82d 100644
--- a/YuppleMayham/include/gameplay/gameactor.h
+++ b/YuppleMayham/include/gameplay/gameactor.h
@@ -19,7 +19,7 @@ class EventManager;
class GameActor : public Entity
{
public:
- GameActor(const std::shared_ptr& shader) : Entity(shader) { actorid = SDL_GetTicks(); }
+ GameActor(const std::shared_ptr& shader) : Entity(shader) {}
~GameActor();
void addComponent(std::shared_ptr component);
@@ -28,8 +28,7 @@ public:
void update(float deltaTime) override;
void render(const std::shared_ptr& camera) override;
- const std::optional>& getHeldWeapon() const;
- const int getActorID() const { return actorid; }
+ const std::optional> getHeldWeapon() const;
void setRotation(const float& rotation) override;
@@ -52,7 +51,6 @@ private:
using component_vector_t = std::vector>;
using weapon_vector_t = std::vector>;
- int actorid;
component_vector_t components;
weapon_vector_t weapons;
size_t currentWeaponIndex = 0;
diff --git a/YuppleMayham/include/gameplay/weapons/bullet.h b/YuppleMayham/include/gameplay/weapons/bullet.h
index 1806373..f422729 100644
--- a/YuppleMayham/include/gameplay/weapons/bullet.h
+++ b/YuppleMayham/include/gameplay/weapons/bullet.h
@@ -5,6 +5,7 @@
#include
#include
class Component;
+class AnimationSet;
class Camera;
class Bullet : public Entity
diff --git a/YuppleMayham/include/gameplay/weapons/weapon.h b/YuppleMayham/include/gameplay/weapons/weapon.h
index 03fb8d8..40b1723 100644
--- a/YuppleMayham/include/gameplay/weapons/weapon.h
+++ b/YuppleMayham/include/gameplay/weapons/weapon.h
@@ -30,6 +30,7 @@ public:
Weapon(const WeaponData* data, const std::shared_ptr& weaponShader, const std::shared_ptr& bulletShader, ResourceManager* resourceManager);
void setWielder(const std::shared_ptr& 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& script);
void hookEventManager(const std::shared_ptr& eventManager);
void shoot();
+ void reload();
void update(float deltaTime);
void render(const std::shared_ptr& 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 target, std::shared_ptr 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;
diff --git a/YuppleMayham/include/graphics/animation.h b/YuppleMayham/include/graphics/animation.h
new file mode 100644
index 0000000..8cbc61c
--- /dev/null
+++ b/YuppleMayham/include/graphics/animation.h
@@ -0,0 +1,102 @@
+#ifndef _H_ANIMATION_H
+#define _H_ANIMATION_H
+
+#include
+#include
+#include
+#include
+
+#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& 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;
+
+ 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> animSet);
+ AnimationSet(const int& entityid, const std::unordered_map>& animations);
+
+ std::shared_ptr& operator [](std::string animType) { return anims[animType]; }
+
+ void addAnimation(const std::shared_ptr 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& e);
+
+private:
+ int entityid;
+ bool isDirectional;
+
+ Direction facing = Direction::Down;
+
+ std::unordered_map> anims;
+ std::shared_ptr curAnim;
+
+ std::shared_ptr eventManager;
+};
+
+#endif // _H_ANIMATION_H
\ No newline at end of file
diff --git a/YuppleMayham/include/graphics/sprite.h b/YuppleMayham/include/graphics/sprite.h
index 8c2656c..139e9a9 100644
--- a/YuppleMayham/include/graphics/sprite.h
+++ b/YuppleMayham/include/graphics/sprite.h
@@ -23,8 +23,6 @@ public:
void bind();
virtual void draw() = 0;
- virtual void play() = 0;
- virtual void idle() = 0;
virtual std::shared_ptr clone() const = 0;
protected:
@@ -42,8 +40,6 @@ public:
~SpriteStatic();
void draw() override;
- void play() override { /*unused*/ }
- void idle() override { /*unused*/ }
std::shared_ptr clone() const override {
return std::make_shared(*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 clone() const override {
- return std::make_shared(*this);
- }
-
- bool isPlaying = false;
-
-private:
- void SetupAnimated(float frameSize);
-
struct VertexIDs {
unsigned VAO, VBO;
};
- unsigned EBO;
-
- // Vertex array buffer IDs
- std::vector 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 clone() const override {
- return std::make_shared(*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 clone() const override {
+ return std::make_shared(*this);
+ }
private:
void Setup(float frameSize);
+ void SetupDirectional(float frameSize);
- struct VertexIDs {
- unsigned VAO, VBO;
- };
unsigned EBO;
+ bool isDirectional;
- std::unordered_map idleIDs;
- std::unordered_map> 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 singleIDs;
+
+ // Vertex ids if the sprite atlas is directional
+ std::unordered_map> directionalIDs;
};
#endif // _H_SPRITE_H
\ No newline at end of file
diff --git a/YuppleMayham/include/utility/component.h b/YuppleMayham/include/utility/component.h
index 24cddaf..2f4537f 100644
--- a/YuppleMayham/include/utility/component.h
+++ b/YuppleMayham/include/utility/component.h
@@ -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;
};
@@ -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), Component(nullptr) {}
- SpriteComponent(std::shared_ptr sprite, const std::shared_ptr& eventManager) :
- Component(eventManager), sprite(sprite) {
- auto directionalSprite = std::dynamic_pointer_cast(sprite);
- if (directionalSprite)
- {
- eventManager->subscribe("OnDirectionChange", [sprite, this](std::shared_ptr e) {
- auto directionEvent = std::static_pointer_cast(e);
- auto directionSprite = std::static_pointer_cast(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& getSprite() { return sprite; }
@@ -84,6 +70,42 @@ private:
std::shared_ptr sprite;
};
+class AnimationComponent : public Component
+{
+public:
+ AnimationComponent(std::shared_ptr animSet) : animSet(animSet), Component(nullptr) {};
+ AnimationComponent(std::shared_ptr animSet, const std::shared_ptr& 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& getAnimationSet() { return animSet; }
+
+ ~AnimationComponent() { }
+
+private:
+ std::shared_ptr animSet;
+};
+
class ShaderComponent : public Component
{
public:
diff --git a/YuppleMayham/include/utility/direction.h b/YuppleMayham/include/utility/direction.h
index 2333a15..ab039cd 100644
--- a/YuppleMayham/include/utility/direction.h
+++ b/YuppleMayham/include/utility/direction.h
@@ -4,7 +4,8 @@
#include
enum class Direction {
-
+
+ None = 0,
Down = 1,
Right = 2,
Left = 3,
diff --git a/YuppleMayham/include/utility/events.h b/YuppleMayham/include/utility/events.h
index c33811c..100f1af 100644
--- a/YuppleMayham/include/utility/events.h
+++ b/YuppleMayham/include/utility/events.h
@@ -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 {
diff --git a/YuppleMayham/include/utility/resourcemanager.h b/YuppleMayham/include/utility/resourcemanager.h
index c2161f9..9d86652 100644
--- a/YuppleMayham/include/utility/resourcemanager.h
+++ b/YuppleMayham/include/utility/resourcemanager.h
@@ -9,9 +9,11 @@
#include
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 loadSpriteAnimated (const std::string& path, float frameSize);
- std::shared_ptr loadSpriteDirAnimated (const std::string& path, float frameSize);
+ std::shared_ptr loadSpriteAtlas (const std::string& path, float frameSize, bool isDirectional = false);
std::shared_ptr loadSpriteStatic (const std::string& path);
std::shared_ptr loadAIScript (const std::string& path);
std::shared_ptr loadWeaponScript (const std::string& path);
std::shared_ptr loadTileSet (const std::string& path, float frameSize);
- std::shared_ptr loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath);
- std::shared_ptr loadWeapon (const std::string& name, std::shared_ptr weaponShader, std::shared_ptr bulletShader);
- std::shared_ptr loadScene (const std::string& id);
+ std::shared_ptr loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath);
+ std::shared_ptr loadWeapon (const std::string& name, std::shared_ptr weaponShader, std::shared_ptr bulletShader);
+ std::shared_ptr loadScene (const std::string& id);
+ std::shared_ptr loadAnimationSet(const std::string& name, int entityid = 0);
void clearResources();
diff --git a/YuppleMayham/include/utility/xmlloader.h b/YuppleMayham/include/utility/xmlloader.h
index ea517f0..7a6fbbc 100644
--- a/YuppleMayham/include/utility/xmlloader.h
+++ b/YuppleMayham/include/utility/xmlloader.h
@@ -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 getSceneData(const std::string& id) const {
try {
@@ -66,15 +83,39 @@ public:
return nullptr;
}
}
+
+ const std::shared_ptr 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> getAnimationSet(const std::string& set) const {
+ std::vector> 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> scenes;
+ std::unordered_map> animations;
std::vector weaponData;
};
diff --git a/YuppleMayham/src/gameplay/gameactor.cpp b/YuppleMayham/src/gameplay/gameactor.cpp
index 9c834dc..02a38af 100644
--- a/YuppleMayham/src/gameplay/gameactor.cpp
+++ b/YuppleMayham/src/gameplay/gameactor.cpp
@@ -10,11 +10,11 @@ GameActor::~GameActor() { }
void GameActor::addComponent(std::shared_ptr component)
{
- component->ownerid = actorid;
+ component->ownerid = entityid;
components.push_back(component);
}
-const std::optional>& GameActor::getHeldWeapon() const
+const std::optional> 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->setWielder(std::shared_ptr(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(actorid, newDir));
+ eventManager->notify(std::make_shared(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(entityid));
+ wasMoving = true;
+ }
+ else if (!isMoving && wasMoving)
+ {
+ eventManager->notify(std::make_shared(entityid));
+ wasMoving = false;
+ }
+ }
+ isMoving = false;
}
void GameActor::render(const std::shared_ptr& 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(actorid, getDirectionFromRotation(newRotation)));
+ eventManager->notify(std::make_shared(entityid, getDirectionFromRotation(newRotation)));
//setRotation(glm::degrees(glm::atan(direction.y, direction.x)));
this->rotation = newRotation;
}
diff --git a/YuppleMayham/src/gameplay/scene.cpp b/YuppleMayham/src/gameplay/scene.cpp
index 8be8daf..4eb674e 100644
--- a/YuppleMayham/src/gameplay/scene.cpp
+++ b/YuppleMayham/src/gameplay/scene.cpp
@@ -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(playerShader);
- std::shared_ptr 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(entityAnimation, eventManager));
}
else
- entitySprite = resourceManager->loadSpriteAnimated(entityData.sprite, entityData.frameSize);
+ {
+ auto entitySprite = resourceManager->loadSpriteStatic(entityData.graphic);
+ entity->addComponent(std::make_shared(entitySprite));
+ }
auto defaultWeapon = resourceManager->loadWeapon("pistolGun", weaponShader, bubbleShader);
auto entityWeapon = resourceManager->loadWeapon(entityData.weapon, weaponShader, bubbleShader);
- entity->addComponent(std::make_shared(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(ai));
}
}
- entities.emplace(entity->getActorID(), entity);
+ entities.emplace(entity->getEntityID(), entity);
}
physicsEngine->loadCollisionMap(map->getCollisionMap(), sceneData->map.tileSize);
diff --git a/YuppleMayham/src/gameplay/weapons/weapon.cpp b/YuppleMayham/src/gameplay/weapons/weapon.cpp
index 0f9709c..2028e21 100644
--- a/YuppleMayham/src/gameplay/weapons/weapon.cpp
+++ b/YuppleMayham/src/gameplay/weapons/weapon.cpp
@@ -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& weaponShader, const std::shared_ptr& bulletShader, ResourceManager* resourceManager)
:
@@ -21,25 +19,28 @@ Weapon::Weapon(const WeaponData* data, const std::shared_ptr& 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(-data->bulletSpread, data->bulletSpread)),
bulletModifer (std::make_shared(data->modMin, data->modMax)),
fireSpeed (data->fireSpeed),
- bulletManager (std::make_shared()),
- bulletSprite (data->bulletAnimated ?
- std::make_shared(resourceManager->loadSpriteAnimated(data->bulletSprite, data->bulletFrameSize))
- : std::make_shared(resourceManager->loadSpriteStatic(data->bulletSprite)))
+ bulletManager (std::make_shared())
{
+ if (data->bulletAnimated)
+ bulletSprite = std::make_shared(resourceManager->loadAnimationSet(data->bulletGraphic, entityid));
+ else
+ bulletSprite = std::make_shared(resourceManager->loadSpriteStatic(data->bulletGraphic));
+
if (data->animated)
{
- addComponent(std::make_shared(resourceManager->loadSpriteAnimated(data->sprite, data->frameSize)));
- for (auto& c : components)
- c->play();
+ addComponent(std::make_shared(resourceManager->loadAnimationSet(data->graphic, entityid)));
}
else
- addComponent(std::make_shared(resourceManager->loadSpriteStatic(data->sprite)));
+ addComponent(std::make_shared(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& 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(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)
{
this->eventManager = eventManager;
+
+ for (auto& component : components)
+ {
+ auto animComponent = std::dynamic_pointer_cast(component);
+ if (animComponent != nullptr)
+ {
+ animComponent->getAnimationSet()->attachEventManager(eventManager);
+ }
+ }
+
+ this->eventManager->subscribe("OnAnimationFinished", [&, this](std::shared_ptr e) {
+ auto animFinished = std::static_pointer_cast(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(entityid));
+ }
}
bulletManager->update(deltaTime);
}
@@ -121,6 +170,7 @@ void Weapon::render(const std::shared_ptr& 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(wielder->getActorID(), bulletShader, data.origin, data.direction, bulletSpeed, bulletDrop, bulletSize);
+ auto bullet = std::make_shared(wielder->getEntityID(), bulletShader, data.origin, data.direction, bulletSpeed, bulletDrop, bulletSize);
bullet->addComponent(bulletSprite);
- bullet->addPhysicsComponent(std::make_shared(PhysicsComponentFactory::makeBullet(wielder->getActorID(), data.origin, data.mass, bulletSize.x / 2)));
+ bullet->addPhysicsComponent(std::make_shared(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;
+ }
+}
+*/
diff --git a/YuppleMayham/src/graphics/animation.cpp b/YuppleMayham/src/graphics/animation.cpp
new file mode 100644
index 0000000..ebfeac3
--- /dev/null
+++ b/YuppleMayham/src/graphics/animation.cpp
@@ -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& 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> animSet) : entityid(entityid)
+{
+ for (const auto& anim : animSet)
+ {
+ anims.try_emplace(anim->type, std::make_shared(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>& 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& e)
+{
+ eventManager = e;
+
+ eventManager->subscribe("OnDirectionChange", [this](std::shared_ptr e) {
+ auto directionEvent = std::static_pointer_cast(e);
+ if (directionEvent->entityid == entityid)
+ setFacingDir(directionEvent->direction);
+ });
+
+ eventManager->subscribe("OnEntityMove", [this](std::shared_ptr e) {
+ auto moveEvent = std::static_pointer_cast(e);
+ if (moveEvent->entityid == entityid)
+ {
+ if (isDirectional)
+ {
+ if (anims["move"] != NULL)
+ curAnim = anims["move"];
+ }
+ else
+ play();
+ }
+ });
+
+ eventManager->subscribe("OnEntityStop", [this](std::shared_ptr e) {
+ auto stopEvent = std::static_pointer_cast(e);
+ if (stopEvent->entityid == entityid)
+ {
+ if (isDirectional)
+ {
+ if (anims["idle"] != NULL)
+ curAnim = anims["idle"];
+ }
+ else
+ stop();
+ }
+ });
+
+ eventManager->subscribe("OnEntityReload", [this](std::shared_ptr e) {
+ auto reloadEvent = std::static_pointer_cast(e);
+ if (reloadEvent->entityid == entityid)
+ {
+ if (anims["reload"] != NULL)
+ curAnim = anims["reload"];
+ }
+ });
+
+ eventManager->subscribe("OnEntityFinishReload", [this](std::shared_ptr e) {
+ auto reloadEvent = std::static_pointer_cast(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(entityid, curAnim->getType()));
+ }
+}
+
diff --git a/YuppleMayham/src/graphics/sprite.cpp b/YuppleMayham/src/graphics/sprite.cpp
index 12b1415..1708e76 100644
--- a/YuppleMayham/src/graphics/sprite.cpp
+++ b/YuppleMayham/src/graphics/sprite.cpp
@@ -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 > 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(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 item) {
- glDeleteBuffers(1, &item.second.VBO);
- glDeleteVertexArrays(1, &item.second.VAO);
- };
- auto deleteBufferList = [](std::pair > 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();
-}
diff --git a/YuppleMayham/src/utility/resourcemanager.cpp b/YuppleMayham/src/utility/resourcemanager.cpp
index fb2d044..a97f06b 100644
--- a/YuppleMayham/src/utility/resourcemanager.cpp
+++ b/YuppleMayham/src/utility/resourcemanager.cpp
@@ -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 ResourceManager::loadSpriteAnimated(const std::string& path, float frameSize)
+std::shared_ptr 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(path.c_str(), frameSize);
+ return std::dynamic_pointer_cast(iterator->second);
+ auto sprite = std::make_shared(path.c_str(), frameSize, isDirectional);
sprites[path] = sprite;
return sprite;
}
-std::shared_ptr ResourceManager::loadSpriteDirAnimated(const std::string& path, float frameSize)
-{
- return std::make_shared(path.c_str(), frameSize);
-}
-
std::shared_ptr ResourceManager::loadSpriteStatic(const std::string& path)
{
auto iterator = sprites.find(path);
@@ -78,6 +74,12 @@ std::shared_ptr ResourceManager::loadScene(const std::string& id)
return xmlLoader->getSceneData(id);
}
+std::shared_ptr ResourceManager::loadAnimationSet(const std::string& name, int entityid)
+{
+ auto animSetData = xmlLoader->getAnimationSet(name);
+ return std::make_shared(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!
/*
diff --git a/YuppleMayham/src/utility/xmlloader.cpp b/YuppleMayham/src/utility/xmlloader.cpp
index 6444475..e0f4298 100644
--- a/YuppleMayham/src/utility/xmlloader.cpp
+++ b/YuppleMayham/src/utility/xmlloader.cpp
@@ -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(animData));
+ }
+ }
+ return true;
}
\ No newline at end of file