Compare commits

..
1 Commits
Author SHA1 Message Date
shadesandgrays 7cf3427a97 refactor: seperate engine and game 2026-07-16 21:44:34 +01:00
20 changed files with 61 additions and 558 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ Size=1280,800
Collapsed=1
[Window][VBoxContainer]
Pos=540,235
Pos=540,275
Size=200,250
[Window][Box]
-1
View File
@@ -32,7 +32,6 @@ namespace varicle {
scene_manager.render();
rlImGuiBegin();
scene_manager.ui();
rlImGuiEnd();
EndDrawing();
-109
View File
@@ -1,109 +0,0 @@
#pragma once
#include <variant>
#include <string>
#include <mutex>
#include <iostream>
struct Vec2 {
float x = 0.0f;
float y = 0.0f;
bool operator==(const Vec2& o) const { return x == o.x && y == o.y; }
bool operator!=(const Vec2& o) const { return !(*this == o); }
Vec2 operator+(const Vec2& o) const { return { x + o.x, y + o.y }; }
Vec2 operator-(const Vec2& o) const { return { x - o.x, y - o.y }; }
Vec2 operator*(float scalar) const { return { x * scalar, y * scalar }; }
};
struct Vec3 {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
bool operator==(const Vec3& o) const { return x == o.x && y == o.y && z == o.z; }
bool operator!=(const Vec3& o) const { return !(*this == o); }
Vec3 operator+(const Vec3& o) const { return { x + o.x, y + o.y, z + o.z }; }
Vec3 operator-(const Vec3& o) const { return { x - o.x, y - o.y, z - o.z }; }
Vec3 operator*(float scalar) const { return { x * scalar, y * scalar, z * scalar }; }
};
struct Vec4 {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float w = 1.0f;
bool operator==(const Vec4& o) const { return x == o.x && y == o.y && z == o.z && w == o.w; }
bool operator!=(const Vec4& o) const { return !(*this == o); }
Vec4 operator+(const Vec4& o) const { return { x + o.x, y + o.y, z + o.z, w + o.w }; }
Vec4 operator-(const Vec4& o) const { return { x - o.x, y - o.y, z - o.z, w - o.w }; }
Vec4 operator*(float scalar) const { return { x * scalar, y * scalar, z * scalar, w * scalar }; }
};
// Standalone Interpolation Helpers
inline Vec2 Vec2Lerp(const Vec2& s, const Vec2& e, float a) { return { s.x + (e.x - s.x) * a, s.y + (e.y - s.y) * a }; }
inline Vec3 Vec3Lerp(const Vec3& s, const Vec3& e, float a) { return { s.x + (e.x - s.x) * a, s.y + (e.y - s.y) * a, s.z + (e.z - s.z) * a }; }
inline Vec4 Vec4Lerp(const Vec4& s, const Vec4& e, float a) { return { s.x + (e.x - s.x) * a, s.y + (e.y - s.y) * a, s.z + (e.z - s.z) * a, s.w + (e.w - s.w) * a }; }
enum class VariantType { Null, Float, Vector2, Vector3, Vector4, String };
class EngineVariant{
public:
using InternalVariant = std::variant<std::monostate,float,Vec2,Vec3,Vec4,std::string>;
EngineVariant() : data(std::monostate{}), type(VariantType::Null){}
EngineVariant(float v) : data(v), type(VariantType::Float){}
EngineVariant(Vec2 v) : data(v), type(VariantType::Vector2){}
EngineVariant(Vec3 v) : data(v), type(VariantType::Vector3){}
EngineVariant(Vec4 v) : data(v), type(VariantType::Vector4){}
EngineVariant(std::string v) : data(v), type(VariantType::String){}
VariantType GetType() const { return type; }
template<typename T>
T Get() const {
std::lock_guard<std::mutex> lock(v_mutex);
return std::get<T>(data);
}
void Print() const {
std::lock_guard<std::mutex> lock(v_mutex);
std::cout << "[Variant " << TypeToString(type) << "]: ";
std::visit([](auto &&arg){
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, std::monostate>) std::cout << "Null";
else if constexpr (std::is_same_v<T, float>) std::cout << arg;
else if constexpr (std::is_same_v<T, Vec2>) std::cout << "(" << arg.x << ", " << arg.y << ")";
else if constexpr (std::is_same_v<T, Vec3>) std::cout << "(" << arg.x << ", " << arg.y << ", " << arg.z << ")";
else if constexpr (std::is_same_v<T, Vec4>) std::cout << "(" << arg.x << ", " << arg.y << ", " << arg.z << ", " << arg.w << ")";
else if constexpr (std::is_same_v<T, std::string>) std::cout << "\"" << arg << "\"";
},data);
std::cout << "\n";
}
private:
InternalVariant data;
VariantType type;
mutable std::mutex v_mutex;
static std::string TypeToString(VariantType t) {
switch(t) {
case VariantType::Float: return "Float";
case VariantType::Vector2: return "Vector2";
case VariantType::Vector3: return "Vector3";
case VariantType::Vector4: return "Vector4";
case VariantType::String: return "String";
default: return "Null";
}
}
friend class VariantOpManager;
};
@@ -1,74 +0,0 @@
#pragma once
#include <engine_variant.hpp>
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
enum class OpType {
Assign, // Replace the old value entirely
Add, // Add to the current value
Multiply, // Add to the current value
Lerp // Smoothly blend toward a value
};
struct VariantOpRequest {
EngineVariant* target; // The variable we want to change
OpType operation; // How we want to change it
EngineVariant operand; // The value we are using to make the change
float alpha; // Used ONLY for Lerp (0.0 to 1.0)
};
class VariantOpManager{
public:
VariantOpManager(){}
void ExecuteOperation(const VariantOpRequest& req) {
if (req.target == nullptr) return;
switch (req.operation){
case OpType::Assign:
req.target->data = req.operand.data;
req.target->type = req.operand.type;
break;
case OpType::Add:
req.target->data = std::visit(
overloaded{
[](float p, float n) -> EngineVariant::InternalVariant {return p + n;},
[](Vec2 p, Vec2 n) -> EngineVariant::InternalVariant {return p + n;},
[](Vec3 p, Vec3 n) -> EngineVariant::InternalVariant {return p + n;},
[](Vec4 p, Vec4 n) -> EngineVariant::InternalVariant {return p + n;},
// Fallback
[](auto p, auto n) -> EngineVariant::InternalVariant {return p;},
},req.target->data,req.operand.data);
break;
case OpType::Lerp:
req.target->data = std::visit(overloaded{
[&](float p, float n) -> EngineVariant::InternalVariant { return p + (n - p) * req.alpha; },
[&](Vec2 p, Vec2 n) -> EngineVariant::InternalVariant { return Vec2Lerp(p, n, req.alpha); },
[&](Vec3 p, Vec3 n) -> EngineVariant::InternalVariant { return Vec3Lerp(p, n, req.alpha); },
[&](Vec4 p, Vec4 n) -> EngineVariant::InternalVariant { return Vec4Lerp(p, n, req.alpha); },
[&](const std::string& p, const std::string& n) -> EngineVariant::InternalVariant {
return req.alpha >= 0.5f ? n : p; // Discrete switch for strings
},
// Fallback
[](auto p, auto n) -> EngineVariant::InternalVariant { return p; }
}, req.target->data, req.operand.data);
break;
case OpType::Multiply:
req.target->data = std::visit(overloaded{
[](float p, float n) -> EngineVariant::InternalVariant { return p * n; },
[](Vec2 p, float n) -> EngineVariant::InternalVariant { return p * n; }, // Scale a vector!
[](auto p, auto n) -> EngineVariant::InternalVariant { return p; }
}, req.target->data, req.operand.data);
break;
}
}
};
-2
View File
@@ -1,8 +1,6 @@
#pragma once
#include <cassert>
#include<vector>
#include<algorithm>
#include <memory>
#include <typeindex>
#include <unordered_map>
-2
View File
@@ -1,5 +1,3 @@
#pragma once
#include <raylib.h>
namespace varicle{
-22
View File
@@ -1,22 +0,0 @@
#pragma once
#include <random>
namespace varicle {
// Generate a random float between min and max
inline float RandomRange(float min, float max) {
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_real_distribution<float> dist(min, max);
return dist(gen);
}
// Generate a random int between min and max
inline int RandomRange(int min, int max) {
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<int> dist(min, max);
return dist(gen);
}
}
-4
View File
@@ -1,7 +1,3 @@
#pragma once
struct Player{};
struct PlayerBody{ unsigned int idx;};
struct Fruit{};
+1 -4
View File
@@ -4,7 +4,6 @@
#include "engine/scene/scene.hpp"
#include "game/scenes/main-scene/main-scene.hpp"
#include "game/scenes/menu-scene/menu.hpp"
#include <raylib.h>
@@ -15,9 +14,7 @@ class Game : public varicle::Application{
public:
void on_init() override {
auto& scene_manager = varicle::ServiceLocator::get<varicle::SceneManager>();
scene_manager.register_scene("main", []() { return std::make_unique<MainScene>(); });
scene_manager.register_scene("menu", []() { return std::make_unique<MenuScene>(); });
scene_manager.register_scene("menu", []() { return std::make_unique<MainScene>(); });
change_scene("menu");
}
-67
View File
@@ -1,67 +0,0 @@
#include "body.hpp"
#include "engine/ecs/components.hpp"
#include "game/ecs/components.hpp"
#include "engine/asset/raylib-asset.hpp"
#include "engine/core/service-locator.hpp"
#include <raylib.h>
#include <entt/entt.hpp>
#include <vector>
namespace v = varicle;
void create_body(entt::registry& registry,unsigned int idx){
auto& asset_loader = v::ServiceLocator::get<v::RaylibAssetLoader>();
auto body_texture = asset_loader.get_texture("assets/snake_body.png");
const auto entity = registry.create();
registry.emplace<v::Position>(entity,-10000.0f,-10000.0f);
registry.emplace<PlayerBody>(entity,idx);
registry.emplace<v::Sprite>(entity,body_texture,0.0f,0.0f,70.0f,70.0f,false,false,0.0f);
}
void BodyManager::add_body(entt::registry& registry)
{
create_body(registry,parts_position_stack.size());
parts_position_stack.push_back( v::Position{});
}
void BodyManager::pop_body(){
parts_position_stack.pop_back();
}
void BodyManager::update_body_positions(v::Position player_head_position){
if (parts_position_stack.size() >= 2){
for ( auto i = parts_position_stack.size()-1; i > 0 ;i--){
parts_position_stack[i] = parts_position_stack[i-1];
}
}
if (parts_position_stack.size() >= 1){
parts_position_stack[0] = player_head_position;
}
}
v::Position BodyManager::get_body_position(unsigned int part_idx){
return parts_position_stack[part_idx];
}
void update_body_system(entt::registry& registry){
auto& body_manager = v::ServiceLocator::get<BodyManager>();
auto player_view = registry.view<const Player, v::Position>();
auto player_entity = player_view.front();
if (player_entity == entt::null) return;
body_manager.update_body_positions(player_view.get<v::Position>(player_entity));
auto body_view = registry.view<const PlayerBody, v::Position>();
body_view.each([&body_manager](const auto entity, const PlayerBody& pb, v::Position& pos){
auto new_pos = body_manager.get_body_position(pb.idx);
pos.x = new_pos.x;
pos.y = new_pos.y;
});
}
-26
View File
@@ -1,26 +0,0 @@
#pragma once
// #include "game/ecs/components.hpp"
#include "engine/ecs/components.hpp"
#include <raylib.h>
#include <entt/entt.hpp>
#include <vector>
namespace v = varicle;
class BodyManager{
private:
std::vector<v::Position> parts_position_stack = {};
public:
Texture* body_texture;
BodyManager() = default;
~BodyManager() = default;
void add_body(entt::registry& registry);
void pop_body();
void update_body_positions(v::Position player_head_position);
v::Position get_body_position(unsigned int part_idx);
};
void create_body(entt::registry& registry,unsigned int idx);
void update_body_system(entt::registry& registry);
-57
View File
@@ -1,57 +0,0 @@
#include "game/ecs/components.hpp"
#include "body.hpp"
#include "score-system.hpp"
#include "engine/ecs/components.hpp"
#include "engine/util/math_util.hpp"
#include "engine/asset/raylib-asset.hpp"
#include "engine/core/service-locator.hpp"
#include <raylib.h>
#include <entt/entt.hpp>
namespace v = varicle ;
void create_fruit(entt::registry& registry){
auto& asset_loader = v::ServiceLocator::get<v::RaylibAssetLoader>();
auto fruit_texture = asset_loader.get_texture("assets/fruit.png");
const auto entity = registry.create();
registry.emplace<v::Position>(entity,640.0f,400.0f);
registry.emplace<v::Sprite>(entity,fruit_texture,0.0f,0.0f,30.f,30.0f,false,false,0.0f);
registry.emplace<Fruit>(entity);
}
v::Position get_player_position(entt::registry& registry){
auto player_view = registry.view<const Player>();
if (player_view.empty()) return {};
entt::entity player_entity = player_view.front();
return registry.get<v::Position>(player_entity);
}
void change_position(v::Position& pos){
pos.x = varicle::RandomRange(30, GetScreenWidth()-30);
pos.y = varicle::RandomRange(30, GetScreenHeight()-30);
};
void update_fruit_system(entt::registry& registry,float dt){
auto view = registry.view<const Fruit, v::Position>();
const v::Position player_pos = get_player_position(registry);
for (entt::entity entity : view){
v::Position& pos = view.get<v::Position>(entity);
if (abs(pos.x - player_pos.x) < 35 && abs(pos.y - player_pos.y) < 35 ){
auto& score_system = v::ServiceLocator::get<ScoreSystem>();
auto& body_manager = v::ServiceLocator::get<BodyManager>();
body_manager.add_body(registry);
score_system.add_points(1);
change_position(pos);
}
}
}
-7
View File
@@ -1,7 +0,0 @@
#pragma once
#include <raylib.h>
#include <entt/entt.hpp>
void create_fruit(entt::registry& registry);
void update_fruit_system(entt::registry& registry,float dt);
+59 -32
View File
@@ -1,63 +1,58 @@
#include "main-scene.hpp"
#include "fruit.hpp"
#include "player.hpp"
#include "body.hpp"
#include "score-system.hpp"
#include "engine/asset/raylib-asset.hpp"
#include "engine/core/service-locator.hpp"
#include "engine/render/render-system.hpp"
#include "engine/ecs/components.hpp"
#include "game/ecs/components.hpp"
#include "raylib.h"
#include <raylib.h>
#include <entt/entt.hpp>
#include <memory>
#include <format>
const float TICK_INTERVAL = 0.2f;
namespace v = varicle;
const float SPEED = 300.0f;
void create_player(entt::registry &registry);
void update_player_system(entt::registry& registry,float dt);
void update_movement_system(entt::registry& registry, float dt);
void MainScene::init() {
v::ServiceLocator::provide(std::make_unique<ScoreSystem>());
v::ServiceLocator::provide(std::make_unique<BodyManager>());
auto& asset_loader = v::ServiceLocator::get<v::RaylibAssetLoader>();
asset_loader.load_asset("assets/snake_head.png");
asset_loader.load_asset("assets/snake_body.png");
asset_loader.load_asset("assets/fruit.png");
create_player(registry);
create_fruit(registry);
}
void MainScene::update(float dt) {
update_player_system(registry,dt);
update_fruit_system(registry, dt);
tick_timer += dt;
if (tick_timer >= TICK_INTERVAL) {
tick_timer -= TICK_INTERVAL;
update_body_system(registry);
update_movement_system(registry,dt);
}
update_movement_system(registry,dt);
}
void MainScene::render() {
v::update_render_system(registry);
auto& score_system = v::ServiceLocator::get<ScoreSystem>();
DrawText(std::format("Score: {}",score_system.get_current_score()).c_str(),40,40,24,BLACK);
}
void MainScene::ui() {
}
////////////////////////////////////////////////
void create_player(entt::registry &registry){
using namespace v;
auto& asset_loader = ServiceLocator::get<RaylibAssetLoader>();
auto player_texture = asset_loader.get_texture("assets/snake_head.png");
const auto entity = registry.create();
registry.emplace<Position>(entity,100.0f,100.0f);
registry.emplace<Velocity>(entity,SPEED,0.0f);
registry.emplace<Sprite>(entity,player_texture,0.0f,0.0f,70.0f,70.0f,false,false,0.0f);
registry.emplace<Player>(entity);
}
void update_movement_system(entt::registry& registry, float dt) {
using namespace varicle;
@@ -66,8 +61,40 @@ void update_movement_system(entt::registry& registry, float dt) {
view.each([dt](Position &position, Velocity &velocity) {
// This will only run for pipes, enemies, or particles!
position.x += velocity.dx;
position.y += velocity.dy;
position.x += velocity.dx * dt;
position.y += velocity.dy * dt;
});
}
void update_player_system(entt::registry& registry,float dt){
using namespace varicle;
auto view = registry.view<const Position,Velocity,Sprite>();
// for (entt::entity entity : view){
view.each([](const auto entity, const Position& pos, Velocity& vel, Sprite& sprite){
if (IsKeyPressed(KEY_W) && vel.dy <= 0.0f){
vel.dx = 0;
vel.dy = -SPEED;
} else if (IsKeyPressed(KEY_S) && vel.dy >= 0.0f){
vel.dx = 0;
vel.dy = SPEED;
} else if (IsKeyPressed(KEY_A) && vel.dx <= 0.0f){
vel.dx = -SPEED;
vel.dy = 0;
} else if (IsKeyPressed(KEY_D) && vel.dx >= 0.0f){
vel.dx = SPEED;
vel.dy = 0;
}
if (vel.dx != 0.0f || vel.dy != 0.0f) {
// atan2 returns radians. Convert to degrees if your renderer expects it!
float radians = std::atan2(vel.dy, vel.dx);
sprite.rotation = radians * (180.0f / 3.14159265f);
}
});
}
@@ -1,5 +1,3 @@
#pragma once
#include "engine/scene/scene.hpp"
#include <entt/entt.hpp>
@@ -7,7 +5,6 @@
class MainScene : public varicle::Scene {
private:
entt::registry registry;
float tick_timer = 0.0f;
public:
void init() override;
@@ -16,5 +13,3 @@ class MainScene : public varicle::Scene {
void ui() override ;
};
void update_movement_system(entt::registry& registry, float dt);
-57
View File
@@ -1,57 +0,0 @@
#include "engine/ecs/components.hpp"
#include "engine/core/service-locator.hpp"
#include "engine/asset/raylib-asset.hpp"
#include "game/ecs/components.hpp"
#include <entt/entt.hpp>
const float SPEED = 60.0f;
void create_player(entt::registry &registry){
using namespace varicle;
auto& asset_loader = ServiceLocator::get<RaylibAssetLoader>();
auto player_texture = asset_loader.get_texture("assets/snake_head.png");
const auto entity = registry.create();
registry.emplace<Position>(entity,100.0f,100.0f);
registry.emplace<Velocity>(entity,SPEED,0.0f);
registry.emplace<Sprite>(entity,player_texture,0.0f,0.0f,70.0f,70.0f,false,false,0.0f);
registry.emplace<Player>(entity);
}
void update_player_system(entt::registry& registry,float dt){
using namespace varicle;
auto view = registry.view<const Position,Velocity,Sprite>();
// for (entt::entity entity : view){
view.each([](const auto entity, const Position& pos, Velocity& vel, Sprite& sprite){
if (IsKeyPressed(KEY_W) && vel.dy <= 0.0f){
vel.dx = 0;
vel.dy = -SPEED;
} else if (IsKeyPressed(KEY_S) && vel.dy >= 0.0f){
vel.dx = 0;
vel.dy = SPEED;
} else if (IsKeyPressed(KEY_A) && vel.dx <= 0.0f){
vel.dx = -SPEED;
vel.dy = 0;
} else if (IsKeyPressed(KEY_D) && vel.dx >= 0.0f){
vel.dx = SPEED;
vel.dy = 0;
}
if (vel.dx != 0.0f || vel.dy != 0.0f) {
// atan2 returns radians. Convert to degrees if your renderer expects it!
float radians = std::atan2(vel.dy, vel.dx);
sprite.rotation = radians * (180.0f / 3.14159265f);
}
});
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include <entt/entt.hpp>
void create_player(entt::registry &registry);
void update_player_system(entt::registry& registry,float dt);
@@ -1,15 +0,0 @@
#pragma once
class ScoreSystem{
private:
unsigned int current_score;
unsigned int high_score;
public:
ScoreSystem() :current_score(0), high_score(0){}
int get_current_score(){return current_score;}
int get_high_score(){return high_score;}
void reset_score(){current_score = 0;}
void reset_high_score(){high_score = 0;}
void add_points(unsigned int points){ current_score +=points;}
};
-54
View File
@@ -1,54 +0,0 @@
#include "menu.hpp"
#include "engine/core/service-locator.hpp"
#include "raylib.h"
#include <imgui.h>
void MenuScene::init() {}
void MenuScene::update(float dt) {
}
void MenuScene::render() {
}
void MenuScene::ui() {
// Get the current screen/viewport size
ImVec2 screen_size = ImGui::GetIO().DisplaySize;
// size of button container
ImVec2 vbox_size = ImVec2(200.0f, 250.0f);
// set next container to be center of the screen
ImVec2 screen_center = ImVec2(screen_size.x * 0.5f, screen_size.y * 0.5f);
ImGui::SetNextWindowPos(screen_center, ImGuiCond_Always,ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(vbox_size);
// remove sub window decoration
ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground;
ImGui::Begin("VBoxContainer", nullptr, flags);
// set button width to be size fill container
ImVec2 button_size = ImVec2(ImGui::GetContentRegionAvail().x, 50.0f);
// ImGui::SetCursorPos(centeredPos);
if (ImGui::Button("Play",button_size)){
v::ServiceLocator::get<v::SceneManager>().switch_to_scene("main");
}
ImGui::Dummy(ImVec2(0, 10)); // Gap
if (ImGui::Button("Quit",button_size)){
v::ServiceLocator::get<v::SceneManager>().quit();
}
ImGui::End();
// ImGui::Begin("Debug");
// ImGui::Button("Hello",button_size);
// ImGui::TextUnformatted(std::format("window size: {} {} ", screen_size.x,screen_size.y).c_str());
// ImGui::End();
}
-13
View File
@@ -1,13 +0,0 @@
#pragma once
#include "engine/scene/scene.hpp"
namespace v = varicle;
class MenuScene : public v::Scene{
public:
void init() override;
void update(float dt) override;
void render() override;
void ui() override ;
};