AI - Crowd Simulation(集群模拟)

类似鱼群,鸟群这种群体运动模拟。

是Microscopic Models 微观模型,定义每一个个体的行为,然后合在一起。

主要是根据一定范围内族群其他对象的运动状态决定自己的运动状态

Cohesion

保证个体不会脱离群体

求物体一定半径范围内的其他临近物体的所有位置,相加取平均位置,用这个位置进行一个追寻力seek

cpp 复制代码
//求物体一定半径范围内的其他临近物体的所有位置,用这个位置进行一个追寻力seek
Vec2 MoveNode::cohesion() {
    Vec2 averagePos = Vec2::ZERO;
    int count = 0;
    for (auto obj : _cohesionObj) {
        if (obj->getId() != _id) {
            averagePos += obj->getPosition();
            count++;
        }
    }
    if (count > 0) { 
        averagePos *= (1 / (float)count); 
        return seek(averagePos) * _cohesionWeight;
    }
    return Vec2::ZERO;
}

一定范围内的个体会自发的聚集在一起

separation

保证个体不会聚集太密

求物体一定半径范围内的其他临近物体的位置,用当前物体位置分别减去临近物体位置,获取单位方向向量,乘以根据距离远近算出来的权重

越近权重越大。在把所有向量相加取平均值

cpp 复制代码
Vec2 MoveNode::separation() {
    Vec2 steering = Vec2::ZERO;
    int count = 0;
    for (auto obj : _separationObj) {
        if (obj->getId() != _id) {
            float dist = this->getPosition().getDistance(obj->getPosition());

            Vec2 normalVector = (this->getPosition() - obj->getPosition()).getNormalized();
            Vec2 desiredVelocity = normalVector;

            desiredVelocity *= (1 / dist);
            steering += desiredVelocity;
            count++;
        }
    }
    if (count > 0) steering *= (1 / (float)count);
    return steering * _dtSpeed * _separationWeight;
}

一定范围内的个体会逐渐分散开来

alignment

保证个体的运动方向是跟随群体的

求物体一定半径范围内的其他临近物体的所有速度向量,相加取平均值

cpp 复制代码
Vec2 MoveNode::alignment() {
    Vec2 steering = Vec2::ZERO;
    int count = 0;
    for (auto obj : _alignmentObj) {
        if (obj->getId() != _id) {
            steering += obj->getVelocity();
            count++;
        }
    }
    if (count > 0) steering *= (1 / (float)count);
    return steering * _alignmentWeight;
}

可以看到一开始各自不同移动方向的个体,在靠近群体的时候,逐渐跟随上群体的方向

合并效果

给三种力分别设置不同的权重,组合在一起可以对比群体运动的效果

cpp 复制代码
node->setCohesionWeight(0.5);
node->setSeparationWeight(30);
node->setAlignmentWeight(0);

对齐力权重为0,即只有聚集力和分散力

集群只是聚成一团,但并没有一个整体的运动方向

cpp 复制代码
node->setCohesionWeight(0.5);
node->setSeparationWeight(0);
node->setAlignmentWeight(1);

分散力权重为0,即只有聚集力和对齐力

集群几乎直接聚集成同一个点,进行移动

cpp 复制代码
node->setCohesionWeight(0);
node->setSeparationWeight(30);
node->setAlignmentWeight(1);

聚集力权重为0,即只有分散力和对齐力

个体会随着周围的群体方向行进,但是容易散开来

cpp 复制代码
node->setCohesionWeight(0.5);
node->setSeparationWeight(30);
node->setAlignmentWeight(1);

三种力都有的

可以通过对三种力设置不同的权重来控制集群的密集程度运动轨迹

我这里是简单粗暴的把所有物体加入遍历来筛选周围物体,实际项目中需要各种优化如AOI等来减少遍历的个数

源码

CrowdSimulation.h

cpp 复制代码
#ifndef __CROWD_SIMULATION_SCENE_H__
#define __CROWD_SIMULATION_SCENE_H__

#include "cocos2d.h"
#include "MoveNodeManager.h"
USING_NS_CC;
using namespace std;

class CrowdSimulationScene : public Scene
{
public:
	static Scene* createScene();

    virtual bool init();

    virtual bool onTouchBegan(Touch* touch, Event* unused_event);
    void setSeekPos(Vec2 seekPos);
    void setFleePos(Vec2 fleePos);
    void setWanderPos(Vec2 wanderPos);
    void showPursuitModel(Vec2 tarPos);
    void showCombineModel(Vec2 tarPos);
    void showCohsionModel();
    void showSeparationModel();
    void showAlignmentModel();
    void showCrowdModel();

    // implement the "static create()" method manually
    CREATE_FUNC(CrowdSimulationScene);

    void update(float dt);

protected:
    EventListenerTouchOneByOne* _touchListener;
    Vec2 _touchBeganPosition;
    DrawNode* _mapDrawNode;
    DrawNode* _mapDrawNode1;

    MoveNodeManager* _manager;
    MoveNode* _moveNode;
    MoveNode* _moveNode1;
    vector<MoveNode*> _fleeNodes;
    bool _isDrawMoveLine;
};

#endif

CrowdSimulation.cpp

cpp 复制代码
#include "CrowdSimulationScene.h"

Scene* CrowdSimulationScene::createScene()
{
    return CrowdSimulationScene::create();
}

static void problemLoading(const char* filename)
{
    printf("Error while loading: %s\n", filename);
    printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in CrowdSimulationScene.cpp\n");
}

// on "init" you need to initialize your instance
bool CrowdSimulationScene::init()
{
    //
    // 1. super init first
    if (!Scene::init())
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    auto layer = LayerColor::create(Color4B(255, 255, 255, 255));
    layer:setContentSize(visibleSize);
    this->addChild(layer);

    _mapDrawNode = DrawNode::create();
    this->addChild(_mapDrawNode);

    _mapDrawNode1 = DrawNode::create();
    this->addChild(_mapDrawNode1);

    _touchListener = EventListenerTouchOneByOne::create();
    _touchListener->setSwallowTouches(true);
    _touchListener->onTouchBegan = CC_CALLBACK_2(CrowdSimulationScene::onTouchBegan, this);
    this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(_touchListener, layer);

    _manager = new MoveNodeManager();

    this->scheduleUpdate();
    return true;
}

bool CrowdSimulationScene::onTouchBegan(Touch* touch, Event* event)
{
    _touchBeganPosition = touch->getLocation();

    CCLOG("==========°∑ %f£¨ %f", _touchBeganPosition.x, _touchBeganPosition.y);

//    setSeekPos(_touchBeganPosition);
    //setFleePos(_touchBeganPosition);
//    setWanderPos(_touchBeganPosition);
//    showPursuitModel(_touchBeganPosition);
//    showCombineModel(_touchBeganPosition);
//    showCohsionModel();
//    showSeparationModel();
//    showAlignmentModel();
    showCrowdModel();
    return true;
}

void CrowdSimulationScene::update(float dt) {
    if (_isDrawMoveLine && _moveNode->getVelocity() != Vec2::ZERO) _mapDrawNode->drawDot(_moveNode->getPosition(), 3, Color4F(0, 0, 0, 1));
    _mapDrawNode1->clear();
    for (auto e : _fleeNodes) {
        _mapDrawNode1->drawDot(e->getPosition(), 100, Color4F(1, 1, 0, 0.3));
    }
}

void CrowdSimulationScene::setSeekPos(Vec2 seekPos) {
    if (_moveNode == nullptr) {
        _moveNode = _manager->getPlayer();
        _moveNode->setPos(Vec2(100, 100));
        this->addChild(_moveNode);
        _isDrawMoveLine = true;
    }

    _moveNode->setTarPos(seekPos);
    _mapDrawNode->clear();
    _mapDrawNode->drawDot(seekPos, 150, Color4F(0, 1, 1, 0.3));
    _mapDrawNode->drawDot(seekPos, 10, Color4F(0, 1, 1, 1));
}

void CrowdSimulationScene::setFleePos(Vec2 fleePos) {
    if (_moveNode == nullptr) {
        _moveNode = _manager->getPlayer();
        _moveNode->setPos(Vec2(100, 100));
        this->addChild(_moveNode);
        _isDrawMoveLine = true;
    }
    _moveNode->setFleePos(_touchBeganPosition);
    _mapDrawNode->clear();
    _mapDrawNode->drawDot(_touchBeganPosition, 100, Color4F(0, 0, 1, 0.3));
    _mapDrawNode->drawDot(_touchBeganPosition, 10, Color4F(0, 0, 1, 1));
}

void CrowdSimulationScene::setWanderPos(Vec2 wanderPos) {
    if (_moveNode == nullptr) {
        _moveNode = _manager->getWanderNode();
        this->addChild(_moveNode);
    }
    _moveNode->setWanderPos(wanderPos);
    _mapDrawNode->clear();
    _mapDrawNode->drawDot(wanderPos, 200, Color4F(1, 1, 0, 0.3));
}

void CrowdSimulationScene::showPursuitModel(Vec2 tarPos){
    if (_moveNode == nullptr) {
        _moveNode = _manager->getPlayer();
        this->addChild(_moveNode);
        _moveNode1 = _manager->getPursuitNode();
        this->addChild(_moveNode1);
    }
    _moveNode->setPos(Vec2(100, 100));
    _moveNode1->setPos(Vec2(100, 500));
    _moveNode1->switchPursuitObj(_moveNode);
    setSeekPos(tarPos);
}

void CrowdSimulationScene::showCombineModel(Vec2 tarPos) {
    if (_moveNode == nullptr) {
        _moveNode = _manager->getPlayer();
        _moveNode->setPos(Vec2(100, 100));
        this->addChild(_moveNode);
        _isDrawMoveLine = true;
        vector<Vec2> wanderPos = { Vec2(300, 300), Vec2(300, 600), Vec2(450,450),Vec2(600,640),Vec2(500,200),Vec2(650,400),Vec2(850,550) };
        for (auto v : wanderPos) {
            auto fleeNode = _manager->getFleeNode();
            this->addChild(fleeNode);
            fleeNode->setWanderPos(v);
            _fleeNodes.push_back(fleeNode);
            _mapDrawNode1->drawDot(v, 100, Color4F(1, 1, 0, 0.3));
        }
        _moveNode->setFleeObjs(_fleeNodes);
    }
    setSeekPos(tarPos);
}

void CrowdSimulationScene::showCohsionModel() {
    if (_manager->getFlockObjs().empty()) {
        for (int i = 0; i < 30; i++) {
            auto cohesionObj = _manager->getCohesionNode();
            this->addChild(cohesionObj);
            /*float x = RandomHelper::random_real<float>(200, 1200);
            float y = RandomHelper::random_real<float>(200, 500);
            cohesionObj->setPos(Vec2(x, y));*/
        }
    }
    auto objs = _manager->getFlockObjs();
    for (auto obj : objs) {
        float x = RandomHelper::random_real<float>(200, 1200);
        float y = RandomHelper::random_real<float>(200, 500);
        obj->setPos(Vec2(x, y));
    }
}

void CrowdSimulationScene::showSeparationModel() {
    if (_manager->getFlockObjs().empty()) {
        for (int i = 0; i < 30; i++) {
            auto separationObj = _manager->getSeparationNode();
            this->addChild(separationObj);
            /*float x = RandomHelper::random_real<float>(650, 700);
            float y = RandomHelper::random_real<float>(250, 300);
            separationObj->setPos(Vec2(x, y));*/
        }
    }
    auto objs = _manager->getFlockObjs();
    for (auto obj : objs) {
        float x = RandomHelper::random_real<float>(650, 700);
        float y = RandomHelper::random_real<float>(250, 300);
        obj->setPos(Vec2(x, y));
    }
}

void CrowdSimulationScene::showAlignmentModel() {
    if (_manager->getFlockObjs().empty()) {
        for (int i = 0; i < 30; i++) {
            auto separationObj = _manager->getAlignmentNode();
            this->addChild(separationObj);
            /*float x = RandomHelper::random_real<float>(400, 800);
            float y = RandomHelper::random_real<float>(200, 400);
            separationObj->setPos(Vec2(x, y));

            auto angle = RandomHelper::random_real<float>(0, 360);
            float rad = angle * M_PI / 180;
            float len = 1;
            Vec2 v;
            v.x = len * cos(rad);
            v.y = len * sin(rad);
            separationObj->setVelocity(v);*/
        }
    }
    auto objs = _manager->getFlockObjs();
    for (auto obj : objs) {
        float x = RandomHelper::random_real<float>(100, 1300);
        float y = RandomHelper::random_real<float>(100, 540);
        obj->setPos(Vec2(x, y));

        auto angle = RandomHelper::random_real<float>(0, 360);
        float rad = angle * M_PI / 180;
        float len = 1;
        Vec2 v;
        v.x = len * cos(rad);
        v.y = len * sin(rad);
        obj->setVelocity(v);
    }
}

void CrowdSimulationScene::showCrowdModel() {
    if (_manager->getFlockObjs().empty()) {
        for (int i = 0; i < 30; i++) {
            auto flockNode = _manager->getFlockNode();
            this->addChild(flockNode);
            /*float x = RandomHelper::random_real<float>(100, 1300);
            float y = RandomHelper::random_real<float>(100, 540);
            flockNode->setPos(Vec2(x, y));

            auto angle = RandomHelper::random_real<float>(0, 360);
            float rad = angle * M_PI / 180;
            float len = 1;
            Vec2 v;
            v.x = len * cos(rad);
            v.y = len * sin(rad);
            flockNode->setVelocity(v);*/
        }
    }
   
    auto objs = _manager->getFlockObjs();
    for (auto obj : objs) {
        float x = RandomHelper::random_real<float>(100, 1300);
        float y = RandomHelper::random_real<float>(100, 540);
        obj->setPos(Vec2(x, y));

        auto angle = RandomHelper::random_real<float>(0, 360);
        float rad = angle * M_PI / 180;
        float len = 1;
        Vec2 v;
        v.x = len * cos(rad);
        v.y = len * sin(rad);
        obj->setVelocity(v);
    }
}

MoveNodeManager.h

cpp 复制代码
#ifndef __MOVE_NODE_MANAGER_H__
#define __MOVE_NODE_MANAGER_H__

#include "cocos2d.h"
#include "MoveNode.h"
USING_NS_CC;
using namespace std;

class MoveNodeManager
{
public:
	MoveNode* getPlayer();
	MoveNode* getWanderNode();
	MoveNode* getPursuitNode();
	MoveNode* getFleeNode();
	MoveNode* getCohesionNode();
	MoveNode* getSeparationNode();
	MoveNode* getAlignmentNode();
	MoveNode* getFlockNode();
	vector<MoveNode*> getFlockObjs() { return _flockObjs; };

protected:
	int _id = 0;
	vector<MoveNode*> _flockObjs;
};

#endif

MoveNodeManager.cpp

cpp 复制代码
#include "MoveNodeManager.h"

MoveNode* MoveNodeManager::getPlayer() {
	auto node = MoveNode::create();
	node->setId(_id);
	_id++;
	auto body = DrawNode::create();
	node->addChild(body);
	body->drawDot(Vec2(0, 0), 10, Color4F(0, 0, 0, 1));
	auto direct = DrawNode::create();
	node->addChild(direct);
	node->setDirect(direct);
	direct->drawDot(Vec2(0, 0), 3, Color4F(1, 1, 1, 1));
	node->setSpeed(400);
	node->setMaxForce(20);
	node->setMass(10);
	node->setMaxSpeed(400);
	node->setTarSlowRadius(150);
	node->setFleeRadius(100);
	return node;
}

MoveNode* MoveNodeManager::getWanderNode() {
	auto node = MoveNode::create();
	node->setId(_id);
	_id++;
	auto body = DrawNode::create();
	node->addChild(body);
	body->drawDot(Vec2(0, 0), 10, Color4F(0, 1, 0, 1));
	auto direct = DrawNode::create();
	node->addChild(direct);
	node->setDirect(direct);
	direct->drawDot(Vec2(0, 0), 3, Color4F(1, 0, 1, 1));
	node->setSpeed(400);
	node->setMaxForce(20);
	node->setMass(20);
	node->setMaxSpeed(100);
	node->setCircleDistance(30);
	node->setCircleRadius(15);
	node->setChangeAngle(180);
	node->setWanderRadius(200);
	node->setWanderPullBackSteering(50);
	return node;
}

MoveNode* MoveNodeManager::getPursuitNode() {
	auto node = MoveNode::create();
	node->setId(_id);
	_id++;
	auto body = DrawNode::create();
	node->addChild(body);
	body->drawDot(Vec2(0, 0), 10, Color4F(0, 1, 0, 1));
	auto direct = DrawNode::create();
	node->addChild(direct);
	node->setDirect(direct);
	direct->drawDot(Vec2(0, 0), 3, Color4F(1, 0, 1, 1));
	node->setSpeed(400);
	node->setMaxForce(20);
	node->setMass(10);
	node->setMaxSpeed(400);
	return node;
}

MoveNode* MoveNodeManager::getFleeNode() {
	auto node = MoveNode::create();
	node->setId(_id);
	_id++;
	auto body = DrawNode::create();
	node->addChild(body);
	body->drawDot(Vec2(0, 0), 10, Color4F(0, 1, 0, 1));
	auto direct = DrawNode::create();
	node->addChild(direct);
	node->setDirect(direct);
	direct->drawDot(Vec2(0, 0), 3, Color4F(1, 0, 1, 1));
	node->setSpeed(400);
	node->setMaxForce(20);
	node->setMass(10);
	node->setMaxSpeed(50);
	node->setCircleDistance(30);
	node->setCircleRadius(15);
	node->setChangeAngle(180);
	node->setWanderRadius(200);
	node->setWanderPullBackSteering(50);
	return node;
}

MoveNode* MoveNodeManager::getCohesionNode() {
	auto node = MoveNode::create();
	_flockObjs.push_back(node);
	node->setId(_id);
	_id++;
	auto body = DrawNode::create();
	node->addChild(body);
	body->drawDot(Vec2(0, 0), 10, Color4F(0, 0, 0, 1));
	auto direct = DrawNode::create();
	node->addChild(direct);
	node->setDirect(direct);
	direct->drawDot(Vec2(0, 0), 3, Color4F(1, 1, 1, 1));
	node->setSpeed(300);
	node->setMaxForce(20);
	node->setMass(20);
	node->setMaxSpeed(50);
	node->setFleeRadius(50);
	node->setAllObj(&_flockObjs);
	node->setCohesionRadius(100);
	return node;
}

MoveNode* MoveNodeManager::getSeparationNode() {
	auto node = MoveNode::create();
	_flockObjs.push_back(node);
	node->setId(_id);
	_id++;
	auto body = DrawNode::create();
	node->addChild(body);
	body->drawDot(Vec2(0, 0), 10, Color4F(0, 0, 0, 1));
	auto direct = DrawNode::create();
	node->addChild(direct);
	node->setDirect(direct);
	direct->drawDot(Vec2(0, 0), 3, Color4F(1, 1, 1, 1));
	node->setSpeed(300);
	node->setMaxForce(20);
	node->setMass(20);
	node->setMaxSpeed(50);
	node->setFleeRadius(50);
	node->setAllObj(&_flockObjs);
	node->setSeparationRadius(30);
	return node;
}

MoveNode* MoveNodeManager::getAlignmentNode() {
	auto node = MoveNode::create();
	_flockObjs.push_back(node);
	node->setId(_id);
	_id++;
	auto body = DrawNode::create();
	node->addChild(body);
	body->drawDot(Vec2(0, 0), 10, Color4F(0, 0, 0, 1));
	auto direct = DrawNode::create();
	node->addChild(direct);
	node->setDirect(direct);
	direct->drawDot(Vec2(0, 0), 3, Color4F(1, 1, 1, 1));
	node->setSpeed(300);
	node->setMaxForce(20);
	node->setMass(20);
	node->setMaxSpeed(150);
	node->setFleeRadius(50);
	node->setAllObj(&_flockObjs);
	node->setAlignmentRadius(150);
	return node;
}

MoveNode* MoveNodeManager::getFlockNode() {
	auto node = MoveNode::create();
	_flockObjs.push_back(node);
	node->setId(_id);
	_id++;
	auto body = DrawNode::create();
	node->addChild(body);
	body->drawDot(Vec2(0, 0), 10, Color4F(0, 0, 0, 1));
	auto direct = DrawNode::create();
	node->addChild(direct);
	node->setDirect(direct);
	direct->drawDot(Vec2(0, 0), 3, Color4F(1, 1, 1, 1));
	node->setSpeed(300);
	node->setMaxForce(20);
	node->setMass(20);
	node->setMaxSpeed(100);
	node->setFleeRadius(50);
	node->setAllObj(&_flockObjs);
	node->setCohesionRadius(100);
	node->setSeparationRadius(40);
	node->setAlignmentRadius(50);
	node->setCohesionWeight(0.5);
	node->setSeparationWeight(30);
	node->setAlignmentWeight(1);
	return node;
}

MoveNode.h

cpp 复制代码
#ifndef __MOVE_NODE_H__
#define __MOVE_NODE_H__

#include "cocos2d.h"
USING_NS_CC;
using namespace std;

class MoveNode : public Node
{
public:
	static MoveNode* create();

CC_CONSTRUCTOR_ACCESS:
	virtual bool init() override;

	void setId(int id) { _id = id; };
	void setDirect(DrawNode* direct) { _direct = direct; };
	void setSpeed(float speed) { _speed = speed; };
	void setMaxForce(float maxForce) { _maxForce = maxForce; };
	void setMass(float mass) { _mass = mass; };
	void setMaxSpeed(float maxSpeed) { _maxSpeed = maxSpeed; };
	void setTarSlowRadius(float tarSlowRadius) { _tarSlowRadius = tarSlowRadius; };
	void setFleeRadius(float fleeRadius) { _fleeRadius = fleeRadius; };
	void setCircleDistance(float circleDistance) { _circleDistance = circleDistance; };
	void setCircleRadius(float circleRadius) { _circleRadius = circleRadius; };
	void setChangeAngle(float changeAngle) { _changeAngle = changeAngle; };
	void setWanderRadius(float wanderRadius) { _wanderRadius = wanderRadius; };
	void setWanderPullBackSteering(float wanderPullBackSteering) { _wanderPullBackSteering = wanderPullBackSteering; };
	void setPos(Vec2 pos);
	void setTarPos(Vec2 tarPos) { _tarPos = tarPos; };
	void setFleePos(Vec2 fleePos) { _fleePos = fleePos; };
	void setFleeObjs(vector<MoveNode*> fleeObjs) { _fleeObjs = fleeObjs; };
	void setWanderPos(Vec2 wanderPos);
	void switchPursuitObj(MoveNode* pursuitObj);
	void setAllObj(vector<MoveNode*>* allObj) { _allObj = allObj; };
	void setCohesionRadius(float cohesionRadius) { _cohesionRadius = cohesionRadius; };
	void setSeparationRadius(float separationRadius) { _separationRadius = separationRadius; };
	void setAlignmentRadius(float alignmentRadius) { _alignmentRadius = alignmentRadius; };
	void setCohesionWeight(float cohesionWeight) { _cohesionWeight = cohesionWeight; };
	void setSeparationWeight(float separationWeight) { _separationWeight = separationWeight; };
	void setAlignmentWeight(float alignmentWeight) { _alignmentWeight = alignmentWeight; };

	Vec2 seek(Vec2 seekPos);
	Vec2 flee();
	Vec2 wander();
	Vec2 pursuit();

	Vec2 cohesion();
	Vec2 separation();
	Vec2 alignment();

	Vec2 wallAvoid();

	Vec2 turncate(Vec2 vector, float maxNumber);
	Vec2 changeAngle(Vec2 vector, float angle);

	void updatePos();
	void update(float dt);
	void findNeighbourObjs();

	int getId() { return _id; };
	Vec2 getVelocity(){ return _velocity; };
	void setVelocity(Vec2 velocity) { _velocity = velocity; };
protected:
	DrawNode* _direct;

	int _id;
	float _speed; //速度
	float _maxForce; //最大转向力,即最大加速度
	float _mass; //质量
	float _maxSpeed; //最大速度
	float _tarSlowRadius; //抵达目标减速半径
	float _fleeRadius; //逃离目标范围半径
	float _circleDistance; //巡逻前方圆点距离
	float _circleRadius; //巡逻前方圆半径
	float _changeAngle; //巡逻转向最大角度
	float _wanderRadius; //巡逻点范围半径
	float _wanderPullBackSteering; //超出巡逻范围拉回力

	float _alignmentRadius; //方向对齐判断的范围半径
	float _cohesionRadius; //聚集判断的范围半径
	float _separationRadius; //分离判断得范围半径

	float _alignmentWeight = 1.0f; //方向对齐力权重
	float _cohesionWeight = 1.0f; //聚集力权重
	float _separationWeight = 1.0f; //分离力权重

	float _dtSpeed; //每帧速度值
	Vec2 _velocity; //速度
	float _wanderAngle; //巡逻角度
	Vec2 _wanderPos; //巡逻范围中心点
	Vec2 _tarPos; //目标点
	Vec2 _fleePos; //逃离点

	MoveNode* _pursuitObj; //追逐目标
	vector<MoveNode*> _fleeObjs; //逃离目标

	vector<MoveNode*>* _allObj; //所有对象
	vector<MoveNode*> _alignmentObj; //方向对齐目标
	vector<MoveNode*> _cohesionObj; //聚集目标
	vector<MoveNode*> _separationObj; //分离目标

	float wallAvoidRadius = 50.0f; //墙壁碰撞检测半径
};

#endif

MoveNode.cpp

cpp 复制代码
#include "MoveNode.h"

bool MoveSmooth = true;

MoveNode* MoveNode::create() {
    MoveNode* moveNode = new(nothrow) MoveNode();
    if (moveNode && moveNode->init()) {
        moveNode->autorelease();
        return moveNode;
    }
    CC_SAFE_DELETE(moveNode);
    return nullptr;
}

bool MoveNode::init()
{
    _tarPos = Vec2(-1, -1);
    _wanderPos = Vec2(-1, -1);
    _velocity.setZero();
    _pursuitObj = nullptr;
    this->scheduleUpdate();
    return true;
}

void MoveNode::update(float dt)
{
    findNeighbourObjs();
    _dtSpeed = _speed * dt;
    if (MoveSmooth) {
        Vec2 steering = Vec2::ZERO;
        steering += seek(_tarPos);
        steering += flee();
        steering += wander();
        steering += pursuit();
        steering += cohesion();
        steering += separation();
        steering += alignment();
        steering = turncate(steering, _maxForce);
        steering *= ( 1 / (float)_mass );
        _velocity += steering;
    }
    else {
        _velocity += seek(_tarPos);
        _velocity += flee();
        _velocity += wander();
        _velocity += pursuit();
        _velocity += cohesion();
        _velocity += separation();
        _velocity += alignment();
    }

    _velocity += wallAvoid();

    _velocity = turncate(_velocity, _maxSpeed * dt);
    updatePos();
}

Vec2 MoveNode::wallAvoid() {
    Vec2 temp = _velocity.getNormalized();
    temp *= wallAvoidRadius;
    Vec2 tarPos = this->getPosition() + temp;
    if (!Rect(Vec2::ZERO, Director::getInstance()->getVisibleSize()).containsPoint(tarPos)) {
        Vec2 steering = Vec2::ZERO;
        if (tarPos.y >= Director::getInstance()->getVisibleSize().height) steering += Vec2(0, -1);
        if (tarPos.y <= 0) steering += Vec2(0, 1);
        if (tarPos.x >= Director::getInstance()->getVisibleSize().width) steering += Vec2(-1, 0);
        if (tarPos.x <= 0) steering += Vec2(1, 0);
        return steering * _dtSpeed;
    }
    return Vec2::ZERO;
}

void MoveNode::updatePos() {
    Vec2 tarPos = this->getPosition() + _velocity;

    if (!Rect(Vec2::ZERO, Director::getInstance()->getVisibleSize()).containsPoint(tarPos)) {
        _velocity = _velocity *= -100;
    }
    Vec2 directPos = _velocity.getNormalized() *= 5;
    _direct->setPosition(directPos);
    this->setPosition(tarPos);
    if (_velocity == Vec2::ZERO) _tarPos = Vec2(-1, -1);
}

Vec2 MoveNode::turncate(Vec2 vector, float maxNumber) {
    if (vector.getLength() > maxNumber) { 
        vector.normalize();
        vector *= maxNumber;
    }
    return vector;
}

//追逐转向力
Vec2 MoveNode::seek(Vec2 seekPos){
    if (seekPos == Vec2(-1, -1)) return Vec2::ZERO;
    Vec2 normalVector = (seekPos - this->getPosition()).getNormalized();
    float dist = this->getPosition().getDistance(seekPos);
    Vec2 desiredVelocity = normalVector * _dtSpeed;

    //靠近目标减速带
    if (dist < _tarSlowRadius) desiredVelocity *= (dist / _tarSlowRadius);

    Vec2 steering;
    if (MoveSmooth) steering = desiredVelocity - _velocity;
    else steering = desiredVelocity;
    return steering;
}

//躲避转向力
Vec2 MoveNode::flee() {
    Vec2 steering = Vec2::ZERO;
    if (!_fleeObjs.empty()) {
        for (auto eludeObj : _fleeObjs) {
            auto fleePos = eludeObj->getPosition();
            if (fleePos.getDistance(this->getPosition()) < _fleeRadius) {
                Vec2 normalVector = (this->getPosition() - fleePos).getNormalized();
                Vec2 desiredVelocity = normalVector * _dtSpeed;
                Vec2 steeringChild;
                if (MoveSmooth) steeringChild = desiredVelocity - _velocity;
                else steeringChild = desiredVelocity;
                steering += steeringChild;
            }
        }
        return steering;
    }
    if(_fleePos == Vec2::ZERO) return steering;
    if (this->getPosition().getDistance(_fleePos) < _fleeRadius) {
        Vec2 normalVector = (this->getPosition() - _fleePos).getNormalized();
        Vec2 desiredVelocity = normalVector * _dtSpeed;
        if (MoveSmooth) steering = desiredVelocity - _velocity;
        else steering = desiredVelocity;
    }
    return steering;
}

Vec2 MoveNode::changeAngle(Vec2 vector, float angle) {
    float rad = angle * M_PI / 180;
    float len = vector.getLength();
    Vec2 v;
    v.x = len * cos(rad);
    v.y = len * sin(rad);
    return v;
}

Vec2 MoveNode::wander() {
    if (_wanderPos == Vec2(-1, -1)) return Vec2::ZERO;
    Vec2 circleCenter = _velocity.getNormalized();
    circleCenter *= _circleDistance;

    Vec2 displacement = Vec2(0, -1);
    displacement *= _circleRadius;
    displacement = changeAngle(displacement, _wanderAngle);

    float randomValue = RandomHelper::random_real<float>(-0.5f, 0.5f);
    _wanderAngle = _wanderAngle + randomValue * _changeAngle;

    Vec2 wanderForce = circleCenter - displacement;

    float dist = this->getPosition().getDistance(_wanderPos);
    if (dist > _wanderRadius) {
        // 偏离漫游点一定范围的话,给个回头力
        Vec2 desiredVelocity = (_wanderPos - this->getPosition()).getNormalized() * _wanderPullBackSteering;
        desiredVelocity -= _velocity;
        wanderForce += desiredVelocity;
    }
    return wanderForce;
}

Vec2 MoveNode::pursuit() {
    if (_pursuitObj == nullptr) return Vec2::ZERO;
    Vec2 pursuitPos = _pursuitObj->getPosition();
    float t = this->getPosition().getDistance(pursuitPos) / _dtSpeed;
    //float t = 3;
//    Vec2 tarPos = pursuitPos + _pursuitObj->getVelocity() * t;
    Vec2 tarPos = pursuitPos;
    return seek(tarPos);
}

//求物体一定半径范围内的其他临近物体的所有位置,用这个位置进行一个追寻力seek
Vec2 MoveNode::cohesion() {
    Vec2 averagePos = Vec2::ZERO;
    int count = 0;
    for (auto obj : _cohesionObj) {
        if (obj->getId() != _id) {
            averagePos += obj->getPosition();
            count++;
        }
    }
    if (count > 0) { 
        averagePos *= (1 / (float)count); 
        return seek(averagePos) * _cohesionWeight;
    }
    return Vec2::ZERO;
}

//求物体一定半径范围内的其他临近物体的位置,用当前物体位置分别减去临近物体位置,获取单位方向向量,乘以根据距离远近算出来得权重
//越近权重越大。在把所有向量相加取平均值
Vec2 MoveNode::separation() {
    Vec2 steering = Vec2::ZERO;
    int count = 0;
    for (auto obj : _separationObj) {
        if (obj->getId() != _id) {
            float dist = this->getPosition().getDistance(obj->getPosition());

            Vec2 normalVector = (this->getPosition() - obj->getPosition()).getNormalized();
            Vec2 desiredVelocity = normalVector;

            desiredVelocity *= (1 / dist);
            steering += desiredVelocity;
            count++;
        }
    }
    if (count > 0) steering *= (1 / (float)count);
    return steering * _dtSpeed * _separationWeight;
}

//求物体一定半径范围内的其他临近物体的所有速度向量,相加取平均值
Vec2 MoveNode::alignment() {
    Vec2 steering = Vec2::ZERO;
    int count = 0;
    for (auto obj : _alignmentObj) {
        if (obj->getId() != _id) {
            steering += obj->getVelocity();
            count++;
        }
    }
    if (count > 0) steering *= (1 / (float)count);
    return steering * _alignmentWeight;
}

void MoveNode::setPos(Vec2 pos) {
    this->setPosition(pos);
    _velocity.setZero();
}

void MoveNode::setWanderPos(Vec2 wanderPos) {
    _wanderPos = wanderPos;
    setPos(wanderPos);
}

void MoveNode::switchPursuitObj(MoveNode* pursuitObj) {
    if (_pursuitObj == nullptr) _pursuitObj = pursuitObj;
    else {
        _pursuitObj = nullptr;
        _velocity = Vec2::ZERO;
        _tarPos = Vec2(-1, -1);
    }
}

void MoveNode::findNeighbourObjs() {
    if (_allObj == nullptr) return;
    _alignmentObj.clear();
    _cohesionObj.clear();
    _separationObj.clear();
    for (auto obj : *_allObj) {
        float dist = this->getPosition().getDistance(obj->getPosition());
        if (dist < _alignmentRadius) {
            _alignmentObj.push_back(obj);
        }
        if (dist < _cohesionRadius) {
            _cohesionObj.push_back(obj);
        }
        if (dist < _separationRadius) {
            _separationObj.push_back(obj);
        }
    }
}
相关推荐
再不会python就不礼貌了15 分钟前
Ollama 0.4 发布!支持 Llama 3.2 Vision,实现多模态 RAG
人工智能·学习·机器学习·ai·开源·产品经理·llama
AI原吾29 分钟前
探索PyAV:Python中的多媒体处理利器
开发语言·python·ai·pyav
MissMySwallow19 小时前
开源项目工具:LeanTween - 为Unity 3D打造的高效缓动引擎详解(比较麻烦的API版)之二———补间动画控制API系列
3d·unity·游戏引擎
阿_旭19 小时前
Meta Sapiens太强了!人物姿态、部位分割、深度估计、表面法线预测一网打尽【附步骤源码】
深度学习·ai·姿态估计·深度估计·sapiens
AI原吾20 小时前
探索Python视频处理的瑞士军刀:ffmpeg-python库
python·ai·ffmpeg·音视频·ffmpeg-python
努力学习的啊张1 天前
ChatGPT 新体验:AI 搜索功能与订阅支付指南
java·人工智能·gpt·opencv·ai·chatgpt·eclipse
存内计算开发者1 天前
ISSCC 34.9 面向塑性神经网络集片上自学习与推理一体
人工智能·深度学习·神经网络·学习·机器学习·ai·存内计算
Topstip1 天前
Gemini 对话机器人加入开源盲水印技术来检测 AI 生成的内容
人工智能·ai·机器人
RestCloud1 天前
ETLCloud异常问题分析ai功能
人工智能·ai·数据分析·etl·数据集成工具·数据异常
Dontla2 天前
ChatGPT键盘快捷键(按ctrl + /呼出)
ai