减少实体组件系统中的 Duck-typing 劣势
reduce Duck-typing disadvantage in entity-component-system
如何减少实体组件系统中的 Duck-typing 现象?
例子
这是一个coliru demo.
我的 ECS 中有 2 个系统 :-
System_Projectile
: 管理所有射弹和子弹方面。
System_Physic
: 管理物理组件。
有 2 个组件类型:Com_Projectile
, Physics
.
有时,我发现在某些组件中缓存指向另一个实体的指针是很好的:-
class Com_Projectile : public Component{
public:
Entity* physic;
Entity* physicSecondary; //just to show that it is possible to have >1 physic
};
如果我想改变 Com_Projectile
的位置,我会调用 manage(Com_Projectile::physic)
。
class System_Projectile{
public: static void manage(Entity* projectile){
Com_Projectile* comP = getComponent<Com_Projectile>(projectile);
//suffer duck-typing at "comP->physic"
System_Physic::setVelocity(comP->physic,Vec3(1,0,0));
}
};
问题
基于上述片段的真实程序运行正常。
但是,在编码时,Com_Projectile::physic
遭受鸭子打字。
- 我没有得到关于
physic
类型的 C++ 语义线索。
(变量名和注释除外)
因此,我必须意识到。
编码员对类型的误解只会在 运行 时被发现。
在实践中,这种错误很少发生。
我必须记住可以做我想做的事情的系统名称 (System_Physic::
),
然后回忆函数的名称(在本例中为 System_Physic::setVelocity()
)。
- 综上所述,我的大脑有很多间接。
在我过去的日子里,当我使用很多(深)继承时,它要容易得多,就像这样:-
physic->setVelocity(Vec3(1,0,0));
我真的很想念那些列出所有与物理相关的函数的可爱的内容帮助。
问题
如何减少ECS系统某部分的duck-typing?
更具体地说,什么是再次启用可爱内容辅助的设计模式?
我目前的解决方法
让Com_Projectile
缓存Physic* physic
而不是Entity*
:-
class Com_Projectile{
public: Physics* physic; //edited from "Entity* physic"
};
缺点:-
- 它会促进不需要的 (?) 耦合。
- 我必须在
Com_Projectile.h
内转发声明 Physics
。
- 我必须将复杂函数(例如
setVelocity()
)从系统(例如 Sys_Physic::
)移动到组件(例如 Physics::
)中。
- 总的来说,我打破了实体组件系统的宗教信仰
-> 我可能会在某些方面受到惩罚(?)。
How to reduce duck-typing in some certain part of ECS system?
More specifically, what is a design pattern to enable the cute content-assist again?
一个想法是将您的组件实现视为您与系统交互的管道。无论如何,这就是他们的意图,成为一种影响行为的数据驱动方式。
class Physics : public Component<Physics> {
public:
Vector3 GetVelocity() const;
void SetVelocity(const Vector3& velocity);
private:
Vector3 velocity_;
}
现在为了设置速度,只需调用:
Physics* physics = getComponent<Physics>( projectile->physic );
if ( physics )
physics->SetVelocity( Vector3( 1, 0, 0 ) );
然后物理系统的工作是获取物理组件上的速度并将其与任何其他数据属性一起应用于内部物理模拟。
换句话说,将系统的输入状态视为当前组件值和先前系统发出的任何其他可变状态的组合。
除了避免您提到的鸭式输入之外,您最终得到的代码更容易理解,也更容易流动。它还打开了一扇门,很容易被脚本系统和其他外部影响者通过操纵 getter/setters 对您的组件进行变异。
如何减少实体组件系统中的 Duck-typing 现象?
例子
这是一个coliru demo.
我的 ECS 中有 2 个系统 :-
System_Projectile
: 管理所有射弹和子弹方面。
System_Physic
: 管理物理组件。
有 2 个组件类型:Com_Projectile
, Physics
.
有时,我发现在某些组件中缓存指向另一个实体的指针是很好的:-
class Com_Projectile : public Component{
public:
Entity* physic;
Entity* physicSecondary; //just to show that it is possible to have >1 physic
};
如果我想改变 Com_Projectile
的位置,我会调用 manage(Com_Projectile::physic)
。
class System_Projectile{
public: static void manage(Entity* projectile){
Com_Projectile* comP = getComponent<Com_Projectile>(projectile);
//suffer duck-typing at "comP->physic"
System_Physic::setVelocity(comP->physic,Vec3(1,0,0));
}
};
问题
基于上述片段的真实程序运行正常。
但是,在编码时,Com_Projectile::physic
遭受鸭子打字。
- 我没有得到关于
physic
类型的 C++ 语义线索。
(变量名和注释除外) 因此,我必须意识到。
编码员对类型的误解只会在 运行 时被发现。
在实践中,这种错误很少发生。我必须记住可以做我想做的事情的系统名称 (
System_Physic::
),
然后回忆函数的名称(在本例中为System_Physic::setVelocity()
)。- 综上所述,我的大脑有很多间接。
在我过去的日子里,当我使用很多(深)继承时,它要容易得多,就像这样:-
physic->setVelocity(Vec3(1,0,0));
我真的很想念那些列出所有与物理相关的函数的可爱的内容帮助。
问题
如何减少ECS系统某部分的duck-typing?
更具体地说,什么是再次启用可爱内容辅助的设计模式?
我目前的解决方法
让Com_Projectile
缓存Physic* physic
而不是Entity*
:-
class Com_Projectile{
public: Physics* physic; //edited from "Entity* physic"
};
缺点:-
- 它会促进不需要的 (?) 耦合。
- 我必须在
Com_Projectile.h
内转发声明Physics
。 - 我必须将复杂函数(例如
setVelocity()
)从系统(例如Sys_Physic::
)移动到组件(例如Physics::
)中。 - 总的来说,我打破了实体组件系统的宗教信仰
-> 我可能会在某些方面受到惩罚(?)。
How to reduce duck-typing in some certain part of ECS system?
More specifically, what is a design pattern to enable the cute content-assist again?
一个想法是将您的组件实现视为您与系统交互的管道。无论如何,这就是他们的意图,成为一种影响行为的数据驱动方式。
class Physics : public Component<Physics> {
public:
Vector3 GetVelocity() const;
void SetVelocity(const Vector3& velocity);
private:
Vector3 velocity_;
}
现在为了设置速度,只需调用:
Physics* physics = getComponent<Physics>( projectile->physic );
if ( physics )
physics->SetVelocity( Vector3( 1, 0, 0 ) );
然后物理系统的工作是获取物理组件上的速度并将其与任何其他数据属性一起应用于内部物理模拟。
换句话说,将系统的输入状态视为当前组件值和先前系统发出的任何其他可变状态的组合。
除了避免您提到的鸭式输入之外,您最终得到的代码更容易理解,也更容易流动。它还打开了一扇门,很容易被脚本系统和其他外部影响者通过操纵 getter/setters 对您的组件进行变异。