漫游行为就像它的名字一样,角色在场景中毫无目的的移动。这通常用来模拟巡视和觅食,也有纯粹是为了漫游而漫游的。
漫游行为在实现上不像听起来那么容易。简单的使用随机而产生的布朗运动,会让角色感觉像是一个有神经病的傻瓜。我们需要更自然更平滑的感觉。有个办法,通常 设想在角色前方有个圆,然后把圆上任意一点作为目标,每次更新都向这个随机点移动。由于目标点总是落在假象的圆上,所以转向力永远不会一下子就变化很大。
有几个参数可以调整出不同的漫游的风格:圆的尺寸,圆离开角色的距离,目标点的随机范围。
漫游函数:
代码语言:javascript复制 private double _wanderAngle = 0;
private double _wanderDistance = 10;
private double _wanderRadius = 5;
private double _wanderRange = 1;
public void wander() {
Vector2D center = velocity.clone().normalize().multiply(_wanderDistance);
Vector2D offset = new Vector2D(0, 0);
offset.length = _wanderRadius;
offset.angle = _wanderAngle;
Random randObj = new Random();
_wanderAngle = randObj.NextDouble() * _wanderRange - _wanderRange * 0.5;
Vector2D force = center.add(offset);
_steeringForce = _steeringForce.add(force);
}
一开始先通过单位化速度确定圆的中心点位于速度向量的正前方,然后乘以漫游距离,就是圆心的所在地。接着增加另一个偏移量来确定随机点。由于该点落在圆上,所以偏移量的长度等于圆的半径,偏移量的角度等于漫游角度。而漫游角度是根据漫游范围做适当的随机调整。接着把偏移量加于中心点就得到了变化所需要的力度向量。最后把这个力度叠加到转向力度上就可以了。
测试:
代码语言:javascript复制<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Steer" xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
mc:Ignorable="d"
x:Class="Steer.WanderTest"
d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot" Background="White">
<local:SteeredVehicle x:Name="myWander" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5">
<ed:RegularPolygon Fill="Blue" Height="40" InnerRadius="1" PointCount="3" Stretch="Fill" Stroke="Black" UseLayoutRounding="False" Width="40" RenderTransformOrigin="0.5,0.5" StrokeThickness="0">
<ed:RegularPolygon.RenderTransform>
<CompositeTransform Rotation="90"/>
</ed:RegularPolygon.RenderTransform>
</ed:RegularPolygon>
</local:SteeredVehicle>
</Grid>
</UserControl>
代码语言:javascript复制 public partial class WanderTest : UserControl
{
public WanderTest()
{
// Required to initialize variables
InitializeComponent();
Loaded = new RoutedEventHandler(WanderTest_Loaded);
}
void WanderTest_Loaded(object sender, RoutedEventArgs e)
{
myWander.position = new Vector2D(200, 200);
CompositionTarget.Rendering = new EventHandler(CompositionTarget_Rendering);
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
myWander.wander();
myWander.update();
}
}