#90 蓝图简化技巧   bp     Coordinate System     UE4     4 months ago (owner) Document
 射线
可以在Pawn里设置一根专用的拾取射线,检测频率20Hz即可:拾取结果需要缓存下来,以供其他模块查询。结果包含:指向的对象类型(UCLASS)、对象引用. 同时,应该实现一个手柄Ray函数,输入是射线长度,输出是射线的Start(world)、End(world)、Direction(world)、LocalDirection,方便后续调用发出射线。如果有某些情境下需要修改射线属性的操作,则还需要增加这些接口。因此总结起来,有一个射线类,属性有:射线类型(single obj、multiple objs、channels)、ignore objects、check types/channels、check result(上面说的两个属性:UCLASS、UObject/AActor/UActorComponent引用等)
 获取可以动态创建的对象
如果动态创建对象在应用场景里是允许的,那么就应该动态创建对象,尽量不要在BeginPlay或其他Init里将其初始化,凡是要初始化时,语义就是离散的了,思维不集中。典型的处理方式是:定义一个Query/GetXXX函数,该函数返回一个XXX实例。函数的实现是先检测XXX的存在性,若存在则直接返回,若不存在则先创建并初始化后再返回。这里还有个细节可以优化一下:如果XXX实例是蓝图Context的,可以用Macro来实现这个函数,并设置两根输出,一个then,表示返回的实例有效,一个Fail,表示XXX实例创建失败。如果XXX实例是Global Context的,则没有更好的办法了,需要在QueryXXX的调用处额外判断一下XXX实例的有效性。
 对象的显隐
这种,一般情况下,在对象显示时,还需要设定其正确的位置和姿态。而隐藏时,最好将对象置于z=-100的地下,避免对WidgetInteraction和射线检测造成副作用。解决这个问题的最好方式是:创建一个函数一次性解决这些问题,函数名叫SetHiddenXXX(obj, hidden)。函数第一件事有可能是检测对象存在性,不存在则先创建对象。在对象存在的前提下,再检测hidden,若为true,则hide对象先,并紧接着调用SetWorldLocation(0,0,-100)将其深埋地下。若hidden为false,则先计算对象的正确位置和姿态,然后将该对象变换到目标位置和目标姿态,然后show出对象。
 强化版FindLookAtRotation
原生的Find Look at Rotation返回的是3D自由空间的Rotation,强化版需要指定参数是否约束Pitch,这个需求很多时候都要用。(keyword: lookat) 实现很简单:(TargetLocation - SourceLocation) * (1,1,0) -> Make Rot From X即可。或者(SourceLocation - TargetLocation) * (-1,-1,0) -> Make Rot From X
 计算TargetLocation
典型的需求描述有2种:
  • 已知一个对象的变换T0,另一个对象的目标位置以相对于已知对象的世界系vector描述,那么TargetLocation就是T0.WorldLocation + delta
  • 已知一个对象的变换T0,另一个对象的目标位置以相对于已知对象的局部系vector描述,这个一般分两种:
    • 自由式:用T0 * delta,或者分开写T0.WorldLocation + (T0.WorldRotation * delta)
    • 水平约束式:T0.WorldLocation +{ [T0.WorldRotation.GetForwardVector() * (1,1,0) -> Make Rot From X] * delta}
 同时计算TargetLocation和TargetRotation
把上面的强化版FindLookAtRotation和计算TargetLocation组合成一个函数即可
 已知向量的局部系表示,求其世界系表示
Parent.T * delta
 已知向量的世界系表示,求其局部系表示
Parent.T.Inverse * delta. 类似的有Parent.Rotation.UnrotateVector(delta)
 sequence播放长度问题
有时很多需求都是在特定时间点,用sequence来实现的。这种应用场景,典型的问题是后面的逻辑,即后面的逻辑依赖sequence播完后来做或者播放多长时间后来做,这本身没什么问题。 问题是这些时间都是在蓝图里硬编码的,不利于需求频繁变更时的快速调整。解决这个问题应该制作一个sequence播放管理器集中管理全部的sequence播放。 这个Manager应该提供sequence注册功能,注册的参数要表明多长时间后要触发什么事件(即便如需要强制停止播放的操作也可以通过抛出事件,在事件里停止播放来实现), 抛出的事件里要能区分注册者、sequence身份,事件名字,事件参数。典型的实现:实现一个全局ManagerActor,每个sequence register时都设置定时器,内部tick检测注册列表中每个sequence的播放状态(unplay、playing、pause、stop、playcount)。 ManagerActor提供一个EventDispatcher,参数是(UObject、sequence、event_name),每处向ManagerActor注册sequence的地方,都要Bind Event到ManagerActor的EventDispatcher,并在事件实现里首先通过UObject参数检测event_name是否自己注册的。