Правене на игри II

Amethyst, ECS

07 януари 2020

Административни неща

Административни неща

Изберете си проекти

Административни неща

Изберете си проекти

Административни неща

Изберете си проекти

Административни неща

Изберете си проекти

Архитектура на игрови двигатели

Архитектура на игрови двигатели

Има много различни начини да се структурира логиката на игра. Някои от тях включват:

Архитектура на игрови двигатели

Има много различни начини да се структурира логиката на игра. Някои от тях включват:

Архитектура на игрови двигатели

Има много различни начини да се структурира логиката на игра. Някои от тях включват:

Архитектура на игрови двигатели

Има много различни начини да се структурира логиката на игра. Някои от тях включват:

Архитектура на игрови двигатели

Има много различни начини да се структурира логиката на игра. Някои от тях включват:

Архитектура на игрови двигатели

Има много различни начини да се структурира логиката на игра. Някои от тях включват:

ООП метода

1 2 3 4 5 6 7 8 9
loop {
    player.update();
    shots.update();
    enemies.update();

    player.draw();
    shots.draw();
    enemies.draw();
}

ООП метода

ООП метода

ООП метода

ООП метода

Недостатъци (в сравнение с ECS)

ООП метода

Недостатъци (в сравнение с ECS)

ООП метода

Недостатъци (в сравнение с ECS)

ООП метода

Недостатъци (в сравнение с ECS)

ООП метода

Една интересно сравнение на ООП с ECS:

Component based

Идеята на компонентната архитектура е да се използва принципа за композиция вместо наследяване.

Component based

Идеята на компонентната архитектура е да се използва принципа за композиция вместо наследяване.
Вместо да се използва наследяване с цел преизползване на код, логиката се разделя на множество независими компоненти.

Component based

Идеята на компонентната архитектура е да се използва принципа за композиция вместо наследяване.
Вместо да се използва наследяване с цел преизползване на код, логиката се разделя на множество независими компоненти.
Тези компоненти се закачат към обектите за да им добавят определено поведение.

Component based

Идеята на компонентната архитектура е да се използва принципа за композиция вместо наследяване.
Вместо да се използва наследяване с цел преизползване на код, логиката се разделя на множество независими компоненти.
Тези компоненти се закачат към обектите за да им добавят определено поведение.
При някои имплементации това добавяне и премахване на поведени може да стане динамично по време на изпълнение на програмата.

Component based / EC

Entity-Component

Component based / EC

Entity-Component

Component based / EC

Entity-Component

Component based / EC

Entity-Component

Забележка: Тука е малко мъгляво дали EC е правилният термин, аз ще го използвам за архитектурата която съм описал по-горе.

Component based / EC

Entity-Component

Забележка: Тука е малко мъгляво дали EC е правилният термин, аз ще го използвам за архитектурата която съм описал по-горе.
Много ресурси представят EC и ECS като различни начини да се имплементира архитектура базирана на компоненти, но имплементацията и поведението им е доста различно затова аз ще ги разграничавам.

EC - Entity

1 2 3 4 5 6 7 8 9 10 11
struct Ferris {
    rendering: RenderingComponent,
    position: PositionComponent,
    shooting: ShootingComponent,
}

// или

struct Entity {
    components: Vec<Box<dyn Component>>,
}

EC - Component

1 2 3 4 5 6 7 8 9 10
struct PositionComponent {
    pos: Vector2<f32>,
    vel: Vector2<f32>,
}

impl Component for PositionComponent {
    fn update(&mut self) {
        self.pos += self.vel * delta_time();
    }
}

EC

1 2 3 4 5 6 7 8 9 10 11
for c in components {
    c.update();
}

// или

for e in entities {
    for c in e.components() {
        c.update();
    }
}

ECS

Entity-Component-System

ECS

Entity-Component-System

ECS

Entity-Component-System

ECS

Entity-Component-System

ECS

Entity-Component-System

ECS - Entity

ECS - Entity

ECS - Component

При ECS данните се съдържат в структура наподобяваща релационна база даани.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// например
let components = Components {
    position: vec![
        (EntityId(0), PositionComponent { pos: .., vel: .. }),
        (EntityId(1), PositionComponent { pos: .., vel: .. }),
        (EntityId(4), PositionComponent { pos: .., vel: .. }),
    ],
    rendering: vec![
        (EntityId(0), RenderingComponent::Sprite("ferris_shooting.png")),
        (EntityId(1), RenderingComponent::Text("Undefined behaviour")),
        (EntityId(4), RenderingComponent::Sprite("pew.png")),
    ],
    shooting: vec![
        (EntityId(0), ShootingComponent { cooldown_remaining: 0.0, cooldown_rate: 1.0 }),
    ],
    enemies: vec![
        (EntityId(1), Enemy {}),
    ],
}

ECS - Component

ECS - Component

ECS - Component

ECS - Component

ECS - Component

ECS - Component

ECS - System

ECS - System

Specs

Specs Parallel ECS

ECS библиотека създадена като част от Amethyst game engine-а.
Но може да се използва и самостоятелно.

Specs - Component

Деклариране на компонент

1 2 3 4 5 6 7 8 9
use specs::{prelude::*};

struct Pos([f32; 2]);

impl Component for Pos {
    // В каква структура ще се пазят стойностите на компонента.
    // Може да се променя като оптимизация.
    type Storage = VecStorage<Self>;
}
fn main() {}
extern crate specs;
use specs::{prelude::*};

struct Pos([f32; 2]);

impl Component for Pos {
    // В каква структура ще се пазят стойностите на компонента.
    // Може да се променя като оптимизация.
    type Storage = VecStorage;
}

Specs - Component

Използвайки features = ["specs-derive"]

1 2 3 4 5
use specs::{prelude::*, Component};

#[derive(Component)]
#[storage(VecStorage)]  // default is `DenseVecStorage`
struct Pos([f32; 2]);
fn main() {}
extern crate specs;
use specs::{prelude::*, Component};

#[derive(Component)]
#[storage(VecStorage)]  // default is `DenseVecStorage`
struct Pos([f32; 2]);

Specs - Resource

Ресурсите в specs са подобни на компоненти с разликата че не са свързани с entity и може да съществува само една стойност от този тип. С други думи са като singleton стойност която се управлява от specs.

Ресурс може да е всеки тип T: 'static + Send + Sync + Any

Примери:
- активна камера
- часовник
- …

Specs - System

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
#[derive(Component)] struct Pos(f32);
#[derive(Component)] struct Vel(f32);
struct DeltaTime(f32);

struct MovementSys;

impl<'a> System<'a> for MovementSys {
    // Данните, които тази система използва
    type SystemData = (
        WriteStorage<'a, Pos>,      // компонент `Pos`
        ReadStorage<'a, Vel>,       // компонент `Vel`
        ReadExpect<'a, DeltaTime>,  // ресурс `DeltaTime`
    );

    fn run(&mut self, data: Self::SystemData) {
        let (mut pos, vel, dt) = data;
        let DeltaTime(dt) = *dt;

        // Използваме `.join()` за да изберем само тези ентитита,
        // които имат и двата компонента.
        for (pos, vel) in (&mut pos, &vel).join() {
            pos.0 += vel.0 * dt;
        }
    }
}
fn main() {}
extern crate specs;
use specs::{prelude::*, Component};
#[derive(Component)] struct Pos(f32);
#[derive(Component)] struct Vel(f32);
struct DeltaTime(f32);

struct MovementSys;

impl<'a> System<'a> for MovementSys {
    // Данните, които тази система използва
    type SystemData = (
        WriteStorage<'a, Pos>,      // компонент `Pos`
        ReadStorage<'a, Vel>,       // компонент `Vel`
        ReadExpect<'a, DeltaTime>,  // ресурс `DeltaTime`
    );

    fn run(&mut self, data: Self::SystemData) {
        let (mut pos, vel, dt) = data;
        let DeltaTime(dt) = *dt;

        // Използваме `.join()` за да изберем само тези ентитита,
        // които имат и двата компонента.
        for (pos, vel) in (&mut pos, &vel).join() {
            pos.0 += vel.0 * dt;
        }
    }
}

Specs - System

Specs - System

Specs - System

Specs - System

Specs - Dispatcher

Specs - Dispatcher

Specs - Dispatcher

Specs - Dispatcher

Specs - Demo

https://github.com/sunjay/rust-simple-game-dev-tutorial

Библиотеки и игрови двигатели

Amethyst

Библиотеки и игрови двигатели

Amethyst

Библиотеки и игрови двигатели

Amethyst

Библиотеки и игрови двигатели

Amethyst

Библиотеки и игрови двигатели

Amethyst

Библиотеки и игрови двигатели

Amethyst

Забележка: Аметист е мощен, но като всеки голям проект изисква време докато човек научи концепциите му. Не си избирайте да правите краен проект на аметист защото ще си загубите времето в учене на API.

Библиотеки и игрови двигатели

Kiss3d

Библиотеки и игрови двигатели

Kiss3d

Библиотеки и игрови двигатели

Kiss3d

Библиотеки и игрови двигатели

Kiss3d

Библиотеки и игрови двигатели

Kiss3d

Библиотеки и игрови двигатели

Kiss3d

Библиотеки и игрови двигатели

Kiss3d

Библиотеки и игрови двигатели

Kiss3d

Въпроси