这是我的处理方法:
相机
我的相机是一个像其他实体一样的实体,它具有以下组成部分:
Transform
具有Translation
,Rotation
和Scale
属性,以及速度等其他属性。
Pov
(视点)已FieldOfView
,AspectRatio
,Near
,Far
,和其他任何需要产生的投影矩阵,除了一个IsOrtho
透视图和正投影之间用于切换标志。Pov
还提供了ProjectionMatrix
渲染系统使用的延迟加载属性,该属性在读取时在内部进行计算,并进行缓存,直到修改了其他任何属性。
没有专用的摄像头系统。渲染系统维护的列表,Pov
并包含用于确定渲染时使用哪个逻辑的逻辑。
输入值
的InputReceiver
部件可以连接到任何实体。它具有一个附加的事件处理程序(如果您的语言支持,则为lambda),该事件处理程序用于保存特定于实体的输入处理,该处理程序将获取当前和先前的键状态,当前和先前的鼠标位置以及按钮状态等参数。(实际上,有用于鼠标和键盘的单独处理程序)。
例如,在我习惯实体/组件时创建的类似Asteroids的测试游戏中,我有两个输入lambda方法。通过处理箭头键和空格键(用于发射)来处理船舶导航。另一个处理一般的键盘输入-退出,暂停等键,重新启动级别等键。我创建了两个组件,将每个lambda附加到其自己的组件,然后将导航接收器组件分配给船实体,另一个分配给船体。不可见的命令处理器实体。
这是事件处理程序,用于处理在附接到飞船InputReceiver
组件(C#)的框架之间保持的关键点:
void ship_input_Hold(object sender, InputEventArgs args)
{
var k = args.Keys;
var e = args.Entity;
var dt = (float)args.GameTime.ElapsedGameTime.TotalSeconds;
var verlet = e.As<VerletMotion>();
var transform = e.As<Transform>();
if (verlet != null)
{
/// calculate applied force
var force = Vector3.Zero;
var forward = transform.RotationMatrix.Up * Settings.ShipSpeedMax;
if (k.Contains(Keys.W))
force += forward;
if (k.Contains(Keys.S))
force -= forward;
verlet.Force += force * dt;
}
if (transform != null)
{
var theta = Vector3.Zero;
if (k.Contains(Keys.A))
theta.Z += Settings.TurnRate;
if (k.Contains(Keys.D))
theta.Z -= Settings.TurnRate;
transform.Rotation += theta * dt;
}
if (k.Contains(Keys.Space))
{
var time = (float)args.GameTime.TotalGameTime.TotalSeconds - _rapidFireLast;
if (time >= _rapidFireDelay)
{
Fire();
_rapidFireLast = (float)args.GameTime.TotalGameTime.TotalSeconds;
}
}
}
如果您的相机手机,给它自己InputReceiver
和Transform
组件,连接lambda和处理程序实现什么样的控制,你想,你就大功告成了。
这样做很巧妙,您可以将InputReceiver
带有导航处理程序的组件从船上移到小行星或其他任何东西上,然后绕开它。或者,通过将Pov
组件分配给场景中的任何其他对象(小行星,路灯等),您可以从该实体的角度查看场景。
InputSystem
维护键盘,鼠标等内部状态的类,将InputSystem
其内部实体集合过滤为具有InputReceiver
组件的实体。在其Update()
方法中,它循环访问该集合,并以与呈现系统使用一个Renderable
组件绘制每个实体相同的方式调用附加到每个组件的输入处理程序。
粒子
这实际上取决于您如何计划与粒子交互。如果您只需要一个行为像一个对象的粒子系统(例如,烟花显示玩家无法触摸或击打),那么我将创建一个实体,并创建一个ParticleRenderGroup
包含粒子所需信息的组件-衰减等- Renderable
组件未涵盖。渲染时,渲染系统将查看实体是否具有RenderParticleGroup
附件并相应地进行处理。
如果您需要单个粒子参与碰撞检测,响应输入等,但是您只想将它们渲染为批处理,则可以创建一个Particle
包含每个粒子的信息的组件,并将其创建为单独的实体。渲染系统仍可以批处理它们,但其他系统会将它们视为单独的对象。(这对于实例化非常有效。)
然后,在您MotionSystem
(或您用于处理更新实体位置等的所有操作中)或专用中ParticleSystem
,对每帧每个粒子执行所需的任何处理。该RenderSystem
会负责建设/配料和他们创建和销毁缓存颗粒的集合,并根据需要使它们。
这种方法的优点在于,您不必为粒子的碰撞,剔除等任何特殊情况。您为其他所有类型的实体编写的代码仍然可以使用。
结论
如果您考虑跨平台使用-不适用于JavaScript,则所有特定于平台的代码(即渲染和输入)都被隔离为两个系统。您的游戏逻辑保留在与平台无关的类(运动,碰撞等)中,因此在移植时不必触摸它们。
我理解并同意Sean的立场,即严格遵守该模式而不是对模式进行调整以满足您的应用程序需求,将鞋拔成规则是很不好的。我只是在输入,相机或粒子中看不到任何需要这种处理的东西。