为一群追逐兔子的狐狸创建植绒算法的最佳方法

时间:2019-07-10 15:52:13

标签: c++

程序会不断更新每一帧,这使得计算狐狸与兔子之间的距离变得困难(这几乎是一个相同的值)

我尝试使用双精度,以便距离值可以补偿很小的变化,但这没有用。

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

我希望狐狸避免彼此碰撞。

1 个答案:

答案 0 :(得分:1)

几个月前,我偶然发现this terrific blog post,它解释了“仿制”植绒行为。要在狐狸中创造逼真的动作,您需要做一些事情。快速摘要:

  • 每只狐狸都需要意识到这只兔子,因此他们可以跟着它走。
  • 每只狐狸都需要意识到每只其他的狐狸,因此它们可以正常工作。您只提到彼此分开(分离),但是还有其他一些选择:
    • 分离是一种行为,如果一只狐狸靠近另一只狐狸,他们都会试图分开。
    • 凝聚力是一种行为,如果在一起有一组“狐狸”,则每个狐狸都试图朝该组的中心移动。
    • 对齐是一种行为,如果狐狸遇到朝特定方向移动的组,它将尝试匹配该方向。

以您的示例为例,如果狐狸严格表现为追逐兔子并彼此远离,则需要使用分隔。如果将狐狸存储在数组中,则伪代码可能如下所示。

WFUserGroup

但是功能呢?

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

这些仅仅是个简单的例子,我发现,如果将狐狸彼此分开多远,也可以实现更好的运动,即它们越近,它们试图分开的速度就越快。我还发现,如果将它们的位置和速度表示为2D向量(类型,而不是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. } 自展开数组),则管理它们将变得更加容易。

如果您愿意重构代码和/或计划使游戏更大,我强烈建议您将移动的实体(狐狸和兔子)分离到自己的类中,而不是在引擎类中使用双精度数组。这使实现实体的功能,更改其行为以及跟踪其位置和速度变得非常容易得多。

另外,如果程序遇到帧速率问题,以每秒数百帧的速度疯狂检查每帧狐狸的位置,则可以考虑在帧之间使用std::vector来减慢执行速度。

祝你游戏好运!