AI - Crowd Simulation(集群模拟)

类似鱼群,鸟群这种群体运动模拟。
是Microscopic Models 微观模型,定义每一个个体的行为,然后合在一起。
主要是根据一定范围内族群其他对象的运动状态决定自己的运动状态

Cohesion

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

//求物体一定半径范围内的其他临近物体的所有位置,用这个位置进行一个追寻力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

保证个体不会聚集太密
求物体一定半径范围内的其他临近物体的位置,用当前物体位置分别减去临近物体位置,获取单位方向向量,乘以根据距离远近算出来的权重
越近权重越大。在把所有向量相加取平均值

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

保证个体的运动方向是跟随群体的
求物体一定半径范围内的其他临近物体的所有速度向量,相加取平均值

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;
}

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

合并效果

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

node->setCohesionWeight(0.5);
node->setSeparationWeight(30);
node->setAlignmentWeight(0);

对齐力权重为0,即只有聚集力和分散力
集群只是聚成一团,但并没有一个整体的运动方向
请添加图片描述

node->setCohesionWeight(0.5);
node->setSeparationWeight(0);
node->setAlignmentWeight(1);

分散力权重为0,即只有聚集力和对齐力
集群几乎直接聚集成同一个点,进行移动
请添加图片描述

node->setCohesionWeight(0);
node->setSeparationWeight(30);
node->setAlignmentWeight(1);

聚集力权重为0,即只有分散力和对齐力
个体会随着周围的群体方向行进,但是容易散开来
请添加图片描述

node->setCohesionWeight(0.5);
node->setSeparationWeight(30);
node->setAlignmentWeight(1);

三种力都有的
可以通过对三种力设置不同的权重来控制集群的密集程度运动轨迹
请添加图片描述

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

源码

CrowdSimulation.h

#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

#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

#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

#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

#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

#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);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/194428.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习回顾:七种网络

一、说明 本文 揭开CNN、Seq2Seq、Faster R-CNN 和 PPO &#xff0c;以及transformer和humg-face— 编码和创新之路。对于此类编程的短小示例&#xff0c;用于对照观察&#xff0c;或做学习实验。 二、CNN网络示例 2.1 CNN用mnist数据集 CNN 专为图像处理而设计&#xff0c;包…

idea创建不了spring2.X版本,无法使用JDK8,最低支持JDK17 , 如何用idea创建spring2.X版本,使用JDK8解决方案

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对博主首页也很感兴趣o (ˉ▽ˉ&#xff1b;) &#x1f4dc;jdk17安装全方位手把手安装教程 / 已有jdk8了&#xff0c;安装JDK17后如何配置环境变量 / 多个不同版本的JDK&#xff0c;如何配置环境变量&a…

FreeRTOS源码阅读笔记6--event_groups.c

通常用的事件标志组是一个32位的变量uxEventBits&#xff0c;可设置的位有24位&#xff0c;一共就是24 种事件。 事件组的结构体类型&#xff1a; 6.1创建事件组xEventGroupCreate() 6.1.1函数原型 返回值&#xff1a;事件组句柄&#xff0c;指向事件组。 6.1.2函数框架 ①…

通过亚马逊云科技云存储服务探索云原生应用的威力

文章作者&#xff1a;Libai 欢迎来到我们关于“使用亚马逊云科技云存储服务构建云原生应用”的文章的第一部分。在本文中&#xff0c;我们将深入探讨云原生应用的世界&#xff0c;并探索亚马逊云科技云存储服务在构建和扩展这些应用中的关键作用。 亚马逊云科技开发者社区为开发…

鸿蒙应用开发-初见:ArkUI

编程范式&#xff1a;命令式->声明式 以一个卡片的实现做下讲解 命令式 简单讲就是需要开发用代码一步一步进行布局&#xff0c;这个过程需要开发全程参与。 Objective-C UIView *cardView [[UIView alloc] init]; cardView.backgroundColor [UIColor whiteColor]; ca…

[socket 弹 shell] msg_box3

前言 题目比较简单&#xff0c;没开 Canary 和 NX. Arch: amd64-64-littleRELRO: Full RELROStack: Canary foundNX: NX disabledPIE: PIE enabledRWX: Has RWX segments 漏洞利用与分析&#xff1a; 白给的函数调用&#xff0c;其中 ptr 10 是用…

Elasticsearch启动失败问题汇总

版本elasticsearch-8.11.1&#xff0c;解压安装完后&#xff0c;修改安装目录下conf/jvm.options&#xff0c; 默认配置如下&#xff1a; -Xms4g -Xmx4g 默认的配置占用内存太多了&#xff0c;调小一些&#xff1a; -Xms256m -Xmx256m由于es和jdk是一个强依赖的关系&#xff0…

[黑马程序员SpringBoot2]——开发实用篇3

目录&#xff1a; jetcache远程缓存方案jetcache本地缓存方案jetcache方法缓存j2cache基本操作springboot整合quartz​​​​​​​springboot整合task发送简单邮件发送多部件邮件消息简介购物订单案例-发送短信ActiveMQ安装springboot整合ActiveMQRabbitMQ安装springboot整合…

代理IP可以用于哪些实际场景?遇到问题如何解决

随着互联网的普及和网络应用的广泛使用&#xff0c;代理IP已成为许多人工作和生活中不可或缺的一部分。代理IP可以用于多种实际场景&#xff0c;并在遇到问题时提供有效的解决方案。下面将详细介绍代理IP的实际应用场景及遇到问题时的解决方法。 一、代理IP的实际应用场景 1. 网…

C#,数值计算——插值和外推,径向基函数插值(RBF_interp)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 径向基函数插值 /// Object for radial basis function interpolation using n points in dim /// dimensions.Call constructor once, then interp as many times as desir…

阿里云服务器部署node和npm

目录 1.链接服务器2.找到node 下载地址3获取链接地址4下载到linux5.解压6.重命名 解压后的文件7.配置环境变量7.1复制当前的bin目录7.2vim /etc/profile7.3在按下ESC按键 8.重启环境变量9.输入node10.npm配置加速镜像 1.链接服务器 2.找到node 下载地址 https://nodejs.org/d…

激光雷达毫米波雷达

一.激光雷达 技术指标&#xff1a; 视场角 线数&#xff08;32/64/128&#xff09; 分辨率&#xff08;激光光束夹角越小分辨率越高&#xff0c;0.1度&#xff09; 探测距离&#xff1a;0.3-200m 反射率&#xff08;一般探测10%以上反射率的目标&#xff09; 分类 按照测距方…

JUC下常用的类

一、Semaphore 信号量 new Semaphore(10) 可以把他理解成停车场&#xff0c;最多停10辆车&#xff0c;多个车进来如果满了就去排队&#xff0c;车走了&#xff0c;车位就空出来了&#xff0c;排队的线程就可以进来主要下面2个方法 Acquire获取锁&#xff1a;通过CAS原子性减1&…

算法通关村第一关|链表基础

1. 单链表概念 对于所有的数据结构的基础都是创建增删改查&#xff0c;学习链表重点也是学习链表的五种基本操作。 单向链表就像一个铁链一样&#xff0c;元素之间相互连接&#xff0c;包含多个结点&#xff0c;每个结点有一个指向后继元素的next指针。表中最后一个元素的nex…

面试常问-如何判断链表有环、?

如何判断链表有环 题目&#xff1a;解决方案一&#xff1a;解决方案二&#xff1a;解决方案三&#xff1a; 题目&#xff1a; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;…

ChromeDriver最新版本下载与安装方法

关于ChromeDriver最新下载地址&#xff1a;https://googlechromelabs.github.io/chrome-for-testing/ 下载与安装 setp1&#xff1a;查看Chrome浏览器版本 首先&#xff0c;需要检查Chrome浏览器的版本。请按照以下步骤进行&#xff1a; 打开Chrome浏览器。 点击浏览器右上角…

7种SQL进阶用法【转】

1.自定义排序(ORDER BY FIELD) 在MySQL中ORDER BY排序除了可以用ASC和DESC之外,还可以使使用自定义排序方式来实现 CREATE TABLE movies ( id INT PRIMARY KEY AUTO_INCREMENT, movie_name VARCHAR(255), actors VARCHAR(255), price DECIMAL(10,2) DEFAULT 50, release date…

文本三剑客(grep,awk,sed)

一.正则表达式 注意事项&#xff1a;使用正则表达式必须加引号。 元字符 表示字符 ① . &#xff1a;在正则表达式中.表示任意单个字符。 [rootpc1 data]#grep -o r.t /etc/passwd #过滤passwd文件中开头为r中间任意单个字符结尾为t的内容 rat rat rat [rootpc1 data]#g…

Open Feign 源码解析(二) --- 如何发送http请求

Open Feign 源码解析二 如何发送http请求&#xff1f; 如何组件化&#xff1f; 定义接口 public interface Client {Response execute(Request request, Options options) throws IOException; }是否存在已有的方案&#xff1f; 1&#xff09;rest template http client o…

《微信小程序开发从入门到实战》学习三十三

第四章 云开发 本章云开发技术的功能与使用&#xff0c;包括以下几点&#xff1a; 1.学习使用云开发控制台 2.学习云开发JSON数据库功能 3.学习云开文件存储功能 4.学习云函数功能 5.使用云开发技术实现投票小程序的服务端功能 投票小程序大部分已经实现。需要实现&#…