C++ 为一群追逐兔子的狐狸创建群集算法的最佳方法

C++ 为一群追逐兔子的狐狸创建群集算法的最佳方法,c++,C++,该程序不断更新每一帧,因此很难计算狐狸和兔子之间的距离。这将是一个几乎相同的值 我尝试过使用双精度,这样距离值可以补偿一个小的变化,但这并没有起作用 define OLC_PGE_APPLICATION #include "olcPixelGameEngine.h" int x = 0; class Example : public olc::PixelGameEngine { private: olc::Sprite* rabbit; olc::Sprite* bush;

该程序不断更新每一帧,因此很难计算狐狸和兔子之间的距离。这将是一个几乎相同的值

我尝试过使用双精度,这样距离值可以补偿一个小的变化,但这并没有起作用

define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h"

int x = 0;
class Example : public olc::PixelGameEngine
{
private:
    olc::Sprite* rabbit;
    olc::Sprite* bush;
    olc::Sprite* fox;
    olc::Sprite* evilfox;
    int size = 3;
    double rabbitPosX = 30.0;
    double rabbitPosY = 30.0;
    double rabbitXvel = 0.0;
    double rabbitYvel = 0.0;
    double FoxPosX[3] = { 50.0f,80.0f,70.0};
    double FoxPosY[3] = { 80.0f,50.0f,200.0};
    double FoxXvel = 0.0;
    double FoxYvel = 0.0;

public:
    Example()
    {
        sAppName = "Example";
    }

public:
    double distanceForm(double x1, double x2, double y1, double y2)
    {
        double distance = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
        return distance;
    }
    bool checkCurFox(double arrX[], double arrY[], int size, int count)
    {       
        for (int i = count+1; i < size; i++)
        {
            if (distanceForm(arrX[count], arrX[i], arrY[count], arrY[i]) > 15)
            {
                return true;
            }
        }

    }
    double moveRabbitX(double rabbitX, double VelX, double ETime)
    {
        if (VelX > 0)
        {
            double NewrabbitX = rabbitX + VelX * ETime;
            return NewrabbitX;
        }
        double NewrabbitX = rabbitX + VelX * ETime;
        return NewrabbitX;
    }
    double moveRabbitY(double rabbitY, double VelY, double ETime)
    {
        if (VelY > 0)
        {
            double NewrabbitY = rabbitY + VelY * ETime;
            return NewrabbitY;
        }
        double NewrabbitY = rabbitY + VelY * ETime;
        return NewrabbitY;      
    }
    double moveFoxX(double FoxX, double VelX, double ETime)
    {
        if (VelX > 0)
        {
            double NewFoxX = FoxX + VelX * ETime;
            return NewFoxX;
        }
        double NewFoxX = FoxX + VelX * ETime;
        return NewFoxX;
    }
    double moveFoxY(double FoxY, double VelY, double ETime)
    {
        if (VelY > 0)
        {
            double NewFoxY = FoxY + VelY * ETime;
            return NewFoxY;
        }
        double NewFoxY = FoxY + VelY * ETime;
        return NewFoxY;

    }
    bool OnUserCreate() override
    {
        // Called once at the start, so create things here  
        //DrawRect(0,0,ScreenWidth(),ScreenHeight(),olc::DARK_VERY_DARK_GREEN);

        /*DrawSprite(30,40,)*/
        FillRect(0, 0, ScreenWidth(), ScreenHeight(), olc::VERY_DARK_GREEN);
        DrawString(80, 0, "Fox Escape", olc::RED, 1);
        rabbit =new olc::Sprite("Rabbit.png");
        DrawSprite(30, 30, rabbit,1);
        /*bush = new olc::Sprite("Bush.png");
        DrawSprite(50, 50, bush, 2);*/
        fox = new olc::Sprite("Fox.png");
        //DrawSprite(50, 70, fox, 1);
        evilfox = new olc::Sprite("evilfox.png");   
        return true;
    }
    bool OnUserUpdate(float fElapsedTime) override
    {

        int Omg = 1;
        // called once per frame
        for (int j = 0; j < 3; j++)
        {
            double distance = distanceForm(rabbitPosX, FoxPosX[j], rabbitPosY, FoxPosY[j]);
            double distanceMxUp = distanceForm(rabbitPosX, FoxPosX[j], rabbitPosY, moveFoxY(FoxPosY[j], -20, fElapsedTime));
            double distanceMxDown = distanceForm(rabbitPosX, FoxPosX[j], rabbitPosY, moveFoxY(FoxPosY[j], +20, fElapsedTime));
            double distanceMyRight = distanceForm(moveFoxX(FoxPosX[j], 20, fElapsedTime), rabbitPosX, FoxPosY[j], rabbitPosY);
            double distanceMyLeft = distanceForm(moveFoxX(FoxPosX[j], -20, fElapsedTime), rabbitPosX, FoxPosY[j], rabbitPosY);

            double arr[4] = { distanceMxUp,distanceMxDown,distanceMyRight,distanceMyLeft };
            double temp;
            for (int i = 0; i < 3; i++)
            {
                for (int j = i + 1; j < 3; j++)
                {
                    if (arr[i] > arr[j])
                    {
                        temp = arr[i];
                        arr[i] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
            int tempX = FoxPosX[j];
            int tempY = FoxPosY[j];
            for (int k = 0; k < 3; k++)
            {
                if (checkCurFox(FoxPosX, FoxPosY, size, j))
                {
                    if (arr[k] == distanceMxUp)
                    {
                        k = 3;
                        double NewFoxY = moveFoxY(FoxPosY[j], -10, fElapsedTime);
                        FillRect(FoxPosX[j], FoxPosY[j], 15, 15, olc::VERY_DARK_GREEN);
                        FoxPosY[j] = NewFoxY;
                        DrawSprite(FoxPosX[j], FoxPosY[j], fox, 1);
                        if (distance < 6)
                        {
                            FillRect(0, 0, ScreenWidth(), ScreenHeight(), olc::Pixel(0, 0, 0));
                            DrawSprite(0, 0, evilfox, 1);
                        }
                    }
                    if (arr[k] == distanceMxDown)
                    {
                        k = 3;
                        double NewFoxY = moveFoxY(FoxPosY[j], 10, fElapsedTime);
                        FillRect(FoxPosX[j], FoxPosY[j], 15, 15, olc::VERY_DARK_GREEN);
                        FoxPosY[j] = NewFoxY;
                        DrawSprite(FoxPosX[j], FoxPosY[j], fox, 1);
                        if (distance < 6)
                        {
                            FillRect(0, 0, ScreenWidth(), ScreenHeight(), olc::Pixel(0, 0, 0));
                            DrawSprite(0, 0, evilfox, 1);
                        }
                    }
                    if (arr[k] == distanceMyRight)
                    {
                        k = 3;
                        double NewFoxX = moveFoxX(FoxPosX[j], 10, fElapsedTime);
                        FillRect(FoxPosX[j], FoxPosY[j], 15, 15, olc::VERY_DARK_GREEN);
                        FoxPosX[j] = NewFoxX;
                        DrawSprite(FoxPosX[j], FoxPosY[j], fox, 1);
                        if (distance < 6)
                        {
                            FillRect(0, 0, ScreenWidth(), ScreenHeight(), olc::Pixel(0, 0, 0));
                            DrawSprite(0, 0, evilfox, 1);
                        }
                    }
                    if (arr[k] == distanceMyLeft)
                    {
                        k = 3;
                        double NewFoxX = moveFoxX(FoxPosX[j], -10, fElapsedTime);
                        FillRect(FoxPosX[j], FoxPosY[j], 15, 15, olc::VERY_DARK_GREEN);
                        FoxPosX[j] = NewFoxX;
                        DrawSprite(FoxPosX[j], FoxPosY[j], fox, 1);
                        if (distance < 6)
                        {
                            FillRect(0, 0, ScreenWidth(), ScreenHeight(), olc::Pixel(0, 0, 0));
                            DrawSprite(0, 0, evilfox, 1);
                        }
                    }
                }
            }
        }
        if (GetKey(olc::Key::RIGHT).bHeld)
        {
            rabbitXvel = 30.0f;
            double NewrabbitX = moveRabbitX(rabbitPosX, rabbitXvel, fElapsedTime);
            FillRect(rabbitPosX, rabbitPosY, 7, 8,olc::VERY_DARK_GREEN);
            rabbitPosX = NewrabbitX;
            DrawSprite(rabbitPosX, rabbitPosY, rabbit);     
        }
        if (GetKey(olc::Key::LEFT).bHeld)
        {
            rabbitXvel = -30.0f;
            double NewrabbitX = moveRabbitX(rabbitPosX, rabbitXvel, fElapsedTime);
            FillRect(rabbitPosX, rabbitPosY, 7, 8, olc::VERY_DARK_GREEN);
            rabbitPosX = NewrabbitX;
            DrawSprite(rabbitPosX, rabbitPosY, rabbit);
        }
        if (GetKey(olc::Key::UP).bHeld)
        {
            rabbitYvel = -30.0f;
            double NewrabbitY = moveRabbitY(rabbitPosY, rabbitYvel, fElapsedTime);
            FillRect(rabbitPosX, rabbitPosY, 7, 8, olc::VERY_DARK_GREEN);
            rabbitPosY = NewrabbitY;
            DrawSprite(rabbitPosX, rabbitPosY, rabbit);

        }
        if (GetKey(olc::Key::DOWN).bHeld)
        {
            rabbitYvel = 30.0f;
            double NewrabbitY = moveRabbitY(rabbitPosY, rabbitYvel, fElapsedTime);
            FillRect(rabbitPosX, rabbitPosY, 7, 8, olc::VERY_DARK_GREEN);
            rabbitPosY = NewrabbitY;
            DrawSprite(rabbitPosX, rabbitPosY, rabbit);

        }

        //Draw(rabbitPosX, rabbitPosY, olc::YELLOW);

        return true;
    }
};

int main()
{
    Example demo;
    if (demo.Construct(256, 240, 4, 4))
        demo.Start();

    return 0;
}

我希望狐狸避免相互碰撞。

几个月前,我偶然发现了,这解释了博伊德的群集行为。要在你的狐狸身上创造逼真的动作,你需要做几件事。快速总结:

每只狐狸都需要注意兔子,这样它们就可以跟着兔子到处跑。 每一只狐狸都需要知道其他每一只狐狸,这样它们才能正常地行为。你只提到分开,但还有几个选择: 分离是一种行为,如果一只狐狸靠近另一只狐狸,它们两个都试图分开。 凝聚力是一种行为,如果一群狐狸在一起,每个狐狸都试图朝着群体的中心移动。 对齐是一种行为,如果狐狸遇到朝某个方向移动的群体,它会尝试匹配该方向。 例如,如果狐狸严格地追逐兔子,并且彼此分开,那么你就需要使用分离。如果将狐狸存储在数组中,伪代码可能是这样的

fox foxarray[size_of_array]
rabbit rabb

//separate the foxes if they get too close
for(each fox in foxarray) {
  current_fox = //the fox you're checking
  for(every other fox in foxarray) {
    other_fox = //the other ones you'll look at

    if(distanceFrom(current_fox, other_fox) < some_threshold_value) {
      /*
      use a function to check the positions of the foxes,
      and then adjust their velocities to move away from 
      one another.
      */
      move_apart(curernt_fox, other_fox) 
    }
  }
}

//chase the rabbit
for(each fox in foxarray) {
  current_fox = //the fox you're checking
/*
this would check their positions, and then adjust the fox's velocity 
to move it towards the rabbit. 
i'm assuming the rabbit is player-controlled, so its velocity
wouldn't be changed
*/
  move_towards(current_fox, rabb) 
}
但是功能呢

void move_apart(fox f1, fox f2) {

  //set f1's velocity away from f2
  //first, get f2's position relative to f1, then add velocity
  //to move away from it
  f2xPos = f2.x - f1.x;
  f2yPos = f2.y - f1.y;


  if(f2xPos < 0) {
    //if f2xPos is negative, add some positive x velocity to move away
    f1xVel += some_velocity;
  }
  else {
    //add some negative velocity to move away
    f1xVel -= some_velocity;
  }

  //repeat for f2 - or, alternatively, call move_apart() with
  //f2 and f1 flipped.

}

//============================================================

void move_towards(fox f, rabbit r) {

  //very similar to move_apart, but you'd flip the signs of
  //the velocity so you can move the fox towards the rabbit.

}
这些只是简单的例子,我发现如果你考虑到狐狸彼此之间的距离,也就是说,它们离得越近,它们尝试分开的速度就越快,那么它们的移动就会更好。我还发现,如果将它们的位置和速度表示为2D向量,而不是std::vector自扩展数组,那么管理它们就会变得更容易

如果您愿意重构代码和/或计划使游戏更大,我强烈建议将移动实体fox和rabbit分离到它们自己的类中,而不是引擎类中的双倍数组。这使得实现实体的功能、改变它们的行为以及跟踪它们的位置和速度变得越来越容易

另外,如果你遇到程序的FraseTATE问题,在每秒几百帧的情况下,每帧检查狐狸的位置,可能会考虑在帧之间使用Eclipse来减慢执行速度。


祝你的比赛好运

现在还不清楚你到底有什么问题。以上代码的结果是什么?它与您想要的有什么不同?也就是说,使用==来比较浮点数是自找麻烦的。另请参见,将群集算法的逻辑与GUI分离是一个好主意。这使代码更清晰,并允许对其进行独立测试。@pcarter结果是狐狸向上移动到兔子身上,但只在第一次处理时向上移动。这是因为差异非常小,因为所用的时间以微秒为单位。你也有大量的定位/移动逻辑。试着把它和代码中有趣的部分分开。