added joystick and attack button is moved to right
This commit is contained in:
parent
b7f5f43777
commit
3bd3202bf8
@ -154,8 +154,8 @@
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "shootButton",
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"x": 1115,
|
||||
"y": 0,
|
||||
"width": 100,
|
||||
"height": 100,
|
||||
"textures": {
|
||||
|
||||
BIN
resources/joystick_base.png
(Stored with Git LFS)
Normal file
BIN
resources/joystick_base.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/joystick_knob.png
(Stored with Git LFS)
Normal file
BIN
resources/joystick_knob.png
(Stored with Git LFS)
Normal file
Binary file not shown.
62
src/Game.cpp
62
src/Game.cpp
@ -1,4 +1,4 @@
|
||||
#include "Game.h"
|
||||
#include "Game.h"
|
||||
#include "AnimatedModel.h"
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include "utils/Utils.h"
|
||||
@ -281,6 +281,13 @@ namespace ZL
|
||||
Environment::shipVelocity = value * 1000.f;
|
||||
});
|
||||
|
||||
// Добавляем джойстик для управления кораблём
|
||||
// centerX=150, centerY=150 (от левого нижнего угла в UI координатах)
|
||||
// baseRadius=120, knobRadius=50
|
||||
uiManager.addJoystick("shipJoystick", 150.0f, 150.0f, 120.0f, 50.0f,
|
||||
renderer, CONST_ZIP_FILE,
|
||||
"resources/joystick_base.png", "resources/joystick_knob.png");
|
||||
|
||||
/*uiManager.setSliderCallback("musicVolumeSlider", [this](const std::string& name, float value) {
|
||||
std::cerr << "Music volume slider changed to: " << value << std::endl;
|
||||
musicVolume = value;
|
||||
@ -604,18 +611,20 @@ namespace ZL
|
||||
sparkEmitter.update(static_cast<float>(delta));
|
||||
planetObject.update(static_cast<float>(delta));
|
||||
|
||||
if (Environment::tapDownHold && !uiManager.isUiInteraction())
|
||||
{
|
||||
float diffx = Environment::tapDownCurrentPos(0) - Environment::tapDownStartPos(0);
|
||||
float diffy = Environment::tapDownCurrentPos(1) - Environment::tapDownStartPos(1);
|
||||
// Управление кораблём через джойстик
|
||||
auto joystick = uiManager.findJoystick("shipJoystick");
|
||||
if (joystick && joystick->isActive) {
|
||||
float joyX = joystick->getDirectionX(); // -1..1
|
||||
float joyY = joystick->getDirectionY(); // -1..1
|
||||
float magnitude = joystick->getMagnitude(); // 0..1
|
||||
|
||||
if (isDraggingShip) {
|
||||
if (std::abs(diffy) > 5.0f || std::abs(diffx) > 5.0f)
|
||||
{
|
||||
float velocity = std::sqrt(diffx * diffx + diffy * diffy);
|
||||
Environment::shipVelocity = velocity * static_cast<float>(delta) / 100.0f;
|
||||
if (magnitude > 0.1f) {
|
||||
// Скорость пропорциональна отклонению джойстика
|
||||
Environment::shipVelocity = magnitude * 500.0f * static_cast<float>(delta) / 100.0f;
|
||||
|
||||
float angleY = std::atan2(-diffx, -diffy);
|
||||
// Угол поворота корабля на основе направления джойстика
|
||||
// joyY отрицательный = вперёд, joyX = влево/вправо
|
||||
float angleY = std::atan2(-joyX, -joyY);
|
||||
|
||||
Eigen::Quaternionf q(Eigen::AngleAxisf(angleY, Eigen::Vector3f::UnitY()));
|
||||
rotateShipMat = q.toRotationMatrix();
|
||||
@ -629,11 +638,17 @@ namespace ZL
|
||||
Environment::shipVelocity = 0.0f;
|
||||
}
|
||||
}
|
||||
else if (isDraggingCamera) {
|
||||
else if (Environment::tapDownHold && !uiManager.isUiInteraction())
|
||||
{
|
||||
float diffx = Environment::tapDownCurrentPos(0) - Environment::tapDownStartPos(0);
|
||||
float diffy = Environment::tapDownCurrentPos(1) - Environment::tapDownStartPos(1);
|
||||
|
||||
// Только управление камерой через drag (вне зоны джойстика)
|
||||
if (isDraggingCamera) {
|
||||
if (std::abs(diffy) > 5.0f || std::abs(diffx) > 5.0f)
|
||||
{
|
||||
float rotationPower = std::sqrt(diffx * diffx + diffy * diffy);
|
||||
float deltaAlpha = rotationPower * static_cast<float>(delta) * static_cast<float>(M_PI) / 500000.f;
|
||||
float deltaAlpha = rotationPower * static_cast<float>(delta) * static_cast<float>(M_PI) / 150000.f;
|
||||
|
||||
Eigen::Vector3f rotationDirection(diffy, diffx, 0.0f);
|
||||
rotationDirection.normalize();
|
||||
@ -877,25 +892,30 @@ namespace ZL
|
||||
}();
|
||||
|
||||
if (!uiManager.isUiInteraction()) {
|
||||
if (mx < 400 && my > 400) {
|
||||
isDraggingShip = true;
|
||||
isDraggingCamera = false;
|
||||
|
||||
shipMoveLockActive = false; // новый drag -> новый lock
|
||||
}
|
||||
else {
|
||||
// Джойстик обрабатывает управление кораблём, поэтому
|
||||
// любое нажатие вне UI элементов - это управление камерой
|
||||
isDraggingShip = false;
|
||||
isDraggingCamera = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Game::handleUp(int mx, int my)
|
||||
{
|
||||
int uiX = mx;
|
||||
int uiY = Environment::height - my;
|
||||
|
||||
// Проверяем был ли активен джойстик до отпускания
|
||||
auto joystick = uiManager.findJoystick("shipJoystick");
|
||||
bool wasJoystickActive = joystick && joystick->isActive;
|
||||
|
||||
uiManager.onMouseUp(uiX, uiY);
|
||||
|
||||
// Сбрасываем состояние если отпустили джойстик
|
||||
if (wasJoystickActive) {
|
||||
Environment::shipVelocity = 0.0f;
|
||||
shipMoveLockActive = false;
|
||||
rotateShipMat = Matrix3f::Identity();
|
||||
}
|
||||
|
||||
if (!uiManager.isUiInteraction()) {
|
||||
Environment::tapDownHold = false;
|
||||
Environment::shipVelocity = 0.0f;
|
||||
|
||||
@ -167,6 +167,91 @@ namespace ZL {
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
}
|
||||
|
||||
void UiJoystick::buildBaseMesh() {
|
||||
baseMesh.data.PositionData.clear();
|
||||
baseMesh.data.TexCoordData.clear();
|
||||
|
||||
float x0 = centerX - baseRadius;
|
||||
float y0 = centerY - baseRadius;
|
||||
float x1 = centerX + baseRadius;
|
||||
float y1 = centerY + baseRadius;
|
||||
|
||||
baseMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
baseMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
baseMesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||
baseMesh.data.TexCoordData.push_back({ 0, 1 });
|
||||
|
||||
baseMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
baseMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
baseMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
baseMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
baseMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
baseMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
baseMesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||
baseMesh.data.TexCoordData.push_back({ 1, 0 });
|
||||
|
||||
baseMesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void UiJoystick::buildKnobMesh() {
|
||||
knobMesh.data.PositionData.clear();
|
||||
knobMesh.data.TexCoordData.clear();
|
||||
|
||||
float cx = centerX + knobOffsetX;
|
||||
float cy = centerY + knobOffsetY;
|
||||
|
||||
float x0 = cx - knobRadius;
|
||||
float y0 = cy - knobRadius;
|
||||
float x1 = cx + knobRadius;
|
||||
float y1 = cy + knobRadius;
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 0, 1 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 1, 1 });
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 1, 0 });
|
||||
|
||||
knobMesh.RefreshVBO();
|
||||
}
|
||||
|
||||
void UiJoystick::draw(Renderer& renderer) const {
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
if (texBase) {
|
||||
glBindTexture(GL_TEXTURE_2D, texBase->getTexID());
|
||||
renderer.DrawVertexRenderStruct(baseMesh);
|
||||
}
|
||||
if (texKnob) {
|
||||
glBindTexture(GL_TEXTURE_2D, texKnob->getTexID());
|
||||
renderer.DrawVertexRenderStruct(knobMesh);
|
||||
}
|
||||
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
}
|
||||
|
||||
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||
std::string content;
|
||||
try {
|
||||
@ -446,6 +531,57 @@ namespace ZL {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiManager::addJoystick(const std::string& name, float centerX, float centerY, float baseRadius, float knobRadius,
|
||||
Renderer& renderer, const std::string& zipFile,
|
||||
const std::string& basePath, const std::string& knobPath) {
|
||||
|
||||
auto j = std::make_shared<UiJoystick>();
|
||||
j->name = name;
|
||||
j->centerX = centerX;
|
||||
j->centerY = centerY;
|
||||
j->baseRadius = baseRadius;
|
||||
j->knobRadius = knobRadius;
|
||||
j->knobOffsetX = 0;
|
||||
j->knobOffsetY = 0;
|
||||
j->isActive = false;
|
||||
|
||||
try {
|
||||
if (!basePath.empty()) {
|
||||
auto data = CreateTextureDataFromPng(basePath.c_str(), zipFile.c_str());
|
||||
j->texBase = std::make_shared<Texture>(data);
|
||||
}
|
||||
if (!knobPath.empty()) {
|
||||
auto data = CreateTextureDataFromPng(knobPath.c_str(), zipFile.c_str());
|
||||
j->texKnob = std::make_shared<Texture>(data);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "UiManager: addJoystick failed to load textures: " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
j->buildBaseMesh();
|
||||
j->buildKnobMesh();
|
||||
|
||||
joysticks.push_back(j);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<UiJoystick> UiManager::findJoystick(const std::string& name) {
|
||||
for (auto& j : joysticks) if (j->name == name) return j;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UiManager::resetJoystick(const std::string& name) {
|
||||
auto j = findJoystick(name);
|
||||
if (j) {
|
||||
j->knobOffsetX = 0;
|
||||
j->knobOffsetY = 0;
|
||||
j->isActive = false;
|
||||
j->buildKnobMesh();
|
||||
}
|
||||
}
|
||||
|
||||
bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||
MenuState prev;
|
||||
prev.root = root;
|
||||
@ -526,6 +662,9 @@ namespace ZL {
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
for (const auto& j : joysticks) {
|
||||
j->draw(renderer);
|
||||
}
|
||||
for (const auto& b : buttons) {
|
||||
b->draw(renderer);
|
||||
}
|
||||
@ -724,6 +863,22 @@ namespace ZL {
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
}
|
||||
|
||||
if (pressedJoystick) {
|
||||
auto j = pressedJoystick;
|
||||
float dx = (float)x - j->centerX;
|
||||
float dy = (float)y - j->centerY;
|
||||
float dist = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (dist > j->baseRadius) {
|
||||
dx = dx / dist * j->baseRadius;
|
||||
dy = dy / dist * j->baseRadius;
|
||||
}
|
||||
|
||||
j->knobOffsetX = dx;
|
||||
j->knobOffsetY = dy;
|
||||
j->buildKnobMesh();
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::onMouseDown(int x, int y) {
|
||||
@ -752,6 +907,27 @@ namespace ZL {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& j : joysticks) {
|
||||
if (j->contains((float)x, (float)y)) {
|
||||
pressedJoystick = j;
|
||||
j->isActive = true;
|
||||
|
||||
float dx = (float)x - j->centerX;
|
||||
float dy = (float)y - j->centerY;
|
||||
float dist = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (dist > j->baseRadius) {
|
||||
dx = dx / dist * j->baseRadius;
|
||||
dy = dy / dist * j->baseRadius;
|
||||
}
|
||||
|
||||
j->knobOffsetX = dx;
|
||||
j->knobOffsetY = dy;
|
||||
j->buildKnobMesh();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::onMouseUp(int x, int y) {
|
||||
@ -771,6 +947,14 @@ namespace ZL {
|
||||
if (pressedSlider) {
|
||||
pressedSlider.reset();
|
||||
}
|
||||
|
||||
if (pressedJoystick) {
|
||||
pressedJoystick->knobOffsetX = 0;
|
||||
pressedJoystick->knobOffsetY = 0;
|
||||
pressedJoystick->isActive = false;
|
||||
pressedJoystick->buildKnobMesh();
|
||||
pressedJoystick.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<UiButton> UiManager::findButton(const std::string& name) {
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -71,6 +73,47 @@ namespace ZL {
|
||||
void draw(Renderer& renderer) const;
|
||||
};
|
||||
|
||||
struct UiJoystick {
|
||||
std::string name;
|
||||
float centerX = 0; // центр джойстика (UI координаты)
|
||||
float centerY = 0;
|
||||
float baseRadius = 100; // радиус основания
|
||||
float knobRadius = 40; // радиус ручки
|
||||
|
||||
float knobOffsetX = 0; // смещение ручки от центра
|
||||
float knobOffsetY = 0;
|
||||
|
||||
bool isActive = false; // нажат ли джойстик
|
||||
|
||||
std::shared_ptr<Texture> texBase; // текстура основания
|
||||
std::shared_ptr<Texture> texKnob; // текстура ручки
|
||||
|
||||
VertexRenderStruct baseMesh;
|
||||
VertexRenderStruct knobMesh;
|
||||
|
||||
// Возвращает направление -1..1 по каждой оси
|
||||
float getDirectionX() const { return baseRadius > 0 ? knobOffsetX / baseRadius : 0; }
|
||||
float getDirectionY() const { return baseRadius > 0 ? -knobOffsetY / baseRadius : 0; }
|
||||
|
||||
// Возвращает силу 0..1
|
||||
float getMagnitude() const {
|
||||
if (baseRadius <= 0) return 0;
|
||||
float dist = std::sqrt(knobOffsetX * knobOffsetX + knobOffsetY * knobOffsetY);
|
||||
float ratio = dist / baseRadius;
|
||||
return ratio < 1.0f ? ratio : 1.0f;
|
||||
}
|
||||
|
||||
bool contains(float px, float py) const {
|
||||
float dx = px - centerX;
|
||||
float dy = py - centerY;
|
||||
return (dx * dx + dy * dy) <= (baseRadius * baseRadius);
|
||||
}
|
||||
|
||||
void buildBaseMesh();
|
||||
void buildKnobMesh();
|
||||
void draw(Renderer& renderer) const;
|
||||
};
|
||||
|
||||
struct UiNode {
|
||||
std::string type;
|
||||
UiRect rect;
|
||||
@ -108,7 +151,7 @@ namespace ZL {
|
||||
void onMouseUp(int x, int y);
|
||||
|
||||
bool isUiInteraction() const {
|
||||
return pressedButton != nullptr || pressedSlider != nullptr;
|
||||
return pressedButton != nullptr || pressedSlider != nullptr || pressedJoystick != nullptr;
|
||||
}
|
||||
|
||||
void stopAllAnimations() {
|
||||
@ -135,6 +178,12 @@ namespace ZL {
|
||||
bool setSliderCallback(const std::string& name, std::function<void(const std::string&, float)> cb);
|
||||
bool setSliderValue(const std::string& name, float value); // programmatic set (clamped 0..1)
|
||||
|
||||
bool addJoystick(const std::string& name, float centerX, float centerY, float baseRadius, float knobRadius,
|
||||
Renderer& renderer, const std::string& zipFile,
|
||||
const std::string& basePath, const std::string& knobPath);
|
||||
std::shared_ptr<UiJoystick> findJoystick(const std::string& name);
|
||||
void resetJoystick(const std::string& name); // сбросить ручку в центр
|
||||
|
||||
bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
|
||||
bool popMenu();
|
||||
void clearMenuStack();
|
||||
@ -176,12 +225,14 @@ namespace ZL {
|
||||
std::shared_ptr<UiNode> root;
|
||||
std::vector<std::shared_ptr<UiButton>> buttons;
|
||||
std::vector<std::shared_ptr<UiSlider>> sliders;
|
||||
std::vector<std::shared_ptr<UiJoystick>> joysticks;
|
||||
|
||||
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
|
||||
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
|
||||
|
||||
std::shared_ptr<UiButton> pressedButton;
|
||||
std::shared_ptr<UiSlider> pressedSlider;
|
||||
std::shared_ptr<UiJoystick> pressedJoystick;
|
||||
|
||||
struct MenuState {
|
||||
std::shared_ptr<UiNode> root;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user