游戏底层逻辑,运动&&寻路(二)

16 篇文章 1 订阅

目标驱动产生控制力

我们知道一个合理的物理世界的运动不是自发的,也不能是突发的。我们不能突然的moveTo。我们首先知道,由牛顿定理可知,物体的变速需要力的作用,而在游戏世界里,力不是由其他物体施加的,而是由目标驱动的,这一原则将是接下来运动的基本哲学。

运动物体

我们先抛开实体的详细设计,在控制力实现过程中逐步添加实体属性。
这里有一个简单的可运动物体的设计(省略了ctor&set&get):

MovingEntity.h

class MovingEntity :public BaseEntity
{

protected:
    Vec2 _velocity;//速度
    Vec2 _heading;//朝向
    Vec2 _side;//侧向,与朝向垂直
    double _mass;//质量
    double _maxSpeed;
    double _maxForce;
    double _maxTurnRate;//旋转速率(弧度每秒)

}

控制力

抛开可运动物体不谈,我们进入经典的SteeringForce设计。

虽然经典,但是可提升空间并不小,计算机哲学永远都有更先进的思维,绝不要被其所限制

  • Tips:此处我们模糊了很多物理概念,比如力和加速度,不要在意,后面会有说明。

  • Tips:吐槽一下,cocos的平面向量Vec2和点Point是用typedef声明的同一元素,单纯使用向量的时候感觉很坑。

1、Seek–靠近

实体将去追赶一个目标(以最快速度),很简单的逻辑,加速度=预期速度-当前速度。
这里写图片描述
具体代码:

Vec2 SteeringBehaviors::seek(const Vec2 target)
{
    Vec2 desiredVelocity = (target - _ownerVehicle->position()).getNormalized()*_ownerVehicle->maxSpeed();

    return (desiredVelocity - _ownerVehicle->velocity());
}
2、Flee–远离

和seek相反,直接代码

Vec2 SteeringBehaviors::flee(const Vec2 target)
{
    Vec2 desiredVelocity = (_ownerVehicle->position() - target).getNormalized()*_ownerVehicle->maxSpeed();

    return (desiredVelocity - _ownerVehicle->velocity());
}
3、Arrive–靠近+缓动

此运动函数的效果是很靠近目标时,速度变慢(很远时和seek效果一致),我们依然计算预期速度,预期速度和距离成正比即可。在这里我们用 [预期速度/约定时间] 来计算速度变化。

#define decelerationWeaker 0.3

enum Deceleration
{
    slow=3,
    normal=2,
    fast=1
};

Vec2 SteeringBehaviors::arrive(const Vec2 target, Deceleration dece)
{
    Vec2 toTarget = target - _ownerVehicle->position();

    double distance = toTarget.getLength();

    if (distance > 0.00001)
    {   
        //减速公式
        double speed = distance / (double)dece*decelerationWeaker;

        speed = std::min(_ownerVehicle->maxSpeed(), speed);

        Vec2 desiredVelocity = toTarget / distance*speed;

        return (desiredVelocity - _ownerVehicle->velocity());
    }
    else
        return Vec2::ZERO;
}
4、Pursuit–追逐

追逐行为要求目标为动态物体,然后我们计算目标的预期位置,从而去seek这个位置
有一种特殊情况,当两者相对前进时,我们就不用预测位置,直接向其移动即可。
这里写图片描述
预测的难点在于预测时间的估计,我们这里不难想象:

  • 这个时间正比于两者的距离,越远追赶上需要的时间越久

  • 反比于速度,此处为两者速度和,速度越快越快追上

Vec2 SteeringBehaviors::pursuit(const Vehicle* evader)
{
    Vec2 toEvader = evader->position() - _ownerVehicle->position();

    //dk how to descripe this angle,追踪者和逃亡者朝向的夹角
    double relativeHeading = _ownerVehicle->heading().dot(evader->heading());

    if (toEvader.dot(_ownerVehicle->heading()) > 0/*are they face towards each-other*/
        && (relativeHeading < -0.95))
    {
        return seek(evader->position());
    }
    //ahead time
    double lookAheadTime = toEvader.getLength() / (_ownerVehicle->maxSpeed() + evader->speed());

    return seek(evader->position() + evader->velocity()*lookAheadTime);
}
5、Evade逃避

evade算法和pursuit算法一致,不过无需计算夹角。

Vec2 SteeringBehaviors::evade(const Vehicle* pursuer)
{

    Vec2 toPursuer = pursuer->position() - _ownerVehicle->position();

    double lookAheadTime = toPursuer.getLength() / (_ownerVehicle->maxSpeed() + pursuer->speed());

    return flee(pursuer->position() + pursuer->velocity()*lookAheadTime);
}
6、Interpose插入中间

该运动描述的是物体跑向两个目标中间,同时也是预测位置。比如保镖跑到其老板和抢劫犯中间。
pursuit相同,时间预测是该算法的困难之处。这里我们用该公式近似得到时间预测:

  • 预测时间=物体到两目标中点的距离/物体最大速度

ok,这下子代码简单了

Vec2 SteeringBehaviors::interpose(const Vehicle* agent1, const Vehicle* agent2)
{
    Vec2 midPo = (agent1->position() + agent2->position()) / 2;

    //regard the time vehicle to mid point with max speed as prediction time
    double time2reachMidPo = Vec2(_ownerVehicle->position() - midPo).getLength() / _ownerVehicle->maxSpeed();

    //predict the position these two agent will be
    Vec2 prePosition1 = agent1->position() + agent1->velocity()*time2reachMidPo;
    Vec2 prePosition2 = agent2->position() + agent2->velocity()*time2reachMidPo;

    //use this var temporarily
    midPo = (prePosition1 + prePosition2) / 2.0;

    return arrive(midPo, fast);
}

我们可以想象,我们之前使用的时间预测算法都是为了照顾效率得出的折衷方案,完全可能有更好的方法来精准预测。

好了,今天先介绍基本的运动算法,接下来的算法涉及到障碍体,下回分晓。


准备写一个有关游戏底层算法,物理算法,以及AI(重点是机器学习在游戏中的应用)的长篇博客,欢迎大家指正交流╰( ̄▽ ̄)╯

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值