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 manuallyCREATE_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 firstif (!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);}}
}
相关文章:

AI - Crowd Simulation(集群模拟)
类似鱼群,鸟群这种群体运动模拟。 是Microscopic Models 微观模型,定义每一个个体的行为,然后合在一起。 主要是根据一定范围内族群其他对象的运动状态决定自己的运动状态 Cohesion 保证个体不会脱离群体 求物体一定半径范围内的其他临近物…...

<JavaEE> Java中线程有多少种状态(State)?状态之间的关系有什么关系?
目录 一、系统内核中的线程状态 二、Java中的线程状态 一、系统内核中的线程状态 状态说明就绪状态线程已经准备就绪,随时可以接受CPU的调度。阻塞状态线程处于阻塞等待,暂时无法在CPU中执行。 二、Java中的线程状态 相比于系统内核,Java…...

正则表达式 通配符 awk文本处理工具
目录 什么是正则表达式 概念 正则表达式的结构 正则表达式的组成 元字符 元字符点(.) 代表字符. 点值表示点需要转义 \ r..t 代表r到t之间任意两个字符 过滤出小写 过滤出非小写 space空格 [[:space:]] 表示次数 位置锚定 例:…...
三、ts高级笔记,
文章目录 18、d.ts声明文件19、Mixin混入20、Decorator装饰器的使用21、-高级proxy拦截_Reflect元储存22、-高级写法Partial-Pick23、Readonly只读_Record套对象24、高阶写法Infer占位符25、Inter实现提取类型和倒叙递归26、object、Object、{}的区别27、localStorage封装28、协…...

二十一、数组(6)
本章概要 数组排序Arrays.sort的使用并行排序binarySearch二分查找parallelPrefix并行前缀 数组排序 根据对象的实际类型执行比较排序。一种方法是为不同的类型编写对应的排序方法,但是这样的代码不能复用。 编程设计的一个主要目标是“将易变的元素与稳定的元素…...

flask依据现有的库表快速生成flask实体类
flask依据现有的库表快速生成flask实体类 在实际开发过程中,flask的sqlalchemy对应的model类写起来重复性较强,如果表比较多会比较繁琐,这个时候可以使用 flask-sqlacodegen 来快速的生成model程序或者py文件,以下是简单的示例&a…...

.NET6 开发一个检查某些状态持续多长时间的类
📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !序言 在代码的世界里,时常碰撞…...
链表K个节点的组内逆序调整问题
链表K个节点的组内逆序调整问题 作者:Grey 原文地址: 博客园:链表K个节点的组内逆序调整问题 CSDN:链表K个节点的组内逆序调整问题 题目描述 LeetCode 25. Reverse Nodes in k-Group 本题的 follow up 是: Fol…...

安卓隐私指示器学习笔记
最近了解到Google 在Android12上新增了权限指示器,可以在信号栏的右侧显示当前访问录音机和Camera的应用,点击后可以跳转到相应应用的权限界面,消费者可以控制权限的开启和关闭。国内手机厂商最近几年都在增加隐私看板供能,消费者…...

【Jenkins】jenkins发送邮件报错:Not sent to the following valid addresses:
jenkins报错未能发送邮件到指定邮箱 注意:这是在系统配置中修改 在系统配置》邮件通知中添加配置信息 注意:这个是在项目的配置下修改 配置完成后,重新执行发送邮件成功!!!...

CSS3制作3D爱心动画
1、什么是CSS css,即层叠样式表的简称,是一种标记语言,有浏览器解释执行用来使页面变得更美观。 2、选择器 css3中新增了一些选择器,如下: 3、新样式 边框 css3新增了三个边框属性,分别是: bo…...

Python Opencv实践 - 全景图片拼接stitcher
做一个全景图片切片的程序Spliter 由于手里没有切割好的全景图片资源,因此首先写了一个切片的程序spliter。 如果有现成的切割好的待拼接的切片文件,则不需要使用spliter。 对于全景图片的拼接,需要注意一点,各个切片图片之间要有…...

echarts 几千条分钟级别在小时级别图标上展示
需求背景解决效果ISQQW代码地址strategyChart.vue 需求背景 需要实现 秒级数据几千条在图表上显示,(以下是 设计图表上是按小时界别显示数据,后端接口为分钟级别数据) 解决效果 ISQQW代码地址 链接 strategyChart.vue <!--/** * author: liuk *…...

操作系统的中断与异常(408常考点)
为了进行核心态和用户态两种状态的切换,引入了中断机制。 中断是计算机系统中的一种事件,它会打断CPU当前正在执行的程序,转而执行另一个程序或者执行特定的处理程序。中断可以来自外部设备(如键盘、鼠标、网络等)、软…...

linux下的工具---vim
一、了解vim 1、vim是linux的开发工具 2、vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行…...

代码随想录算法训练营第六十天|84. 柱状图中最大的矩形
LeetCode 84. 柱状图中最大的矩形 题目链接:84. 柱状图中最大的矩形 - 力扣(LeetCode) 和接雨水还挺像的。 代码: #python class Solution:def largestRectangleArea(self, heights: List[int]) -> int:heights.insert(0, 0…...

P14 C++局部静态变量static延长生命周期
目录 01 前言 02 变量的作用域与生命周期 2.1 什么是作用域: 2.2 什么是变量的生命周期: 03 局部静态 3.1非静态变量例子 3.2静态变量例子 04 全局变量 05 后话 01 前言 在前几期里,我们了解了static关键字在特定上下文中的含义。 …...

C语言:写一个函数,求字符串的长度,在main函数中输入字符串并输出其长度(指针)
分析: 在程序中,定义一个函数 fix,该函数使用指针变量来访问字符串中的每个字符,并计算出字符串的长度。fix 函数的参数为指向 char 类型的指针变量 p,表示需要计算长度的字符串。 在主函数 main 中,定义一…...

CentOS7安装Docker运行环境
1 引言 Docker 是一个用于开发,交付和运行应用程序的开放平台。Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助 Docker,您可以与管理应用程序相同的方式来管理基础架构。通过利用 Docker 的方法来快速交付,…...

单片机调试技巧--栈回溯
在启动文件中修改 IMPORT rt_hw_hard_fault_exceptionEXPORT HardFault_Handler HardFault_Handler PROC; get current contextTST lr, #0x04 ; if(!EXC_RETURN[2])ITE EQMRSEQ r0, msp ; [2]0 > Z1, get fault context from h…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...

rm视觉学习1-自瞄部分
首先先感谢中南大学的开源,提供了很全面的思路,减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接:https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架: 代码框架结构:readme有…...