Compare commits

..

No commits in common. "3437441feb63b9b7068942df535ecb73d91c945a" and "6ad1272d7d0ff818f824ce523b2921b2b313a027" have entirely different histories.

23 changed files with 1550 additions and 1538 deletions

1
.gitignore vendored
View file

@ -366,4 +366,3 @@ FodyWeavers.xsd
*.swp *.swp
/Resources/scenes/.debugScene.xml.swp /Resources/scenes/.debugScene.xml.swp
compile_commands.json compile_commands.json
compile_flags.txt

View file

@ -1,66 +0,0 @@
# Personal Game Engine
A modular, event driven 2D game engine, built from scratch to learn engine architecture and low-level graphics programming.
Supports sprite-based animation, Lua scripting, XML-driven definitions and Tiled map creation.
---
## Features
- Event-based animation and sound system
- Animation and tile-sets supported by texture atlases
- Map creation and collision map support in Tiled map editor
- Component-based entity structure
- Basic audio engine integration via OpenAL (XML definitions)
- Basic physics system
- Lua scripted enemy and weapon behavior
---
## Current Status
**Work in Progress** - This project is still under active development, I am currently cleaning up my resource management system for better ease of use for future users of this engine.
---
## Dependencies
- SDL2, SDL2_image
- OpenGL 3.3
- sol2 (Lua binding)
- TinyXML2
- OpenAL
- Freetype
- GLM
- pkg-config
- CMake
## Build Instructions
1. Clone the repo (git.wienermeister.net/snakert12345/yupplemayham.git)
2. Install list of dependencies. Ensure they're available via pkg-config
3. Build with CMake
```bash
git clone https://git.wienermeister.net/snakert12345/yupplemayham.git
cd yupplemayham
mkdir build && cd build
cmake ..
make
```
---
## Screenshot
![Screenshot](https://git.wienermeister.net/attachments/79b04767-d675-4aa3-a17e-969341105579)
---
## Roadmap
- Save/Load system
- Modular and scalable menu system
- In-game dialog and conversation systems
- Expanded combat system

View file

@ -1,30 +1,42 @@
-- global vars -- global vars
MoveLeft = true patrolDestination = { x=500.0, y=500.0, z=0.0 }
moveLeft = true
-- helper functions -- helper functions
local function watchPosition(actor, pos) function watchPosition(actor, pos)
local y = pos.y - actor.position.y local y = pos.y - actor.position.y
local x = pos.x - actor.position.x local x = pos.x - actor.position.x
local rotation = math.atan2(y, x) local rotation = math.atan2(y, x)
actor.rotation = math.deg(rotation) actor.rotation = math.deg(rotation)
end end
local function distance(a, b) function distance(a, b)
local dx = a.x - b.x local dx = a.x - b.x
local dy = a.y - b.y local dy = a.y - b.y
return math.sqrt(dx * dx + dy * dy) return math.sqrt(dx * dx + dy * dy)
end end
function moveTo(actor, pos)
local a = actor
local p = pos
watchPosition(a, p)
if distance(a.position, p) < 50 then
return true
end
a:moveForward()
return false
end
-- Behaviour Functions called on AI -- Behaviour Functions called on AI
-- These functions are ai behaviour functions called in the game -- These functions are ai behaviour functions called in the game
-- The AI will be spawned in idle mode, so if you want to put the bot into patrol mode -- The AI will be spawned in idle mode, so if you want to put the bot into patrol mode
-- It's on you to do that in this function. -- It's on you to do that in this function.
function idle(actor, target) function idle(actor, target)
local a = actor local a = actor
local t = target local t = target
if t ~= nil then if t ~= nil then
-- print("target is at " .. target.position.x) -- print("target is at " .. target.position.x)
-- watchPosition(actor, target.position) -- watchPosition(actor, target.position)
ai.state = AIState.Patrol ai.state = AIState.Patrol
a.rotation = 180 a.rotation = 180
end end
@ -36,12 +48,24 @@ end
function patrol(actor, target) function patrol(actor, target)
local a = actor local a = actor
local t = target local t = target
if t ~= nil then
-- print("target is at " .. target.position.x)
end
--if moveTo(actor, patrolDestination) == true then
-- patrolDestination = { x=math.random(400.0, 750.0), y=math.random(400.0, 750.0), z=0.0 }
--end
-- performRaycast returns if true if the raycast hits the target position it also sets the getter function
-- distFromWall, at bot creation distFromWall is and infinite float value.
-- This performRaycast function is highly discourage due to slow down
--if raycaster:performRaycast(actor.position, actor.rotation, target.position) == true then
--ai.state = AIState.Alert
--end
if raycaster:bresenhamRaycast(a.position, a.rotation, t.position) == true then if raycaster:bresenhamRaycast(a.position, a.rotation, t.position) == true then
--target hit! --target hit!
ai.state = AIState.Alert ai.state = AIState.Alert
end end
if raycaster:distFromWall() < 3 then if raycaster:distFromWall() < 3 then
local upOrDown = math.random(2) upOrDown = math.random(2)
if moveLeft == true then if moveLeft == true then
if upOrDown == 1 then if upOrDown == 1 then
a.rotation = 180 a.rotation = 180
@ -49,7 +73,7 @@ function patrol(actor, target)
a.rotation = 270 a.rotation = 270
end end
moveLeft = false moveLeft = false
else else
if upOrDown == 1 then if upOrDown == 1 then
a.rotation = 0 a.rotation = 0
else else
@ -66,8 +90,10 @@ function alert(actor, target)
local a = actor local a = actor
local t = target local t = target
if target ~= nil then if target ~= nil then
-- print("target is at " .. target.position.x)
watchPosition(a, t.position) watchPosition(a, t.position)
end end
--print("actor is alert at " .. actor.position.x)
if distance(a.position, t.position) > 300 then if distance(a.position, t.position) > 300 then
a:moveForward() a:moveForward()
end end

View file

@ -1,18 +1,18 @@
-- helper functions -- helper functions
local function lookAway(actor, pos) function lookAway(actor, pos)
local y = actor.position.y - pos.y y = actor.position.y - pos.y
local x = actor.position.x - pos.x x = actor.position.x - pos.x
local rotation = math.atan(y, x) rotation = math.atan(y, x)
actor.rotation = math.deg(rotation) actor.rotation = math.deg(rotation)
end end
local function distance(a, b) function distance(a, b)
return math.sqrt((math.abs(a.x - b.x) ^ 2) + (math.abs(a.y - b.y) ^ 2)) return math.sqrt((math.abs(a.x - b.x)^2) + (math.abs(a.y - b.y)^2))
end end
function idle(actor, target) function idle(actor, target)
-- do nothing here for now -- do nothing here for now
end end
function patrol(actor, target) function patrol(actor, target)

View file

@ -3,7 +3,7 @@
# #
find_package(SDL2 2.30.2 REQUIRED) find_package(SDL2 2.30.2 REQUIRED)
find_package(SDL2_image 2.8.2 REQUIRED) find_package(SDL2_IMAGE 2.8.2 REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(LuaJIT REQUIRED IMPORTED_TARGET GLOBAL luajit) pkg_check_modules(LuaJIT REQUIRED IMPORTED_TARGET GLOBAL luajit)
find_package(sol2 REQUIRED) find_package(sol2 REQUIRED)
@ -42,7 +42,6 @@ add_executable (YuppleMayham
"src/utility/ftfont.cpp" "src/utility/ftfont.cpp"
"src/graphics/sprite.cpp" "src/graphics/sprite.cpp"
"src/graphics/mesh.cpp" "src/graphics/mesh.cpp"
"src/graphics/glwindow.cpp"
"src/gameplay/entity.cpp" "src/gameplay/entity.cpp"
"src/gameplay/gameactor.cpp" "src/gameplay/gameactor.cpp"
"src/graphics/shader.cpp" "src/graphics/shader.cpp"
@ -105,6 +104,6 @@ endif()
target_include_directories(YuppleMayham PRIVATE "${PROJECT_SOURCE_DIR}/YuppleMayham/include" ${LuaJIT_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build}) target_include_directories(YuppleMayham PRIVATE "${PROJECT_SOURCE_DIR}/YuppleMayham/include" ${LuaJIT_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build})
target_link_libraries(YuppleMayham SDL2::SDL2main SDL2::SDL2 SDL2_image::SDL2_image openal glm::glm tinyxml2 freetype ${LuaJIT_LINK_LIBRARIES}) target_link_libraries(YuppleMayham SDL2::SDL2main SDL2::SDL2 SDL2_image::SDL2_image openal glm::glm-header-only tinyxml2 freetype ${LuaJIT_LINK_LIBRARIES})
# TODO: Add tests and install targets if needed. # TODO: Add tests and install targets if needed.

View file

@ -3,43 +3,42 @@
#include "gameplay/entity.h" #include "gameplay/entity.h"
class Camera { class Camera
{
public: public:
Camera(float viewPortW, float viewPortH) Camera(float viewPortW, float viewPortH) :
: position(glm::vec3(0.0f, 0.0f, 0.0f)), position(glm::vec3(0.0f, 0.0f, 0.0f)),
front(glm::vec3(0.0f, 0.0f, -1.0f)), up(glm::vec3(0.0f, 1.0f, 0.0f)), front(glm::vec3(0.0f, 0.0f, -1.0f)),
viewPortW(viewPortW), viewPortH(viewPortH) {}; up(glm::vec3(0.0f, 1.0f, 0.0f)),
viewPortW(viewPortW),
viewPortH(viewPortH)
{};
void setPosition(const glm::vec3 &position) { this->position = position; } void setPosition(const glm::vec3& position) { this->position = position; }
const Entity *getTarget() const { return target; } const Entity* getTarget() const { return target; }
const glm::vec3 getCenterPos() const { const glm::vec3 getCenterPos() const { return glm::vec3(position.x + (viewPortW / 2.f), position.y + (viewPortH / 2.f), 0.0f); }
return glm::vec3(position.x + (viewPortW / 2.f), void setTarget(Entity* target) { this->target = target; }
position.y + (viewPortH / 2.f), 0.0f); void unsetTarget() { target = nullptr; }
} bool isTargeting() { return (target != nullptr); }
void setTarget(Entity *target) { this->target = target; }
void unsetTarget() { target = nullptr; }
bool isTargeting() { return (target != nullptr); }
void update(double deltaTime); void update(double deltaTime);
void setViewportSize(float width, float height);
const glm::vec3 worldToLocal(const glm::vec3 &worldCoordinates); const glm::vec3 worldToLocal(const glm::vec3& worldCoordinates);
glm::mat4 getViewMatrix();
glm::mat4 getProjectionMatrix();
const glm::vec3 getPosition() const { return position; }
glm::mat4 getViewMatrix();
glm::mat4 getProjectionMatrix();
const glm::vec3 getPosition() const { return position; }
private: private:
Entity *target = nullptr; Entity* target = nullptr;
glm::vec3 position; glm::vec3 position;
glm::vec3 front; glm::vec3 front;
glm::vec3 up; glm::vec3 up;
float viewPortW, viewPortH; float viewPortW, viewPortH;
float speed = 300.0f; float speed = 300.0f;
}; };
#endif // _H_CAMERA_H #endif // _H_CAMERA_H

View file

@ -1,12 +1,10 @@
#ifndef _H_GAME_H #ifndef _H_GAME_H
#define _H_GAME_H #define _H_GAME_H
#include <SDL_events.h>
#include <memory> #include <memory>
#include <SDL_events.h>
#include <utility/events.h> #include <utility/events.h>
#include "gameplay/camera.h"
class InputHandler; class InputHandler;
class Scene; class Scene;
class Text; class Text;
@ -15,42 +13,47 @@ class Renderer;
class AudioEngine; class AudioEngine;
class GLWindow; class GLWindow;
enum { GAME_QUITTING = 0, GAME_RUNNING = 1, GAME_MENU = 2, GAME_PLAYING = 4 }; enum {
GAME_QUITTING = 0,
GAME_RUNNING = 1,
GAME_MENU = 2,
GAME_PLAYING = 4
};
class Game { class Game
{
public: public:
Game() {} Game() {}
bool init(); bool init();
bool loadDebugScene(); bool loadDebugScene();
void captureInput(SDL_Event& e);
void executeInputs();
void captureInput(SDL_Event &e); void update(double deltaTime);
void executeInputs(); void render();
void update(double deltaTime); const unsigned getGameState() const { return game_state; }
void render();
const unsigned getGameState() const { return game_state; } const unsigned getWindowWidth() const;
const unsigned getWindowHeight() const;
const unsigned getWindowWidth() const; void quit();
const unsigned getWindowHeight() const; ~Game();
void quit();
~Game();
private: private:
unsigned game_state = GAME_QUITTING; unsigned game_state = GAME_QUITTING;
std::shared_ptr<GLWindow> window; std::shared_ptr<GLWindow> window;
std::unique_ptr<Camera> camera; std::shared_ptr<Scene> currentScene;
std::shared_ptr<Scene> currentScene; std::shared_ptr<InputHandler> inputHandler;
std::shared_ptr<InputHandler> inputHandler; std::shared_ptr<ResourceManager> resourceManager;
std::shared_ptr<ResourceManager> resourceManager; std::shared_ptr<Renderer> renderer;
std::shared_ptr<Renderer> renderer; std::shared_ptr<Text> textHandler;
std::shared_ptr<Text> textHandler; std::shared_ptr<AudioEngine> audioEngine;
std::shared_ptr<AudioEngine> audioEngine; std::shared_ptr<EventManager> globalEventManager;
std::shared_ptr<EventManager> globalEventManager;
}; };
#endif #endif

View file

@ -2,35 +2,52 @@
#define _H_GLWINDOW_H #define _H_GLWINDOW_H
#include <SDL_video.h> #include <SDL_video.h>
//#include <SDL_opengl.h>
#include <thirdparty/glad/glad.h> #include <thirdparty/glad/glad.h>
#include "utility/logger.h" class GLWindow
{
class GLWindow {
public: public:
GLWindow(const char *windowName, int width, int height) GLWindow(const char* windowName, int width, int height) :
: w(width), h(height), name(windowName) {}; w(width),
~GLWindow(); h(height),
name(windowName) {};
~GLWindow();
bool Init(); bool Init();
void swap() { SDL_GL_SwapWindow(window); } void swap() { SDL_GL_SwapWindow(window); }
void makeCurrent() { SDL_GL_MakeCurrent(window, glContext); } void makeCurrent() { SDL_GL_MakeCurrent(window, glContext); }
unsigned Width() const { return w; } unsigned width() const { return w; }
unsigned Height() const { return h; } unsigned height() const { return h; }
void resizeWindow(size_t w, size_t h);
SDL_Window *getWindow() const { return window; }
const SDL_GLContext &getContext() const { return glContext; }
SDL_Window* getWindow() const { return window; }
const SDL_GLContext& getContext() const { return glContext; }
private: private:
SDL_Window *window = nullptr; SDL_Window* window = nullptr;
SDL_GLContext glContext = NULL; SDL_GLContext glContext = NULL;
unsigned w, h; unsigned w, h;
const char *name; const char* name;
}; };
bool GLWindow::Init()
{
window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_OPENGL);
if (!window)
return false;
glContext = SDL_GL_CreateContext(window);
if (!glContext)
return false;
return true;
}
GLWindow::~GLWindow()
{
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window);
}
#endif // _H_GLWINDOW_H #endif // _H_GLWINDOW_H

View file

@ -1,67 +1,74 @@
#ifndef _H_RENDERER_H #ifndef _H_RENDERER_H
#define _H_RENDERER_H #define _H_RENDERER_H
#include <unordered_map>
#include <memory>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <memory>
#include <unordered_map>
#include "graphics/drawable.h"
#include "graphics/glwindow.h"
#include "graphics/postprocess.h"
#include "graphics/quad.h" #include "graphics/quad.h"
#include "graphics/drawable.h"
#include "graphics/postprocess.h"
class ResourceManager; class ResourceManager;
class Shader; class Shader;
enum class RenderLayer { Background, Map, GameObjects, Effects, HUD, Menu }; enum class RenderLayer {
Background,
Map,
GameObjects,
Effects,
HUD,
Menu
};
class Renderer { class Renderer
{
public: public:
Renderer(const std::shared_ptr<ResourceManager> &, Renderer(const std::shared_ptr<ResourceManager>&);
const std::shared_ptr<GLWindow> &);
void clear(); void clear();
void hookEventManager(const std::weak_ptr<EventManager> eventManager); void hookEventManager(const std::weak_ptr<EventManager> eventManager);
void setProjAndViewMatrix(const glm::mat4 &proj, const glm::mat4 &view); void setProjAndViewMatrix(const glm::mat4& proj, const glm::mat4& view);
void addDrawable(RenderLayer renderLayer, Drawable *drawable); void addDrawable(RenderLayer renderLayer, Drawable *drawable);
void removeDrawable(RenderLayer renderLayer, Drawable *drawable); void removeDrawable(RenderLayer renderLayer, Drawable *drawable);
void render(); void render();
private: private:
std::unordered_map<RenderLayer, std::vector<Drawable *>> worldLayerPool; std::unordered_map<RenderLayer, std::vector<Drawable*>> worldLayerPool;
std::unordered_map<RenderLayer, std::vector<Drawable *>> HUDLayerPool; std::unordered_map<RenderLayer, std::vector<Drawable*>> HUDLayerPool;
std::vector<RenderLayer> renderingOrder = { std::vector<RenderLayer> renderingOrder = {
RenderLayer::Background, RenderLayer::Map, RenderLayer::GameObjects, RenderLayer::Background,
RenderLayer::Effects, RenderLayer::HUD, RenderLayer::Menu}; RenderLayer::Map,
RenderLayer::GameObjects,
RenderLayer::Effects,
RenderLayer::HUD,
RenderLayer::Menu
};
void uploadUniforms(const unsigned shaderID, void uploadUniforms(const unsigned shaderID, const std::vector<Uniform>& uniforms);
const std::vector<Uniform> &uniforms);
unsigned uboMatrices; unsigned uboMatrices;
union FrameBuffer { union FrameBuffer {
unsigned int frame; unsigned int frame;
unsigned int texture; unsigned int texture;
} worldBuffer, hudBuffer; }worldBuffer, hudBuffer;
std::unique_ptr<ScreenQuad> screenQuad; std::unique_ptr<ScreenQuad> screenQuad;
std::shared_ptr<ResourceManager> resourceManager; std::shared_ptr<ResourceManager> resourceManager;
std::shared_ptr<GLWindow> glWindow;
void initFrameBuffers(); void initFrameBuffers();
void initUniformBuffers(); void initUniformBuffers();
void resizeFrameBuffers(); std::unique_ptr<Postprocessor> postProcessor;
std::unique_ptr<Postprocessor> postProcessor; void sortLayerPool(auto& layerPool);
void renderPool(auto& layerPool);
void sortLayerPool(auto &layerPool);
void renderPool(auto &layerPool);
}; };
#endif #endif

View file

@ -1,10 +1,10 @@
#ifndef _H_EVENTS_H #ifndef _H_EVENTS_H
#define _H_EVENTS_H #define _H_EVENTS_H
#include <functional>
#include <memory>
#include <string> #include <string>
#include <typeindex> #include <typeindex>
#include <memory>
#include <functional>
#include <unordered_map> #include <unordered_map>
#include "utility/direction.h" #include "utility/direction.h"
@ -14,90 +14,90 @@ class Bullet;
struct PhysicsComponent; struct PhysicsComponent;
struct BulletFiredEvent { struct BulletFiredEvent {
std::weak_ptr<Bullet> bullet; std::weak_ptr<Bullet> bullet;
}; };
struct BulletDiedEvent { struct BulletDiedEvent {
std::shared_ptr<PhysicsComponent> physObj; std::shared_ptr<PhysicsComponent> physObj;
}; };
struct BulletCollideEvent { struct BulletCollideEvent {
unsigned int ownerID; unsigned int ownerID;
unsigned int victimID; unsigned int victimID;
std::shared_ptr<PhysicsComponent> bullet; std::shared_ptr<PhysicsComponent> bullet;
glm::vec2 normal; glm::vec2 normal;
}; };
struct DirectionChangeEvent { struct DirectionChangeEvent {
int entityid; int entityid;
Direction direction; Direction direction;
}; };
struct EntityMoveEvent { struct EntityMoveEvent {
int entityid; int entityid;
}; };
struct EntityStopEvent { struct EntityStopEvent {
int entityid; int entityid;
}; };
struct EntityReloadEvent { struct EntityReloadEvent {
int entityid; int entityid;
glm::vec3 position; glm::vec3 position;
std::string weaponType; std::string weaponType;
}; };
struct EntityFinishReloadEvent { struct EntityFinishReloadEvent {
int entityid; int entityid;
}; };
struct EntityFireEvent { struct EntityFireEvent {
int entityid; int entityid;
float fireDelay; float fireDelay;
glm::vec3 firePosition; glm::vec3 firePosition;
std::string weaponType; std::string weaponType;
}; };
struct AnimationFinishedEvent { struct AnimationFinishedEvent {
int entityid; int entityid;
std::string animType; std::string animType;
}; };
struct ScreenShakeEvent { struct ScreenShakeEvent {
float intensity; float intensity;
float duration; float duration;
}; };
struct ScreenBlurEvent { struct ScreenBlurEvent {
float intensity; float intensity;
float duration; float duration;
};
struct WindowResizeEvent {
size_t width;
size_t height;
}; };
class EventManager { class EventManager {
public: public:
template <typename T> using EventCallback = std::function<void(const T &)>; template <typename T>
using EventCallback = std::function<void(const T&)>;
template <typename T> void subscribe(EventCallback<T> cb) {
auto wrapper = [cb](void *ev) { cb(*static_cast<T *>(ev)); };
callbacks[typeid(T)].push_back(wrapper);
}
template <typename T> void notify(const T &event) {
auto iterator = callbacks.find(typeid(T));
if (iterator != callbacks.end()) {
for (auto &cb : iterator->second) {
cb((void *)&event);
}
}
}
template <typename T>
void subscribe(EventCallback<T> cb) {
auto wrapper = [cb](void *ev) {
cb(*static_cast<T*>(ev));
};
callbacks[typeid(T)].push_back(wrapper);
}
template <typename T>
void notify(const T& event) {
auto iterator = callbacks.find(typeid(T));
if (iterator != callbacks.end())
{
for (auto& cb : iterator->second)
{
cb((void*)&event);
}
}
}
private: private:
std::unordered_map<std::type_index, std::vector<std::function<void(void *)>>> std::unordered_map <std::type_index, std::vector <std::function<void(void*)>>> callbacks;
callbacks;
}; };
#endif //_H_EVENTS_H #endif //_H_EVENTS_H

View file

@ -4,91 +4,84 @@
#include "utility/raycaster.h" #include "utility/raycaster.h"
#include "utility/script.h" #include "utility/script.h"
AI::AI(GameActor *actor, std::unique_ptr<Raycaster> raycaster) AI::AI(GameActor* actor, std::unique_ptr<Raycaster> raycaster)
: state(AIState::Idle), raycaster(std::move(raycaster)), actor(actor), : state(AIState::Idle), raycaster(std::move(raycaster)), actor(actor),
lastGCTime(std::chrono::high_resolution_clock::now()), GCTimeout(3) {} lastGCTime(std::chrono::high_resolution_clock::now()), GCTimeout(3)
{}
/* void AI::attachBehaviourScript(std::unique_ptr<AIScript> behaviour)
* The behavior script works off of three different states, idle, patrol and {
* alert. When the script is idle, this is */ // passing out instance of raycaster and this AI into our scripting api
void AI::attachBehaviourScript(std::unique_ptr<AIScript> behaviour) { // pay special attention each ai script has control of only their own instance of ai!
// passing out instance of raycaster and this AI into our scripting api this->behaviour = std::move(behaviour);
// pay special attention each ai script has control of only their own instance this->behaviour->lua["raycaster"] = sol::make_reference(this->behaviour->lua, raycaster.get());
// of ai! this->behaviour->lua["ai"] = sol::make_reference(this->behaviour->lua, this);
this->behaviour = std::move(behaviour);
this->behaviour->lua["raycaster"] =
sol::make_reference(this->behaviour->lua, raycaster.get());
this->behaviour->lua["ai"] = sol::make_reference(this->behaviour->lua, this);
idleFunc = this->behaviour->lua["idle"]; idleFunc = this->behaviour->lua["idle"];
patrolFunc = this->behaviour->lua["patrol"]; patrolFunc = this->behaviour->lua["patrol"];
alertFunc = this->behaviour->lua["alert"]; alertFunc = this->behaviour->lua["alert"];
} }
void AI::update() { void AI::update()
try { {
// If our ai doesn't have a reference to the actor it's controlling it will try {
// do nothing. If our ai script does have an actor but no target, it will if (actor && target) {
// default to its idle state. switch (state)
if (actor && target) { {
switch (state) { case AIState::Idle:
case AIState::Idle: if (idleFunc.valid())
if (idleFunc.valid()) { {
auto result = idleFunc(actor, target); auto result = idleFunc(actor, target);
if (!result.valid()) { if (!result.valid())
sol::error err = result; {
LOG(ERROR, "Lua error: {}", err.what(), NULL); sol::error err = result;
} std::cerr << "lua error: " << err.what() << std::endl;
} }
break; }
case AIState::Patrol: break;
if (patrolFunc.valid()) { case AIState::Patrol:
auto result = patrolFunc(actor, target); if (patrolFunc.valid())
if (!result.valid()) { {
sol::error err = result; auto result = patrolFunc(actor, target);
LOG(ERROR, "Lua error: {}", err.what(), NULL); if (!result.valid())
} {
} sol::error err = result;
break; std::cerr << "lua error: " << err.what() << std::endl;
case AIState::Alert: }
if (alertFunc.valid()) { }
auto result = alertFunc(actor, target); break;
if (!result.valid()) { case AIState::Alert:
sol::error err = result; if (alertFunc.valid())
LOG(ERROR, "Lua error: {}", err.what(), NULL); {
} auto result = alertFunc(actor, target);
} if (!result.valid())
break; {
} sol::error err = result;
} else if (actor) { std::cerr << "lua error: " << err.what() << std::endl;
if (idleFunc.valid()) { }
auto result = idleFunc(actor, nullptr); }
if (!result.valid()) { break;
sol::error err = result; }
LOG(ERROR, "Lua Error: {}", err.what()); }
} std::chrono::high_resolution_clock::time_point curTime = std::chrono::high_resolution_clock::now();
} if (curTime - lastGCTime >= GCTimeout)
} {
// Collect garbage just to be sure. behaviour->lua.collect_gc();
std::chrono::high_resolution_clock::time_point curTime = lastGCTime = curTime;
std::chrono::high_resolution_clock::now(); }
if (curTime - lastGCTime >= GCTimeout) { }
behaviour->lua.collect_gc(); catch (const std::exception& e) {
lastGCTime = curTime; std::cerr << "Error during AI update: " << e.what() << std::endl;
} state = AIState::Idle;
} catch (const std::exception &e) { }
LOG(ERROR, "Problem occured during AI update: {}", e.what());
state = AIState::Idle;
}
} }
AI::~AI() { AI::~AI()
// Just set all of the references to null, given they are smart pointers, they {
// are freed when no longer used. behaviour->lua["raycaster"] = sol::nil;
behaviour->lua["raycaster"] = sol::nil; behaviour->lua["ai"] = sol::nil;
behaviour->lua["ai"] = sol::nil; behaviour->lua["idle"] = sol::nil;
behaviour->lua["idle"] = sol::nil; behaviour->lua["patrol"] = sol::nil;
behaviour->lua["patrol"] = sol::nil; behaviour->lua["alert"] = sol::nil;
behaviour->lua["alert"] = sol::nil; behaviour->lua.collect_gc();
behaviour->lua.collect_gc();
} }

View file

@ -1,31 +1,26 @@
#include "gameplay/camera.h" #include "gameplay/camera.h"
// follow our target set using the setTarget. If there is no target set the void Camera::update(double deltaTime)
// camera will not move. {
void Camera::update(double deltaTime) { if (target == nullptr)
if (target == nullptr) return;
return; float smoothingFactor = 5.0f;
float smoothingFactor = 5.0f; //if (glm::distance(target->getCenter(), getCenterPos()) > 20.f)
// if (glm::distance(target->getCenter(), getCenterPos()) > 20.f) position += (target->getCenter() - getCenterPos()) * smoothingFactor * static_cast<float>(deltaTime);
position += (target->getCenter() - getCenterPos()) * smoothingFactor *
static_cast<float>(deltaTime);
} }
glm::mat4 Camera::getViewMatrix() { glm::mat4 Camera::getViewMatrix()
return glm::lookAt(position, position + front, up); {
return glm::lookAt(position, position + front, up);
} }
glm::mat4 Camera::getProjectionMatrix() { glm::mat4 Camera::getProjectionMatrix()
return glm::ortho(0.f, viewPortW, viewPortH, 0.f); {
return glm::ortho(0.f, viewPortW, viewPortH, 0.f);
} }
void Camera::setViewportSize(float width, float height) { const glm::vec3 Camera::worldToLocal(const glm::vec3& worldCoordinates)
viewPortW = width; {
viewPortH = height; //return worldCoordinates - position;
} return glm::vec3(getViewMatrix() * glm::vec4(worldCoordinates, 1.0f));
// The local coordinates are the corrdinates relative to the camera.
const glm::vec3 Camera::worldToLocal(const glm::vec3 &worldCoordinates) {
// return worldCoordinates - position;
return glm::vec3(getViewMatrix() * glm::vec4(worldCoordinates, 1.0f));
} }

View file

@ -32,7 +32,6 @@ void Entity::flip()
flipped = !flipped; flipped = !flipped;
} }
// Add physics component implies more than one can be attached to an entity...
void Entity::addPhysicsComponent(const std::shared_ptr<PhysicsComponent>& physics) void Entity::addPhysicsComponent(const std::shared_ptr<PhysicsComponent>& physics)
{ {
this->physics = physics; this->physics = physics;
@ -40,8 +39,6 @@ void Entity::addPhysicsComponent(const std::shared_ptr<PhysicsComponent>& physic
void Entity::update(double deltaTime) void Entity::update(double deltaTime)
{ {
// If our entity has a physics component attached and that they're moving,
// we will update the entity position to match the rigidBody position.
if (physics && physics->rigidBody.velocity != glm::vec2(0.0f)) if (physics && physics->rigidBody.velocity != glm::vec2(0.0f))
{ {
position = glm::vec3(physics->rigidBody.position, 0.f); position = glm::vec3(physics->rigidBody.position, 0.f);
@ -50,24 +47,21 @@ void Entity::update(double deltaTime)
} }
else if (!physics) else if (!physics)
{ {
// In the case that the entity does not have a physics component we will handle movement on the entity itself.
position += deltaPosition * 1.f; position += deltaPosition * 1.f;
updateModelMatrix(); updateModelMatrix();
deltaPosition = glm::vec3(0.f); deltaPosition = glm::vec3(0.f);
} }
} }
// The entity will only need to update its model matrix and the flipped flag on its attached shader.
// The shader and sprite component will handle the drawing.
void Entity::draw() void Entity::draw()
{ {
//glm::mat4 mvp = camera->getProjectionMatrix() * camera->getViewMatrix() * modelMatrix;
editUniform("model", modelMatrix); editUniform("model", modelMatrix);
editUniform("flip", flipped); editUniform("flip", flipped);
} }
void Entity::updateModelMatrix() void Entity::updateModelMatrix()
{ {
// Quick sanity check to make sure our Z position is zero. Since we are in 2D
position.z = 0.f; position.z = 0.f;
glm::mat4 rotationMat = (isRotatable) ? glm::rotate(glm::mat4(1.f), glm::radians(rotation), glm::vec3(0.0f, 0.0f, 1.0f)) : glm::mat4(1.0f); glm::mat4 rotationMat = (isRotatable) ? glm::rotate(glm::mat4(1.f), glm::radians(rotation), glm::vec3(0.0f, 0.0f, 1.0f)) : glm::mat4(1.0f);
glm::mat4 translation = glm::translate(glm::mat4(1.f), position); glm::mat4 translation = glm::translate(glm::mat4(1.f), position);

View file

@ -1,138 +1,138 @@
#include "gameplay/game.h" #include "gameplay/game.h"
#include "gameplay/input.h" #include "gameplay/input.h"
#include "gameplay/scene.h" #include "gameplay/scene.h"
/*due for possible removal!*/
#include "gameplay/gameactor.h"
#include "gameplay/weapons/weapon.h"
/*-------------------------*/
#include "utility/command.h" #include "utility/command.h"
#include "utility/resourcemanager.h"
#include "utility/ftfont.h" #include "utility/ftfont.h"
#include "utility/logger.h" #include "utility/logger.h"
#include "utility/resourcemanager.h"
#include "graphics/glwindow.h" #include "graphics/glwindow.h"
#include "sound/engine.h" #include "sound/engine.h"
#include <SDL_video.h>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <memory> #include <memory>
#include <thread>
#include <utility/events.h> #include <utility/events.h>
bool Game::init() { bool Game::init()
window = std::make_shared<GLWindow>("Yupple Mayham", 1024, 768); {
window = std::make_shared<GLWindow>("Yupple Mayham", 800, 600);
if (!window->Init()) if (!window->Init())
ERROR_LOG("Failed to init GLWindow: {}", SDL_GetError()); ERROR_LOG("Failed to init GLWindow: {}", SDL_GetError());
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
ERROR_LOG("Failed to load GLLoader", NULL); ERROR_LOG("Failed to load GLLoader", NULL);
#if _DEBUG #if _DEBUG
LOG_LEVEL(DEBUG); LOG_LEVEL(DEBUG);
#else #else
LOG_LEVEL(INFO); LOG_LEVEL(INFO);
#endif #endif
SDL_GL_SetSwapInterval(1); SDL_GL_SetSwapInterval(1);
glViewport(0, 0, window->Width(), window->Height()); glViewport(0, 0, window->width(), window->height());
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// For now we'll init default bindings, but as we move forward controls will // For now we'll init default bindings, but as we move forward controls will be set by the player and saved in controls.xml
// be set by the player and saved in controls.xml inputHandler = std::make_shared<InputHandler>();
inputHandler = std::make_shared<InputHandler>(); inputHandler->bindKeyCommand(SDLK_w, 0, std::make_unique<MoveUpCommand>());
inputHandler->bindKeyCommand(SDLK_w, 0, std::make_unique<MoveUpCommand>()); inputHandler->bindKeyCommand(SDLK_a, 0, std::make_unique<MoveLeftCommand>());
inputHandler->bindKeyCommand(SDLK_a, 0, std::make_unique<MoveLeftCommand>()); inputHandler->bindKeyCommand(SDLK_s, 0, std::make_unique<MoveDownCommand>());
inputHandler->bindKeyCommand(SDLK_s, 0, std::make_unique<MoveDownCommand>()); inputHandler->bindKeyCommand(SDLK_d, 0, std::make_unique<MoveRightCommand>());
inputHandler->bindKeyCommand(SDLK_d, 0, std::make_unique<MoveRightCommand>());
inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100, inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100, std::make_unique<ShootCommand>());
std::make_unique<ShootCommand>()); inputHandler->bindMouseMotion(std::make_unique<FollowMouseCommand>());
inputHandler->bindMouseMotion(std::make_unique<FollowMouseCommand>()); inputHandler->bindMouseScroll(std::make_unique<CycleCommand>());
inputHandler->bindMouseScroll(std::make_unique<CycleCommand>());
game_state |= GAME_RUNNING; game_state |= GAME_RUNNING;
globalEventManager = std::make_shared<EventManager>(); globalEventManager = std::make_shared<EventManager>();
resourceManager = std::make_shared<ResourceManager>(); resourceManager = std::make_shared<ResourceManager>();
renderer = std::make_shared<Renderer>(resourceManager, window); renderer = std::make_shared<Renderer>(resourceManager);
audioEngine = std::make_shared<AudioEngine>(resourceManager); audioEngine = std::make_shared<AudioEngine>(resourceManager);
camera = std::make_unique<Camera>(static_cast<float>(window->Width()), audioEngine->hookEventManager(globalEventManager);
static_cast<float>(window->Height())); /* Testing */
audioEngine->hookEventManager(globalEventManager); audioEngine->pushMusic("music/short_song.ogg");
/* Testing */ audioEngine->pushMusic("music/bright.ogg");
audioEngine->pushMusic("music/short_song.ogg"); audioEngine->pushMusic("music/main_song.ogg");
audioEngine->pushMusic("music/bright.ogg"); audioEngine->playMusic();
audioEngine->pushMusic("music/main_song.ogg"); /* */
audioEngine->playMusic(); renderer->hookEventManager(globalEventManager);
/* */ textHandler = std::make_shared<Text>();
renderer->hookEventManager(globalEventManager); if (!textHandler->loadFonts("fonts"))
textHandler = std::make_shared<Text>(); return false;
if (!textHandler->loadFonts("fonts")) return true;
return false;
return true;
} }
const unsigned Game::getWindowWidth() const { return window->Width(); } const unsigned Game::getWindowWidth() const { return window->width(); }
const unsigned Game::getWindowHeight() const { return window->Height(); } const unsigned Game::getWindowHeight() const { return window->height(); }
bool Game::loadDebugScene() { bool Game::loadDebugScene()
currentScene = std::make_shared<Scene>(SCENE_SHOOTER, resourceManager, {
globalEventManager); currentScene = std::make_shared<Scene>(SCENE_SHOOTER, resourceManager, globalEventManager);
currentScene->init(); currentScene->init();
audioEngine->hookSceneManager(currentScene->getEventManager()); audioEngine->hookSceneManager(currentScene->getEventManager());
if (currentScene->getPlayer() == nullptr) if (currentScene->getPlayer() == nullptr)
return false; return false;
inputHandler->setActor(currentScene->getPlayer().get()); inputHandler->setActor(currentScene->getPlayer().get());
camera->setTarget(currentScene->getPlayer().get()); return true;
return true;
} }
void Game::captureInput(SDL_Event &e) { void Game::captureInput(SDL_Event& e)
inputHandler->captureInput(e); {
if (e.type == SDL_WINDOWEVENT_RESIZED) { inputHandler->captureInput(e);
size_t width = static_cast<size_t>(e.window.data1);
size_t height = static_cast<size_t>(e.window.data2);
globalEventManager->notify<WindowResizeEvent>({width, height});
window->resizeWindow(width, height);
camera->setViewportSize(static_cast<float>(e.window.data1),
static_cast<float>(e.window.data2));
}
} }
void Game::executeInputs() { void Game::executeInputs()
inputHandler->handleInput(); {
inputHandler->executeCommands(); inputHandler->handleInput();
inputHandler->executeCommands();
} }
void Game::update(double deltaTime) { void Game::update(double deltaTime)
if (currentScene) { {
currentScene->update(deltaTime); if (currentScene) {
if (auto player = currentScene->getPlayer()) { currentScene->update(deltaTime);
audioEngine->updateListener(player->getPosition()); if (auto player = currentScene->getPlayer())
player->setLocalPosition(camera->worldToLocal(player->getPosition())); audioEngine->updateListener(player->getPosition());
} }
} audioEngine->poll();
camera->update(deltaTime);
audioEngine->poll();
} }
void Game::render() { void Game::render()
glClearColor(0.05f, 0.25f, 0.05f, 1.0f); {
glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.05f, 0.25f, 0.05f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderer->setProjAndViewMatrix(camera->getProjectionMatrix(), if (currentScene)
camera->getViewMatrix()); {
currentScene->render(renderer);
/*Debug ammo indicator*/
textHandler->DrawText("comic.ttf", std::to_string(currentScene->getPlayer()->getHeldWeapon()->getMagazine()), glm::vec2(10, 10), 0.5f);
textHandler->DrawText("comic.ttf", "/", glm::vec2(50, 10), 0.5f);
textHandler->DrawText("comic.ttf", std::to_string(currentScene->getPlayer()->getHeldWeapon()->getAmmo()), glm::vec2(90, 10), 0.5f);
}
if (currentScene) window->swap();
currentScene->render(renderer);
window->swap();
} }
void Game::quit() { game_state = GAME_QUITTING; } void Game::quit()
{
Game::~Game() { game_state = GAME_QUITTING;
if (audioEngine) { }
audioEngine->killMusic();
} Game::~Game()
resourceManager->clearResources(); {
if (audioEngine) {
audioEngine->killMusic();
}
resourceManager->clearResources();
} }

View file

@ -3,181 +3,156 @@
#include "gameplay/physics.h" #include "gameplay/physics.h"
#include "gameplay/scene.h" #include "gameplay/scene.h"
#include "gameplay/weapons/weapon.h" #include "gameplay/weapons/weapon.h"
#include "utility/component.h"
#include "utility/direction.h"
#include "utility/events.h" #include "utility/events.h"
#include "utility/direction.h"
#include "utility/component.h"
#include <memory> #include <memory>
// The components are smart pointer references to resources owned by the GameActor::~GameActor() { }
// resource manager. So the GameActor is not responsible for cleaning resources.
GameActor::~GameActor() {}
// Adding a component also means attaching the id of the entity to the void GameActor::addComponent(std::unique_ptr<Component> component)
// component, so it's aware of who owns it. This is important for event handling {
void GameActor::addComponent(std::unique_ptr<Component> component) { component->ownerid = entityid;
component->ownerid = entityid; components.push_back(std::move(component));
components.push_back(std::move(component));
} }
Weapon *GameActor::getHeldWeapon() const { Weapon* GameActor::getHeldWeapon() const
return (weapons.empty() || currentWeaponIndex >= weapons.size()) {
? nullptr return (weapons.empty() || currentWeaponIndex >= weapons.size()) ? nullptr : weapons[currentWeaponIndex].get();
: weapons[currentWeaponIndex].get();
} }
std::span<Weapon *> GameActor::getAllWeapons() { return weaponCache; } std::span<Weapon*> GameActor::getAllWeapons()
{
// Keep a seperate vector that is used as the cache for external calls to return weaponCache;
// getAllWeapons()
void GameActor::pickupWeapon(std::unique_ptr<Weapon> weapon) {
weapon->setWielder(this);
if (auto eventManager = sceneContext->getEventManager().lock()) {
weapon->hookEventManager(eventManager);
}
weaponCache.push_back(weapon.get());
weapons.push_back(std::move(weapon));
// wield the newly picked up weapon.
getHeldWeapon()->putaway();
currentWeaponIndex = weapons.size() - 1;
getHeldWeapon()->wield();
} }
void GameActor::setRotation(const float &rotation) { void GameActor::pickupWeapon(std::unique_ptr<Weapon> weapon)
// Any attached animation component would be interested if their owner needs {
// their sprite swapped weapon->setWielder(this);
if (!isRotatable) { if (auto eventManager = sceneContext->getEventManager().lock()) {
if (auto eventManager = sceneContext->getEventManager().lock()) { weapon->hookEventManager(eventManager);
Direction newDir = getDirectionFromRotation(rotation); }
if (getDirectionFromRotation(this->rotation) != newDir) weaponCache.push_back(weapon.get());
eventManager->notify<DirectionChangeEvent>({entityid, newDir}); weapons.push_back(std::move(weapon));
} // wield the newly picked up weapon.
} getHeldWeapon()->putaway();
this->rotation = rotation; currentWeaponIndex = weapons.size() - 1;
updateModelMatrix(); getHeldWeapon()->wield();
} }
void GameActor::update(double deltaTime) { void GameActor::setRotation(const float& rotation)
Entity::update(deltaTime); {
if (!isRotatable) {
for (const auto &component : components) if (auto eventManager = sceneContext->getEventManager().lock()) {
component->update(); Direction newDir = getDirectionFromRotation(rotation);
for (const auto &weapon : weapons) if (getDirectionFromRotation(this->rotation) != newDir)
weapon->update(deltaTime); eventManager->notify<DirectionChangeEvent>({ entityid, newDir });
}
// Not the cleanest solution, but this is to make sure the animation isn't }
// starting to move over and over. this->rotation = rotation;
if (isMoving && !wasMoving) { updateModelMatrix();
if (auto event = sceneContext->getEventManager().lock()) {
event->notify<EntityMoveEvent>({entityid});
}
wasMoving = true;
} else if (!isMoving && wasMoving) {
if (auto event = sceneContext->getEventManager().lock()) {
event->notify<EntityStopEvent>({entityid});
}
wasMoving = false;
}
isMoving = false;
} }
void GameActor::draw() { void GameActor::update(double deltaTime)
Entity::draw(); {
for (const auto &component : components) { Entity::update(deltaTime);
component->bind();
component->render(); for (const auto& component : components)
} component->update();
for (const auto& weapon : weapons)
weapon->update(deltaTime);
if (isMoving && !wasMoving)
{
if (auto event = sceneContext->getEventManager().lock()) {
event->notify<EntityMoveEvent>({ entityid });
}
wasMoving = true;
}
else if (!isMoving && wasMoving)
{
if (auto event = sceneContext->getEventManager().lock()) {
event->notify<EntityStopEvent>({ entityid });
}
wasMoving = false;
}
isMoving = false;
} }
void GameActor::moveUp() { void GameActor::draw()
if (physics) {
physics->rigidBody.applyForce(glm::vec3(0.f, -1.f, 0.f), 1500.25f); Entity::draw();
isMoving = true;
} // regular loop through components, but if the component returns true to kill, we erase it.
void GameActor::moveDown() { // Components are always assumed to be smart pointers!
if (physics) for (const auto& component : components)
physics->rigidBody.applyForce(glm::vec3(0.f, 1.f, 0.f), 1500.25f); {
isMoving = true; component->bind();
} component->render();
void GameActor::moveLeft() { }
if (physics) //for (auto& weapon : weapons)
physics->rigidBody.applyForce(glm::vec3(-1.f, 0.f, 0.f), 1500.25f); // weapon->draw();
isMoving = true;
}
void GameActor::moveRight() {
if (physics)
physics->rigidBody.applyForce(glm::vec3(1.f, 0.f, 0.f), 1500.25f);
isMoving = true;
} }
void GameActor::moveUp() { if (physics) physics->rigidBody.applyForce(glm::vec3( 0.f,-1.f, 0.f), 1500.25f); isMoving = true; }
void GameActor::moveDown() { if (physics) physics->rigidBody.applyForce(glm::vec3( 0.f, 1.f, 0.f), 1500.25f); isMoving = true; }
void GameActor::moveLeft() { if (physics) physics->rigidBody.applyForce(glm::vec3(-1.f, 0.f, 0.f), 1500.25f); isMoving = true; }
void GameActor::moveRight(){ if (physics) physics->rigidBody.applyForce(glm::vec3( 1.f, 0.f, 0.f), 1500.25f); isMoving = true; }
// top-down shooter mode controls // top-down shooter mode controls
void GameActor::fireWeapon() const { void GameActor::fireWeapon()const {
if (auto weapon = getHeldWeapon()) { if (auto weapon = getHeldWeapon()) {
if (weapon->shoot()) { if (weapon->shoot()) {
// If it's the player that is shooting apply a shake and blur effect to if (sceneContext->getPlayerID() == entityid) {
// give the shots some weight if (auto gEvent = sceneContext->getGlobalEventManager().lock()) {
if (sceneContext->getPlayerID() == entityid) { gEvent->notify<ScreenShakeEvent>({0.01f, 0.8f});
if (auto gEvent = sceneContext->getGlobalEventManager().lock()) { gEvent->notify<ScreenBlurEvent>({1.f, 0.8f});
gEvent->notify<ScreenShakeEvent>({0.01f, 0.8f}); }
gEvent->notify<ScreenBlurEvent>({1.f, 0.8f}); }
} }
} }
}
}
} }
void GameActor::cycleUpWeapons() { void GameActor::cycleUpWeapons() {
if (!weapons.empty()) { if (!weapons.empty()) {
weapons[currentWeaponIndex]->putaway(); weapons[currentWeaponIndex]->putaway();
currentWeaponIndex = (currentWeaponIndex + 1) % weapons.size(); currentWeaponIndex = (currentWeaponIndex + 1) % weapons.size();
weapons[currentWeaponIndex]->wield(); weapons[currentWeaponIndex]->wield();
} }
} }
void GameActor::cycleDownWeapons() { void GameActor::cycleDownWeapons() {
if (!weapons.empty()) { if (!weapons.empty()) {
weapons[currentWeaponIndex]->putaway(); weapons[currentWeaponIndex]->putaway();
currentWeaponIndex = currentWeaponIndex = (currentWeaponIndex + weapons.size() - 1) % weapons.size();
(currentWeaponIndex + weapons.size() - 1) % weapons.size(); weapons[currentWeaponIndex]->wield();
weapons[currentWeaponIndex]->wield(); }
}
} }
void GameActor::cycleWeapons(const MouseState &mouse_state) { void GameActor::cycleWeapons(const MouseState& mouse_state)
if (mouse_state.scroll < 0) {
cycleUpWeapons(); if (mouse_state.scroll < 0)
else if (mouse_state.scroll > 0) cycleUpWeapons();
cycleDownWeapons(); else if (mouse_state.scroll > 0)
cycleDownWeapons();
} }
void GameActor::followMouse(const MouseState &mouse_state) { void GameActor::followMouse(const MouseState& mouse_state)
glm::vec2 direction = glm::vec2(mouse_state.x, mouse_state.y) - {
glm::vec2(localPosition.x, localPosition.y); glm::vec2 direction = glm::vec2(mouse_state.x, mouse_state.y) - glm::vec2(localPosition.x, localPosition.y);
float newRotation = glm::degrees(glm::atan(direction.y, direction.x)); float newRotation = glm::degrees(glm::atan(direction.y, direction.x));
if (getDirectionFromRotation(rotation) != if (getDirectionFromRotation(rotation) != getDirectionFromRotation(newRotation)) {
getDirectionFromRotation(newRotation)) { if (auto event = sceneContext->getEventManager().lock()) {
if (auto event = sceneContext->getEventManager().lock()) { event->notify<DirectionChangeEvent>({ entityid, getDirectionFromRotation(newRotation) });
event->notify<DirectionChangeEvent>( }
{entityid, getDirectionFromRotation(newRotation)}); }
} //setRotation(glm::degrees(glm::atan(direction.y, direction.x)));
} this->rotation = newRotation;
// setRotation(glm::degrees(glm::atan(direction.y, direction.x)));
this->rotation = newRotation;
} }
void GameActor::strafeLeft() { void GameActor::strafeLeft() { position.x += sin(glm::radians(rotation)) * speed; position.y -= cos(glm::radians(rotation)) * speed; }
position.x += sin(glm::radians(rotation)) * speed; void GameActor::strafeRight() { position.x -= sin(glm::radians(rotation)) * speed; position.y += cos(glm::radians(rotation)) * speed; }
position.y -= cos(glm::radians(rotation)) * speed; void GameActor::moveBackward() { position.x -= cos(glm::radians(rotation)) * speed; position.y -= sin(glm::radians(rotation)) * speed; }
} void GameActor::moveForward() {
void GameActor::strafeRight() { if (physics) {
position.x -= sin(glm::radians(rotation)) * speed; physics->rigidBody.velocity.x += cos(glm::radians(rotation)) * speed;
position.y += cos(glm::radians(rotation)) * speed; physics->rigidBody.velocity.y += sin(glm::radians(rotation)) * speed;
} }
void GameActor::moveBackward() { isMoving = true;
position.x -= cos(glm::radians(rotation)) * speed;
position.y -= sin(glm::radians(rotation)) * speed;
}
void GameActor::moveForward() { // More than likely the most useful of the
// movement commands so far. But this could
// change in the future
if (physics) {
physics->rigidBody.velocity.x += cos(glm::radians(rotation)) * speed;
physics->rigidBody.velocity.y += sin(glm::radians(rotation)) * speed;
}
isMoving = true;
} }

View file

@ -3,51 +3,56 @@
#include <SDL_timer.h> #include <SDL_timer.h>
void InputHandler::handleInput() { void InputHandler::handleInput()
// If our input handler is not attached to an actor we return. {
// This will be due for change given we will need to handle input during menus if (!actor) return;
if (!actor) Uint32 currentTime = SDL_GetTicks();
return; // check for bound keys that were pressed,
Uint32 currentTime = SDL_GetTicks(); // next check if the hasn't been executed within the amount specified in delay
// check for bound keys that were pressed, // if not execute the command and set lastExecution to currentTime
// next check if the hasn't been executed within the amount specified in delay for (auto& [key, command] : keyCommands)
// if not execute the command and set lastExecution to currentTime {
for (auto &[key, command] : keyCommands) { if (keys[key] == true)
if (keys[key] == true) { {
if (currentTime - command.lastExecution >= command.delay) { if (currentTime - command.lastExecution >= command.delay)
commandQueue.push_back(command.cmd.get()); {
command.lastExecution = currentTime; commandQueue.push_back(command.cmd.get());
} command.lastExecution = currentTime;
} }
} }
// Same with the mouse, for this context we'll be checking for motion events }
// and for click events // Same with the mouse, for this context we'll be checking for motion events and for click events
for (auto &[button, command] : mouseCommands) { for (auto& [button, command] : mouseCommands)
if (mouseButtons[button] == true) { {
if (currentTime - command.lastExecution >= command.delay) { if (mouseButtons[button] == true)
mouseQueue.push_back(command.cmd.get()); {
command.lastExecution = currentTime; if (currentTime - command.lastExecution >= command.delay)
} {
} mouseQueue.push_back(command.cmd.get());
} command.lastExecution = currentTime;
if (mouseMotionCommand) }
mouseMotionCommand->execute(*actor, mouse_state); }
if (mouseScrollCommand) }
mouseScrollCommand->execute(*actor, mouse_state); if (mouseMotionCommand)
mouse_state.scroll = mouseMotionCommand->execute(*actor, mouse_state);
0.0f; // clear mouse scroll since we have handled the event. if (mouseScrollCommand)
mouseScrollCommand->execute(*actor, mouse_state);
mouse_state.scroll = 0.0f; // clear mouse scroll since we have handled the event.
} }
// Executes every captured command during the frame // Executes every captured command during the frame
void InputHandler::executeCommands() { void InputHandler::executeCommands()
for (auto &command : commandQueue) { {
command->execute(*actor); for (auto& command : commandQueue) {
} command->execute(*actor);
for (auto &mouse : mouseQueue) { }
mouse->execute(*actor, mouse_state); for (auto& mouse : mouseQueue) {
} mouse->execute(*actor, mouse_state);
commandQueue.clear(); }
mouseQueue.clear(); commandQueue.clear();
mouseQueue.clear();
} }
InputHandler::~InputHandler() {} InputHandler::~InputHandler()
{
}

View file

@ -1,129 +1,125 @@
#include "gameplay/map.h" #include "gameplay/map.h"
#include "gameplay/camera.h"
#include "graphics/shader.h"
#include "graphics/texture.h" #include "graphics/texture.h"
#include "utility/logger.h"
#include "utility/resourcemanager.h"
#include "utility/xmlloader.h" #include "utility/xmlloader.h"
#include "utility/resourcemanager.h"
#include "utility/logger.h"
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
Map::Map(const MapData *mapData, const unsigned shaderID, Map::Map(const MapData* mapData, const unsigned shaderID, std::shared_ptr<ResourceManager> resourceManager) :
std::shared_ptr<ResourceManager> resourceManager) mapData(mapData),
: mapData(mapData), tileIds(mapData->tiles) { tileIds(mapData->tiles)
this->shaderID = shaderID; {
this->shaderID = shaderID;
// Tiles are held on layers, drawn back to front for (auto& tileSet : mapData->tileSets)
for (auto &tileSet : mapData->tileSets) tileSetData.push_back(resourceManager->loadTileSet(tileSet.path));
tileSetData.push_back(resourceManager->loadTileSet(tileSet.path));
if (!tileSetData.empty()) { if (!tileSetData.empty())
// Storing all of the tilesets we will need to load and sending it to our {
// texture instance handle std::vector<const char*> buffer;
std::vector<const char *> buffer; for (int layer = 0; layer < tileIds.size(); layer++)
for (int layer = 0; layer < tileIds.size(); layer++) { {
buffer.clear(); buffer.clear();
for (auto &set : tileSetData) for (auto& set : tileSetData)
buffer.push_back(set->file.c_str()); buffer.push_back(set->file.c_str());
if (!buffer.empty()) if (!buffer.empty())
instanceHandles.push_back( instanceHandles.push_back(std::make_shared<TileTextureInstance>(buffer));
std::make_shared<TileTextureInstance>(buffer)); }
}
loadMap(); loadMap();
createCollisionMap(); createCollisionMap();
} }
}
#include <glm/ext.hpp>
void Map::loadMap()
{
tileData.resize(tileIds.size());
for (int layer = 0; layer < tileIds.size(); layer++)
{
for (int y = 0; y < tileIds[layer].size(); y++)
{
for (int x = 0; x < tileIds[layer][y].size(); x++)
{
glm::mat4 modelMatrix =
glm::translate(glm::mat4(1.f), glm::vec3(x * mapData->tileSize, y * mapData->tileSize, 0.0f)) *
glm::scale(glm::mat4(1.f), glm::vec3(mapData->tileSize, mapData->tileSize, 1.0f));
int textureIndex = static_cast<int>(getTileSetIndex(tileIds[layer][y][x]));
glm::vec2 originalSize = (textureIndex != -1) ?
glm::vec2(tileSetData[textureIndex]->width, tileSetData[textureIndex]->height) :
glm::vec2(0.0f);
int tilesPerRow = (textureIndex != -1) ? tileSetData[textureIndex]->columns : 0;
int startID = (textureIndex != -1) ? mapData->tileSets[textureIndex].startID : 0;
int tileIndex = tileIds[layer][y][x];
tileData[layer].push_back({modelMatrix, originalSize, tileIndex, textureIndex, tilesPerRow, startID});
}
}
instanceHandles[layer]->updateInstanceData(tileData[layer]);
}
glm::vec2 canvasSize = glm::vec2(instanceHandles[0]->getTextureArray()->getWidth(), instanceHandles[0]->getTextureArray()->getHeight());
editUniformOnce("uCanvasSize", canvasSize);
} }
void Map::loadMap() { void Map::createCollisionMap()
tileData.resize(tileIds.size()); {
// Tiles are drawn back to front, we track the tileset the tile comes from and // Match collisionMap to map size
// additional information about the tilesets themselves so the shader can collisionMap.resize(tileIds[0].size());
// properly draw the right tile. Each layer is its own instance for (int y = 0; y < tileIds[0].size(); ++y)
for (int layer = 0; layer < tileIds.size(); layer++) { {
for (int y = 0; y < tileIds[layer].size(); y++) { collisionMap[y].resize(tileIds[0][y].size(), 0);
for (int x = 0; x < tileIds[layer][y].size(); x++) { }
glm::mat4 modelMatrix =
glm::translate(
glm::mat4(1.f),
glm::vec3(x * mapData->tileSize, y * mapData->tileSize, 0.0f)) *
glm::scale(glm::mat4(1.f),
glm::vec3(mapData->tileSize, mapData->tileSize, 1.0f));
int textureIndex = for (int layer = 0; layer < tileIds.size(); layer++)
static_cast<int>(getTileSetIndex(tileIds[layer][y][x])); {
glm::vec2 originalSize = for (int y = 0; y < tileIds[layer].size(); y++)
(textureIndex != -1) ? glm::vec2(tileSetData[textureIndex]->width, {
tileSetData[textureIndex]->height) for (int x = 0; x < tileIds[layer][y].size(); x++)
: glm::vec2(0.0f); {
int tilesPerRow = int id = tileIds[layer][y][x];
(textureIndex != -1) ? tileSetData[textureIndex]->columns : 0; size_t tileSetIndex = getTileSetIndex(id);
int startID = if (tileSetIndex == -1)
(textureIndex != -1) ? mapData->tileSets[textureIndex].startID : 0; collisionMap[y][x] = 0;
int tileIndex = tileIds[layer][y][x]; else
{
tileData[layer].push_back({modelMatrix, originalSize, tileIndex, int startID = mapData->tileSets[tileSetIndex].startID;
textureIndex, tilesPerRow, startID}); auto& tile = tileSetData[tileSetIndex]->tiles[id - startID];
} if (!tile->walkable)
} collisionMap[y][x] = 1;
instanceHandles[layer]->updateInstanceData(tileData[layer]); }
} }
// The canvas size is the same for every tile atlas, each one growing to match }
// the size of the larget tileset }
glm::vec2 canvasSize =
glm::vec2(instanceHandles[0]->getTextureArray()->getWidth(),
instanceHandles[0]->getTextureArray()->getHeight());
editUniformOnce("uCanvasSize", canvasSize);
} }
// The collision map is just a 2D array of 0's and 1's, 1's being collidable and void Map::draw()
// 0's not. This may see some changes in the future to properly address special {
// tiles, such as portals. for (int layer = 0; layer < instanceHandles.size(); layer++)
void Map::createCollisionMap() { {
// Match collisionMap to map size instanceHandles[layer]->draw();
collisionMap.resize(tileIds[0].size()); }
for (int y = 0; y < tileIds[0].size(); ++y) {
collisionMap[y].resize(tileIds[0][y].size(), 0);
}
for (int layer = 0; layer < tileIds.size(); layer++) {
for (int y = 0; y < tileIds[layer].size(); y++) {
for (int x = 0; x < tileIds[layer][y].size(); x++) {
int id = tileIds[layer][y][x];
size_t tileSetIndex = getTileSetIndex(id);
if (tileSetIndex == -1)
collisionMap[y][x] = 0;
else {
int startID = mapData->tileSets[tileSetIndex].startID;
auto &tile = tileSetData[tileSetIndex]->tiles[id - startID];
if (!tile->walkable)
collisionMap[y][x] = 1;
}
}
}
}
} }
// Draw each layer in it's own instance /*
void Map::draw() { Use this function to get the tileSetIndex from a tile ID.
for (int layer = 0; layer < instanceHandles.size(); layer++) { the index of the tileSet is the same index as the texture index used in the instanceHandle
instanceHandles[layer]->draw();
}
}
/* returns tileSetIndex of the tile id passed. Unless the id is either 0 or
Use this function to get the tileSetIndex from a tile ID.
the index of the tileSet is the same index as the texture index used in the
instanceHandle
returns tileSetIndex of the tile id passed. Unless the id is either 0 or
the returned tileSetIndex is out of bounds, returns -1 the returned tileSetIndex is out of bounds, returns -1
*/ */
size_t Map::getTileSetIndex(int id) const { size_t Map::getTileSetIndex(int id) const
// work from the bottom, break if ID > startID {
// If we get a textureIndex of -1 then we are on an empty tile // work from the bottom, break if ID > startID
size_t tileSetIndex = mapData->tileSets.size() - 1; // If we get a textureIndex of -1 then we are on an empty tile
for (; tileSetIndex != -1; --tileSetIndex) { size_t tileSetIndex = mapData->tileSets.size() - 1;
if (id >= mapData->tileSets[tileSetIndex].startID) for (; tileSetIndex != -1; --tileSetIndex)
break; {
} if (id >= mapData->tileSets[tileSetIndex].startID)
return (tileSetIndex >= mapData->tileSets.size()) ? -1 : tileSetIndex; break;
}
return (tileSetIndex >= mapData->tileSets.size()) ? -1 : tileSetIndex;
} }

View file

@ -2,215 +2,212 @@
#include "gameplay/weapons/bullet.h" #include "gameplay/weapons/bullet.h"
#include "utility/events.h" #include "utility/events.h"
void PhysicsEngine::hookEventManager( #include "utility/logger.h"
const std::shared_ptr<EventManager> &eventManager) {
this->eventManager = eventManager; void PhysicsEngine::hookEventManager(const std::shared_ptr<EventManager>& eventManager)
this->eventManager->subscribe<BulletFiredEvent>( {
[this](const BulletFiredEvent &event) { this->eventManager = eventManager;
if (auto bullet = event.bullet.lock()) { this->eventManager->subscribe<BulletFiredEvent>([this](const BulletFiredEvent& event) {
this->addObject(bullet->getPhysicsComponent()); if (auto bullet = event.bullet.lock()) {
} this->addObject(bullet->getPhysicsComponent());
}); }
this->eventManager->subscribe<BulletDiedEvent>( });
[this](const BulletDiedEvent &event) { this->eventManager->subscribe<BulletDiedEvent>([this](const BulletDiedEvent& event) {
this->removeObject(event.physObj); this->removeObject(event.physObj);
}); });
} }
std::shared_ptr<PhysicsComponent> std::shared_ptr<PhysicsComponent> PhysicsEngine::createObject(const unsigned int ID,
PhysicsEngine::createObject(const unsigned int ID, const glm::vec3 &pos, const glm::vec3& pos,
float mass, PhysicsComponent::Collider::Shape shape, float mass,
glm::vec3 dimensions, const glm::vec3 offset) { PhysicsComponent::Collider::Shape shape,
auto component = std::make_shared<PhysicsComponent>(); glm::vec3 dimensions, const
component->ID = ID; glm::vec3 offset)
component->rigidBody.position = pos; {
component->rigidBody.acceleration = glm::vec2(0.f); auto component = std::make_shared <PhysicsComponent>();
component->rigidBody.mass = mass; component->ID = ID;
component->collider.shape = shape; component->rigidBody.position = pos;
component->collider.dimensions = dimensions; component->rigidBody.acceleration = glm::vec2(0.f);
component->collider.offset = offset; component->rigidBody.mass = mass;
component->collider.shape = shape;
component->collider.dimensions = dimensions;
component->collider.offset = offset;
addObject(component); addObject(component);
return component; return component;
} }
void PhysicsEngine::loadCollisionMap( void PhysicsEngine::loadCollisionMap(const std::vector<std::vector<int>>& collisionMap, float tileSize)
const std::vector<std::vector<int>> &collisionMap, float tileSize) { {
this->collisionMap = collisionMap; this->collisionMap = collisionMap;
this->tileSize = tileSize; this->tileSize = tileSize;
} }
void PhysicsEngine::addObject( void PhysicsEngine::addObject(const std::shared_ptr<PhysicsComponent>& component)
const std::shared_ptr<PhysicsComponent> &component) { {
if (component) if (component)
objects.emplace_back(component); objects.emplace_back(component);
} }
void PhysicsEngine::removeObject( void PhysicsEngine::removeObject(const std::shared_ptr<PhysicsComponent>& component)
const std::shared_ptr<PhysicsComponent> &component) { {
if (std::find(objects.begin(), objects.end(), component) != objects.end()) if (std::find(objects.begin(), objects.end(), component) != objects.end())
objects.erase(std::remove(objects.begin(), objects.end(), component)); objects.erase(std::remove(objects.begin(), objects.end(), component));
} }
int PhysicsEngine::getTileCollider(const glm::vec2 &position) { int PhysicsEngine::getTileCollider(const glm::vec2& position)
int x = static_cast<int>((position.x + 0.5f * tileSize) / tileSize); {
int y = static_cast<int>((position.y + 0.5f * tileSize) / tileSize); int x = static_cast<int>((position.x + 0.5f * tileSize) / tileSize);
if (y >= 0 && y < collisionMap.size()) { int y = static_cast<int>((position.y + 0.5f * tileSize) / tileSize);
if (x >= 0 && x < collisionMap[y].size()) if (y >= 0 && y < collisionMap.size())
return collisionMap[y][x]; {
} if (x >= 0 && x < collisionMap[y].size())
return 0; return collisionMap[y][x];
}
return 0;
} }
void PhysicsEngine::getPossibleCollisions() { void PhysicsEngine::getPossibleCollisions()
objCollisions.clear(); {
for (size_t i = 0; i < objects.size(); ++i) { objCollisions.clear();
auto &obj = objects[i]; for (size_t i = 0; i < objects.size(); ++i)
for (size_t j = i + 1; j < objects.size(); ++j) { {
auto &colliderObj = objects[j]; auto& obj = objects[i];
if (obj.get() == colliderObj.get() || obj->ID == colliderObj->ID) for (size_t j = i + 1; j < objects.size(); ++j)
continue; {
float colliderRight = colliderObj->rigidBody.position.x + auto& colliderObj = objects[j];
colliderObj->collider.dimensions.x; if (obj.get() == colliderObj.get() || obj->ID == colliderObj->ID) continue;
float colliderBottom = colliderObj->rigidBody.position.y + float colliderRight = colliderObj->rigidBody.position.x + colliderObj->collider.dimensions.x;
colliderObj->collider.dimensions.y; float colliderBottom = colliderObj->rigidBody.position.y + colliderObj->collider.dimensions.y;
float objectRight = float objectRight = obj->rigidBody.position.x + obj->collider.dimensions.x;
obj->rigidBody.position.x + obj->collider.dimensions.x; float objectBottom = obj->rigidBody.position.y + obj->collider.dimensions.y;
float objectBottom = if ((obj->rigidBody.position.x <= colliderRight &&
obj->rigidBody.position.y + obj->collider.dimensions.y; objectRight >= colliderObj->rigidBody.position.x) ||
if ((obj->rigidBody.position.x <= colliderRight && (obj->rigidBody.position.y <= colliderBottom &&
objectRight >= colliderObj->rigidBody.position.x) || objectBottom >= colliderObj->rigidBody.position.y))
(obj->rigidBody.position.y <= colliderBottom && objCollisions.push_back(CollisionPair(obj.get(), colliderObj.get()));
objectBottom >= colliderObj->rigidBody.position.y)) }
objCollisions.push_back(CollisionPair(obj.get(), colliderObj.get())); }
}
}
} }
void PhysicsEngine::resolvePossibleCollisions() { void PhysicsEngine::resolvePossibleCollisions()
for (auto &objs : objCollisions) { {
// Solve for two circles, we'll need to expand upon this for different for (auto& objs : objCollisions)
// colliders... {
float sumOfRadius = // Solve for two circles, we'll need to expand upon this for different colliders...
objs.first->collider.dimensions.x + objs.second->collider.dimensions.x; float sumOfRadius = objs.first->collider.dimensions.x + objs.second->collider.dimensions.x;
glm::vec2 objFirstCenter = glm::vec2 objFirstCenter = objs.first->rigidBody.position + objs.first->collider.offset;
objs.first->rigidBody.position + objs.first->collider.offset; glm::vec2 objSecondCenter = objs.second->rigidBody.position + objs.second->collider.offset;
glm::vec2 objSecondCenter = glm::vec2 distance = objFirstCenter - objSecondCenter;
objs.second->rigidBody.position + objs.second->collider.offset; if (glm::length(distance) < sumOfRadius)
glm::vec2 distance = objFirstCenter - objSecondCenter; {
if (glm::length(distance) < sumOfRadius) { // We got impact!
// We got impact! glm::vec2 normal = distance / glm::length(distance);
glm::vec2 normal = distance / glm::length(distance); // That impact is a bullet hitting a gameactor!
// That impact is a bullet hitting a gameactor! if ((objs.first->isBullet || objs.second->isBullet) && !(objs.first->isBullet && objs.second->isBullet))
if ((objs.first->isBullet || objs.second->isBullet) && {
!(objs.first->isBullet && objs.second->isBullet)) { eventManager->notify(std::make_shared<BulletCollideEvent>(
eventManager->notify(std::make_shared<BulletCollideEvent>( ( objs.first->isBullet) ? objs.first->ID : objs.second->ID,
(objs.first->isBullet) ? objs.first->ID : objs.second->ID, (!objs.first->isBullet) ? objs.first->ID : objs.second->ID,
(!objs.first->isBullet) ? objs.first->ID : objs.second->ID, std::make_shared<PhysicsComponent>(( objs.first->isBullet) ? objs.first : objs.second),
std::make_shared<PhysicsComponent>( normal
(objs.first->isBullet) ? objs.first : objs.second), ));
normal)); }
} // Apply impulse force
// Apply impulse force float penetrationDepth = sumOfRadius - glm::length(distance);
float penetrationDepth = sumOfRadius - glm::length(distance); glm::vec2 correctionVector = normal * (penetrationDepth / ((1 / objs.first->rigidBody.mass) + (1 / objs.second->rigidBody.mass)));
glm::vec2 correctionVector = glm::vec2 vrel = objs.first->rigidBody.velocity - objs.second->rigidBody.velocity;
normal * (penetrationDepth / ((1 / objs.first->rigidBody.mass) +
(1 / objs.second->rigidBody.mass)));
glm::vec2 vrel =
objs.first->rigidBody.velocity - objs.second->rigidBody.velocity;
// smallest elasticity of the two colliders // smallest elasticity of the two colliders
float e = std::min(objs.first->rigidBody.elasticity, float e = std::min(objs.first->rigidBody.elasticity, objs.second->rigidBody.elasticity);
objs.second->rigidBody.elasticity); float impulseMag = (-(1 + e) * glm::dot(vrel, normal)) / ((1 / objs.first->rigidBody.mass) + (1 / objs.second->rigidBody.mass));
float impulseMag = (-(1 + e) * glm::dot(vrel, normal)) /
((1 / objs.first->rigidBody.mass) +
(1 / objs.second->rigidBody.mass));
objs.first->rigidBody.position += objs.first->rigidBody.position += (correctionVector / objs.first->rigidBody.mass);
(correctionVector / objs.first->rigidBody.mass); objs.second->rigidBody.position -= (correctionVector / objs.second->rigidBody.mass);
objs.second->rigidBody.position -= objs.first->rigidBody.velocity += impulseMag * normal / objs.first->rigidBody.mass;
(correctionVector / objs.second->rigidBody.mass); objs.second->rigidBody.velocity -= impulseMag * normal / objs.second->rigidBody.mass;
objs.first->rigidBody.velocity += }
impulseMag * normal / objs.first->rigidBody.mass; }
objs.second->rigidBody.velocity -=
impulseMag * normal / objs.second->rigidBody.mass;
}
}
} }
void PhysicsEngine::resolveWorldCollision( void PhysicsEngine::resolveWorldCollision(const std::shared_ptr<PhysicsComponent>& obj)
const std::shared_ptr<PhysicsComponent> &obj) { {
switch (obj->collider.shape) { switch (obj->collider.shape)
case PhysicsComponent::Collider::Shape::Circle: {
float radius = obj->collider.dimensions.x; case PhysicsComponent::Collider::Shape::Circle:
glm::vec2 position = obj->rigidBody.position + obj->collider.offset; float radius = obj->collider.dimensions.x;
int topTile = getTileCollider(position - glm::vec2(0, radius)); glm::vec2 position = obj->rigidBody.position + obj->collider.offset;
int bottomTile = getTileCollider(position + glm::vec2(0, radius)); int topTile = getTileCollider(position - glm::vec2(0, radius));
int leftTile = getTileCollider(position - glm::vec2(radius, 0)); int bottomTile = getTileCollider(position + glm::vec2(0, radius));
int rightTile = getTileCollider(position + glm::vec2(radius, 0)); int leftTile = getTileCollider(position - glm::vec2(radius, 0));
if (obj->isBullet) { int rightTile = getTileCollider(position + glm::vec2(radius, 0));
if (topTile || bottomTile || leftTile || rightTile) { if (obj->isBullet)
eventManager->notify(std::make_shared<BulletDiedEvent>(obj)); {
return; if (topTile || bottomTile || leftTile || rightTile)
} {
} eventManager->notify(std::make_shared<BulletDiedEvent>(obj));
int tileY = static_cast<int>((position.y) / tileSize); return;
int tileX = static_cast<int>((position.x) / tileSize); }
if (topTile) { }
// obj->rigidBody.velocity.y = -obj->rigidBody.velocity.y; int tileY = static_cast<int>((position.y) / tileSize);
obj->rigidBody.position.y = int tileX = static_cast<int>((position.x) / tileSize);
(tileY + 1) * tileSize + obj->collider.offset.y; if (topTile)
} {
if (bottomTile) { //obj->rigidBody.velocity.y = -obj->rigidBody.velocity.y;
// obj->rigidBody.velocity.y = -obj->rigidBody.velocity.y; obj->rigidBody.position.y = (tileY+1) * tileSize + obj->collider.offset.y;
obj->rigidBody.position.y = (tileY)*tileSize - obj->collider.offset.y; }
} if (bottomTile)
if (leftTile) { {
// obj->rigidBody.velocity.x = -obj->rigidBody.velocity.x; //obj->rigidBody.velocity.y = -obj->rigidBody.velocity.y;
obj->rigidBody.position.x = obj->rigidBody.position.y = (tileY) * tileSize - obj->collider.offset.y;
(tileX + 1) * tileSize + obj->collider.offset.x; }
} if (leftTile)
if (rightTile) { {
// obj->rigidBody.velocity.x = -obj->rigidBody.velocity.x; //obj->rigidBody.velocity.x = -obj->rigidBody.velocity.x;
obj->rigidBody.position.x = (tileX)*tileSize - obj->collider.offset.x; obj->rigidBody.position.x = (tileX + 1) * tileSize + obj->collider.offset.x;
} }
} if (rightTile)
{
//obj->rigidBody.velocity.x = -obj->rigidBody.velocity.x;
obj->rigidBody.position.x = (tileX) * tileSize - obj->collider.offset.x;
}
}
} }
void PhysicsEngine::update(double deltaTime) { void PhysicsEngine::update(double deltaTime)
for (auto &obj : objects) { {
if (!obj) for (auto& obj : objects)
continue; {
glm::vec2 frictionForce = obj->rigidBody.velocity * -0.1f; if (!obj) continue;
if (std::abs(obj->rigidBody.acceleration.x) == glm::vec2 frictionForce = obj->rigidBody.velocity * -0.1f;
std::abs(obj->rigidBody.acceleration.y)) { if (std::abs(obj->rigidBody.acceleration.x) == std::abs(obj->rigidBody.acceleration.y))
obj->rigidBody.acceleration.x *= 0.75f; {
obj->rigidBody.acceleration.y *= 0.75f; obj->rigidBody.acceleration.x *= 0.75f;
} obj->rigidBody.acceleration.y *= 0.75f;
if (!obj->isBullet) }
obj->rigidBody.velocity += (frictionForce); if (!obj->isBullet)
obj->rigidBody.velocity += obj->rigidBody.acceleration; obj->rigidBody.velocity += (frictionForce);
obj->rigidBody.velocity += obj->rigidBody.acceleration;
float maxSpeed = 500.f; float maxSpeed = 500.f;
float curSpeed = glm::length(obj->rigidBody.velocity); float curSpeed = glm::length(obj->rigidBody.velocity);
if (curSpeed > maxSpeed) { if (curSpeed > maxSpeed)
// Move at maxspeed {
obj->rigidBody.velocity = // Move at maxspeed
glm::normalize(obj->rigidBody.velocity) * maxSpeed; obj->rigidBody.velocity = glm::normalize(obj->rigidBody.velocity) * maxSpeed;
} }
obj->rigidBody.acceleration = glm::vec2(0.f); obj->rigidBody.acceleration = glm::vec2(0.f);
if (obj->collider.dimensions != glm::vec2(0.f)) { if (obj->collider.dimensions != glm::vec2(0.f))
// check map collisions {
resolveWorldCollision(obj); // check map collisions
} resolveWorldCollision(obj);
obj->rigidBody.position += }
obj->rigidBody.velocity * static_cast<float>(deltaTime); obj->rigidBody.position += obj->rigidBody.velocity * static_cast<float>(deltaTime);
// Make sure we keep our Z at 0.f, a better choice would be to remove the // Make sure we keep our Z at 0.f, a better choice would be to remove the need for vec3 all together!
// need for vec3 all together! // TODO: REMOVE VEC3 NO NEED FOR Z COODINATE!
// TODO: REMOVE VEC3 NO NEED FOR Z COODINATE! // obj->rigidBody.position.z = 0.f;
// obj->rigidBody.position.z = 0.f; }
} getPossibleCollisions();
getPossibleCollisions(); if (!objCollisions.empty())
if (!objCollisions.empty()) resolvePossibleCollisions();
resolvePossibleCollisions();
} }

View file

@ -1,190 +1,249 @@
#include "gameplay/scene.h" #include "gameplay/scene.h"
#include "gameplay/ai.h" #include "gameplay/camera.h"
#include "gameplay/gameactor.h" #include "gameplay/gameactor.h"
#include "gameplay/weapons/weapons.h"
#include "gameplay/weapons/bulletmanager.h"
#include "gameplay/map.h" #include "gameplay/map.h"
#include "gameplay/physics.h" #include "gameplay/physics.h"
#include "gameplay/weapons/bulletmanager.h" #include "gameplay/ai.h"
#include "gameplay/weapons/weapons.h"
#include "graphics/sprite.h"
#include "graphics/animation.h" #include "graphics/animation.h"
#include "graphics/background.h" #include "graphics/background.h"
#include "graphics/sprite.h"
#include "utility/component.h"
#include "utility/debugdraw.h"
#include "utility/events.h"
#include "utility/ftfont.h"
#include "utility/raycaster.h"
#include "utility/resourcemanager.h"
#include "utility/script.h" #include "utility/script.h"
#include "utility/component.h"
#include "utility/ftfont.h"
#include "utility/xmlloader.h" #include "utility/xmlloader.h"
#include "utility/resourcemanager.h"
#include "utility/events.h"
#include "utility/raycaster.h"
#include "utility/debugdraw.h"
#include <memory> #include <memory>
Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources, #include <execution>
std::weak_ptr<EventManager> globalEvents)
: type(sceneType), resourceManager(resources), // Scene xml files, should contain a node called <player> that holds the sprite location
globalEventManager(globalEvents) { /*
physicsEngine = std::make_shared<PhysicsEngine>(); like this:
eventManager = std::make_shared<EventManager>(); <player sprite="sprites/player2Atlas.png" frameSize=64.0>
<x=5/>
<y=6/>
</player>
*/
Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources, std::weak_ptr<EventManager> globalEvents)
: type(sceneType), resourceManager(resources), globalEventManager(globalEvents)
{
camera = std::make_shared<Camera>(800.f, 600.f);
physicsEngine = std::make_shared<PhysicsEngine>();
eventManager = std::make_shared<EventManager>();
} }
void Scene::init() { void Scene::init()
physicsEngine->hookEventManager(eventManager); {
loadDebugShooterScene(); physicsEngine->hookEventManager(eventManager);
//if (sceneType == SCENE_SHOOTER)
loadDebugShooterScene();
} }
// This function is full of hardcoded values and test sprites, NOT for use with // This function is full of hardcoded values and test sprites, NOT for use with final product
// final product void Scene::loadDebugShooterScene()
void Scene::loadDebugShooterScene() { {
hookSceneEvents(); hookSceneEvents();
sceneData = resourceManager->loadScene("000"); sceneData = resourceManager->loadScene("000");
if (!sceneData) if (!sceneData)
return; return;
EntityData playerData = sceneData->entities[0]; EntityData playerData = sceneData->entities[0];
auto mapData = sceneData->map; auto mapData = sceneData->map;
auto playerShader = resourceManager->loadShader( auto playerShader = resourceManager->loadShader("GL_player", "shaders/GL_player.vert", "shaders/GL_player.frag");
"GL_player", "shaders/GL_player.vert", "shaders/GL_player.frag"); auto bubbleShader = resourceManager->loadShader("GL_bubble", "shaders/GL_bubble.vert", "shaders/GL_bubble.frag");
auto bubbleShader = resourceManager->loadShader( auto weaponShader = resourceManager->loadShader("GL_pistol", "shaders/GL_pistol.vert", "shaders/GL_pistol.frag");
"GL_bubble", "shaders/GL_bubble.vert", "shaders/GL_bubble.frag");
auto weaponShader = resourceManager->loadShader(
"GL_pistol", "shaders/GL_pistol.vert", "shaders/GL_pistol.frag");
if (!sceneData->bgFile.empty()) { if (!sceneData->bgFile.empty()) {
LOG(INFO, "Found background loading '{}'", sceneData->bgFile); LOG(INFO, "Found background loading '{}'", sceneData->bgFile);
background = resourceManager->loadBackground(sceneData->bgFile); background = resourceManager->loadBackground(sceneData->bgFile);
} }
// creating map from scene // creating map from scene
auto tileShader = resourceManager->loadShader( auto tileShader = resourceManager->loadShader("GL_tile", "shaders/GL_tile.vert", "shaders/GL_tile.frag");
"GL_tile", "shaders/GL_tile.vert", "shaders/GL_tile.frag"); map = std::make_shared<Map>(mapData, tileShader, resourceManager);
map = std::make_shared<Map>(mapData, tileShader, resourceManager);
for (EntityData entityData : sceneData->entities) { for (EntityData entityData : sceneData->entities)
auto entity = std::make_shared<GameActor>(this, playerShader); {
// Directional is the kind of sprite sheet we are using, in this case for auto entity = std::make_shared<GameActor>(this, playerShader);
// directional, I have the sprite sheet handle the rotations instead of just // Directional is the kind of sprite sheet we are using, in this case for directional, I have the sprite sheet handle the rotations
// rotating the object, this makes it look quite a bit better from the end // instead of just rotating the object, this makes it look quite a bit better from the end user perspective.
// user perspective. if (entityData.animated)
if (entityData.animated) { {
auto entityAnimation = resourceManager->loadAnimationSet( auto entityAnimation = resourceManager->loadAnimationSet(entityData.graphic, entity->getEntityID());
entityData.graphic, entity->getEntityID()); // because we don't want to have the engine rotate the object based on the entities rotation,
// because we don't want to have the engine rotate the object based on the // we set the this value to false so we no longer rotate the object.
// entities rotation, we set the this value to false so we no longer if (entityAnimation->getDirectional())
// rotate the object. entity->setRotatable(false);
if (entityAnimation->getDirectional()) entity->addComponent(std::make_unique<AnimationComponent>(entityAnimation, eventManager));
entity->setRotatable(false); }
entity->addComponent( else
std::make_unique<AnimationComponent>(entityAnimation, eventManager)); {
} else { auto entitySprite = resourceManager->loadSpriteStatic(entityData.graphic);
auto entitySprite = resourceManager->loadSpriteStatic(entityData.graphic); entity->addComponent(std::make_unique<SpriteComponent>(entitySprite));
entity->addComponent(std::make_unique<SpriteComponent>(entitySprite)); }
} auto defaultWeapon = resourceManager->loadWeapon("gun/pistol", weaponShader, bubbleShader);
auto defaultWeapon = auto entityWeapon = resourceManager->loadWeapon(entityData.weapon, weaponShader, bubbleShader);
resourceManager->loadWeapon("gun/pistol", weaponShader, bubbleShader);
auto entityWeapon = resourceManager->loadWeapon(entityData.weapon,
weaponShader, bubbleShader);
entity->pickupWeapon(std::move(defaultWeapon)); entity->pickupWeapon(std::move(defaultWeapon));
entity->pickupWeapon(std::move(entityWeapon)); entity->pickupWeapon(std::move(entityWeapon));
entity->setPosition(glm::vec3(entityData.x * mapData->tileSize, entity->setPosition(glm::vec3(entityData.x * mapData->tileSize, entityData.y * mapData->tileSize, 0.f));
entityData.y * mapData->tileSize, 0.f)); entity->setScale(glm::vec3(mapData->tileSize, mapData->tileSize, 1.f));
entity->setScale(glm::vec3(mapData->tileSize, mapData->tileSize, 1.f));
entity->addPhysicsComponent(physicsEngine->createObject( entity->addPhysicsComponent(
entity->getEntityID(), entity->getPosition(), 49.0, physicsEngine->createObject(entity->getEntityID(),
PhysicsComponent::Collider::Shape::Circle, entity->getPosition(),
glm::vec3(mapData->tileSize / 2))); 49.0,
PhysicsComponent::Collider::Shape::Circle,
glm::vec3(mapData->tileSize / 2))
);
if (entityData.isPlayer) { if (entityData.isPlayer)
player = entity; {
} else { player = entity;
// attach ai camera->setTarget(entity.get());
if (!entityData.script.empty()) { entity->setLocalPosition(camera->worldToLocal(entity->getPosition()));
auto behaviour = resourceManager->loadAIScript(entityData.script); }
auto rayCaster = std::make_unique<Raycaster>( else
40.f, 300.f, map->getCollisionMap(), mapData->tileSize); {
auto ai = std::make_shared<AI>(entity.get(), std::move(rayCaster)); // attach ai
ai->setTarget(player.get()); if (!entityData.script.empty())
ai->attachBehaviourScript(std::move(behaviour)); {
entity->addComponent(std::make_unique<AIComponent>(ai)); auto behaviour = resourceManager->loadAIScript(entityData.script);
} auto rayCaster = std::make_unique<Raycaster>(40.f, 300.f, map->getCollisionMap(), mapData->tileSize);
} auto ai = std::make_shared<AI>(entity.get(), std::move(rayCaster));
entities.emplace(entity->getEntityID(), entity); ai->setTarget(player.get());
SDL_Delay(1); // This is to make sure each entity gets a unique ID ai->attachBehaviourScript(std::move(behaviour));
} entity->addComponent(std::make_unique<AIComponent>(ai));
}
}
entities.emplace(entity->getEntityID(), entity);
SDL_Delay(1); // This is to make sure each entity gets a unique ID
}
physicsEngine->loadCollisionMap(map->getCollisionMap(), mapData->tileSize); physicsEngine->loadCollisionMap(map->getCollisionMap(), mapData->tileSize);
// Setup map and other entities...
} }
std::shared_ptr<GameActor> Scene::getPlayer() const { std::shared_ptr<GameActor> Scene::getPlayer() const
return (!player) ? nullptr : player; {
return (!player) ? nullptr : player;
} }
void Scene::update(double deltaTime) { void Scene::update(double deltaTime)
{
for (const auto &[id, e] : entities) { for (const auto& [id, e] : entities)
e->update(deltaTime); {
} e->update(deltaTime);
physicsEngine->update(deltaTime); if (camera->getTarget() == e.get())
e->setLocalPosition(camera->worldToLocal(e->getPosition()));
}
physicsEngine->update(deltaTime);
camera->update(deltaTime);
} }
void Scene::render(std::shared_ptr<Renderer> renderer) { void Scene::render(std::shared_ptr<Renderer> renderer)
renderer->clear(); {
renderer->clear();
renderer->addDrawable(RenderLayer::Map, map.get()); renderer->setProjAndViewMatrix(camera->getProjectionMatrix(), camera->getViewMatrix());
if (background) { renderer->addDrawable(RenderLayer::Map, map.get());
renderer->addDrawable(RenderLayer::Background, if (background) {
static_cast<Drawable *>(background)); renderer->addDrawable(RenderLayer::Background, static_cast<Drawable*>(background));
} }
for (auto &[id, e] : entities) { //map->draw();
renderer->addDrawable(RenderLayer::GameObjects, e.get()); for (auto& [id, e] : entities)
if (e->getHeldWeapon()) { {
renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon()); //e->draw();
const auto &weapons = e->getAllWeapons(); renderer->addDrawable(RenderLayer::GameObjects, e.get());
for (auto &w : weapons) { if (e->getHeldWeapon()) {
for (auto b : w->getBulletManager()->getBullets()) { renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon());
renderer->addDrawable(RenderLayer::GameObjects, b.get()); const auto& weapons = e->getAllWeapons();
} for (auto& w : weapons) {
} for (auto b : w->getBulletManager()->getBullets()) {
} renderer->addDrawable(RenderLayer::GameObjects, b.get());
} }
}
}
}
renderer->render(); renderer->render();
DEBUG_TEXT(glm::vec3(10.f, 10.f, 0.f), glm::vec4(0.f, 0.f, 0.f, 1.f), 0.5f, /*
"{} / {}", getPlayer()->getHeldWeapon()->getMagazine(), for (const auto& bullet : player->getHeldWeapon()->getBulletManager()->getBullets()) {
getPlayer()->getHeldWeapon()->getAmmo()); DebugDrawer::getInstance().addLine(player->getCenter(), bullet->getCenter(), glm::vec4(1.f, 0.f, 0.f, 0.f));
DEBUG_TEXT(
glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x,
camera.get()->worldToLocal(bullet->getCenter()).y, 0.f),
glm::vec4(1.f, 0.f, 0.f, 1.f),
0.2f,
"( {}, {}, {} )",
bullet->getCenter().x,
bullet->getCenter().y,
bullet->getCenter().z
);
DEBUG_TEXT(
glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x,
camera.get()->worldToLocal(bullet->getCenter()).y + 10.f, 0.f),
glm::vec4(1.f, 1.f, 0.f, 1.f),
0.2f,
"( {}, {} )",
bullet->getPhysicsComponent()->rigidBody.position.x,
bullet->getPhysicsComponent()->rigidBody.position.y
);
DEBUG_TEXT(
glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x,
camera.get()->worldToLocal(bullet->getCenter()).y + 20.f, 0.f),
glm::vec4(0.f, 1.f, 1.f, 1.f),
0.2f,
"( {}, {} )",
bullet->getPhysicsComponent()->rigidBody.velocity.x,
bullet->getPhysicsComponent()->rigidBody.velocity.y);
}
*/
DebugDrawer::getInstance().draw(camera->getProjectionMatrix() * camera->getViewMatrix());
} }
void Scene::unloadScene() { void Scene::unloadScene()
// xmlLoader.reset(); {
//xmlLoader.reset();
} }
void Scene::hookSceneEvents() { void Scene::hookSceneEvents()
std::weak_ptr<Scene> weakSelf = shared_from_this(); {
eventManager->subscribe<BulletCollideEvent>( std::weak_ptr<Scene> weakSelf = shared_from_this();
[weakSelf](const BulletCollideEvent &e) { eventManager->subscribe<BulletCollideEvent>([weakSelf](const BulletCollideEvent& e) {
if (auto self = weakSelf.lock()) { if (auto self = weakSelf.lock()) {
GameActor *shooter = self->getGameActorByID(e.ownerID); GameActor* shooter = self->getGameActorByID(e.ownerID);
GameActor *target = self->getGameActorByID(e.victimID); GameActor* target = self->getGameActorByID(e.victimID);
if (shooter && target) if (shooter && target)
if (auto weapon = shooter->getHeldWeapon()) if (auto weapon = shooter->getHeldWeapon())
weapon->onHitCallback(target, e.bullet.get(), e.normal); weapon->onHitCallback(target, e.bullet.get(), e.normal);
} }
}); });
} }
GameActor *Scene::getGameActorByID(const unsigned int ID) { GameActor* Scene::getGameActorByID(const unsigned int ID)
auto iterator = entities.find(ID); {
if (iterator == entities.end()) auto iterator = entities.find(ID);
return nullptr; if (iterator == entities.end())
return iterator->second.get(); return nullptr;
return iterator->second.get();
} }
std::span<std::weak_ptr<GameActor>> Scene::getAllEntities() const { std::span<std::weak_ptr<GameActor>> Scene::getAllEntities() const
entityCache.clear(); {
for (const auto &[_, entity] : entities) { entityCache.clear();
entityCache.push_back(entity); for (const auto& [_, entity] : entities) {
} entityCache.push_back(entity);
return entityCache; }
return entityCache;
} }

View file

@ -1,249 +1,291 @@
#include "gameplay/weapons/weapon.h" #include "gameplay/weapons/weapon.h"
#include "gameplay/weapons/bulletmanager.h"
#include "gameplay/weapons/bullet.h"
#include "gameplay/gameactor.h" #include "gameplay/gameactor.h"
#include "gameplay/physics.h" #include "gameplay/physics.h"
#include "gameplay/weapons/bullet.h"
#include "gameplay/weapons/bulletmanager.h"
#include <SDL_timer.h> #include <SDL_timer.h>
#include "utility/component.h"
#include "utility/debugdraw.h" #include "utility/debugdraw.h"
#include "utility/component.h"
#include "utility/events.h" #include "utility/events.h"
#include "utility/logger.h"
#include "utility/resourcemanager.h" #include "utility/resourcemanager.h"
#include "utility/script.h" #include "utility/script.h"
#include "utility/logger.h"
// TODO: Regular clean up, make this mess readable! // TODO: Regular clean up, make this mess readable!
Weapon::Weapon(const WeaponData *data, const unsigned weaponShaderID, Weapon::Weapon(const WeaponData* data, const unsigned weaponShaderID, const unsigned bulletShaderID, ResourceManager* resourceManager)
const unsigned bulletShaderID, ResourceManager *resourceManager) :
: Entity(weaponShaderID), weaponType(data->id), Entity (weaponShaderID),
weaponSize(glm::vec2(data->sizeX, data->sizeY)), weaponType (data->id),
weaponOffset(glm::vec2(data->offsetX, data->offsetY)), weaponSize (glm::vec2(data->sizeX, data->sizeY)),
weaponMag(data->clipSize), weaponMagSize(data->clipSize), weaponOffset (glm::vec2(data->offsetX, data->offsetY)),
weaponAmmo(data->maxAmmo), bulletSpeed(data->bulletSpeed), weaponMag (data->clipSize),
bulletDrop(data->bulletDrop), fireSpeed(data->fireSpeed), weaponMagSize (data->clipSize),
bulletSize(glm::vec2(data->bulletSizeX, data->bulletSizeY)), weaponAmmo (data->maxAmmo),
bulletShaderID(bulletShaderID), bulletSpeed (data->bulletSpeed),
bulletManager(std::make_shared<BulletManager>()), bulletDrop (data->bulletDrop),
bulletSpread(std::make_unique<UTIL::RandomGenerator>(-data->bulletSpread, fireSpeed (data->fireSpeed),
data->bulletSpread)), bulletSize (glm::vec2(data->bulletSizeX, data->bulletSizeY)),
bulletModifer( bulletShaderID (bulletShaderID),
std::make_unique<UTIL::RandomGenerator>(data->modMin, data->modMax)) { bulletManager (std::make_shared<BulletManager>()),
if (data->bulletAnimated) bulletSpread (std::make_unique<UTIL::RandomGenerator>(-data->bulletSpread, data->bulletSpread)),
bulletSprite = std::make_unique<AnimationComponent>( bulletModifer (std::make_unique<UTIL::RandomGenerator>(data->modMin, data->modMax))
resourceManager->loadAnimationSet(data->bulletGraphic, entityid)); {
else if (data->bulletAnimated)
bulletSprite = std::make_unique<SpriteComponent>( bulletSprite = std::make_unique<AnimationComponent>(resourceManager->loadAnimationSet(data->bulletGraphic, entityid));
resourceManager->loadSpriteStatic(data->bulletGraphic)); else
bulletSprite = std::make_unique<SpriteComponent>(resourceManager->loadSpriteStatic(data->bulletGraphic));
if (data->animated) { if (data->animated)
addComponent(std::make_unique<AnimationComponent>( {
resourceManager->loadAnimationSet(data->id, entityid))); addComponent(std::make_unique<AnimationComponent>(resourceManager->loadAnimationSet(data->id, entityid)));
} else }
addComponent(std::make_unique<SpriteComponent>( else
resourceManager->loadSpriteStatic(data->graphic))); addComponent(std::make_unique<SpriteComponent>(resourceManager->loadSpriteStatic(data->graphic)));
this->setScale(glm::vec3(weaponSize.x, weaponSize.y, 1.0f)); this->setScale(glm::vec3(weaponSize.x, weaponSize.y, 1.0f));
}; };
void Weapon::addComponent(std::unique_ptr<Component> comp) { void Weapon::addComponent(std::unique_ptr<Component> comp) {
components.push_back(std::move(comp)); components.push_back(std::move(comp));
} }
void Weapon::reload() { void Weapon::reload()
if (auto event = eventManager.lock()) { {
event->notify<EntityReloadEvent>( if (auto event = eventManager.lock())
{entityid, wielder->getPosition(), weaponType}); {
reloading = true; event->notify<EntityReloadEvent>({ entityid, wielder->getPosition(), weaponType });
if (weaponAmmo < weaponMagSize) { reloading = true;
weaponMag = weaponAmmo; if (weaponAmmo < weaponMagSize) {
weaponAmmo = 0; weaponMag = weaponAmmo;
} else { weaponAmmo = 0;
weaponMag = weaponMagSize; }
weaponAmmo -= weaponMagSize; else {
} weaponMag = weaponMagSize;
} weaponAmmo -= weaponMagSize;
}
}
} }
bool Weapon::shoot() { bool Weapon::shoot()
bool shotsFired = false; {
if (wielder) { bool shotsFired = false;
Uint32 currentTime = SDL_GetTicks(); if (wielder)
if (currentTime - lastFireTime >= fireSpeed && !reloading) { {
if (weaponMag > 0) { Uint32 currentTime = SDL_GetTicks();
shotsFired = true; if (currentTime - lastFireTime >= fireSpeed && !reloading)
if (auto event = eventManager.lock()) {
event->notify<EntityFireEvent>( if (weaponMag > 0)
{entityid, fireSpeed, wielder->getPosition(), weaponType}); {
if (!weaponScript || !weaponScript->lua["onShoot"].valid()) { shotsFired = true;
// create bullet using this generated data if (auto event = eventManager.lock())
BulletData b = genBulletData(); event->notify<EntityFireEvent>({entityid, fireSpeed, wielder->getPosition(), weaponType});
createBullet(b); if (!weaponScript || !weaponScript->lua["onShoot"].valid())
weaponMag -= 1; {
} else { // create bullet using this generated data
auto result = weaponScript->lua["onShoot"](); BulletData b = genBulletData();
if (!result.valid()) { createBullet(b);
sol::error err = result; weaponMag -= 1;
std::cerr << "lua error: " << err.what() << std::endl; }
} else {
// auto reload auto result = weaponScript->lua["onShoot"]();
if ((weaponMag -= 1) <= 0) if (!result.valid())
reload(); {
} sol::error err = result;
} else if (weaponMag <= 0) std::cerr << "lua error: " << err.what() << std::endl;
reload(); }
lastFireTime = currentTime; // auto reload
} if ((weaponMag -= 1) <= 0) reload();
} }
return shotsFired; }
else if (weaponMag <= 0) reload();
lastFireTime = currentTime;
}
}
return shotsFired;
} }
void Weapon::hookEventManager(std::weak_ptr<EventManager> eventManager) { void Weapon::hookEventManager(std::weak_ptr<EventManager> eventManager)
this->eventManager = eventManager; {
this->eventManager = eventManager;
for (auto &component : components) { for (auto& component : components)
if (component->getType() == Component::TYPE::ANIMATION) { {
auto animComponent = static_cast<AnimationComponent *>(component.get()); if (component->getType() == Component::TYPE::ANIMATION)
animComponent->getAnimationSet()->attachEventManager(eventManager); {
} auto animComponent = static_cast<AnimationComponent*>(component.get());
} animComponent->getAnimationSet()->attachEventManager(eventManager);
}
}
if (auto event = this->eventManager.lock()) { if (auto event = this->eventManager.lock()) {
auto self = this; auto self = this;
event->subscribe<AnimationFinishedEvent>( event->subscribe<AnimationFinishedEvent>([self](const AnimationFinishedEvent& e) {
[self](const AnimationFinishedEvent &e) { if (self) {
if (self) { if (e.entityid == self->entityid && e.animType == "reload")
if (e.entityid == self->entityid && e.animType == "reload") { {
if (self->reloading) { if (self->reloading)
self->reloading = false; {
self->wasReloading = true; self->reloading = false;
} self->wasReloading = true;
} }
} }
}); }
} });
}
bulletManager->hookEventManager(eventManager); bulletManager->hookEventManager(eventManager);
} }
void Weapon::attachScript(std::unique_ptr<WeaponScript> script) { void Weapon::attachScript(std::unique_ptr<WeaponScript> script)
weaponScript = std::move(script); {
weaponScript->lua["weapon"] = this; weaponScript = std::move(script);
LOG(DEBUG, "Weapon state bound", NULL); weaponScript->lua["weapon"] = this;
LOG(DEBUG, "Weapon state bound", NULL);
} }
void Weapon::onHitCallback(GameActor *target, PhysicsComponent *bullet, void Weapon::onHitCallback(GameActor* target, PhysicsComponent* bullet, const glm::vec2& normal)
const glm::vec2 &normal) { {
if (weaponScript && weaponScript->lua["onHit"].valid()) { if (weaponScript && weaponScript->lua["onHit"].valid())
auto result = weaponScript->lua["onHit"](target, bullet, normal); {
if (!result.valid()) { auto result = weaponScript->lua["onHit"](target, bullet, normal);
sol::error err = result; if (!result.valid())
std::cerr << "lua error: " << err.what() << std::endl; {
} sol::error err = result;
} std::cerr << "lua error: " << err.what() << std::endl;
}
}
} }
void Weapon::update(double deltaTime) { void Weapon::update(double deltaTime)
Entity::update(deltaTime); {
if (wielded) { Entity::update(deltaTime);
// move the weapon into place as the wielder rotates and moves if (wielded)
if (wielder) {
adjustWeapon(); // move the weapon into place as the wielder rotates and moves
if (wielder)
adjustWeapon();
for (auto &component : components) for (auto& component : components)
component->update(); component->update();
if (wasReloading) { if (wasReloading)
wasReloading = false; {
if (auto event = eventManager.lock()) { wasReloading = false;
event->notify<EntityFinishReloadEvent>({entityid}); if (auto event = eventManager.lock()) {
} event->notify<EntityFinishReloadEvent>({entityid});
} }
} }
bulletManager->update(deltaTime); }
bulletManager->update(deltaTime);
} }
void Weapon::draw() { void Weapon::draw()
Entity::draw(); {
if (wielded) { Entity::draw();
for (auto &component : components) { if (wielded)
component->play(); {
component->bind(); for (auto& component : components)
component->render(); {
} component->play();
} component->bind();
// bulletManager->draw(); component->render();
}
}
//bulletManager->draw();
} }
void Weapon::adjustWeapon() { void Weapon::adjustWeapon()
float rotation = glm::radians(wielder->getRotation()); {
float rotation = glm::radians(wielder->getRotation());
glm::vec3 offset = glm::vec3( glm::vec3 offset = glm::vec3(
cos(rotation) * ((wielder->getScale().x) - weaponSize.x * 0.5f), cos(rotation) * ((wielder->getScale().x) - weaponSize.x * 0.5f),
sin(rotation) * ((wielder->getScale().y) - weaponSize.y * 0.5f), 0.0f); sin(rotation) * ((wielder->getScale().y) - weaponSize.y * 0.5f),
glm::vec3 origin = wielder->getCenter() + offset; 0.0f
// origin.x += (weaponSize.x) * 0.25f; );
// origin.y += (weaponSize.y) * 0.25f; glm::vec3 origin = wielder->getCenter() + offset;
//origin.x += (weaponSize.x) * 0.25f;
//origin.y += (weaponSize.y) * 0.25f;
// Flip the texture if the weapon is facing upwards or downwards // Flip the texture if the weapon is facing upwards or downwards
Direction d = getDirectionFromRotation(glm::degrees(rotation)); Direction d = getDirectionFromRotation(glm::degrees(rotation));
if ((lastDir == Direction::Up || lastDir == Direction::Left) && if ((lastDir == Direction::Up || lastDir == Direction::Left) &&
(d == Direction::Down || d == Direction::Right) && !isFlipped()) (d == Direction::Down || d == Direction::Right) && !isFlipped())
flip(); flip();
if ((lastDir == Direction::Down || lastDir == Direction::Right) && if ((lastDir == Direction::Down || lastDir == Direction::Right) &&
(d == Direction::Up || d == Direction::Left) && isFlipped()) (d == Direction::Up || d == Direction::Left) && isFlipped())
flip(); flip();
setRotation(wielder->getRotation() - 180); setRotation(wielder->getRotation() - 180);
setPosition(origin); setPosition(origin);
lastDir = getDirectionFromRotation(glm::degrees(rotation)); lastDir = getDirectionFromRotation(glm::degrees(rotation));
} }
Weapon::BulletData Weapon::genBulletData() { Weapon::BulletData Weapon::genBulletData()
BulletData b; {
float rotation = glm::radians(wielder->getRotation()); BulletData b;
float spreadOffset = float rotation = glm::radians(wielder->getRotation());
glm::radians(static_cast<float>(bulletSpread->genFloat())); float spreadOffset = glm::radians(static_cast<float>(bulletSpread->genFloat()));
b.mass = 0.1f; b.mass = 0.1f;
glm::vec2 facing = glm::vec2 facing = glm::vec2(
glm::vec2(cos(rotation + spreadOffset), sin(rotation + spreadOffset)); cos(rotation + spreadOffset),
b.direction = glm::normalize(facing); sin(rotation + spreadOffset)
b.sizeMod = bulletModifer->genFloat(); );
b.speedMod = bulletModifer->genFloat(); b.direction = glm::normalize(facing);
b.dropMod = bulletModifer->genFloat(); b.sizeMod = bulletModifer->genFloat();
b.speedMod = bulletModifer->genFloat();
b.dropMod = bulletModifer->genFloat();
double radius = wielder->getScale().x + (weaponSize.x * 0.5) + weaponOffset.x; double radius = wielder->getScale().x + (weaponSize.x * 0.5) + weaponOffset.x;
b.origin = glm::vec3( b.origin = glm::vec3(
// x offset from the wielder // x offset from the wielder
wielder->getCenter().x + cos(rotation) * radius - wielder->getCenter().x + cos(rotation) * radius - sin(rotation) * weaponOffset.y,
sin(rotation) * weaponOffset.y, // y offset from the wielder
// y offset from the wielder wielder->getCenter().y + sin(rotation) * radius + cos(rotation) * weaponOffset.y,
wielder->getCenter().y + sin(rotation) * radius + 0.0f
cos(rotation) * weaponOffset.y, );
0.0f); b.origin.x -= ((bulletSize.x) * b.sizeMod) * 0.5f;
b.origin.x -= ((bulletSize.x) * b.sizeMod) * 0.5f; b.origin.y -= ((bulletSize.y) * b.sizeMod) * 0.5f;
b.origin.y -= ((bulletSize.y) * b.sizeMod) * 0.5f; return b;
return b;
} }
void Weapon::createBullet(const Weapon::BulletData &data) { void Weapon::createBullet(const Weapon::BulletData& data)
auto bullet = std::make_shared<Bullet>(wielder->getEntityID(), bulletShaderID, {
data.origin, data.direction, auto bullet = std::make_shared<Bullet>(wielder->getEntityID(), bulletShaderID, data.origin, data.direction, bulletSpeed, bulletDrop, bulletSize);
bulletSpeed, bulletDrop, bulletSize); bullet->addComponent(bulletSprite.get());
bullet->addComponent(bulletSprite.get()); bullet->addPhysicsComponent(std::make_shared<PhysicsComponent>(PhysicsComponentFactory::makeBullet(wielder->getEntityID(), data.origin, data.mass, bulletSize.x / 2)));
bullet->addPhysicsComponent( bullet->getPhysicsComponent()->rigidBody.velocity += bulletSpeed * data.direction / data.mass;
std::make_shared<PhysicsComponent>(PhysicsComponentFactory::makeBullet(
wielder->getEntityID(), data.origin, data.mass, bulletSize.x / 2)));
bullet->getPhysicsComponent()->rigidBody.velocity +=
bulletSpeed * data.direction / data.mass;
if (auto event = eventManager.lock()) if (auto event = eventManager.lock())
event->notify<BulletFiredEvent>({bullet}); event->notify<BulletFiredEvent>({bullet});
bulletManager->addBullet(bullet); bulletManager->addBullet(bullet);
} }
Weapon::~Weapon() { Weapon::~Weapon()
if (weaponScript) { {
weaponScript->lua["onShoot"] = sol::nil; if (weaponScript) {
weaponScript->lua.collect_gc(); weaponScript->lua["onShoot"] = sol::nil;
} weaponScript->lua.collect_gc();
}
} }
/*
!| 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

@ -1,28 +0,0 @@
#include "graphics/glwindow.h"
#include <SDL_error.h>
#include <SDL_video.h>
bool GLWindow::Init() {
window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_OPENGL);
if (!window) {
LOG(ERROR, "Failed to create window! {}", SDL_GetError());
return false;
}
glContext = SDL_GL_CreateContext(window);
if (!glContext) {
LOG(ERROR, "Failed to create opengl context! {}", SDL_GetError());
return false;
}
return true;
}
void GLWindow::resizeWindow(size_t width, size_t height) {
w = width;
h = height;
}
GLWindow::~GLWindow() {
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window);
}

View file

@ -1,227 +1,221 @@
#include "graphics/renderer.h" #include "graphics/renderer.h"
#include "graphics/shader.h" #include "graphics/shader.h"
#include "utility/events.h"
#include "utility/logger.h"
#include "utility/resourcemanager.h" #include "utility/resourcemanager.h"
#include "utility/logger.h"
#include "utility/events.h"
#include <algorithm> #include <algorithm>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <graphics/postprocess.h> #include <graphics/postprocess.h>
#include <memory> #include <memory>
Renderer::Renderer(const std::shared_ptr<ResourceManager> &r, Renderer::Renderer(const std::shared_ptr<ResourceManager>& r)
const std::shared_ptr<GLWindow> &w) : resourceManager(r)
: resourceManager(r), glWindow(w) { {
initFrameBuffers(); initFrameBuffers();
initUniformBuffers(); initUniformBuffers();
screenQuad = std::make_unique<ScreenQuad>(); screenQuad = std::make_unique<ScreenQuad>();
postProcessor = std::make_unique<Postprocessor>(r); postProcessor = std::make_unique<Postprocessor>(r);
} }
void Renderer::hookEventManager(std::weak_ptr<EventManager> eventManager) { void Renderer::hookEventManager(std::weak_ptr<EventManager> eventManager)
if (auto e = eventManager.lock()) { {
e->subscribe<ScreenShakeEvent>([this](const ScreenShakeEvent &event) { if (auto e = eventManager.lock()) {
postProcessor->ApplyEffect(Postprocessor::SHAKE, event.intensity, e->subscribe<ScreenShakeEvent>([this](const ScreenShakeEvent& event) {
event.duration); postProcessor->ApplyEffect(Postprocessor::SHAKE,
}); event.intensity,
e->subscribe<ScreenBlurEvent>([this](const ScreenBlurEvent &event) { event.duration
postProcessor->ApplyEffect(Postprocessor::BLUR, event.intensity, );
event.duration); });
}); e->subscribe<ScreenBlurEvent>([this](const ScreenBlurEvent& event) {
e->subscribe<WindowResizeEvent>( postProcessor->ApplyEffect(Postprocessor::BLUR,
[this](const WindowResizeEvent &event) { resizeFrameBuffers(); }); event.intensity,
} event.duration
} );
});
// Initialize the framebuffers used by the renderer to allow for post-processing
// effects
void Renderer::initFrameBuffers() {
glGenFramebuffers(1, &worldBuffer.frame);
glGenFramebuffers(1, &hudBuffer.frame);
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
// World buffer creation
glGenTextures(1, &worldBuffer.texture);
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glWindow->Width(), glWindow->Height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
// Attaching empty texture as color buffer for framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
worldBuffer.texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG(ERROR, "Failed to complete world framebuffer: {}",
glCheckFramebufferStatus(GL_FRAMEBUFFER));
assert(1 == 0); // force crash
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// same thing is done for the hud texture
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
glGenTextures(1, &hudBuffer.texture);
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glWindow->Width(), glWindow->Height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
hudBuffer.texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG(ERROR, "Failed to complete hud framebuffer: {}",
glCheckFramebufferStatus(GL_FRAMEBUFFER));
assert(1 == 0); // force crash
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Renderer::resizeFrameBuffers() {
glDeleteFramebuffers(1, &worldBuffer.frame);
glDeleteFramebuffers(1, &hudBuffer.frame);
glDeleteTextures(1, &worldBuffer.texture);
glDeleteTextures(1, &hudBuffer.texture);
initFrameBuffers();
}
void Renderer::initUniformBuffers() {
glGenBuffers(1, &uboMatrices);
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboMatrices);
// glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 *
// sizeof(glm::mat4));
}
void Renderer::setProjAndViewMatrix(const glm::mat4 &proj,
const glm::mat4 &view) {
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4),
glm::value_ptr(proj));
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4),
glm::value_ptr(view));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
void Renderer::addDrawable(RenderLayer renderLayer, Drawable *drawable) {
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu)
HUDLayerPool[renderLayer].push_back(drawable);
else
worldLayerPool[renderLayer].push_back(drawable);
}
void Renderer::removeDrawable(RenderLayer renderLayer, Drawable *drawable) {
auto erase = [&](auto &pool) {
pool.erase(std::remove(pool.begin(), pool.end(), drawable), pool.end());
};
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu) {
erase(HUDLayerPool[renderLayer]);
} else
erase(worldLayerPool[renderLayer]);
}
void Renderer::render() {
// Bind the world frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
// clear color and depth buffer
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderPool(worldLayerPool);
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderPool(HUDLayerPool);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
postProcessor->applyPostProcess(0);
screenQuad->draw();
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
// postProcessor->applyPostProcess(1);
screenQuad->draw();
}
void Renderer::renderPool(auto &layerPool) {
sortLayerPool(layerPool);
Shader *curShader = nullptr;
for (const auto &layer : renderingOrder) {
unsigned curShaderID = static_cast<unsigned>(-1);
for (const auto &item : layerPool[layer]) {
if (item->getShaderID() != curShaderID) {
curShaderID = item->getShaderID();
curShader = resourceManager->getShaderByID(curShaderID);
curShader->use();
}
if (!item->getOneShotUniforms().empty()) {
uploadUniforms(curShaderID, item->getOneShotUniforms());
item->clearOneShot();
}
uploadUniforms(curShaderID, item->getUniforms());
item->clearUniforms();
item->draw();
} }
}
} }
void Renderer::clear() { // Initialize the framebuffers used by the renderer to allow for post-processing effects
worldLayerPool.clear(); void Renderer::initFrameBuffers()
HUDLayerPool.clear(); {
glGenFramebuffers(1, &worldBuffer.frame);
glGenFramebuffers(1, &hudBuffer.frame);
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
// World buffer creation
glGenTextures(1, &worldBuffer.texture);
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
// !!! NEED TO CREATE STATIC WINDOW SIZING !!!
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 800, 600, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
//Attaching empty texture as color buffer for framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, worldBuffer.texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG(ERROR, "Failed to complete world framebuffer: {}", glCheckFramebufferStatus(GL_FRAMEBUFFER));
assert(1 == 0); // force crash
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// same thing is done for the hud texture
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
glGenTextures(1, &hudBuffer.texture);
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 800, 600, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hudBuffer.texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG(ERROR, "Failed to complete hud framebuffer: {}", glCheckFramebufferStatus(GL_FRAMEBUFFER));
assert(1 == 0); // force crash
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
void Renderer::uploadUniforms(const unsigned shaderID, void Renderer::initUniformBuffers()
const std::vector<Uniform> &uniforms) { {
Shader *shader = resourceManager->getShaderByID(shaderID); glGenBuffers(1, &uboMatrices);
if (shader == nullptr) {
LOG(ERROR, "No shader found with id {} !", shaderID); glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
return; glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
} glBindBuffer(GL_UNIFORM_BUFFER, 0);
for (const auto &uniform : uniforms) {
std::visit( glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboMatrices);
[&](auto &&arg) { //glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 * sizeof(glm::mat4));
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, bool>) {
shader->setBool(uniform.name, arg);
} else if constexpr (std::is_same_v<T, int>) {
shader->setInt(uniform.name, arg);
} else if constexpr (std::is_same_v<T, float>) {
shader->setFloat(uniform.name, arg);
} else if constexpr (std::is_same_v<T, glm::vec2>) {
shader->setVec2(uniform.name, glm::value_ptr(arg));
} else if constexpr (std::is_same_v<T, glm::mat4>) {
shader->setMatrix4f(uniform.name, glm::value_ptr(arg));
}
},
uniform.value);
}
} }
void Renderer::sortLayerPool(auto &layerPool) {
// Sort by shader id, this works to batch shaders together to avoid shader void Renderer::setProjAndViewMatrix(const glm::mat4& proj, const glm::mat4& view)
// switching too much {
for (auto &[_, pool] : layerPool) { glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
std::sort(pool.begin(), pool.end(), glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(proj));
[](const Drawable *a, const Drawable *b) { glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view));
return a->getShaderID() < b->getShaderID(); glBindBuffer(GL_UNIFORM_BUFFER, 0);
}); }
}
void Renderer::addDrawable(RenderLayer renderLayer, Drawable *drawable)
{
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu)
HUDLayerPool[renderLayer].push_back(drawable);
else
worldLayerPool[renderLayer].push_back(drawable);
}
void Renderer::removeDrawable(RenderLayer renderLayer, Drawable *drawable)
{
auto erase = [&](auto& pool) {
pool.erase(std::remove(pool.begin(), pool.end(), drawable), pool.end());
};
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu) {
erase(HUDLayerPool[renderLayer]);
}
else
erase(worldLayerPool[renderLayer]);
}
void Renderer::render()
{
// Bind the world frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
// clear color and depth buffer
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderPool(worldLayerPool);
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderPool(HUDLayerPool);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
postProcessor->applyPostProcess(0);
screenQuad->draw();
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
postProcessor->applyPostProcess(1);
screenQuad->draw();
}
void Renderer::renderPool(auto& layerPool)
{
sortLayerPool(layerPool);
Shader* curShader = nullptr;
for (const auto& layer : renderingOrder) {
unsigned curShaderID = static_cast<unsigned>(-1);
for (const auto& item : layerPool[layer]) {
if (item->getShaderID() != curShaderID) {
curShaderID = item->getShaderID();
curShader = resourceManager->getShaderByID(curShaderID);
curShader->use();
}
if (!item->getOneShotUniforms().empty()) {
uploadUniforms(curShaderID, item->getOneShotUniforms());
item->clearOneShot();
}
uploadUniforms(curShaderID, item->getUniforms());
item->clearUniforms();
item->draw();
}
}
}
void Renderer::clear()
{
worldLayerPool.clear();
HUDLayerPool.clear();
}
void Renderer::uploadUniforms(const unsigned shaderID, const std::vector<Uniform>& uniforms)
{
Shader *shader = resourceManager->getShaderByID(shaderID);
if (shader == nullptr) {
LOG(ERROR, "No shader found with id {} !", shaderID);
return;
}
for (const auto& uniform : uniforms) {
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, bool>) {
shader->setBool(uniform.name, arg);
}
else if constexpr (std::is_same_v<T, int>) {
shader->setInt(uniform.name, arg);
}
else if constexpr (std::is_same_v<T, float>) {
shader->setFloat(uniform.name, arg);
}
else if constexpr (std::is_same_v<T, glm::vec2>) {
shader->setVec2(uniform.name, glm::value_ptr(arg));
}
else if constexpr (std::is_same_v<T, glm::mat4>) {
shader->setMatrix4f(uniform.name, glm::value_ptr(arg));
}
}, uniform.value);
}
}
void Renderer::sortLayerPool(auto& layerPool)
{
// Sort by shader id, this works to batch shaders together to avoid shader switching too much
for (auto& [_,pool] : layerPool) {
std::sort(pool.begin(), pool.end(),
[](const Drawable* a, const Drawable* b) {
return a->getShaderID() < b->getShaderID();
});
}
} }

View file

@ -1,22 +1,28 @@
#undef GLM_FORCE_FAST_MATH
#define GLM_FORCE_PURE
#define GLM_FORCE_CTOR_INIT
#define GLM_FORCE_COLUMN_MAJOR
#include <SDL.h> #include <SDL.h>
#include <SDL_events.h> #include <SDL_events.h>
#include <SDL_image.h> #include <SDL_image.h>
#include <iostream> #include <iostream>
// TODO: Fix circular dependency issues, mostly with input.h needing gameactor.h and command.h
#include "gameplay/game.h" #include "gameplay/game.h"
#include "utility/logger.h"
const float vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
int main(int argc, char* args[]) int main(int argc, char* args[])
{ {
if (SDL_Init(SDL_INIT_VIDEO) < 0) { if (SDL_Init(SDL_INIT_VIDEO) < 0)
LOG(ERROR, "Failed to initialize SDL!", NULL);
return -1; return -1;
} if (IMG_Init(IMG_INIT_PNG) < 0)
if (IMG_Init(IMG_INIT_PNG) < 0) {
LOG(ERROR, "Could not initialize png library", NULL);
return -1; return -1;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
@ -25,13 +31,13 @@ int main(int argc, char* args[])
Game* game = new Game(); Game* game = new Game();
if (!game->init()) if (!game->init())
{ {
LOG(ERROR, "Failed to init game!", NULL); std::cout << "Failed to init game!" << std::endl;
return -1; return -1;
} }
if (!game->loadDebugScene()) if (!game->loadDebugScene())
{ {
LOG(ERROR, "Failed to load debug scene!", NULL); std::cout << "Failed to load debug scene!" << std::endl;
return -1; return -1;
} }