AI - Crowd Simulation(集群模拟)

news2024/11/23 12:52:13

类似鱼群,鸟群这种群体运动模拟。
是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);
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

API协作管理工具:Apipost

在当今快速发展的数字化时代&#xff0c;API已成为企业与开发者实现数据互通、应用集成的重要桥梁。然而&#xff0c;随着API数量的不断增加&#xff0c;API开发、调试、测试、文档等工作也变得越来越复杂。为了解决这一痛点&#xff0c;一款名为Apipost的API协同研发工具应运而…

C_6微机原理

一、单项选择题&#xff08;本大题共 15小题&#xff0c;每小题3分&#xff0c;共45分。在每小题给出的四个备选项中&#xff0c;选出一个正确的答案&#xff0c;请将选定的答案填涂在答题纸的相应位置上。 n1 位有符号数 的补码表示范围为&#xff08;&#xff09; A. -2n&l…

【libGDX】加载G3DJ模型

1 前言 libGDX 提供了自己的 3D 格式模型文件&#xff0c;称为 G3D&#xff0c;包含 g3dj&#xff08;Json 格式&#xff09;和 g3db&#xff08;Binary 格式&#xff09;文件&#xff0c;官方介绍见 → importing-blender-models-in-libgdx。 对于 fbx 文件&#xff0c;libGDX…

pinia从入门到使用

pinia: 比vuex更适合vue3的状态管理工具&#xff0c;只保留了vuex 原有的 state, getters&#xff0c;actions 作用等同于 data computed methods&#xff0c;可以有多个 state 1.安装创建导入 安装&#xff1a;npm install pinia 或 yarn add pinia 创建stores/index.js inde…

0基础学习VR全景平台篇第123篇:VR视频航拍补天 - PR软件教程

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 嗨&#xff0c;大家好&#xff0c;今天我们来介绍【航拍VR视频补天】。之前已经教给了大家如何处理航拍图片的补天&#xff0c;肯定有很多小伙伴也在好奇&#xff0c;航拍的VR视频…

2023年亚太杯APMCM数学建模大赛B题玻璃温室小气候调控

2023年亚太杯APMCM数学建模大赛 B题 玻璃温室小气候调控 原题再现 温室作物的产量受各种气候因素的影响&#xff0c;包括温度、湿度和风速[1]。其中&#xff0c;适宜的温度和风速对植物生长至关重要[2]。为了调节玻璃温室内的温度、风速等气候因素&#xff0c;在温室设计中常…

C语言:求二维数组鞍点 。鞍点就是指二维数组中在该位置上的元素在该行上最大,在该列上最小,也可能没有鞍点。

分析&#xff1a; 在主函数 main 中&#xff0c;程序首先定义一个二维数组 a[5][5] 和五个整型变量 i、j、max、maxj 和 k&#xff0c;并用于寻找鞍点。然后使用 printf 函数输出提示信息。 接下来&#xff0c;程序使用两个 for 循环结构&#xff0c;从键盘输入一个 5x5 的二…

Linux 栈回溯

目录 前言一、什么是栈回溯&#xff1f;二、栈回溯的实现原理三、参考阅读 前言 日常工作中&#xff0c;我们在开发软件程序时&#xff0c;经常会遇到程序奔溃的问题&#xff0c;导致程序奔溃的原因有很多&#xff0c;我们一般都是定位到相关代码&#xff0c;再去查询具体原因。…

vue3中shallowReactive与shallowRef

shallowReactive与shallowRef shallowReactive: 只处理了对象内最外层属性的响应式(也就是浅响应式) shallowRef: 只处理了value的响应式, 不进行对象的reactive处理 总结: reactive与ref实现的是深度响应式, 而shallowReactive与shallowRef是浅响应式。 什么时候用浅响应…

JVM——垃圾回收器(Serial,SerialOld,ParNew,CMS,Parallel Scavenge,Parallel Old)

目录 1.垃圾回收器的组合关系1.年轻代-Serial垃圾回收器2.老年代-SerialOld垃圾回收器3.年轻代-ParNew垃圾回收器4.老年代- CMS(Concurrent Mark Sweep)垃圾回收器CMS执行步骤&#xff1a;CMS垃圾回收器存在的问题缺点&#xff1a;CMS垃圾回收器存在的问题 – 线程资源争抢问题…

【计算机网络笔记】ARP协议

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

论文解读:《数据增强:通过强化学习引导的条件生成进行文本数据扩充》

Title:<Data Boost: Text Data Augmentation Through Reinforcement Learning Guided Conditional Generation> 期刊&#xff1a;EMNLP &#xff08;顶级国际会议&#xff09; 作者 Ruibo Liu; Guangxuan Xu; Chenyan Jia; Weicheng Ma; Lili Wang; et al 出版日期 20…

利用STM32和蓝牙模块构建智能物联网设备的开发指南

智能物联网设备在现代生活中扮演着重要的角色&#xff0c;而STM32微控制器和蓝牙模块则为实现智能物联网设备提供了基础支持。本文将介绍如何使用STM32微控制器和蓝牙模块构建智能物联网设备的开发指南&#xff0c;包括硬件设计、蓝牙模块配置、传感器数据采集和云平台连接等关…

最新版小权云黑系统 骗子添加查询源码

小权云黑系统添加骗子&#xff0c;查询骗子&#xff0c;可添加团队后台方便审核用&#xff0c;在线反馈留言系统&#xff0c;前台提交骗子&#xff0c;后台需要审核才能过&#xff0c;后台使用光年UI界面&#xff0c;新增导航列表&#xff0c;可给网站添加导航友链&#xff0c;…

使用opencv将8位图像raw数据转成bmp文件的方法

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> 这里说的图像raw数据是只包含图像数据的缓存。主要使用了cv::imencode接口将 cv::Mat转化为图像缓存。 #include <opencv2/opencv.hpp>/* 生成一幅…

excel对号怎么打

对号无论是老师批改作业&#xff0c;还是在标注某些数据的时候都会用到&#xff0c;但这个符号在键盘上是没有的&#xff0c;那么excel对号怎么打出来呢&#xff0c;其实只要使用插入符号功能就可以了。 excel对号怎么打&#xff1a; 第一步&#xff0c;选中想要打出对号的单…

基于51单片机的超声波测距系统【程序+proteus仿真+参考论文+原理图+PCB等16个文件夹资料】

一、项目功能简介 整个设计系统由STC89C52单片机LCD1602显示模块声光报警模块存储模块超声波模块按键模块组成。 具体功能&#xff1a; 1、超声波测量距离&#xff0c;显示在LCD1602。 2、存储模块可以存储超声波报警值。 3、通过按键可设置报警值大小。 4、超声波报警距…

金蝶云星空表单插件传递参数到服务插件

文章目录 金蝶云星空表单插件传递参数到服务插件业务需求开发实现传递参数接收参数 金蝶云星空表单插件传递参数到服务插件 业务需求 操作售后单行反关闭时将当前选中行的序号传递到服务端&#xff0c;然后在服务端接收序列号&#xff0c;根据序列号处理相关逻辑。 开发实现…

obsidian官网下载太慢

obsidian真的很强大&#xff0c;速度快&#xff0c;丝滑&#xff0c;但是官网很慢 上百度网盘链接&#xff01; 链接&#xff1a;https://pan.baidu.com/s/1CWoNRuyhtezLTTJOfRf9Jg?pwdxopt 提取码&#xff1a;xopt

在OpenCV中基于深度学习的边缘检测

引言 如何在OpenCV中使用基于深度学习的边缘检测&#xff0c;它比目前流行的canny边缘检测器更精确。边缘检测在许多用例中是有用的&#xff0c;如视觉显著性检测&#xff0c;目标检测&#xff0c;跟踪和运动分析&#xff0c;结构从运动&#xff0c;3D重建&#xff0c;自动驾驶…