0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ゼロからEntityComponentSystemを書いてみる 7

Posted at

前回は、こんなコードを書きました。

main.rs
use rand;

#[derive(Debug)]
struct Position {
    x: i32,
    y: i32,
}

#[derive(Debug)]
struct Velocity {
    x: i32,
    y: i32,
}

fn main() {
    
    // idは配列の要素数を表す
    let mut id = 0;

    // 配列をやめて、ベクタで宣言する
    // Position構造体のベクタ
    let mut pos_ary: Vec<Position> = vec![];  

    // Velocity構造体のベクタ
    let mut vel_ary: Vec<Velocity> = vec![];
    
    // 効果を確かめるために、10回 create_entity を呼び出す
    // ひとつのエンティティにつきコンポーネントを1つ追加する
    for i in 0..10 {
        // ランダムな値を取得する
        let (x, y) = rand::random();
        create_entity(&mut id);
        add_component(&mut pos_ary, Position { x, y });
    }

    for i in 0..10 {
        let (x, y) = rand::random();
        create_entity(&mut id);
        add_component(&mut vel_ary, Velocity{ x, y });
    }


    // ベクタの要素を出力する
    for i in 0..id  {
        println!("{}: {:?}", i, pos_ary[i]);
        println!("{}: {:?}", i, vel_ary[i]);
    }
}

// ベクタにコンポーネントをを追加するのをやめて、id に +1 するだけにした
fn create_entity (id: &mut usize) {
    *id += 1;
}

// ベクタにコンポーネントを追加する
fn add_component <T> (v: &mut Vec<T>, data: T) {
    v.push(data)
}

// ジェネリックなデータ型を引数に取る set_data 関数
fn generic_set_data <T> (id: &usize, v: &mut Vec<T>, data: T) {
    v[*id] = data;
}

このコードは、Positionコンポーネントのみを持つエンティティと、Velocityコンポーネントをのみをもつエンティティの混在がでないという問題を抱えています。

この問題の解決策は、4つ考えられます。

1.コンポーネントに id 変数を宣言する。id と forループの i が一致するとき、i 番目の要素を出力する。
2.ハッシュマップを使う。
3.BTreeMapを使う。
4.Optionを使う

ハッシュマップを採用します。

まず、コンポーネントのベクタをハッシュマップに変更します。

main.rs
    // 配列をやめて、ベクタで宣言する
    // Position構造体のベクタ
    let mut pos_ary: HashMap<u32, Position> = HashMap::new();  

    // Velocity構造体のベクタ
    let mut vel_ary: HashMap<u32, Velocity> = HashMap::new();

関数も変更します。

main.rs
// id に +1 する
fn create_entity (id: &mut u32) {
    *id += 1;
}

// ハッシュマップにコンポーネントを追加する
fn add_component <T> (id: u32, v: &mut HashMap<u32, T>, data: T) {
    v.insert(id, data);
}

各要素の値を出力するコードも変更します。

main.rs
    // 各要素の値を出力する
    for i in 0..id  {
        let pos = pos_ary.get(&i);
        let vel = vel_ary.get(&i);
        
        println!("{}: {:?}", i, pos);
        println!("{}: {:?}", i, vel);
    }

結果を見てみましょう。

0: None
0: None
1: Some(Position { x: 1536936817, y: -881105329 })
1: None
2: Some(Position { x: -2040585312, y: -551881425 })
2: None
3: Some(Position { x: 789773711, y: 815499931 })
3: None
4: Some(Position { x: 200650208, y: -83080655 })
4: None
5: Some(Position { x: -1903167806, y: -1053880685 })
5: None
6: Some(Position { x: -648517341, y: 889193868 })
6: None
7: Some(Position { x: 1998793074, y: 274351345 })
7: None
8: Some(Position { x: -1217186116, y: 275993654 })
8: None
9: Some(Position { x: 583914829, y: 1490226212 })
9: None
10: Some(Position { x: 918494257, y: 1210460144 })
10: None
11: None
11: Some(Velocity { x: -1727294034, y: 946129579 })
12: None
12: Some(Velocity { x: 406257091, y: -356835914 })
13: None
13: Some(Velocity { x: -638588694, y: -504180474 })
14: None
14: Some(Velocity { x: 993214070, y: -1938126616 })
15: None
15: Some(Velocity { x: -974260676, y: -1990694451 })
16: None
16: Some(Velocity { x: -1469770398, y: -932279446 })
17: None
17: Some(Velocity { x: 1138711337, y: -322756406 })
18: None
18: Some(Velocity { x: 280498882, y: -1977411056 })
19: None
19: Some(Velocity { x: -2037751429, y: 1416591547 })

0 番目の要素が None になっています。
修正します。

create_entity 関数が u32型の値を返すようにします。

main.rs
fn create_entity (id: &mut u32) -> u32 {
    let key = *id;
    *id += 1;
    key
}

あわせて、呼び出し側も変更します。

main.rs
    // 効果を確かめるために、10回 create_entity を呼び出す
    // ひとつのエンティティにつきコンポーネントを1つ追加する
    for i in 0..10 {
        // ランダムな値を取得する
        let (x, y) = rand::random();
        let key = create_entity(&mut id);
        add_component(&key, &mut pos_ary, Position { x, y });
    }

    for i in 0..10 {
        let (x, y) = rand::random();
        let key = create_entity(&mut id);
        add_component(&key, &mut vel_ary, Velocity{ x, y });
    }

結果を確認します。

0: Some(Position { x: -839061235, y: -365847017 })
0: None
1: Some(Position { x: -1801046191, y: -770121788 })
1: None
2: Some(Position { x: -1507317072, y: 1248768014 })
2: None
3: Some(Position { x: 1271896141, y: -587799380 })
3: None
4: Some(Position { x: -627725508, y: 1485137160 })
4: None
5: Some(Position { x: 105356064, y: -1655941689 })
5: None
6: Some(Position { x: 249916924, y: 1070710514 })
6: None
7: Some(Position { x: 166956946, y: 367932017 })
7: None
8: Some(Position { x: -1775060107, y: 590920045 })
8: None
9: Some(Position { x: -124983509, y: 1691969561 })
9: None
10: None
10: Some(Velocity { x: -1414329386, y: -199053630 })
11: None
11: Some(Velocity { x: -1783384692, y: -895296214 })
12: None
12: Some(Velocity { x: 292302999, y: 1997326939 })
13: None
13: Some(Velocity { x: -1350633686, y: 321964089 })
14: None
14: Some(Velocity { x: -86689530, y: -1506862968 })
15: None
15: Some(Velocity { x: 1384602762, y: -1200089883 })
16: None
16: Some(Velocity { x: 999537713, y: 683206156 })
17: None
17: Some(Velocity { x: 1648641932, y: -189889895 })
18: None
18: Some(Velocity { x: 118258545, y: 358352168 })
19: None
19: Some(Velocity { x: -59912852, y: 752216025 })

0 番目の要素も問題なく出力されました。

もう一つ確認で、PositionとVelocityの両方を持つエンティティを作ってみます。
さすがに全部で30回エンティティを作る必要はないので、それぞれ2回づつにします。

main.rs
    // 効果を確かめるために、2回づつ create_entity を呼び出す
    // Positionコンポーネントのみ
    for i in 0..2 {
        let (x, y) = rand::random();
        let key = create_entity(&mut id);
        add_component(&key, &mut pos_ary, Position { x, y });
    }

    // Velocityコンポーネントのみ
    for i in 0..2 {
        let (x, y) = rand::random();
        let key = create_entity(&mut id);
        add_component(&key, &mut vel_ary, Velocity{ x, y });
    }

    // PositionとVelocityの両方
    for i in 0..2 {
        let (x ,y) = rand::random();
        let key = create_entity(&mut id);
        add_component(&key, &mut pos_ary, Position { x, y });
        add_component(&key, &mut vel_ary, Velocity { x, y })
    }

結果を見てみます。

0: Some(Position { x: -734287718, y: 1496360554 })
0: None
1: Some(Position { x: 103039172, y: -281049534 })
1: None
2: None
2: Some(Velocity { x: 1944417521, y: 1142856997 })
3: None
3: Some(Velocity { x: 37530280, y: 1275998568 })
4: Some(Position { x: -2029445904, y: 1119008877 })
4: Some(Velocity { x: -2029445904, y: 1119008877 })
5: Some(Position { x: 1707604226, y: 574752548 })
5: Some(Velocity { x: 1707604226, y: 574752548 })

問題なく出力されました。

最後にコードの全体を記載して終わります。

main.rs
use std::collections::HashMap;

use rand;

#[derive(Debug)]
struct Position {
    x: i32,
    y: i32,
}

#[derive(Debug)]
struct Velocity {
    x: i32,
    y: i32,
}

fn main() {
    
    // idは配列の要素数を表す
    let mut id = 0;

    // ベクタをやめて、ハッシュマップで宣言する
    // Position構造体のハッシュマップ
    let mut pos_ary: HashMap<u32, Position> = HashMap::new();  

    // Velocity構造体のハッシュマップ
    let mut vel_ary: HashMap<u32, Velocity> = HashMap::new();
    
    // 効果を確かめるために、2回づつ create_entity を呼び出す
    // Positionコンポーネントのみ
    for i in 0..2 {
        let (x, y) = rand::random();
        let key = create_entity(&mut id);
        add_component(&key, &mut pos_ary, Position { x, y });
    }

    // Velocityコンポーネントのみ
    for i in 0..2 {
        let (x, y) = rand::random();
        let key = create_entity(&mut id);
        add_component(&key, &mut vel_ary, Velocity{ x, y });
    }

    // PositionとVelocityの両方
    for i in 0..2 {
        let (x ,y) = rand::random();
        let key = create_entity(&mut id);
        add_component(&key, &mut pos_ary, Position { x, y });
        add_component(&key, &mut vel_ary, Velocity { x, y })
    }


    // 各要素の値を出力する
    for i in 0..id  {
        let pos = pos_ary.get(&i);
        let vel = vel_ary.get(&i);
        
        println!("{}: {:?}", i, pos);
        println!("{}: {:?}", i, vel);
    }
}

// id に +1 して作成したエンティティを返す
fn create_entity (id: &mut u32) -> u32 {
    let key = *id;
    *id += 1;
    key
}

// ハッシュマップにコンポーネントを追加する
fn add_component <T> (id: &u32, v: &mut HashMap<u32, T>, data: T) {
    v.insert(*id, data);
}

// ジェネリックなデータ型を引数に取る set_data 関数
fn generic_set_data <T> (id: &usize, v: &mut Vec<T>, data: T) {
    v[*id] = data;
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?