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

接着上次的来,我们在群体算法之前把基本的个体运动解决掉。

9、WallAvoidance避开墙壁

此处的墙被抽象为一条线段,不论你的游戏使用的是一条线段作为墙面的碰撞检测,或者用一个几何形状作为墙面,几何形状我们可以看作多条线段的集合,都可以用此方法。

墙类的实现

首先是线段类,作为基类,拥有几种几何计算的方法,便于计算平面线段的交点,不多说。

struct Seg
{
    Seg(Point p1, Point p2):
        _from(p1), _to(p2)
    {

    }
#define eps 1e-6
    //static math functions
    static int sgn(double x)
    {
        return x<-eps ? -1 : (x>eps);
    }

    static double Cross(const Point& p1, const Point& p2, const Point& p3, const Point& p4)
    {
        return (p2.x - p1.x)*(p4.y - p3.y) - (p2.y - p1.y)*(p4.x - p3.x);
    }

    static double Area(const Point& p1, const Point& p2, const Point& p3)
    {
        return Cross(p1, p2, p1, p3);
    }

    static double fArea(const Point& p1, const Point& p2, const Point& p3)
    {
        return fabs(Area(p1, p2, p3));
    }

    static bool Meet(const Point& p1, const Point& p2, const Point& p3, const Point& p4)
    {
        return max(min(p1.x, p2.x), min(p3.x, p4.x)) <= min(max(p1.x, p2.x), max(p3.x, p4.x))
            && max(min(p1.y, p2.y), min(p3.y, p4.y)) <= min(max(p1.y, p2.y), max(p3.y, p4.y))
            && sgn(Cross(p3, p2, p3, p4) * Cross(p3, p4, p3, p1)) >= 0
            && sgn(Cross(p1, p4, p1, p2) * Cross(p1, p2, p1, p3)) >= 0;
    }

    static Point Inter(const Point& p1, const Point& p2, const Point& p3, const Point& p4)
    {
        double s1 = fArea(p1, p2, p3), s2 = fArea(p1, p2, p4);
        return Point((p4.x*s1 + p3.x*s2) / (s1 + s2), (p4.y*s1 + p3.y*s2) / (s1 + s2));
    }

    static double PointToSegDist(double x, double y, double x1, double y1, double x2, double y2)
    {
        double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
        if (cross <= 0) return sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));

        double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
        if (cross >= d2) return sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));

        double r = cross / d2;
        double px = x1 + (x2 - x1) * r;
        double py = y1 + (y2 - y1) * r;
        return sqrt((x - px) * (x - px) + (py - y1) * (py - y1));
    }

    Point _from;
    Point _to;
};

墙类保护继承于线段类,我们并不把Seg类的几何计算方法暴露出来。

class Wall :public BaseEntity,protected Seg//inherite math functions as protected functions
{
public:
    Wall(Vec2, Vec2);
    virtual ~Wall();
public:
    virtual void update();
    virtual bool handleMsg(const Telegram&);
public:
    Vec2 from()const { return _from; }
    Vec2 to()const { return _to; }
    Vec2 normal()const
    {
        Vec2 normalLine(_to.x - _from.x, _from.x - _to.x);
        return (normalLine.getNormalized());
    }
    static bool lineIntersection(const Vec2 entPo, const Vec2 fleer, const Wall wall, double& entity2wall, Vec2& intersection);
};

bool Wall::lineIntersection(const Vec2 entPo, const Vec2 feeler, const Wall wall, double& entity2wall, Vec2& intersection)
{
    if (!Seg::Meet(entPo, feeler, wall.from(), wall.to()))
    {
        return false;
    }
    else
    {
        entity2wall = Seg::PointToSegDist(entPo.x, entPo.y, wall.from().x, wall.from().y, wall.to().x, wall.to().y);
        intersection = Seg::Inter(entPo, feeler, wall.from(), wall.to());
        return true;
    }
}
碰撞的避免

类似于上一篇,这次我们用“触须”来作为辅助图形

在猫科动物运动过程中,触须会帮助它们判断前方物体是否会碰撞,例如洞口能否钻过,此处仿真了这一点。

这里写图片描述

一般我们使用三根触须,触须从实体坐标出发,方向分别为沿朝向,然后两个45°角,沿朝向的长度为两侧的两倍。
触须会提前插入墙壁,产生的斥力作用于交点,斥力大小与插入深度成正比。

触须长度满足这样的规则:
类型一:永不碰撞规则
物体以最大速度运动,垂直于墙壁时,从触须接触墙壁开始,产生的斥力刚好让其在墙壁前停止运动。
m·v=∫f·dt 冲量的积分刚好等于其动量,此处f表示斥力的计算公式,在此处,f=插入墙壁深度。
类型二:允许碰撞
如果允许碰撞,表示你设定的最大速度超过了“安全速度”,我们需要使用基本刚体碰撞来避免其重叠(这步在游戏逻辑层的最后实现)。

触须

我们先来创建触须,添加成员变量:

std::vector<Vec2> _feelers;

double _wallDetectionFeelerLength;
void SteeringBehaviors::createFeelers()
{
    //feeler pointing straight in front
    _feelers[0] = _ownerVehicle->position() + _wallDetectionFeelerLength * _ownerVehicle->heading();

    //feeler to left
    Vec2 temp = _ownerVehicle->heading();
    //Vec2DRotateAroundOrigin(temp, halfPI * 3.5f);
    temp.rotate(Vec2::ZERO, halfPI*3.5f);
    _feelers[1] = _ownerVehicle->position() + _wallDetectionFeelerLength / 2.0f * temp;

    //feeler to right
    temp = _ownerVehicle->heading();
    temp.rotate(Vec2::ZERO, halfPI * 0.5f);
    _feelers[2] = _ownerVehicle->position() + _wallDetectionFeelerLength / 2.0f * temp;
}

其实就是设定好大小和角度而已。

紧接着我们开始计算_wallDetectionFeelerLength

在总时间t内,动量等于冲量的积分:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值