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を書いてみる 6

Last updated at Posted at 2023-05-04

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

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 を呼び出す
    for i in 0..10 {
        // ランダムな値を取得する
        let (x, y) = rand::random();
        generic_create_entity(&mut id, &mut pos_ary, Position { x, y });
        generic_create_entity(&mut id, &mut vel_ary, Velocity { x, y });
    }


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

// ジェネリックなデータ型を引数に取る create_entity 関数
fn generic_create_entity <T> (id: &mut usize, v: &mut Vec<T>, data: T) {
    v.push(data);
    *id += 1;
}

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

このコードは、ベクタの範囲外にアクセスしてしまう実行時エラーが発生します。

原因は、forでループするごとに2回 generic_create_entity 関数を呼び出しているからです。

generic_create_entity 関数は内部で id を +1 するようになっています。
そうすることで、id とベクタの要素数が一致するようにしていました。
しかし、2つコンポーネントを持つときに前提が崩れます。

さて、解決策は2つ考えられます。

generic_create_entity 関数の外で id を +1する。
generic_create_entiry 関数でベクタにコンポーネントを追加するのをやめる。この場合はコンポーネントを追加する関数を別に定義する。

後者を選択します。

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

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

呼び出し側も変更します。

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

Cargo runをして、動くか確認します。

0: Position { x: -660079295, y: 1825477436 }
3: Velocity { x: 1925488918, y: 867039476 }
4: Position { x: -580269154, y: 2063526753 }
4: Velocity { x: -580269154, y: 2063526753 }
5: Position { x: 814984666, y: 1106294742 }
5: Velocity { x: 814984666, y: 1106294742 }
6: Position { x: 1538517374, y: 1132620795 }
6: Velocity { x: 1538517374, y: 1132620795 }
7: Position { x: 497063939, y: -126158228 }
7: Velocity { x: 497063939, y: -126158228 }
8: Position { x: -853418764, y: 743967430 }
8: Velocity { x: -853418764, y: 743967430 }
9: Position { x: 1599348111, y: 563693441 }
9: Velocity { x: 1599348111, y: 563693441 }

エラーは、発生しませんでした。
めでたしめでたし、とはなりません。
というのも、Positionコンポーネントのみを持つエンティティと、Velocityコンポーネントをのみをもつエンティティの混在ができません。

例えば、こんなコードはエラーになります。

main.rs
    // ひとつのエンティティにつきコンポーネントを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 });
     }

エラー内容は、ベクタの範囲外にアクセスするといったものです。

0: Position { x: -140031214, y: 1932431071 }
0: Velocity { x: -1797666031, y: -1399117212 }
1: Position { x: 1807449926, y: 385782984 }
1: Velocity { x: 1789379074, y: 228539768 }
2: Position { x: 1036897023, y: 724644213 }
2: Velocity { x: 1996904484, y: -975977116 }
3: Position { x: -1873213305, y: 1334489366 }
3: Velocity { x: 1436019688, y: -270224867 }
4: Position { x: -543927070, y: -495643909 }
4: Velocity { x: 165570458, y: -1878530085 }
5: Position { x: 672617972, y: -55078610 }
5: Velocity { x: 1498926927, y: -1492524075 }
6: Position { x: -938536916, y: 482969888 }
6: Velocity { x: -733716627, y: -1553570199 }
7: Position { x: 2018722633, y: -923909018 }
7: Velocity { x: -1317984055, y: -1380790659 }
8: Position { x: 791429712, y: 58472768 }
8: Velocity { x: -1858233802, y: 10838087 }
9: Position { x: 1906118180, y: 1435231638 }
9: Velocity { x: 222468546, y: -430294130 }
thread 'main' panicked at 'index out of bounds: the len is 10 but the index is 10', src\main.rs:45:33
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\EntitiyCompornentSystem-Rust.exe` (exit code: 101)

次回は、これを解決します。

最後に、コードの全体を記載して終わります。
※このコードは、前述のエラーを出力します。

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;
}
0
0
1

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?