From ff3946a0196edbc8c572b3e9f92bea0668f55a23 Mon Sep 17 00:00:00 2001 From: Vlad Date: Tue, 30 Dec 2025 16:47:32 +0600 Subject: [PATCH] added projectile --- CMakeLists.txt | 2 + Game.cpp | 91 ++++++++++++++++++++++++++--- Game.h | 11 ++++ Projectile.cpp | 71 ++++++++++++++++++++++ Projectile.h | 36 ++++++++++++ config/spark_config.json | 4 +- config/spark_projectile_config.json | 15 +++++ 7 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 Projectile.cpp create mode 100644 Projectile.h create mode 100644 config/spark_projectile_config.json diff --git a/CMakeLists.txt b/CMakeLists.txt index f44b693..b5bcee0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -442,6 +442,8 @@ add_executable(space-game001 PlanetObject.h UiManager.cpp UiManager.h + Projectile.h + Projectile.cpp ) # Установка проекта по умолчанию для Visual Studio diff --git a/Game.cpp b/Game.cpp index 616bdc5..c84fa0d 100755 --- a/Game.cpp +++ b/Game.cpp @@ -123,11 +123,10 @@ namespace ZL , newTickCount(0) , lastTickCount(0) { - std::vector emissionPoints = { - Vector3f{-2.1f, 0.9f, 5.0f}, - Vector3f{2.1f, 0.9f, 5.0f} - }; - sparkEmitter = SparkEmitter(emissionPoints, 100.0f); + projectiles.reserve(maxProjectiles); + for (int i = 0; i < maxProjectiles; ++i) { + projectiles.emplace_back(std::make_unique()); + } } Game::~Game() { @@ -163,6 +162,8 @@ namespace ZL #endif bool cfgLoaded = sparkEmitter.loadFromJsonFile("../config/spark_config.json", renderer, CONST_ZIP_FILE); + bool projCfgLoaded = projectileEmitter.loadFromJsonFile("../config/spark_projectile_config.json", renderer, CONST_ZIP_FILE); + projectileEmitter.setEmissionPoints(std::vector()); uiManager.loadFromFile("../config/ui.json", renderer, CONST_ZIP_FILE); uiManager.setButtonCallback("playButton", [this](const std::string& name) { @@ -364,10 +365,18 @@ namespace ZL renderer.LoadIdentity(); renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); - + renderer.TranslateMatrix({ 0, -Environment::zoom * 0.03f, 0 }); glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); renderer.DrawVertexRenderStruct(spaceship); + + for (const auto& p : projectiles) { + if (p && p->isActive()) { + p->draw(renderer); + } + } + sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height); + projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height); renderer.PopMatrix(); renderer.PopProjectionMatrix(); @@ -584,14 +593,74 @@ namespace ZL { Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f }; Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection); - Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; } + for (auto& p : projectiles) { + if (p && p->isActive()) { + p->update(static_cast(delta), renderer); + } + } + + std::vector projCameraPoints; + for (const auto& p : projectiles) { + if (p && p->isActive()) { + Vector3f worldPos = p->getPosition(); + Vector3f rel = worldPos - Environment::shipPosition; + Vector3f camPos = MultMatrixVector(Environment::inverseShipMatrix, rel); + projCameraPoints.push_back(camPos); + } + } + if (!projCameraPoints.empty()) { + projectileEmitter.setEmissionPoints(projCameraPoints); + projectileEmitter.emit(); + } + else { + projectileEmitter.setEmissionPoints(std::vector()); + } + + std::vector shipCameraPoints; + for (const auto& lp : shipLocalEmissionPoints) { + Vector3f adjusted = lp + Vector3f{ 0.0f, -Environment::zoom * 0.03f, 0.0f }; + shipCameraPoints.push_back(adjusted); + } + if (!shipCameraPoints.empty()) { + sparkEmitter.setEmissionPoints(shipCameraPoints); + } + + sparkEmitter.update(static_cast(delta)); + projectileEmitter.update(static_cast(delta)); + lastTickCount = newTickCount; } } + void Game::fireProjectiles() { + std::vector localOffsets = { + Vector3f{ -1.5f, 0.9f, 5.0f }, + Vector3f{ 1.5f, 0.9f, 5.0f } + }; + + const float projectileSpeed = 60.0f; + const float lifeMs = 5000.0f; + const float size = 0.5f; + + Vector3f localForward = { 0,0,-1 }; + Vector3f worldForward = MultMatrixVector(Environment::shipMatrix, localForward).normalized(); + + for (const auto& lo : localOffsets) { + Vector3f worldPos = Environment::shipPosition + MultMatrixVector(Environment::shipMatrix, lo); + Vector3f worldVel = worldForward * projectileSpeed; + + for (auto& p : projectiles) { + if (!p->isActive()) { + p->init(worldPos, worldVel, lifeMs, size, projectileTexture, renderer); + break; + } + } + } + } + void Game::render() { SDL_GL_MakeCurrent(ZL::Environment::window, glContext); ZL::CheckGlError(); @@ -622,6 +691,14 @@ namespace ZL bool uiHandled = false; + if (event.button.button == SDL_BUTTON_LEFT && !uiManager.isUiInteraction()) { + uint64_t now = SDL_GetTicks64(); + if (now - lastProjectileFireTime >= static_cast(projectileCooldownMs)) { + lastProjectileFireTime = now; + fireProjectiles(); + } + } + for (const auto& button : uiManager.findButton("") ? std::vector>{} : std::vector>{}) { (void)button; } diff --git a/Game.h b/Game.h index 80d938d..2535cbc 100755 --- a/Game.h +++ b/Game.h @@ -7,6 +7,7 @@ #include "SparkEmitter.h" #include "PlanetObject.h" #include "UiManager.h" +#include "Projectile.h" namespace ZL { @@ -38,6 +39,8 @@ namespace ZL { void drawBoxes(); void drawUI(); + void fireProjectiles(); + SDL_Window* window; SDL_GLContext glContext; Renderer renderer; @@ -88,8 +91,16 @@ namespace ZL { VertexDataStruct boxBase; SparkEmitter sparkEmitter; + SparkEmitter projectileEmitter; PlanetObject planetObject; UiManager uiManager; + + std::vector> projectiles; + std::shared_ptr projectileTexture; + float projectileCooldownMs = 500.0f; + uint64_t lastProjectileFireTime = 0; + int maxProjectiles = 32; + std::vector shipLocalEmissionPoints; }; diff --git a/Projectile.cpp b/Projectile.cpp new file mode 100644 index 0000000..34aaa1c --- /dev/null +++ b/Projectile.cpp @@ -0,0 +1,71 @@ +#include "Projectile.h" + +namespace ZL { + + Projectile::Projectile() + : pos({ 0,0,0 }) + , vel({ 0,0,0 }) + , life(0.0f) + , maxLife(0.0f) + , active(false) + , size(0.5f) + , texture(nullptr) + { + } + + void Projectile::init(const Vector3f& startPos, const Vector3f& startVel, float lifeMs, float s, std::shared_ptr tex, Renderer& renderer) { + pos = startPos; + vel = startVel; + life = 0.0f; + maxLife = lifeMs; + size = s; + texture = tex; + active = true; + + rebuildMesh(renderer); + mesh.RefreshVBO(); + } + + void Projectile::update(float deltaMs, Renderer& renderer) { + if (!active) return; + + pos = pos + vel * (deltaMs / 1000.0f); + life += deltaMs; + if (life >= maxLife) { + active = false; + return; + } + + rebuildMesh(renderer); + mesh.RefreshVBO(); + } + + void Projectile::rebuildMesh(Renderer&) { + float half = size * 0.5f; + + mesh.data.PositionData.clear(); + mesh.data.TexCoordData.clear(); + + mesh.data.PositionData.push_back({ pos.v[0] - half, pos.v[1] - half, pos.v[2] }); + mesh.data.PositionData.push_back({ pos.v[0] - half, pos.v[1] + half, pos.v[2] }); + mesh.data.PositionData.push_back({ pos.v[0] + half, pos.v[1] + half, pos.v[2] }); + + mesh.data.PositionData.push_back({ pos.v[0] - half, pos.v[1] - half, pos.v[2] }); + mesh.data.PositionData.push_back({ pos.v[0] + half, pos.v[1] + half, pos.v[2] }); + mesh.data.PositionData.push_back({ pos.v[0] + half, pos.v[1] - half, pos.v[2] }); + + mesh.data.TexCoordData.push_back({ 0.0f, 0.0f }); + mesh.data.TexCoordData.push_back({ 0.0f, 1.0f }); + mesh.data.TexCoordData.push_back({ 1.0f, 1.0f }); + mesh.data.TexCoordData.push_back({ 0.0f, 0.0f }); + mesh.data.TexCoordData.push_back({ 1.0f, 1.0f }); + mesh.data.TexCoordData.push_back({ 1.0f, 0.0f }); + } + + void Projectile::draw(Renderer& renderer) const { + if (!active || !texture) return; + glBindTexture(GL_TEXTURE_2D, texture->getTexID()); + renderer.DrawVertexRenderStruct(mesh); + } + +} // namespace ZL \ No newline at end of file diff --git a/Projectile.h b/Projectile.h new file mode 100644 index 0000000..627d472 --- /dev/null +++ b/Projectile.h @@ -0,0 +1,36 @@ +#pragma once + +#include "ZLMath.h" +#include "Renderer.h" +#include "TextureManager.h" +#include + +namespace ZL { + + class Projectile { + public: + Projectile(); + ~Projectile() = default; + + void init(const Vector3f& startPos, const Vector3f& startVel, float lifeMs, float size, std::shared_ptr tex, Renderer& renderer); + void update(float deltaMs, Renderer& renderer); + void draw(Renderer& renderer) const; + + bool isActive() const { return active; } + + Vector3f getPosition() const { return pos; } + + private: + Vector3f pos; + Vector3f vel; + float life; + float maxLife; + bool active; + float size; + VertexRenderStruct mesh; + std::shared_ptr texture; + + void rebuildMesh(Renderer& renderer); + }; + +} // namespace ZL \ No newline at end of file diff --git a/config/spark_config.json b/config/spark_config.json index b043f60..6dce53a 100644 --- a/config/spark_config.json +++ b/config/spark_config.json @@ -5,10 +5,10 @@ "biasX": 0.3, "emissionPoints": [ { - "position": [-2.1, 0.9, 5.0] + "position": [-2.1, 0.4, 5.0] }, { - "position": [2.1, 0.9, 5.0] + "position": [2.1, 0.4, 5.0] } ], "speedRange": [0.5, 2.0], diff --git a/config/spark_projectile_config.json b/config/spark_projectile_config.json new file mode 100644 index 0000000..de75856 --- /dev/null +++ b/config/spark_projectile_config.json @@ -0,0 +1,15 @@ +{ + "emissionPoints": [ + { "position": [0.0, 0.0, 0.0] } + ], + "texture": "./resources/sand.png", + "speedRange": [10.0, 30.0], + "zSpeedRange": [-1.0, 1.0], + "scaleRange": [0.5, 1.0], + "lifeTimeRange": [200.0, 800.0], + "emissionRate": 50.0, + "maxParticles": 10, + "particleSize": 0.09, + "biasX": 0.1, + "shaderProgramName": "default" + } \ No newline at end of file