コンポーネント
概念
名札的なもの。データを持たせることができる。
使い方
作り方
Componentアトリビュートを構造体に付与するだけです。
use ecson::prelude::*;
#[derive(Component)]
struct User(String);
#[derive(Component)]
struct Velocity {
x: f32,
y: f32,
} アタッチ方法
1. エンティティ生成時
spawnの引数です
commands.spawn(User("hino".to_string())) 2. 後付け
insertを使います
fn add_component_system(
mut commands: Commands,
query: Query<Entity, With<User>>,
) {
for entity in &query {
// Velocity コンポーネントを付ける
commands.entity(entity).insert(Velocity { x: 1.0, y: 0.0 })
}
} すでに同じ型のコンポーネントがついている場合、
insertを使うと新しい値で上書きされます。
外し方
removeを使います
fn remove_component_system(
mut commands: Commands,
query: Query<Entity, With<Velocity>>,
) {
for entity in &query {
// Velocity コンポーネントを外す
commands.entity(entity).remove::<Velocity>();
}
} 複数のコンポーネントを一度にまとめて
全てにコンポーネント付け外し操作には、タプルを使ってまとめて指定できます
commands.spawn((ComponentA, ComponentB));
commands.entity(entity).insert((ComponentA, ComponentB));
commands.entity(entity).remove::<(ComponentA, ComponentB)>(); またはメソッドチェーンも使えます。可読性や条件分岐によって使い分けてください。
commands.entity(entity)
.insert(ComponentA)
.insert(ComponentB); ポイント
- コアコンセプトでも触れた通り、設計上コンポーネントの付け外しはスケジュールのタイミングで一括適用されます。
- 頻発すぎるコンポーネントの付け外しは、メモリレイアウトの再計算が発生するため、
boolやEnumをうまく使用しましょう。 - 実行対象を分けたいだけなら、
struct Room;など、ユニット構造体をマーカーとして使いましょう。
組み込みコンポーネント
ここからは、直接触れることのできる組み込みコンポーネントを紹介します。
コアコンポーネント
ecson::prelude::* に含まれており、追加の設定なしで使用できます。
ClientId
クライアントを一意に識別するネットワークIDです。接続が確立されると自動的に付与されます。
#[derive(Component, Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ClientId(pub u64); すべての接続エンティティが持つコンポーネントです。Query のフィルターとして使うことで、接続中のクライアントだけを対象にできます。
fn my_system(query: Query<(Entity, &ClientId)>) {
for (entity, client_id) in query.iter() {
println!("接続中: {}", client_id.0);
}
} Room
クライアントが現在所属しているルーム名を表します。ChatCorePlugin / ChatFullPlugin を使う場合は自動で付与・削除されますが、手動で操作することも可能です。
#[derive(Component, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Room(pub String); Option<&Room> でクエリすることで、ルームに入っているかどうかを確認できます。
fn my_system(query: Query<(Entity, &ClientId, Option<&Room>)>) {
for (entity, client_id, room) in query.iter() {
match room {
Some(r) => println!("{} はルーム '{}' にいます", client_id.0, r.0),
None => println!("{} はどのルームにも属していません", client_id.0),
}
}
} Username
クライアントの表示名(ニックネーム)を保持します。ChatCorePlugin / ChatFullPlugin を使う場合は /nick コマンドで自動的に付与されますが、任意のタイミングで commands.entity(e).insert(Username(...)) で付与・更新できます。
#[derive(Component)]
pub struct Username(pub String); fn my_system(query: Query<(&ClientId, Option<&Username>)>) {
for (client_id, username) in query.iter() {
let name = username.map(|u| u.0.as_str()).unwrap_or("名無し");
println!("{}: {}", client_id.0, name);
}
} Presenceコンポーネント
PresencePlugin を追加すると使用できます。
PresenceStatus
クライアントの在席状態を表します。PresencePlugin によって自動的に管理されますが、クエリして参照することができます。
#[derive(Component, Clone, Debug, PartialEq, Eq, Default)]
pub enum PresenceStatus {
#[default]
Online,
Away,
Busy,
} クライアントは /status online / /status away / /status busy を送信することで状態を変更できます。
fn my_system(query: Query<(&ClientId, &PresenceStatus)>) {
for (client_id, status) in query.iter() {
println!("{}: {}", client_id.0, status); // "online" / "away" / "busy"
}
} Snapshotコンポーネント
SnapshotPlugin を追加すると使用できます。
Snapshotable
このコンポーネントが付与されたエンティティは、スナップショットの収集対象になります。マーカーコンポーネントなのでフィールドはありません。
#[derive(Component)]
pub struct Snapshotable; // スナップショットに含めたいエンティティに付与する
commands.entity(entity).insert(Snapshotable); SnapshotSubscriber
スナップショットの送信先を絞り込むためのコンポーネントです。このコンポーネントを持つエンティティにのみスナップショットが送信されます。
#[derive(Component)]
pub struct SnapshotSubscriber; // スナップショットを受け取らせたいクライアントに付与する
commands.entity(client_entity).insert(SnapshotSubscriber); Lobbyコンポーネント
LobbyPlugin を追加すると使用できます。
InLobby
クライアントが参加しているロビー名を保持します。LobbyPlugin によって /lobby join コマンドで自動付与・削除されます。
#[derive(Component)]
pub struct InLobby(pub String); Option<&InLobby> でクエリして、クライアントがロビーに参加しているかを確認できます。
fn my_system(query: Query<(&ClientId, Option<&InLobby>)>) {
for (client_id, in_lobby) in query.iter() {
match in_lobby {
Some(l) => println!("{} はロビー '{}' にいます", client_id.0, l.0),
None => println!("{} はロビー未参加です", client_id.0),
}
}
} Spatialコンポーネント
Spatial2DPlugin / Spatial3DFlatPlugin / Spatial3DPlugin のいずれかを追加すると使用できます。
Position2D
2D空間上のクライアント座標(XY平面)です。Spatial2DPlugin が有効な場合、接続時に自動で付与されます。
#[derive(Component, Clone, Debug, Default)]
pub struct Position2D {
pub x: f32,
pub y: f32,
} Position3D
3D空間上のクライアント座標(XYZ)です。Spatial3DFlatPlugin / Spatial3DPlugin が有効な場合、接続時に自動で付与されます。
#[derive(Component, Clone, Debug, Default)]
pub struct Position3D {
pub x: f32,
pub y: f32,
pub z: f32,
} fn my_system(query: Query<(&ClientId, &Position2D)>) {
for (client_id, pos) in query.iter() {
println!("{} の座標: ({}, {})", client_id.0, pos.x, pos.y);
}
} Note:
SpatialZone2D/SpatialZone3Dもエンティティに付与されますが、これらはエンジン内部でAOIゾーン計算に使われるものです。通常は直接操作する必要はありません。