Documentation

Components

Π’ этом Ρ€Π°Π·Π΄Π΅Π»Π΅ ΠΎΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ Π² систСмС ECS Wudgine.

ΠžΡΠ½ΠΎΠ²Ρ‹ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²

ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ Π² Wudgine β€” это ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΡ€ΠΈΠΊΡ€Π΅ΠΏΠ»ΡΡŽΡ‚ΡΡ ΠΊ сущностям. Они Π½Π΅ содСрТат Π»ΠΎΠ³ΠΈΠΊΠΈ, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π΄Π°Π½Π½Ρ‹Π΅.

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²

// ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ простого ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
struct HealthComponent {
    float maxHealth = 100.0f;
    float currentHealth = 100.0f;
    
    // ΠžΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ: сСриализация
    template<typename Archive>
    void serialize(Archive& archive) {
        archive(maxHealth, currentHealth);
    }
};

// РСгистрация ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
world.registerComponent<HealthComponent>();

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° ΠΊ сущности
Entity player = world.createEntity("Player");
auto& health = player.addComponent<HealthComponent>();
health.maxHealth = 200.0f;
health.currentHealth = 200.0f;

Π–ΠΈΠ·Π½Π΅Π½Π½Ρ‹ΠΉ Ρ†ΠΈΠΊΠ» ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²

ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ ΠΈΠΌΠ΅ΡŽΡ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ события ΠΆΠΈΠ·Π½Π΅Π½Π½ΠΎΠ³ΠΎ Ρ†ΠΈΠΊΠ»Π°:

  • OnCreate: ВызываСтся ΠΏΡ€ΠΈ создании ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
  • OnDestroy: ВызываСтся ΠΏΠ΅Ρ€Π΅Π΄ ΡƒΠ½ΠΈΡ‡Ρ‚ΠΎΠΆΠ΅Π½ΠΈΠ΅ΠΌ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
  • OnEnable: ВызываСтся ΠΏΡ€ΠΈ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
  • OnDisable: ВызываСтся ΠΏΡ€ΠΈ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
// ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ с ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ ΠΆΠΈΠ·Π½Π΅Π½Π½ΠΎΠ³ΠΎ Ρ†ΠΈΠΊΠ»Π°
struct AIComponent {
    float detectionRadius = 10.0f;
    Entity target;
    
    void onCreate() {
        Debug::log("AI Component created");
    }
    
    void onDestroy() {
        Debug::log("AI Component destroyed");
    }
    
    void onEnable() {
        Debug::log("AI Component enabled");
    }
    
    void onDisable() {
        Debug::log("AI Component disabled");
    }
};

ВстроСнныС ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹

TransformComponent

ΠžΠΏΡ€Π΅Π΄Π΅Π»ΡΠ΅Ρ‚ ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, ΠΏΠΎΠ²ΠΎΡ€ΠΎΡ‚ ΠΈ ΠΌΠ°ΡΡˆΡ‚Π°Π± сущности Π² пространствС:

// ИспользованиС TransformComponent
auto& transform = entity.addComponent<TransformComponent>();
transform.position = Vector3(0.0f, 1.0f, 0.0f);
transform.rotation = Quaternion::fromEuler(0.0f, 45.0f, 0.0f);
transform.scale = Vector3(1.0f, 1.0f, 1.0f);

// ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΡ
Vector3 forward = transform.getForward();
Vector3 right = transform.getRight();
Vector3 up = transform.getUp();

// ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚
Vector3 localPoint = Vector3(1.0f, 0.0f, 0.0f);
Vector3 worldPoint = transform.localToWorld(localPoint);
Vector3 backToLocal = transform.worldToLocal(worldPoint);

HierarchyComponent

Π‘ΠΎΠ·Π΄Π°Π΅Ρ‚ иСрархичСскиС ΠΎΡ‚Π½ΠΎΡˆΠ΅Π½ΠΈΡ ΠΌΠ΅ΠΆΠ΄Ρƒ сущностями:

// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ
Entity parent = world.createEntity("Parent");
Entity child = world.createEntity("Child");

// Установка родитСля
child.setParent(parent);

// ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΡ… сущностСй
auto children = parent.getChildren();
for (auto& childEntity : children) {
    Debug::log("Child: {}", childEntity.getName());
}

// ΠžΠ±Ρ…ΠΎΠ΄ ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ
parent.traverseHierarchy([](Entity entity, int depth) {
    std::string indent(depth * 2, ' ');
    Debug::log("{}Entity: {}", indent, entity.getName());
    return true; // ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚ΡŒ ΠΎΠ±Ρ…ΠΎΠ΄
});

MeshRendererComponent

ΠžΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ Π·Π° Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³ 3D-ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ:

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Ρ€Π΅Π½Π΄Π΅Ρ€Π΅Ρ€Π° мСша
auto& meshRenderer = entity.addComponent<MeshRendererComponent>();
meshRenderer.setMesh("assets/models/character.mesh");
meshRenderer.setMaterial("assets/materials/character.mat");

// Настройка свойств Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
meshRenderer.setCastShadows(true);
meshRenderer.setReceiveShadows(true);
meshRenderer.setLayer(RenderLayer::Default);
meshRenderer.setVisible(true);

CameraComponent

ΠžΠΏΡ€Π΅Π΄Π΅Π»ΡΠ΅Ρ‚ ΠΊΠ°ΠΌΠ΅Ρ€Ρƒ для Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π° сцСны:

// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠ°ΠΌΠ΅Ρ€Ρ‹
auto& camera = entity.addComponent<CameraComponent>();
camera.setProjectionType(ProjectionType::Perspective);
camera.setFieldOfView(60.0f);
camera.setNearClip(0.1f);
camera.setFarClip(1000.0f);
camera.setPriority(0); // Основная ΠΊΠ°ΠΌΠ΅Ρ€Π°

// Настройка постобработки
camera.enablePostProcessing(true);
camera.addPostProcessEffect<BloomEffect>();

LightComponent

Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ для источников свСта:

// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π½Π°ΠΏΡ€Π°Π²Π»Π΅Π½Π½ΠΎΠ³ΠΎ свСта
Entity sun = world.createEntity("Sun");
auto& dirLight = sun.addComponent<DirectionalLightComponent>();
dirLight.setDirection(Vector3(-0.5f, -1.0f, -0.3f));
dirLight.setColor(Vector3(1.0f, 0.95f, 0.9f));
dirLight.setIntensity(5.0f);
dirLight.setCastShadows(true);

// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Ρ‚ΠΎΡ‡Π΅Ρ‡Π½ΠΎΠ³ΠΎ свСта
Entity lamp = world.createEntity("Lamp");
auto& pointLight = lamp.addComponent<PointLightComponent>();
pointLight.setColor(Vector3(1.0f, 0.8f, 0.6f));
pointLight.setIntensity(2.0f);
pointLight.setRange(10.0f);
pointLight.setCastShadows(true);

RigidbodyComponent

ДобавляСт физичСскоС Ρ‚Π΅Π»ΠΎ для симуляции Ρ„ΠΈΠ·ΠΈΠΊΠΈ:

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ физичСского Ρ‚Π΅Π»Π°
auto& rigidbody = entity.addComponent<RigidbodyComponent>();
rigidbody.setMass(10.0f);
rigidbody.setDrag(0.1f);
rigidbody.setAngularDrag(0.05f);
rigidbody.setUseGravity(true);
rigidbody.setKinematic(false);

// ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ сил
rigidbody.addForce(Vector3(0.0f, 0.0f, 10.0f));
rigidbody.addTorque(Vector3(0.0f, 1.0f, 0.0f));

ColliderComponent

ΠžΠΏΡ€Π΅Π΄Π΅Π»ΡΠ΅Ρ‚ Ρ„ΠΎΡ€ΠΌΡƒ для физичСских столкновСний:

// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠΎΠ»Π»Π°ΠΉΠ΄Π΅Ρ€Π°-ΠΊΠΎΡ€ΠΎΠ±ΠΊΠΈ
auto& boxCollider = entity.addComponent<BoxColliderComponent>();
boxCollider.setSize(Vector3(1.0f, 1.0f, 1.0f));
boxCollider.setCenter(Vector3(0.0f, 0.5f, 0.0f));
boxCollider.setMaterial("assets/physics/metal.phymat");

AudioComponent

ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ для воспроизвСдСния Π·Π²ΡƒΠΊΠΎΠ²:

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ источника Π·Π²ΡƒΠΊΠ°
auto& audioSource = entity.addComponent<AudioSourceComponent>();
audioSource.setClip("assets/audio/explosion.wav");
audioSource.setVolume(0.8f);
audioSource.setPitch(1.0f);
audioSource.setLoop(false);
audioSource.setSpatialBlend(1.0f); // 3D Π·Π²ΡƒΠΊ
audioSource.setMinDistance(1.0f);
audioSource.setMaxDistance(20.0f);

// ВоспроизвСдСниС Π·Π²ΡƒΠΊΠ°
audioSource.play();

AnimatorComponent

УправляСт Π°Π½ΠΈΠΌΠ°Ρ†ΠΈΠ΅ΠΉ пСрсонаТСй ΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²:

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π°Π½ΠΈΠΌΠ°Ρ‚ΠΎΡ€Π°
auto& animator = entity.addComponent<AnimatorComponent>();
animator.setController("assets/animations/character.controller");

// ВоспроизвСдСниС Π°Π½ΠΈΠΌΠ°Ρ†ΠΈΠΈ
animator.play("Walk");

// Настройка ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Π°Π½ΠΈΠΌΠ°Ρ†ΠΈΠΈ
animator.setFloat("Speed", 1.5f);
animator.setBool("IsJumping", true);
animator.setTrigger("Attack");

// БмСшиваниС Π°Π½ΠΈΠΌΠ°Ρ†ΠΈΠΉ
animator.crossFade("Run", 0.2f);

WebUIComponent

Π˜Π½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ Π²Π΅Π±-интСрфСйсы Π² ΠΈΠ³Ρ€ΠΎΠ²Ρ‹Π΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹:

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π²Π΅Π±-интСрфСйса
auto& webUI = entity.addComponent<WebUIComponent>();
webUI.setSource("assets/ui/inventory.html");
webUI.setSize(512, 512);
webUI.setTransparent(true);
webUI.setInteractive(true);

// ВзаимодСйствиС с JavaScript
webUI.callJavaScriptFunction("updateInventory", {{"gold", 100}, {"items", itemsArray}});

// РСгистрация ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎΠ³ΠΎ Π²Ρ‹Π·ΠΎΠ²Π° ΠΈΠ· JavaScript
webUI.registerCallback("onItemSelected", [](const json& args) {
    int itemId = args["itemId"];
    Debug::log("Item selected: {}", itemId);
});

ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² с Π΄Π°Π½Π½Ρ‹ΠΌΠΈ

// ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ Π·Π΄ΠΎΡ€ΠΎΠ²ΡŒΡ с Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ функциями
struct HealthComponent {
    float maxHealth = 100.0f;
    float currentHealth = 100.0f;
    bool isInvulnerable = false;
    float regenerationRate = 0.0f;
    
    // Π’ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ (Π½Π΅ Π»ΠΎΠ³ΠΈΠΊΠ°!)
    float getHealthPercentage() const {
        return currentHealth / maxHealth;
    }
    
    bool isDead() const {
        return currentHealth <= 0.0f;
    }
    
    // БСриализация
    template<typename Archive>
    void serialize(Archive& archive) {
        archive(maxHealth, currentHealth, isInvulnerable, regenerationRate);
    }
};

ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ с событиями

// ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ с событиями
struct DamageableComponent {
    using DamageEvent = std::function<void(float damageAmount, Entity source)>;
    using DeathEvent = std::function<void(Entity killer)>;
    
    float armor = 0.0f;
    float damageMultiplier = 1.0f;
    
    DamageEvent onDamage;
    DeathEvent onDeath;
    
    // БСриализация (события Π½Π΅ ΡΠ΅Ρ€ΠΈΠ°Π»ΠΈΠ·ΡƒΡŽΡ‚ΡΡ)
    template<typename Archive>
    void serialize(Archive& archive) {
        archive(armor, damageMultiplier);
    }
};

// ИспользованиС событий ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°
auto& damageable = entity.addComponent<DamageableComponent>();
damageable.onDamage = [](float amount, Entity source) {
    Debug::log("Received {} damage from {}", amount, source.getName());
};
damageable.onDeath = [](Entity killer) {
    Debug::log("Killed by {}", killer.getName());
};

ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ с зависимостями

// ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚, зависящий ΠΎΡ‚ Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²
struct HealthUIComponent {
    Entity healthBarEntity;
    float updateInterval = 0.1f;
    float lastUpdateTime = 0.0f;
    
    void onCreate() {
        // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° зависимостСй
        if (!entity.hasComponent<HealthComponent>()) {
            Debug::logWarning("HealthUIComponent requires HealthComponent");
        }
    }
    
    // БСриализация
    template<typename Archive>
    void serialize(Archive& archive) {
        archive(healthBarEntity, updateInterval);
    }
};

РСфлСксия ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²

Wudgine ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ систСму рСфлСксии для автоматичСской рСгистрации ΠΈ сСриализации ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²:

// ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° с рСфлСксиСй
struct PlayerComponent {
    float moveSpeed = 5.0f;
    float jumpForce = 10.0f;
    int playerLevel = 1;
    std::string playerName = "Player";
    
    REFLECT_COMPONENT(PlayerComponent)
    
    static void registerReflection() {
        REFLECT_PROPERTY(moveSpeed, "Move Speed", "Speed at which the player moves")
            .setRange(1.0f, 20.0f)
            .setDefaultValue(5.0f);
            
        REFLECT_PROPERTY(jumpForce, "Jump Force", "Force applied when jumping")
            .setRange(5.0f, 30.0f);
            
        REFLECT_PROPERTY(playerLevel, "Level", "Current player level")
            .setRange(1, 100);
            
        REFLECT_PROPERTY(playerName, "Name", "Player's name");
    }
};

// АвтоматичСская рСгистрация
REGISTER_COMPONENT(PlayerComponent)

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ шаги

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

БистСмы

Π˜Π·ΡƒΡ‡ΠΈΡ‚Π΅ систСмы, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°ΡŽΡ‚ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹.

Запросы ΠΈ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹

Π£Π·Π½Π°ΠΉΡ‚Π΅, ΠΊΠ°ΠΊ эффСктивно Π·Π°ΠΏΡ€Π°ΡˆΠΈΠ²Π°Ρ‚ΡŒ сущности с ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½Ρ‹ΠΌΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°ΠΌΠΈ.

Wudgine β€’ Β© 2025