前回は、こんなコードを書きました。
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;
}