Added readme and various comments and general cleanup

This commit is contained in:
ethan 2025-04-17 11:30:11 -04:00
parent 701ac3e151
commit 3437441feb
17 changed files with 964 additions and 937 deletions

1
.gitignore vendored
View file

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

66
README.md Normal file
View file

@ -0,0 +1,66 @@
# 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

@ -3,42 +3,43 @@
#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)), front(glm::vec3(0.0f, 0.0f, -1.0f)), up(glm::vec3(0.0f, 1.0f, 0.0f)),
up(glm::vec3(0.0f, 1.0f, 0.0f)), viewPortW(viewPortW), viewPortH(viewPortH) {};
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 { return glm::vec3(position.x + (viewPortW / 2.f), position.y + (viewPortH / 2.f), 0.0f); } const glm::vec3 getCenterPos() const {
void setTarget(Entity* target) { this->target = target; } return glm::vec3(position.x + (viewPortW / 2.f),
void unsetTarget() { target = nullptr; } position.y + (viewPortH / 2.f), 0.0f);
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

@ -20,6 +20,8 @@ public:
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; } SDL_Window *getWindow() const { return window; }
const SDL_GLContext &getContext() const { return glContext; } const SDL_GLContext &getContext() const { return glContext; }

View file

@ -56,6 +56,8 @@ private:
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 sortLayerPool(auto &layerPool);

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> template <typename T> using EventCallback = std::function<void(const 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*)>>> callbacks; std::unordered_map<std::type_index, std::vector<std::function<void(void *)>>>
callbacks;
}; };
#endif //_H_EVENTS_H #endif //_H_EVENTS_H

View file

@ -4,103 +4,91 @@
#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) {}
{}
/* /*
* The behavior script works off of three different states, idle, patrol and alert. * The behavior script works off of three different states, idle, patrol and
* When the script is idle, this is */ * alert. When the script is idle, this is */
void AI::attachBehaviourScript(std::unique_ptr<AIScript> behaviour) void AI::attachBehaviourScript(std::unique_ptr<AIScript> behaviour) {
{ // passing out instance of raycaster and this AI into our scripting api
// passing out instance of raycaster and this AI into our scripting api // pay special attention each ai script has control of only their own instance
// pay special attention each ai script has control of only their own instance of ai! // of ai!
this->behaviour = std::move(behaviour); this->behaviour = std::move(behaviour);
this->behaviour->lua["raycaster"] = sol::make_reference(this->behaviour->lua, raycaster.get()); this->behaviour->lua["raycaster"] =
this->behaviour->lua["ai"] = sol::make_reference(this->behaviour->lua, this); 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 {
try { // If our ai doesn't have a reference to the actor it's controlling it will
// If our ai doesn't have a reference to the actor it's controlling it will do nothing. // do nothing. If our ai script does have an actor but no target, it will
// If our ai script does have an actor but no target, it will default to its idle state. // default to its idle state.
if (actor && target) { if (actor && target) {
switch (state) switch (state) {
{ case AIState::Idle:
case AIState::Idle: if (idleFunc.valid()) {
if (idleFunc.valid()) auto result = idleFunc(actor, target);
{ if (!result.valid()) {
auto result = idleFunc(actor, target); sol::error err = result;
if (!result.valid())
{
sol::error err = result;
LOG(ERROR, "Lua error: {}", err.what(), NULL); LOG(ERROR, "Lua error: {}", err.what(), NULL);
}
}
break;
case AIState::Patrol:
if (patrolFunc.valid())
{
auto result = patrolFunc(actor, target);
if (!result.valid())
{
sol::error err = result;
LOG(ERROR, "Lua error: {}", err.what(), NULL);
}
}
break;
case AIState::Alert:
if (alertFunc.valid())
{
auto result = alertFunc(actor, target);
if (!result.valid())
{
sol::error err = result;
LOG(ERROR, "Lua error: {}", err.what(), NULL);
}
}
break;
}
} else if (actor) {
switch (state)
{
case AIState::Idle:
if (idleFunc.valid()) {
auto result = idleFunc(actor, nullptr);
if (!result.valid()) {
sol::error err = result;
LOG(ERROR, "Lua Error: {}", err.what());
}
} }
}
break;
case AIState::Patrol:
if (patrolFunc.valid()) {
auto result = patrolFunc(actor, target);
if (!result.valid()) {
sol::error err = result;
LOG(ERROR, "Lua error: {}", err.what(), NULL);
}
}
break;
case AIState::Alert:
if (alertFunc.valid()) {
auto result = alertFunc(actor, target);
if (!result.valid()) {
sol::error err = result;
LOG(ERROR, "Lua error: {}", err.what(), NULL);
}
}
break;
}
} else if (actor) {
if (idleFunc.valid()) {
auto result = idleFunc(actor, nullptr);
if (!result.valid()) {
sol::error err = result;
LOG(ERROR, "Lua Error: {}", err.what());
}
} }
} }
// Collect garbage just to be sure. // Collect garbage just to be sure.
std::chrono::high_resolution_clock::time_point curTime = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point curTime =
if (curTime - lastGCTime >= GCTimeout) std::chrono::high_resolution_clock::now();
{ if (curTime - lastGCTime >= GCTimeout) {
behaviour->lua.collect_gc(); behaviour->lua.collect_gc();
lastGCTime = curTime; lastGCTime = curTime;
} }
} } catch (const std::exception &e) {
catch (const std::exception& e) {
LOG(ERROR, "Problem occured during AI update: {}", e.what()); LOG(ERROR, "Problem occured during AI update: {}", e.what());
state = AIState::Idle; state = AIState::Idle;
} }
} }
AI::~AI() AI::~AI() {
{ // Just set all of the references to null, given they are smart pointers, they
// Just set all of the references to null, given they are smart pointers, they are freed when no longer used. // 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,28 +1,31 @@
#include "gameplay/camera.h" #include "gameplay/camera.h"
// follow our target set using the setTarget. If there is no target set the camera will not move. // 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) {
viewPortW = width;
viewPortH = height;
} }
// The local coordinates are the corrdinates relative to the camera. // The local coordinates are the corrdinates relative to the camera.
const glm::vec3 Camera::worldToLocal(const glm::vec3& worldCoordinates) const glm::vec3 Camera::worldToLocal(const glm::vec3 &worldCoordinates) {
{ // return worldCoordinates - position;
//return worldCoordinates - position; return glm::vec3(getViewMatrix() * glm::vec4(worldCoordinates, 1.0f));
return glm::vec3(getViewMatrix() * glm::vec4(worldCoordinates, 1.0f));
} }

View file

@ -11,6 +11,7 @@
#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 <utility/events.h> #include <utility/events.h>
@ -85,7 +86,17 @@ bool Game::loadDebugScene() {
return true; return true;
} }
void Game::captureInput(SDL_Event &e) { inputHandler->captureInput(e); } void Game::captureInput(SDL_Event &e) {
inputHandler->captureInput(e);
if (e.type == SDL_WINDOWEVENT_RESIZED) {
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->handleInput();

View file

@ -3,156 +3,181 @@
#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/events.h"
#include "utility/direction.h"
#include "utility/component.h" #include "utility/component.h"
#include "utility/direction.h"
#include "utility/events.h"
#include <memory> #include <memory>
GameActor::~GameActor() { } // The components are smart pointer references to resources owned by the
// resource manager. So the GameActor is not responsible for cleaning resources.
GameActor::~GameActor() {}
void GameActor::addComponent(std::unique_ptr<Component> component) // Adding a component also means attaching the id of the entity to the
{ // component, so it's aware of who owns it. This is important for event handling
component->ownerid = entityid; void GameActor::addComponent(std::unique_ptr<Component> component) {
components.push_back(std::move(component)); component->ownerid = entityid;
components.push_back(std::move(component));
} }
Weapon* GameActor::getHeldWeapon() const Weapon *GameActor::getHeldWeapon() const {
{ return (weapons.empty() || currentWeaponIndex >= weapons.size())
return (weapons.empty() || currentWeaponIndex >= weapons.size()) ? nullptr : weapons[currentWeaponIndex].get(); ? nullptr
: weapons[currentWeaponIndex].get();
} }
std::span<Weapon*> GameActor::getAllWeapons() std::span<Weapon *> GameActor::getAllWeapons() { return weaponCache; }
{
return weaponCache; // Keep a seperate vector that is used as the cache for external calls to
// 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::pickupWeapon(std::unique_ptr<Weapon> weapon) void GameActor::setRotation(const float &rotation) {
{ // Any attached animation component would be interested if their owner needs
weapon->setWielder(this); // their sprite swapped
if (auto eventManager = sceneContext->getEventManager().lock()) { if (!isRotatable) {
weapon->hookEventManager(eventManager); if (auto eventManager = sceneContext->getEventManager().lock()) {
} Direction newDir = getDirectionFromRotation(rotation);
weaponCache.push_back(weapon.get()); if (getDirectionFromRotation(this->rotation) != newDir)
weapons.push_back(std::move(weapon)); eventManager->notify<DirectionChangeEvent>({entityid, newDir});
// wield the newly picked up weapon. }
getHeldWeapon()->putaway(); }
currentWeaponIndex = weapons.size() - 1; this->rotation = rotation;
getHeldWeapon()->wield(); updateModelMatrix();
} }
void GameActor::setRotation(const float& rotation) void GameActor::update(double deltaTime) {
{ Entity::update(deltaTime);
if (!isRotatable) {
if (auto eventManager = sceneContext->getEventManager().lock()) { for (const auto &component : components)
Direction newDir = getDirectionFromRotation(rotation); component->update();
if (getDirectionFromRotation(this->rotation) != newDir) for (const auto &weapon : weapons)
eventManager->notify<DirectionChangeEvent>({ entityid, newDir }); weapon->update(deltaTime);
}
} // Not the cleanest solution, but this is to make sure the animation isn't
this->rotation = rotation; // starting to move over and over.
updateModelMatrix(); 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::update(double deltaTime) void GameActor::draw() {
{ Entity::draw();
Entity::update(deltaTime); for (const auto &component : components) {
component->bind();
for (const auto& component : components) component->render();
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::draw() void GameActor::moveUp() {
{ if (physics)
Entity::draw(); physics->rigidBody.applyForce(glm::vec3(0.f, -1.f, 0.f), 1500.25f);
isMoving = true;
// regular loop through components, but if the component returns true to kill, we erase it. }
// Components are always assumed to be smart pointers! void GameActor::moveDown() {
for (const auto& component : components) if (physics)
{ physics->rigidBody.applyForce(glm::vec3(0.f, 1.f, 0.f), 1500.25f);
component->bind(); isMoving = true;
component->render(); }
} void GameActor::moveLeft() {
//for (auto& weapon : weapons) if (physics)
// weapon->draw(); 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;
} }
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 (sceneContext->getPlayerID() == entityid) { // If it's the player that is shooting apply a shake and blur effect to
if (auto gEvent = sceneContext->getGlobalEventManager().lock()) { // give the shots some weight
gEvent->notify<ScreenShakeEvent>({0.01f, 0.8f}); if (sceneContext->getPlayerID() == entityid) {
gEvent->notify<ScreenBlurEvent>({1.f, 0.8f}); if (auto gEvent = sceneContext->getGlobalEventManager().lock()) {
} 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 + weapons.size() - 1) % weapons.size(); currentWeaponIndex =
weapons[currentWeaponIndex]->wield(); (currentWeaponIndex + weapons.size() - 1) % weapons.size();
} weapons[currentWeaponIndex]->wield();
}
} }
void GameActor::cycleWeapons(const MouseState& mouse_state) void GameActor::cycleWeapons(const MouseState &mouse_state) {
{ if (mouse_state.scroll < 0)
if (mouse_state.scroll < 0) cycleUpWeapons();
cycleUpWeapons(); else if (mouse_state.scroll > 0)
else if (mouse_state.scroll > 0) cycleDownWeapons();
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 direction = glm::vec2(mouse_state.x, mouse_state.y) - glm::vec2(localPosition.x, localPosition.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) != getDirectionFromRotation(newRotation)) { if (getDirectionFromRotation(rotation) !=
if (auto event = sceneContext->getEventManager().lock()) { getDirectionFromRotation(newRotation)) {
event->notify<DirectionChangeEvent>({ entityid, getDirectionFromRotation(newRotation) }); if (auto event = sceneContext->getEventManager().lock()) {
} 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() { position.x += sin(glm::radians(rotation)) * speed; position.y -= cos(glm::radians(rotation)) * speed; } void GameActor::strafeLeft() {
void GameActor::strafeRight() { position.x -= sin(glm::radians(rotation)) * speed; position.y += cos(glm::radians(rotation)) * speed; } position.x += sin(glm::radians(rotation)) * speed;
void GameActor::moveBackward() { position.x -= cos(glm::radians(rotation)) * speed; position.y -= sin(glm::radians(rotation)) * speed; } position.y -= cos(glm::radians(rotation)) * speed;
void GameActor::moveForward() { }
if (physics) { void GameActor::strafeRight() {
physics->rigidBody.velocity.x += cos(glm::radians(rotation)) * speed; position.x -= sin(glm::radians(rotation)) * speed;
physics->rigidBody.velocity.y += sin(glm::radians(rotation)) * speed; position.y += cos(glm::radians(rotation)) * speed;
} }
isMoving = true; void GameActor::moveBackward() {
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,56 +3,51 @@
#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.
if (!actor) return; // This will be due for change given we will need to handle input during menus
Uint32 currentTime = SDL_GetTicks(); if (!actor)
// check for bound keys that were pressed, return;
// next check if the hasn't been executed within the amount specified in delay Uint32 currentTime = SDL_GetTicks();
// if not execute the command and set lastExecution to currentTime // check for bound keys that were pressed,
for (auto& [key, command] : keyCommands) // next check if the hasn't been executed within the amount specified in delay
{ // if not execute the command and set lastExecution to currentTime
if (keys[key] == true) for (auto &[key, command] : keyCommands) {
{ if (keys[key] == true) {
if (currentTime - command.lastExecution >= command.delay) if (currentTime - command.lastExecution >= command.delay) {
{ commandQueue.push_back(command.cmd.get());
commandQueue.push_back(command.cmd.get()); command.lastExecution = currentTime;
command.lastExecution = currentTime; }
} }
} }
} // Same with the mouse, for this context we'll be checking for motion events
// Same with the mouse, for this context we'll be checking for motion events and for click events // and for click events
for (auto& [button, command] : mouseCommands) for (auto &[button, command] : mouseCommands) {
{ if (mouseButtons[button] == true) {
if (mouseButtons[button] == true) if (currentTime - command.lastExecution >= command.delay) {
{ mouseQueue.push_back(command.cmd.get());
if (currentTime - command.lastExecution >= command.delay) command.lastExecution = currentTime;
{ }
mouseQueue.push_back(command.cmd.get()); }
command.lastExecution = currentTime; }
} if (mouseMotionCommand)
} mouseMotionCommand->execute(*actor, mouse_state);
} if (mouseScrollCommand)
if (mouseMotionCommand) mouseScrollCommand->execute(*actor, mouse_state);
mouseMotionCommand->execute(*actor, mouse_state); mouse_state.scroll =
if (mouseScrollCommand) 0.0f; // clear mouse scroll since we have handled the event.
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) {
for (auto& command : commandQueue) { command->execute(*actor);
command->execute(*actor); }
} for (auto &mouse : mouseQueue) {
for (auto& mouse : mouseQueue) { mouse->execute(*actor, mouse_state);
mouse->execute(*actor, mouse_state); }
} commandQueue.clear();
commandQueue.clear(); mouseQueue.clear();
mouseQueue.clear();
} }
InputHandler::~InputHandler() InputHandler::~InputHandler() {}
{
}

View file

@ -1,125 +1,129 @@
#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/xmlloader.h"
#include "utility/resourcemanager.h"
#include "utility/logger.h" #include "utility/logger.h"
#include "utility/resourcemanager.h"
#include "utility/xmlloader.h"
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
Map::Map(const MapData* mapData, const unsigned shaderID, std::shared_ptr<ResourceManager> resourceManager) : Map::Map(const MapData *mapData, const unsigned shaderID,
mapData(mapData), std::shared_ptr<ResourceManager> resourceManager)
tileIds(mapData->tiles) : mapData(mapData), tileIds(mapData->tiles) {
{ this->shaderID = shaderID;
this->shaderID = shaderID;
for (auto& tileSet : mapData->tileSets) // Tiles are held on layers, drawn back to front
tileSetData.push_back(resourceManager->loadTileSet(tileSet.path)); for (auto &tileSet : mapData->tileSets)
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
std::vector<const char*> buffer; // texture instance handle
for (int layer = 0; layer < tileIds.size(); layer++) std::vector<const char *> buffer;
{ 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(std::make_shared<TileTextureInstance>(buffer)); instanceHandles.push_back(
} 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::createCollisionMap() void Map::loadMap() {
{ tileData.resize(tileIds.size());
// Match collisionMap to map size // Tiles are drawn back to front, we track the tileset the tile comes from and
collisionMap.resize(tileIds[0].size()); // additional information about the tilesets themselves so the shader can
for (int y = 0; y < tileIds[0].size(); ++y) // properly draw the right tile. Each layer is its own instance
{ for (int layer = 0; layer < tileIds.size(); layer++) {
collisionMap[y].resize(tileIds[0][y].size(), 0); 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));
for (int layer = 0; layer < tileIds.size(); layer++) int textureIndex =
{ static_cast<int>(getTileSetIndex(tileIds[layer][y][x]));
for (int y = 0; y < tileIds[layer].size(); y++) glm::vec2 originalSize =
{ (textureIndex != -1) ? glm::vec2(tileSetData[textureIndex]->width,
for (int x = 0; x < tileIds[layer][y].size(); x++) tileSetData[textureIndex]->height)
{ : glm::vec2(0.0f);
int id = tileIds[layer][y][x]; int tilesPerRow =
size_t tileSetIndex = getTileSetIndex(id); (textureIndex != -1) ? tileSetData[textureIndex]->columns : 0;
if (tileSetIndex == -1) int startID =
collisionMap[y][x] = 0; (textureIndex != -1) ? mapData->tileSets[textureIndex].startID : 0;
else int tileIndex = tileIds[layer][y][x];
{
int startID = mapData->tileSets[tileSetIndex].startID; tileData[layer].push_back({modelMatrix, originalSize, tileIndex,
auto& tile = tileSetData[tileSetIndex]->tiles[id - startID]; textureIndex, tilesPerRow, 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);
} }
void Map::draw() // The collision map is just a 2D array of 0's and 1's, 1's being collidable and
{ // 0's not. This may see some changes in the future to properly address special
for (int layer = 0; layer < instanceHandles.size(); layer++) // tiles, such as portals.
{ void Map::createCollisionMap() {
instanceHandles[layer]->draw(); // Match collisionMap to map size
} 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
Use this function to get the tileSetIndex from a tile ID. void Map::draw() {
the index of the tileSet is the same index as the texture index used in the instanceHandle for (int layer = 0; layer < instanceHandles.size(); layer++) {
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
// work from the bottom, break if ID > startID // If we get a textureIndex of -1 then we are on an empty tile
// If we get a textureIndex of -1 then we are on an empty tile size_t tileSetIndex = mapData->tileSets.size() - 1;
size_t tileSetIndex = mapData->tileSets.size() - 1; for (; tileSetIndex != -1; --tileSetIndex) {
for (; tileSetIndex != -1; --tileSetIndex) if (id >= mapData->tileSets[tileSetIndex].startID)
{ break;
if (id >= mapData->tileSets[tileSetIndex].startID) }
break; return (tileSetIndex >= mapData->tileSets.size()) ? -1 : tileSetIndex;
}
return (tileSetIndex >= mapData->tileSets.size()) ? -1 : tileSetIndex;
} }

View file

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

View file

@ -21,18 +21,6 @@
#include <memory> #include <memory>
#include <execution>
// Scene xml files, should contain a node called <player> that holds the sprite
// location
/*
like this:
<player sprite="sprites/player2Atlas.png" frameSize=64.0>
<x=5/>
<y=6/>
</player>
*/
Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources, Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources,
std::weak_ptr<EventManager> globalEvents) std::weak_ptr<EventManager> globalEvents)
: type(sceneType), resourceManager(resources), : type(sceneType), resourceManager(resources),
@ -43,7 +31,6 @@ Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources,
void Scene::init() { void Scene::init() {
physicsEngine->hookEventManager(eventManager); physicsEngine->hookEventManager(eventManager);
// if (sceneType == SCENE_SHOOTER)
loadDebugShooterScene(); loadDebugShooterScene();
} }
@ -128,8 +115,6 @@ void Scene::loadDebugShooterScene() {
} }
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 {
@ -152,9 +137,7 @@ void Scene::render(std::shared_ptr<Renderer> renderer) {
renderer->addDrawable(RenderLayer::Background, renderer->addDrawable(RenderLayer::Background,
static_cast<Drawable *>(background)); static_cast<Drawable *>(background));
} }
// map->draw();
for (auto &[id, e] : entities) { for (auto &[id, e] : entities) {
// e->draw();
renderer->addDrawable(RenderLayer::GameObjects, e.get()); renderer->addDrawable(RenderLayer::GameObjects, e.get());
if (e->getHeldWeapon()) { if (e->getHeldWeapon()) {
renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon()); renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon());
@ -168,37 +151,6 @@ void Scene::render(std::shared_ptr<Renderer> renderer) {
} }
renderer->render(); renderer->render();
/*
for (const auto& bullet :
player->getHeldWeapon()->getBulletManager()->getBullets()) {
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);
}
*/
DEBUG_TEXT(glm::vec3(10.f, 10.f, 0.f), glm::vec4(0.f, 0.f, 0.f, 1.f), 0.5f, 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(), "{} / {}", getPlayer()->getHeldWeapon()->getMagazine(),
getPlayer()->getHeldWeapon()->getAmmo()); getPlayer()->getHeldWeapon()->getAmmo());

View file

@ -1,291 +1,249 @@
#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/debugdraw.h"
#include "utility/component.h" #include "utility/component.h"
#include "utility/debugdraw.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, const unsigned bulletShaderID, ResourceManager* resourceManager) Weapon::Weapon(const WeaponData *data, const unsigned weaponShaderID,
: const unsigned bulletShaderID, ResourceManager *resourceManager)
Entity (weaponShaderID), : Entity(weaponShaderID), weaponType(data->id),
weaponType (data->id), weaponSize(glm::vec2(data->sizeX, data->sizeY)),
weaponSize (glm::vec2(data->sizeX, data->sizeY)), weaponOffset(glm::vec2(data->offsetX, data->offsetY)),
weaponOffset (glm::vec2(data->offsetX, data->offsetY)), weaponMag(data->clipSize), weaponMagSize(data->clipSize),
weaponMag (data->clipSize), weaponAmmo(data->maxAmmo), bulletSpeed(data->bulletSpeed),
weaponMagSize (data->clipSize), bulletDrop(data->bulletDrop), fireSpeed(data->fireSpeed),
weaponAmmo (data->maxAmmo), bulletSize(glm::vec2(data->bulletSizeX, data->bulletSizeY)),
bulletSpeed (data->bulletSpeed), bulletShaderID(bulletShaderID),
bulletDrop (data->bulletDrop), bulletManager(std::make_shared<BulletManager>()),
fireSpeed (data->fireSpeed), bulletSpread(std::make_unique<UTIL::RandomGenerator>(-data->bulletSpread,
bulletSize (glm::vec2(data->bulletSizeX, data->bulletSizeY)), data->bulletSpread)),
bulletShaderID (bulletShaderID), bulletModifer(
bulletManager (std::make_shared<BulletManager>()), std::make_unique<UTIL::RandomGenerator>(data->modMin, data->modMax)) {
bulletSpread (std::make_unique<UTIL::RandomGenerator>(-data->bulletSpread, data->bulletSpread)), if (data->bulletAnimated)
bulletModifer (std::make_unique<UTIL::RandomGenerator>(data->modMin, data->modMax)) bulletSprite = std::make_unique<AnimationComponent>(
{ resourceManager->loadAnimationSet(data->bulletGraphic, entityid));
if (data->bulletAnimated) else
bulletSprite = std::make_unique<AnimationComponent>(resourceManager->loadAnimationSet(data->bulletGraphic, entityid)); bulletSprite = std::make_unique<SpriteComponent>(
else resourceManager->loadSpriteStatic(data->bulletGraphic));
bulletSprite = std::make_unique<SpriteComponent>(resourceManager->loadSpriteStatic(data->bulletGraphic));
if (data->animated) if (data->animated) {
{ addComponent(std::make_unique<AnimationComponent>(
addComponent(std::make_unique<AnimationComponent>(resourceManager->loadAnimationSet(data->id, entityid))); resourceManager->loadAnimationSet(data->id, entityid)));
} } else
else addComponent(std::make_unique<SpriteComponent>(
addComponent(std::make_unique<SpriteComponent>(resourceManager->loadSpriteStatic(data->graphic))); 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()) {
if (auto event = eventManager.lock()) event->notify<EntityReloadEvent>(
{ {entityid, wielder->getPosition(), weaponType});
event->notify<EntityReloadEvent>({ entityid, wielder->getPosition(), weaponType }); reloading = true;
reloading = true; if (weaponAmmo < weaponMagSize) {
if (weaponAmmo < weaponMagSize) { weaponMag = weaponAmmo;
weaponMag = weaponAmmo; weaponAmmo = 0;
weaponAmmo = 0; } else {
} weaponMag = weaponMagSize;
else { weaponAmmo -= weaponMagSize;
weaponMag = weaponMagSize; }
weaponAmmo -= weaponMagSize; }
}
}
} }
bool Weapon::shoot() bool Weapon::shoot() {
{ bool shotsFired = false;
bool shotsFired = false; if (wielder) {
if (wielder) Uint32 currentTime = SDL_GetTicks();
{ if (currentTime - lastFireTime >= fireSpeed && !reloading) {
Uint32 currentTime = SDL_GetTicks(); if (weaponMag > 0) {
if (currentTime - lastFireTime >= fireSpeed && !reloading) shotsFired = true;
{ if (auto event = eventManager.lock())
if (weaponMag > 0) event->notify<EntityFireEvent>(
{ {entityid, fireSpeed, wielder->getPosition(), weaponType});
shotsFired = true; if (!weaponScript || !weaponScript->lua["onShoot"].valid()) {
if (auto event = eventManager.lock()) // create bullet using this generated data
event->notify<EntityFireEvent>({entityid, fireSpeed, wielder->getPosition(), weaponType}); BulletData b = genBulletData();
if (!weaponScript || !weaponScript->lua["onShoot"].valid()) createBullet(b);
{ weaponMag -= 1;
// create bullet using this generated data } else {
BulletData b = genBulletData(); auto result = weaponScript->lua["onShoot"]();
createBullet(b); if (!result.valid()) {
weaponMag -= 1; sol::error err = result;
} std::cerr << "lua error: " << err.what() << std::endl;
else { }
auto result = weaponScript->lua["onShoot"](); // auto reload
if (!result.valid()) if ((weaponMag -= 1) <= 0)
{ reload();
sol::error err = result; }
std::cerr << "lua error: " << err.what() << std::endl; } else if (weaponMag <= 0)
} reload();
// auto reload lastFireTime = currentTime;
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) {
if (component->getType() == Component::TYPE::ANIMATION) auto animComponent = static_cast<AnimationComponent *>(component.get());
{ 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>([self](const AnimationFinishedEvent& e) { event->subscribe<AnimationFinishedEvent>(
if (self) { [self](const AnimationFinishedEvent &e) {
if (e.entityid == self->entityid && e.animType == "reload") if (self) {
{ if (e.entityid == self->entityid && e.animType == "reload") {
if (self->reloading) if (self->reloading) {
{ self->reloading = false;
self->reloading = false; self->wasReloading = true;
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 = std::move(script); weaponScript->lua["weapon"] = this;
weaponScript->lua["weapon"] = this; LOG(DEBUG, "Weapon state bound", NULL);
LOG(DEBUG, "Weapon state bound", NULL);
} }
void Weapon::onHitCallback(GameActor* target, PhysicsComponent* bullet, const glm::vec2& normal) void Weapon::onHitCallback(GameActor *target, PhysicsComponent *bullet,
{ const glm::vec2 &normal) {
if (weaponScript && weaponScript->lua["onHit"].valid()) if (weaponScript && weaponScript->lua["onHit"].valid()) {
{ auto result = weaponScript->lua["onHit"](target, bullet, normal);
auto result = weaponScript->lua["onHit"](target, bullet, normal); if (!result.valid()) {
if (!result.valid()) sol::error err = result;
{ 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);
Entity::update(deltaTime); if (wielded) {
if (wielded) // move the weapon into place as the wielder rotates and moves
{ if (wielder)
// move the weapon into place as the wielder rotates and moves adjustWeapon();
if (wielder)
adjustWeapon();
for (auto& component : components) for (auto &component : components)
component->update(); component->update();
if (wasReloading) if (wasReloading) {
{ wasReloading = false;
wasReloading = false; if (auto event = eventManager.lock()) {
if (auto event = eventManager.lock()) { event->notify<EntityFinishReloadEvent>({entityid});
event->notify<EntityFinishReloadEvent>({entityid}); }
} }
} }
} bulletManager->update(deltaTime);
bulletManager->update(deltaTime);
} }
void Weapon::draw() void Weapon::draw() {
{ Entity::draw();
Entity::draw(); if (wielded) {
if (wielded) for (auto &component : components) {
{ component->play();
for (auto& component : components) component->bind();
{ component->render();
component->play(); }
component->bind(); }
component->render(); // bulletManager->draw();
}
}
//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), sin(rotation) * ((wielder->getScale().y) - weaponSize.y * 0.5f), 0.0f);
0.0f glm::vec3 origin = wielder->getCenter() + offset;
); // origin.x += (weaponSize.x) * 0.25f;
glm::vec3 origin = wielder->getCenter() + offset; // origin.y += (weaponSize.y) * 0.25f;
//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;
BulletData b; float rotation = glm::radians(wielder->getRotation());
float rotation = glm::radians(wielder->getRotation()); float spreadOffset =
float spreadOffset = glm::radians(static_cast<float>(bulletSpread->genFloat())); glm::radians(static_cast<float>(bulletSpread->genFloat()));
b.mass = 0.1f; b.mass = 0.1f;
glm::vec2 facing = glm::vec2( glm::vec2 facing =
cos(rotation + spreadOffset), glm::vec2(cos(rotation + spreadOffset), sin(rotation + spreadOffset));
sin(rotation + spreadOffset) b.direction = glm::normalize(facing);
); b.sizeMod = bulletModifer->genFloat();
b.direction = glm::normalize(facing); b.speedMod = bulletModifer->genFloat();
b.sizeMod = bulletModifer->genFloat(); b.dropMod = 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 - sin(rotation) * weaponOffset.y, wielder->getCenter().x + cos(rotation) * radius -
// y offset from the wielder sin(rotation) * weaponOffset.y,
wielder->getCenter().y + sin(rotation) * radius + cos(rotation) * weaponOffset.y, // y offset from the wielder
0.0f wielder->getCenter().y + sin(rotation) * radius +
); cos(rotation) * weaponOffset.y,
b.origin.x -= ((bulletSize.x) * b.sizeMod) * 0.5f; 0.0f);
b.origin.y -= ((bulletSize.y) * b.sizeMod) * 0.5f; b.origin.x -= ((bulletSize.x) * b.sizeMod) * 0.5f;
return b; b.origin.y -= ((bulletSize.y) * b.sizeMod) * 0.5f;
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,
auto bullet = std::make_shared<Bullet>(wielder->getEntityID(), bulletShaderID, data.origin, data.direction, bulletSpeed, bulletDrop, bulletSize); data.origin, data.direction,
bullet->addComponent(bulletSprite.get()); bulletSpeed, bulletDrop, bulletSize);
bullet->addPhysicsComponent(std::make_shared<PhysicsComponent>(PhysicsComponentFactory::makeBullet(wielder->getEntityID(), data.origin, data.mass, bulletSize.x / 2))); bullet->addComponent(bulletSprite.get());
bullet->getPhysicsComponent()->rigidBody.velocity += bulletSpeed * data.direction / data.mass; bullet->addPhysicsComponent(
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) {
if (weaponScript) { weaponScript->lua["onShoot"] = sol::nil;
weaponScript->lua["onShoot"] = sol::nil; weaponScript->lua.collect_gc();
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,5 +1,6 @@
#include "graphics/glwindow.h" #include "graphics/glwindow.h"
#include <SDL_error.h> #include <SDL_error.h>
#include <SDL_video.h>
bool GLWindow::Init() { bool GLWindow::Init() {
window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED, window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED,
@ -16,6 +17,11 @@ bool GLWindow::Init() {
return true; return true;
} }
void GLWindow::resizeWindow(size_t width, size_t height) {
w = width;
h = height;
}
GLWindow::~GLWindow() { GLWindow::~GLWindow() {
SDL_GL_DeleteContext(glContext); SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window); SDL_DestroyWindow(window);

View file

@ -28,6 +28,8 @@ void Renderer::hookEventManager(std::weak_ptr<EventManager> eventManager) {
postProcessor->ApplyEffect(Postprocessor::BLUR, event.intensity, postProcessor->ApplyEffect(Postprocessor::BLUR, event.intensity,
event.duration); event.duration);
}); });
e->subscribe<WindowResizeEvent>(
[this](const WindowResizeEvent &event) { resizeFrameBuffers(); });
} }
} }
@ -41,7 +43,6 @@ void Renderer::initFrameBuffers() {
// World buffer creation // World buffer creation
glGenTextures(1, &worldBuffer.texture); glGenTextures(1, &worldBuffer.texture);
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture); glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glWindow->Width(), glWindow->Height(), glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glWindow->Width(), glWindow->Height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
@ -88,6 +89,15 @@ void Renderer::initFrameBuffers() {
glBindFramebuffer(GL_FRAMEBUFFER, 0); 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() { void Renderer::initUniformBuffers() {
glGenBuffers(1, &uboMatrices); glGenBuffers(1, &uboMatrices);