前回は、こんなコードを書きました
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 関数でベクタにコンポーネントを追加するのをやめる。この場合はコンポーネントを追加する関数を別に定義する。
後者を選択します。
// ベクタにコンポーネントを追加するのをやめて、id に +1 するだけにした
fn create_entity (id: &mut usize) {
*id += 1;
}
// ベクタにコンポーネントを追加する
fn add_component <T> (v: &mut Vec<T>, data: T) {
v.push(data)
}
呼び出し側も変更します。
// 効果を確かめるために、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コンポーネントをのみをもつエンティティの混在ができません。
例えば、こんなコードはエラーになります。
// ひとつのエンティティにつきコンポーネントを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)
次回は、これを解決します。
最後に、コードの全体を記載して終わります。
※このコードは、前述のエラーを出力します。
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;
}