Documentation

Plugin Examples

Π’ этом Ρ€Π°Π·Π΄Π΅Π»Π΅ прСдставлСны ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠ² для Wudgine, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠΌΠΎΠ³ΡƒΡ‚ Π²Π°ΠΌ ΠΏΠΎΠ½ΡΡ‚ΡŒ, ΠΊΠ°ΠΊ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ собствСнныС Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ.

Плагин для постобработки

Π­Ρ‚ΠΎΡ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ дСмонстрируСт созданиС ΠΏΠ»Π°Π³ΠΈΠ½Π° для добавлСния эффСктов постобработки:

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΠΏΠ»Π°Π³ΠΈΠ½Π°

PostProcessPlugin/
β”œβ”€β”€ CMakeLists.txt
β”œβ”€β”€ plugin.json
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ PostProcessPlugin.h
β”‚   β”œβ”€β”€ PostProcessPlugin.cpp
β”‚   β”œβ”€β”€ Effects/
β”‚   β”‚   β”œβ”€β”€ BloomEffect.h
β”‚   β”‚   β”œβ”€β”€ BloomEffect.cpp
β”‚   β”‚   β”œβ”€β”€ VignetteEffect.h
β”‚   β”‚   └── VignetteEffect.cpp
β”‚   └── Components/
β”‚       β”œβ”€β”€ PostProcessComponent.h
β”‚       └── PostProcessComponent.cpp
└── resources/
    β”œβ”€β”€ shaders/
    β”‚   β”œβ”€β”€ bloom.frag
    β”‚   └── vignette.frag
    └── icons/
        └── post_process.png

Π€Π°ΠΉΠ» манифСста

{
  "name": "PostProcessPlugin",
  "version": "1.0.0",
  "author": "Wudgine Team",
  "description": "Плагин для добавлСния эффСктов постобработки",
  "engineVersion": "1.0.0",
  "dependencies": [],
  "entryPoint": "PostProcessPlugin"
}

Основной класс плагина

// PostProcessPlugin.h
#pragma once

#include <Wudgine/Plugin/IPlugin.h>
#include <Wudgine/Core/ServiceLocator.h>
#include <Wudgine/Render/RenderPipeline.h>

class PostProcessPlugin : public Wudgine::Plugin::IPlugin {
public:
    PostProcessPlugin();
    ~PostProcessPlugin();

    // РСализация интСрфСйса IPlugin
    bool Initialize() override;
    void Shutdown() override;
    
    const char* GetName() const override { return "PostProcessPlugin"; }
    const char* GetVersion() const override { return "1.0.0"; }
    const char* GetAuthor() const override { return "Wudgine Team"; }
    const char* GetDescription() const override { return "Плагин для добавлСния эффСктов постобработки"; }
    
    // Настройки ΠΏΠ»Π°Π³ΠΈΠ½Π°
    void RenderSettings() override;

private:
    // РСгистрация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² ΠΈ систСм
    void RegisterComponents();
    void RegisterSystems();
    void RegisterEditorTools();
    
    // Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° рСсурсов
    bool LoadResources();
    
    // Настройки ΠΏΠ»Π°Π³ΠΈΠ½Π°
    struct Settings {
        bool enableBloom = true;
        float bloomIntensity = 1.0f;
        bool enableVignette = true;
        float vignetteIntensity = 0.5f;
        
        template<typename Archive>
        void serialize(Archive& ar) {
            ar & enableBloom;
            ar & bloomIntensity;
            ar & enableVignette;
            ar & vignetteIntensity;
        }
    };
    
    Settings m_settings;
};
// PostProcessPlugin.cpp
#include "PostProcessPlugin.h"
#include "Components/PostProcessComponent.h"
#include "Effects/BloomEffect.h"
#include "Effects/VignetteEffect.h"

#include <Wudgine/Core/Log.h>
#include <Wudgine/Core/SettingsManager.h>
#include <Wudgine/ECS/ComponentRegistry.h>
#include <Wudgine/Editor/EditorToolManager.h>
#include <Wudgine/Resource/ResourceManager.h>
#include <imgui.h>

PostProcessPlugin::PostProcessPlugin() = default;
PostProcessPlugin::~PostProcessPlugin() = default;

bool PostProcessPlugin::Initialize() {
    WG_LOG_INFO("[PostProcessPlugin] Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ...");
    
    // Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° настроСк
    if (Wudgine::Core::SettingsManager::GetInstance().HasPluginSettings(GetName())) {
        m_settings = Wudgine::Core::SettingsManager::GetInstance().LoadPluginSettings<Settings>(GetName());
    }
    
    // РСгистрация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² ΠΈ систСм
    RegisterComponents();
    RegisterSystems();
    
    // РСгистрация инструмСнтов Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π° (Ссли Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π°)
    if (Wudgine::Core::IsEditor()) {
        RegisterEditorTools();
    }
    
    // Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° рСсурсов
    if (!LoadResources()) {
        WG_LOG_ERROR("[PostProcessPlugin] Ошибка Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ рСсурсов");
        return false;
    }
    
    // РСгистрация Ρ…ΡƒΠΊΠΎΠ² Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
    auto& renderPipeline = Wudgine::Render::RenderPipeline::GetInstance();
    
    // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ эффСктов постобработки Π² ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
    if (m_settings.enableBloom) {
        renderPipeline.AddPostProcessEffect(new BloomEffect(m_settings.bloomIntensity));
    }
    
    if (m_settings.enableVignette) {
        renderPipeline.AddPostProcessEffect(new VignetteEffect(m_settings.vignetteIntensity));
    }
    
    WG_LOG_INFO("[PostProcessPlugin] Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½Π°");
    return true;
}

void PostProcessPlugin::Shutdown() {
    WG_LOG_INFO("[PostProcessPlugin] Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹...");
    
    // Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ настроСк
    Wudgine::Core::SettingsManager::GetInstance().SavePluginSettings(GetName(), m_settings);
    
    // Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ эффСктов ΠΈΠ· ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π° Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
    auto& renderPipeline = Wudgine::Render::RenderPipeline::GetInstance();
    renderPipeline.RemoveAllPostProcessEffects();
    
    WG_LOG_INFO("[PostProcessPlugin] Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΎ");
}

void PostProcessPlugin::RegisterComponents() {
    // РСгистрация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° постобработки
    Wudgine::ECS::ComponentRegistry::GetInstance().RegisterComponent<PostProcessComponent>(
        "PostProcessComponent",
        []() { return new PostProcessComponent(); }
    );
}

void PostProcessPlugin::RegisterSystems() {
    // Π’ этом ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ систСмы Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ
}

void PostProcessPlugin::RegisterEditorTools() {
    // РСгистрация инспСктора для ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° постобработки
    class PostProcessInspector : public Wudgine::Editor::ComponentInspector {
    public:
        bool CanInspect(Wudgine::ECS::Component* component) override {
            return dynamic_cast<PostProcessComponent*>(component) != nullptr;
        }
        
        void Render(Wudgine::ECS::Component* component) override {
            auto* ppComponent = static_cast<PostProcessComponent*>(component);
            
            // ΠžΡ‚Ρ€ΠΈΡΠΎΠ²ΠΊΠ° ΠΏΠΎΠ»Π΅ΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
            bool enableBloom = ppComponent->IsBloomEnabled();
            if (ImGui::Checkbox("Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Bloom", &enableBloom)) {
                ppComponent->SetBloomEnabled(enableBloom);
                MarkDirty();
            }
            
            float bloomIntensity = ppComponent->GetBloomIntensity();
            if (ImGui::SliderFloat("Π˜Π½Ρ‚Π΅Π½ΡΠΈΠ²Π½ΠΎΡΡ‚ΡŒ Bloom", &bloomIntensity, 0.0f, 2.0f)) {
                ppComponent->SetBloomIntensity(bloomIntensity);
                MarkDirty();
            }
            
            bool enableVignette = ppComponent->IsVignetteEnabled();
            if (ImGui::Checkbox("Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Π’ΠΈΠ½ΡŒΠ΅Ρ‚ΠΊΡƒ", &enableVignette)) {
                ppComponent->SetVignetteEnabled(enableVignette);
                MarkDirty();
            }
            
            float vignetteIntensity = ppComponent->GetVignetteIntensity();
            if (ImGui::SliderFloat("Π˜Π½Ρ‚Π΅Π½ΡΠΈΠ²Π½ΠΎΡΡ‚ΡŒ Π’ΠΈΠ½ΡŒΠ΅Ρ‚ΠΊΠΈ", &vignetteIntensity, 0.0f, 1.0f)) {
                ppComponent->SetVignetteIntensity(vignetteIntensity);
                MarkDirty();
            }
        }
    };
    
    Wudgine::Editor::InspectorManager::GetInstance().RegisterInspector(new PostProcessInspector());
}

bool PostProcessPlugin::LoadResources() {
    // Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° ΡˆΠ΅ΠΉΠ΄Π΅Ρ€ΠΎΠ²
    auto& resourceManager = Wudgine::Resource::ResourceManager::GetInstance();
    
    if (!resourceManager.LoadShader("plugins/PostProcessPlugin/shaders/bloom.frag")) {
        WG_LOG_ERROR("[PostProcessPlugin] НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΡˆΠ΅ΠΉΠ΄Π΅Ρ€ bloom.frag");
        return false;
    }
    
    if (!resourceManager.LoadShader("plugins/PostProcessPlugin/shaders/vignette.frag")) {
        WG_LOG_ERROR("[PostProcessPlugin] НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΡˆΠ΅ΠΉΠ΄Π΅Ρ€ vignette.frag");
        return false;
    }
    
    return true;
}

void PostProcessPlugin::RenderSettings() {
    ImGui::Checkbox("Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Bloom", &m_settings.enableBloom);
    
    if (m_settings.enableBloom) {
        ImGui::SliderFloat("Π˜Π½Ρ‚Π΅Π½ΡΠΈΠ²Π½ΠΎΡΡ‚ΡŒ Bloom", &m_settings.bloomIntensity, 0.0f, 2.0f);
    }
    
    ImGui::Checkbox("Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Π’ΠΈΠ½ΡŒΠ΅Ρ‚ΠΊΡƒ", &m_settings.enableVignette);
    
    if (m_settings.enableVignette) {
        ImGui::SliderFloat("Π˜Π½Ρ‚Π΅Π½ΡΠΈΠ²Π½ΠΎΡΡ‚ΡŒ Π’ΠΈΠ½ΡŒΠ΅Ρ‚ΠΊΠΈ", &m_settings.vignetteIntensity, 0.0f, 1.0f);
    }
    
    // ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ
    auto& renderPipeline = Wudgine::Render::RenderPipeline::GetInstance();
    renderPipeline.UpdatePostProcessEffects();
    
    // Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ настроСк
    Wudgine::Core::SettingsManager::GetInstance().SavePluginSettings(GetName(), m_settings);
}

// Экспорт ΠΏΠ»Π°Π³ΠΈΠ½Π°
extern "C" {
    WUDGINE_PLUGIN_API Wudgine::Plugin::IPlugin* CreatePlugin() {
        return new PostProcessPlugin();
    }
}

ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ постобработки

// PostProcessComponent.h
#pragma once

#include <Wudgine/ECS/Component.h>
#include <Wudgine/Core/Serializable.h>

class PostProcessComponent : public Wudgine::ECS::Component, public Wudgine::Core::Serializable {
public:
    PostProcessComponent();
    ~PostProcessComponent() override;
    
    // Bloom
    bool IsBloomEnabled() const { return m_bloomEnabled; }
    void SetBloomEnabled(bool enabled);
    
    float GetBloomIntensity() const { return m_bloomIntensity; }
    void SetBloomIntensity(float intensity);
    
    // Vignette
    bool IsVignetteEnabled() const { return m_vignetteEnabled; }
    void SetVignetteEnabled(bool enabled);
    
    float GetVignetteIntensity() const { return m_vignetteIntensity; }
    void SetVignetteIntensity(float intensity);
    
    // БСриализация
    template<typename Archive>
    void serialize(Archive& ar) {
        ar & m_bloomEnabled;
        ar & m_bloomIntensity;
        ar & m_vignetteEnabled;
        ar & m_vignetteIntensity;
    }

private:
    bool m_bloomEnabled;
    float m_bloomIntensity;
    bool m_vignetteEnabled;
    float m_vignetteIntensity;
};
// PostProcessComponent.cpp
#include "PostProcessComponent.h"
#include <Wudgine/Render/RenderPipeline.h>

PostProcessComponent::PostProcessComponent()
    : m_bloomEnabled(true)
    , m_bloomIntensity(1.0f)
    , m_vignetteEnabled(true)
    , m_vignetteIntensity(0.5f)
{
}

PostProcessComponent::~PostProcessComponent() = default;

void PostProcessComponent::SetBloomEnabled(bool enabled) {
    m_bloomEnabled = enabled;
    
    // ОбновлСниС эффСкта Π² ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π΅ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
    auto& renderPipeline = Wudgine::Render::RenderPipeline::GetInstance();
    if (enabled) {
        renderPipeline.EnablePostProcessEffect("Bloom");
    } else {
        renderPipeline.DisablePostProcessEffect("Bloom");
    }
}

void PostProcessComponent::SetBloomIntensity(float intensity) {
    m_bloomIntensity = intensity;
    
    // ОбновлСниС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² эффСкта
    auto& renderPipeline = Wudgine::Render::RenderPipeline::GetInstance();
    renderPipeline.SetPostProcessEffectParameter("Bloom", "intensity", intensity);
}

void PostProcessComponent::SetVignetteEnabled(bool enabled) {
    m_vignetteEnabled = enabled;
    
    // ОбновлСниС эффСкта Π² ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π΅ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
    auto& renderPipeline = Wudgine::Render::RenderPipeline::GetInstance();
    if (enabled) {
        renderPipeline.EnablePostProcessEffect("Vignette");
    } else {
        renderPipeline.DisablePostProcessEffect("Vignette");
    }
}

void PostProcessComponent::SetVignetteIntensity(float intensity) {
    m_vignetteIntensity = intensity;
    
    // ОбновлСниС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² эффСкта
    auto& renderPipeline = Wudgine::Render::RenderPipeline::GetInstance();
    renderPipeline.SetPostProcessEffectParameter("Vignette", "intensity", intensity);
}

Π€Π°ΠΉΠ» CMake

# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(PostProcessPlugin)

# ВСрсия C++
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹Π΅ Ρ„Π°ΠΉΠ»Ρ‹
set(SOURCES
    src/PostProcessPlugin.cpp
    src/Components/PostProcessComponent.cpp
    src/Effects/BloomEffect.cpp
    src/Effects/VignetteEffect.cpp
)

# Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΡ‡Π½Ρ‹Π΅ Ρ„Π°ΠΉΠ»Ρ‹
set(HEADERS
    src/PostProcessPlugin.h
    src/Components/PostProcessComponent.h
    src/Effects/BloomEffect.h
    src/Effects/VignetteEffect.h
)

# ΠŸΡƒΡ‚ΡŒ ΠΊ Wudgine SDK
set(WUDGINE_SDK_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../WudgineSDK" CACHE PATH "ΠŸΡƒΡ‚ΡŒ ΠΊ Wudgine SDK")

# Π’ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΉ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΡ‡Π½Ρ‹Ρ… Ρ„Π°ΠΉΠ»ΠΎΠ²
include_directories(
    ${WUDGINE_SDK_PATH}/include
    ${CMAKE_CURRENT_SOURCE_DIR}/src
)

# Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ ΠΏΠ»Π°Π³ΠΈΠ½Π°
add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS})

# ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ макроса для экспорта API ΠΏΠ»Π°Π³ΠΈΠ½Π°
target_compile_definitions(${PROJECT_NAME} PRIVATE WUDGINE_PLUGIN_EXPORTS)

# БвязываниС с Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°ΠΌΠΈ Wudgine
target_link_libraries(${PROJECT_NAME} 
    ${WUDGINE_SDK_PATH}/lib/WudgineCore
    ${WUDGINE_SDK_PATH}/lib/WudgineRender
    ${WUDGINE_SDK_PATH}/lib/WudgineECS
)

# ΠšΠΎΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ рСсурсов ΠΏΠ»Π°Π³ΠΈΠ½Π°
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    ${CMAKE_CURRENT_SOURCE_DIR}/resources
    ${CMAKE_BINARY_DIR}/plugins/${PROJECT_NAME}/resources
)

# ΠšΠΎΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Ρ„Π°ΠΉΠ»Π° манифСста
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy
    ${CMAKE_CURRENT_SOURCE_DIR}/plugin.json
    ${CMAKE_BINARY_DIR}/plugins/${PROJECT_NAME}/plugin.json
)

# ΠšΠΎΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ ΠΏΠ»Π°Π³ΠΈΠ½Π°
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy
    $<TARGET_FILE:${PROJECT_NAME}>
    ${CMAKE_BINARY_DIR}/plugins/${PROJECT_NAME}/$<TARGET_FILE_NAME:${PROJECT_NAME}>
)

Плагин для Ρ„ΠΈΠ·ΠΈΠΊΠΈ

Π­Ρ‚ΠΎΡ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ дСмонстрируСт созданиС ΠΏΠ»Π°Π³ΠΈΠ½Π° для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ физичСского Π΄Π²ΠΈΠΆΠΊΠ°:

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΠΏΠ»Π°Π³ΠΈΠ½Π°

PhysicsPlugin/
β”œβ”€β”€ CMakeLists.txt
β”œβ”€β”€ plugin.json
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ PhysicsPlugin.h
β”‚   β”œβ”€β”€ PhysicsPlugin.cpp
β”‚   β”œβ”€β”€ Components/
β”‚   β”‚   β”œβ”€β”€ RigidBodyComponent.h
β”‚   β”‚   β”œβ”€β”€ RigidBodyComponent.cpp
β”‚   β”‚   β”œβ”€β”€ ColliderComponent.h
β”‚   β”‚   └── ColliderComponent.cpp
β”‚   └── Systems/
β”‚       β”œβ”€β”€ PhysicsSystem.h
β”‚       └── PhysicsSystem.cpp
└── resources/
    └── icons/
        └── physics.png

Π€Π°ΠΉΠ» манифСста

{
  "name": "PhysicsPlugin",
  "version": "1.0.0",
  "author": "Wudgine Team",
  "description": "Плагин для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ физичСского Π΄Π²ΠΈΠΆΠΊΠ°",
  "engineVersion": "1.0.0",
  "dependencies": [],
  "entryPoint": "PhysicsPlugin"
}

Основной класс плагина

// PhysicsPlugin.h
#pragma once

#include <Wudgine/Plugin/IPlugin.h>

class PhysicsPlugin : public Wudgine::Plugin::IPlugin {
public:
    PhysicsPlugin();
    ~PhysicsPlugin();

    // РСализация интСрфСйса IPlugin
    bool Initialize() override;
    void Shutdown() override;
    
    const char* GetName() const override { return "PhysicsPlugin"; }
    const char* GetVersion() const override { return "1.0.0"; }
    const char* GetAuthor() const override { return "Wudgine Team"; }
    const char* GetDescription() const override { return "Плагин для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ физичСского Π΄Π²ΠΈΠΆΠΊΠ°"; }
    
    // Настройки ΠΏΠ»Π°Π³ΠΈΠ½Π°
    void RenderSettings() override;

private:
    // РСгистрация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² ΠΈ систСм
    void RegisterComponents();
    void RegisterSystems();
    void RegisterEditorTools();
    
    // Настройки ΠΏΠ»Π°Π³ΠΈΠ½Π°
    struct Settings {
        float gravity = -9.81f;
        int solverIterations = 10;
        bool enableContinuousDetection = true;
        
        template<typename Archive>
        void serialize(Archive& ar) {
            ar & gravity;
            ar & solverIterations;
            ar & enableContinuousDetection;
        }
    };
    
    Settings m_settings;
};
// PhysicsPlugin.cpp
#include "PhysicsPlugin.h"
#include "Components/RigidBodyComponent.h"
#include "Components/ColliderComponent.h"
#include "Systems/PhysicsSystem.h"

#include <Wudgine/Core/Log.h>
#include <Wudgine/Core/SettingsManager.h>
#include <Wudgine/ECS/ComponentRegistry.h>
#include <Wudgine/ECS/SystemRegistry.h>
#include <Wudgine/Editor/EditorToolManager.h>
#include <imgui.h>

PhysicsPlugin::PhysicsPlugin() = default;
PhysicsPlugin::~PhysicsPlugin() = default;

bool PhysicsPlugin::Initialize() {
    WG_LOG_INFO("[PhysicsPlugin] Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ...");
    
    // Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° настроСк
    if (Wudgine::Core::SettingsManager::GetInstance().HasPluginSettings(GetName())) {
        m_settings = Wudgine::Core::SettingsManager::GetInstance().LoadPluginSettings<Settings>(GetName());
    }
    
    // РСгистрация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² ΠΈ систСм
    RegisterComponents();
    RegisterSystems();
    
    // РСгистрация инструмСнтов Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π° (Ссли Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π°)
    if (Wudgine::Core::IsEditor()) {
        RegisterEditorTools();
    }
    
    // Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ физичСского Π΄Π²ΠΈΠΆΠΊΠ° с настройками
    PhysicsSystem::GetInstance().SetGravity(m_settings.gravity);
    PhysicsSystem::GetInstance().SetSolverIterations(m_settings.solverIterations);
    PhysicsSystem::GetInstance().SetContinuousDetection(m_settings.enableContinuousDetection);
    
    WG_LOG_INFO("[PhysicsPlugin] Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½Π°");
    return true;
}

void PhysicsPlugin::Shutdown() {
    WG_LOG_INFO("[PhysicsPlugin] Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹...");
    
    // Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ настроСк
    Wudgine::Core::SettingsManager::GetInstance().SavePluginSettings(GetName(), m_settings);
    
    // Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ физичСского Π΄Π²ΠΈΠΆΠΊΠ°
    PhysicsSystem::GetInstance().Shutdown();
    
    WG_LOG_INFO("[PhysicsPlugin] Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΎ");
}

void PhysicsPlugin::RegisterComponents() {
    // РСгистрация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² Ρ„ΠΈΠ·ΠΈΠΊΠΈ
    Wudgine::ECS::ComponentRegistry::GetInstance().RegisterComponent<RigidBodyComponent>(
        "RigidBodyComponent",
        []() { return new RigidBodyComponent(); }
    );
    
    Wudgine::ECS::ComponentRegistry::GetInstance().RegisterComponent<ColliderComponent>(
        "ColliderComponent",
        []() { return new ColliderComponent(); }
    );
}

void PhysicsPlugin::RegisterSystems() {
    // РСгистрация систСмы Ρ„ΠΈΠ·ΠΈΠΊΠΈ
    Wudgine::ECS::SystemRegistry::GetInstance().RegisterSystem<PhysicsSystem>(
        "PhysicsSystem",
        []() { return &PhysicsSystem::GetInstance(); }
    );
}

void PhysicsPlugin::RegisterEditorTools() {
    // РСгистрация инспСктора для ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° Ρ‚Π²Π΅Ρ€Π΄ΠΎΠ³ΠΎ Ρ‚Π΅Π»Π°
    class RigidBodyInspector : public Wudgine::Editor::ComponentInspector {
    public:
        bool CanInspect(Wudgine::ECS::Component* component) override {
            return dynamic_cast<RigidBodyComponent*>(component) != nullptr;
        }
        
        void Render(Wudgine::ECS::Component* component) override {
            auto* rbComponent = static_cast<RigidBodyComponent*>(component);
            
            // ΠžΡ‚Ρ€ΠΈΡΠΎΠ²ΠΊΠ° ΠΏΠΎΠ»Π΅ΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
            float mass = rbComponent->GetMass();
            if (ImGui::DragFloat("Масса", &mass, 0.1f, 0.0f, 1000.0f)) {
                rbComponent->SetMass(mass);
                MarkDirty();
            }
            
            bool isKinematic = rbComponent->IsKinematic();
            if (ImGui::Checkbox("ΠšΠΈΠ½Π΅ΠΌΠ°Ρ‚ΠΈΡ‡Π΅ΡΠΊΠΈΠΉ", &isKinematic)) {
                rbComponent->SetKinematic(isKinematic);
                MarkDirty();
            }
            
            float linearDamping = rbComponent->GetLinearDamping();
            if (ImGui::SliderFloat("Π›ΠΈΠ½Π΅ΠΉΠ½ΠΎΠ΅ Π·Π°Ρ‚ΡƒΡ…Π°Π½ΠΈΠ΅", &linearDamping, 0.0f, 1.0f)) {
                rbComponent->SetLinearDamping(linearDamping);
                MarkDirty();
            }
            
            float angularDamping = rbComponent->GetAngularDamping();
            if (ImGui::SliderFloat("Π£Π³Π»ΠΎΠ²ΠΎΠ΅ Π·Π°Ρ‚ΡƒΡ…Π°Π½ΠΈΠ΅", &angularDamping, 0.0f, 1.0f)) {
                rbComponent->SetAngularDamping(angularDamping);
                MarkDirty();
            }
        }
    };
    
    // РСгистрация инспСктора для ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° ΠΊΠΎΠ»Π»Π°ΠΉΠ΄Π΅Ρ€Π°
    class ColliderInspector : public Wudgine::Editor::ComponentInspector {
    public:
        bool CanInspect(Wudgine::ECS::Component* component) override {
            return dynamic_cast<ColliderComponent*>(component) != nullptr;
        }
        
        void Render(Wudgine::ECS::Component* component) override {
            auto* colliderComponent = static_cast<ColliderComponent*>(component);
            
            // ΠžΡ‚Ρ€ΠΈΡΠΎΠ²ΠΊΠ° ΠΏΠΎΠ»Π΅ΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
            const char* colliderTypes[] = { "Box", "Sphere", "Capsule", "Mesh" };
            int currentType = static_cast<int>(colliderComponent->GetColliderType());
            
            if (ImGui::Combo("Π’ΠΈΠΏ ΠΊΠΎΠ»Π»Π°ΠΉΠ΄Π΅Ρ€Π°", &currentType, colliderTypes, IM_ARRAYSIZE(colliderTypes))) {
                colliderComponent->SetColliderType(static_cast<ColliderComponent::ColliderType>(currentType));
                MarkDirty();
            }
            
            // ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Π² зависимости ΠΎΡ‚ Ρ‚ΠΈΠΏΠ° ΠΊΠΎΠ»Π»Π°ΠΉΠ΄Π΅Ρ€Π°
            switch (colliderComponent->GetColliderType()) {
                case ColliderComponent::ColliderType::Box: {
                    auto size = colliderComponent->GetBoxSize();
                    if (ImGui::DragFloat3("Π Π°Π·ΠΌΠ΅Ρ€", &size.x, 0.1f, 0.01f, 100.0f)) {
                        colliderComponent->SetBoxSize(size);
                        MarkDirty();
                    }
                    break;
                }
                case ColliderComponent::ColliderType::Sphere: {
                    float radius = colliderComponent->GetSphereRadius();
                    if (ImGui::DragFloat("Радиус", &radius, 0.1f, 0.01f, 100.0f)) {
                        colliderComponent->SetSphereRadius(radius);
                        MarkDirty();
                    }
                    break;
                }
                case ColliderComponent::ColliderType::Capsule: {
                    float radius = colliderComponent->GetCapsuleRadius();
                    float height = colliderComponent->GetCapsuleHeight();
                    
                    if (ImGui::DragFloat("Радиус", &radius, 0.1f, 0.01f, 100.0f)) {
                        colliderComponent->SetCapsuleRadius(radius);
                        MarkDirty();
                    }
                    
                    if (ImGui::DragFloat("Высота", &height, 0.1f, 0.01f, 100.0f)) {
                        colliderComponent->SetCapsuleHeight(height);
                        MarkDirty();
                    }
                    break;
                }
                case ColliderComponent::ColliderType::Mesh: {
                    std::string meshPath = colliderComponent->GetMeshPath();
                    char buffer[256];
                    strcpy(buffer, meshPath.c_str());
                    
                    if (ImGui::InputText("ΠŸΡƒΡ‚ΡŒ ΠΊ мСш", buffer, sizeof(buffer))) {
                        colliderComponent->SetMeshPath(buffer);
                        MarkDirty();
                    }
                    
                    if (ImGui::Button("ΠžΠ±Π·ΠΎΡ€...")) {
                        // ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ Π΄ΠΈΠ°Π»ΠΎΠ³Π° Π²Ρ‹Π±ΠΎΡ€Π° Ρ„Π°ΠΉΠ»Π°
                        // ...
                    }
                    break;
                }
            }
            
            float friction = colliderComponent->GetFriction();
            if (ImGui::SliderFloat("Π’Ρ€Π΅Π½ΠΈΠ΅", &friction, 0.0f, 1.0f)) {
                colliderComponent->SetFriction(friction);
                MarkDirty();
            }
            
            float restitution = colliderComponent->GetRestitution();
            if (ImGui::SliderFloat("Π£ΠΏΡ€ΡƒΠ³ΠΎΡΡ‚ΡŒ", &restitution, 0.0f, 1.0f)) {
                colliderComponent->SetRestitution(restitution);
                MarkDirty();
            }
        }
    };
    
    Wudgine::Editor::InspectorManager::GetInstance().RegisterInspector(new RigidBodyInspector());
    Wudgine::Editor::InspectorManager::GetInstance().RegisterInspector(new ColliderInspector());
}

void PhysicsPlugin::RenderSettings() {
    ImGui::DragFloat("Гравитация", &m_settings.gravity, 0.1f, -20.0f, 20.0f);
    ImGui::SliderInt("Π˜Ρ‚Π΅Ρ€Π°Ρ†ΠΈΠΈ Ρ€Π΅ΡˆΠ°Ρ‚Π΅Π»Ρ", &m_settings.solverIterations, 1, 20);
    ImGui::Checkbox("НСпрСрывноС ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ столкновСний", &m_settings.enableContinuousDetection);
    
    // ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ
    PhysicsSystem::GetInstance().SetGravity(m_settings.gravity);
    PhysicsSystem::GetInstance().SetSolverIterations(m_settings.solverIterations);
    PhysicsSystem::GetInstance().SetContinuousDetection(m_settings.enableContinuousDetection);
    
    // Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ настроСк
    Wudgine::Core::SettingsManager::GetInstance().SavePluginSettings(GetName(), m_settings);
}

// Экспорт ΠΏΠ»Π°Π³ΠΈΠ½Π°
extern "C" {
    WUDGINE_PLUGIN_API Wudgine::Plugin::IPlugin* CreatePlugin() {
        return new PhysicsPlugin();
    }
}

Плагин для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с внСшним инструмСнтом

Π­Ρ‚ΠΎΡ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ дСмонстрируСт созданиС ΠΏΠ»Π°Π³ΠΈΠ½Π° для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с внСшним инструмСнтом:

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΠΏΠ»Π°Π³ΠΈΠ½Π°

ExternalToolPlugin/
β”œβ”€β”€ CMakeLists.txt
β”œβ”€β”€ plugin.json
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ ExternalToolPlugin.h
β”‚   β”œβ”€β”€ ExternalToolPlugin.cpp
β”‚   β”œβ”€β”€ Tools/
β”‚   β”‚   β”œβ”€β”€ ExternalToolIntegration.h
β”‚   β”‚   └── ExternalToolIntegration.cpp
β”‚   └── Editor/
β”‚       β”œβ”€β”€ ExternalToolWindow.h
β”‚       └── ExternalToolWindow.cpp
└── resources/
    └── icons/
        └── external_tool.png

Π€Π°ΠΉΠ» манифСста

{
  "name": "ExternalToolPlugin",
  "version": "1.0.0",
  "author": "Wudgine Team",
  "description": "Плагин для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с внСшним инструмСнтом",
  "engineVersion": "1.0.0",
  "dependencies": [],
  "entryPoint": "ExternalToolPlugin"
}

Основной класс плагина

// ExternalToolPlugin.h
#pragma once

#include <Wudgine/Plugin/IPlugin.h>
#include "Tools/ExternalToolIntegration.h"

class ExternalToolPlugin : public Wudgine::Plugin::IPlugin {
public:
    ExternalToolPlugin();
    ~ExternalToolPlugin();

    // РСализация интСрфСйса IPlugin
    bool Initialize() override;
    void Shutdown() override;
    
    const char* GetName() const override { return "ExternalToolPlugin"; }
    const char* GetVersion() const override { return "1.0.0"; }
    const char* GetAuthor() const override { return "Wudgine Team"; }
    const char* GetDescription() const override { return "Плагин для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с внСшним инструмСнтом"; }
    
    // Настройки ΠΏΠ»Π°Π³ΠΈΠ½Π°
    void RenderSettings() override;
    
    // Доступ ΠΊ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ
    ExternalToolIntegration* GetIntegration() { return m_integration.get(); }

private:
    // РСгистрация инструмСнтов Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π°
    void RegisterEditorTools();
    
    // Настройки ΠΏΠ»Π°Π³ΠΈΠ½Π°
    struct Settings {
        std::string externalToolPath;
        bool autoSync = true;
        int syncInterval = 5; // Π² сСкундах
        
        template<typename Archive>
        void serialize(Archive& ar) {
            ar & externalToolPath;
            ar & autoSync;
            ar & syncInterval;
        }
    };
    
    Settings m_settings;
    std::unique_ptr<ExternalToolIntegration> m_integration;
};
// ExternalToolPlugin.cpp
#include "ExternalToolPlugin.h"
#include "Editor/ExternalToolWindow.h"

#include <Wudgine/Core/Log.h>
#include <Wudgine/Core/SettingsManager.h>
#include <Wudgine/Editor/EditorToolManager.h>
#include <Wudgine/Editor/EditorMenuManager.h>
#include <imgui.h>

ExternalToolPlugin::ExternalToolPlugin() = default;
ExternalToolPlugin::~ExternalToolPlugin() = default;

bool ExternalToolPlugin::Initialize() {
    WG_LOG_INFO("[ExternalToolPlugin] Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ...");
    
    // Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° настроСк
    if (Wudgine::Core::SettingsManager::GetInstance().HasPluginSettings(GetName())) {
        m_settings = Wudgine::Core::SettingsManager::GetInstance().LoadPluginSettings<Settings>(GetName());
    }
    
    // Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с внСшним инструмСнтом
    m_integration = std::make_unique<ExternalToolIntegration>(m_settings.externalToolPath);
    
    // Настройка ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ
    m_integration->SetAutoSync(m_settings.autoSync);
    m_integration->SetSyncInterval(m_settings.syncInterval);
    
    // РСгистрация инструмСнтов Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π° (Ссли Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π°)
    if (Wudgine::Core::IsEditor()) {
        RegisterEditorTools();
    }
    
    // Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ
    if (!m_integration->Initialize()) {
        WG_LOG_ERROR("[ExternalToolPlugin] Ошибка ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с внСшним инструмСнтом");
        return false;
    }
    
    WG_LOG_INFO("[ExternalToolPlugin] Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½Π°");
    return true;
}

void ExternalToolPlugin::Shutdown() {
    WG_LOG_INFO("[ExternalToolPlugin] Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹...");
    
    // Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ настроСк
    Wudgine::Core::SettingsManager::GetInstance().SavePluginSettings(GetName(), m_settings);
    
    // Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ
    if (m_integration) {
        m_integration->Shutdown();
    }
    
    WG_LOG_INFO("[ExternalToolPlugin] Π—Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΎ");
}

void ExternalToolPlugin::RegisterEditorTools() {
    // РСгистрация ΠΎΠΊΠ½Π° внСшнСго инструмСнта
    auto* window = new ExternalToolWindow(m_integration.get());
    Wudgine::Editor::EditorToolManager::GetInstance().RegisterTool(window);
    
    // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΡƒΠ½ΠΊΡ‚ΠΎΠ² мСню
    Wudgine::Editor::EditorMenuManager::GetInstance().AddMenuItem(
        "Π˜Π½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚Ρ‹/Π’Π½Π΅ΡˆΠ½ΠΈΠΉ инструмСнт/ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ",
        [window]() {
            window->SetVisible(true);
        }
    );
    
    Wudgine::Editor::EditorMenuManager::GetInstance().AddMenuItem(
        "Π˜Π½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚Ρ‹/Π’Π½Π΅ΡˆΠ½ΠΈΠΉ инструмСнт/Π‘ΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ",
        [this]() {
            m_integration->SyncNow();
        }
    );
    
    Wudgine::Editor::EditorMenuManager::GetInstance().AddMenuItem(
        "Π˜Π½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚Ρ‹/Π’Π½Π΅ΡˆΠ½ΠΈΠΉ инструмСнт/Π—Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ внСшний инструмСнт",
        [this]() {
            m_integration->LaunchExternalTool();
        }
    );
}

void ExternalToolPlugin::RenderSettings() {
    char buffer[256];
    strcpy(buffer, m_settings.externalToolPath.c_str());
    
    if (ImGui::InputText("ΠŸΡƒΡ‚ΡŒ ΠΊ Π²Π½Π΅ΡˆΠ½Π΅ΠΌΡƒ инструмСнту", buffer, sizeof(buffer))) {
        m_settings.externalToolPath = buffer;
        m_integration->SetExternalToolPath(m_settings.externalToolPath);
    }
    
    if (ImGui::Button("ΠžΠ±Π·ΠΎΡ€...")) {
        // ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ Π΄ΠΈΠ°Π»ΠΎΠ³Π° Π²Ρ‹Π±ΠΎΡ€Π° Ρ„Π°ΠΉΠ»Π°
        // ...
    }
    
    if (ImGui::Checkbox("АвтоматичСская синхронизация", &m_settings.autoSync)) {
        m_integration->SetAutoSync(m_settings.autoSync);
    }
    
    if (m_settings.autoSync) {
        if (ImGui::SliderInt("Π˜Π½Ρ‚Π΅Ρ€Π²Π°Π» синхронизации (сСк)", &m_settings.syncInterval, 1, 60)) {
            m_integration->SetSyncInterval(m_settings.syncInterval);
        }
    }
    
    if (ImGui::Button("ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ соСдинСниС")) {
        bool connected = m_integration->TestConnection();
        if (connected) {
            WG_LOG_INFO("[ExternalToolPlugin] Π‘ΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ с внСшним инструмСнтом установлСно");
        } else {
            WG_LOG_ERROR("[ExternalToolPlugin] Ошибка соСдинСния с внСшним инструмСнтом");
        }
    }
    
    // Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ настроСк
    Wudgine::Core::SettingsManager::GetInstance().SavePluginSettings(GetName(), m_settings);
}

// Экспорт ΠΏΠ»Π°Π³ΠΈΠ½Π°
extern "C" {
    WUDGINE_PLUGIN_API Wudgine::Plugin::IPlugin* CreatePlugin() {
        return new ExternalToolPlugin();
    }
}

Π§Ρ‚ΠΎ дальшС?

Π’Π΅ΠΏΠ΅Ρ€ΡŒ, ΠΊΠΎΠ³Π΄Π° Π²Ρ‹ ознакомились с ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°ΠΌΠΈ ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠ², Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅:

ЭкспСримСнтируйтС с ΠΏΠ»Π°Π³ΠΈΠ½Π°ΠΌΠΈ ΠΈ создавайтС ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹Π΅ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ для Wudgine, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠΌΠΎΠ³ΡƒΡ‚ Π²Π°ΠΌ Π² Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ΠΈΠ³Ρ€!

Wudgine β€’ Β© 2025