美文网首页
网易C++--书籍练习案例02--互动粒子

网易C++--书籍练习案例02--互动粒子

作者: heiqimingren | 来源:发表于2020-11-08 19:01 被阅读0次

第一个版本:静止小球的初始化和显示

#define WIDTH 1024         //屏幕的宽
#define HEIGHT 768         //屏幕的高
#define NUM_MOVERS 800     //小球的数量

//定义小球结构
struct Mover
{
    COLORREF color;         //颜色
    float x, y;             //坐标
    float vx, vy;           //速度
    float radius;           //半径
};

//定义全局变量
Mover movers[NUM_MOVERS];      //小球数组,每个数组元素都是一个结构体

void startup()
{
    //设置随机种子
    srand((unsigned int)time(NULL));
    //初始化小球数组
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        movers[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
        movers[i].x = rand() % WIDTH;
        movers[i].y = rand() % HEIGHT;
        movers[i].vx = float(cos(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦,重点!
        movers[i].vy = float(sin(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦
        movers[i].radius = (rand() % 34) / 15.0;
    }
    initgraph(WIDTH, HEIGHT);
    BeginBatchDraw();
}
void show()
{
    clearrectangle(0, 0, WIDTH - 1, HEIGHT - 1);   //清空画面中的全部矩形区域
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        //画小球
        setcolor(movers[i].color);
        setfillstyle(movers[i].color);
        fillcircle(int(movers[i].x + 0.5), int(movers[i].y + 0.5), int(movers[i].radius + 0.5));    
    }
    FlushBatchDraw();
    Sleep(2);
}
void updateWithoutInput()
{

}
void updateWithInput()
{

}
void gameover()
{
    EndBatchDraw();
    closegraph();
}
int main()
{
    startup();
    while (1)
    {
        show();
        updateWithoutInput();
        updateWithInput();
    }
    gameover();
    return 0;
}

image.png

================================================================
第二版本:小球的运动与反弹

#include<stdio.h>
#include<string.h>
#include<stdlib.h>    //清屏命令在这里。
#include <windows.h> //延时10毫秒-sleep,gotoxy函数
#include <iostream>
#include <conio.h>       //getch()----不用按回车,就可以输入字符。    
#include <graphics.h>      //EasyX库,所使用的!
#include <math.h>         //三角函数
#include <algorithm>      //sort函数排序方法,https://blog.csdn.net/BIGKALA/article/details/81624691,这是方法适用的地址
#include <time.h>


using namespace std;



#define WIDTH 1024         //屏幕的宽
#define HEIGHT 768         //屏幕的高
#define NUM_MOVERS 800     //小球的数量

//定义小球结构
struct Mover
{
    COLORREF color;         //颜色
    float x, y;             //坐标
    float vx, vy;           //速度
    float radius;           //半径
};

//定义全局变量
Mover movers[NUM_MOVERS];      //小球数组,每个数组元素都是一个结构体

void startup()
{
    //设置随机种子
    srand((unsigned int)time(NULL));

    //初始化小球数组
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        movers[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
        movers[i].x = rand() % WIDTH;
        movers[i].y = rand() % HEIGHT;
        movers[i].vx = float(cos(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦,重点!
        movers[i].vy = float(sin(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦
        movers[i].radius = (rand() % 34) / 15.0;
    }
    initgraph(WIDTH, HEIGHT);
    BeginBatchDraw();
}
void show()
{
    clearrectangle(0, 0, WIDTH - 1, HEIGHT - 1);   //清空画面中的全部矩形区域
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        //画小球
        setcolor(movers[i].color);
        setfillstyle(movers[i].color);
        fillcircle(int(movers[i].x + 0.5), int(movers[i].y + 0.5), int(movers[i].radius + 0.5));
        
    }
    FlushBatchDraw();
    Sleep(2);

}


void updateWithoutInput()
{
    for (int i = 0; i < NUM_MOVERS;i++)      //对所有小球进行遍历
    {
        float x = movers[i].x;      //当前小球的坐标
        float y = movers[i].y;
        float vx = movers[i].vx;     //当前小球的速度
        float vy = movers[i].vy;

        //根据 位置+速度,来更新小球的坐标
        float nextX = x + vx;
        float nextY = y + vy;

        //如果小球超过上下左右四个边界,就将位置设为边界处,速度反向
        if (nextX>WIDTH)  //右边边界
        {
            nextX = WIDTH;
            vx = -1 * vx;
        }
        else if (nextX<0 )  //左边边界
        {
            nextX = 0;
            vx = -1 * vx;
        }
        if (nextY>HEIGHT )   //下边边界
        {
            nextY = HEIGHT;
            vy = -1 * vy;
        }
        else if (nextY<0 )      //上边边界
        {
            nextY = 0;
            vy = -1 * vy;
        }

        //更新小球位置,速度的结构体数组
        movers[i].vx = vx;
        movers[i].vy = vy;
        movers[i].x = nextX;
        movers[i].y = nextY;


    }

    
}
void updateWithInput()
{

}
void gameover()
{
    EndBatchDraw();
    closegraph();
}

int main()
{
    startup();
    while (1)
    {
        show();
        updateWithoutInput();
        updateWithInput();
    }
    gameover();
    return 0;
}

效果图片。注意图片里的小球都是动态的


image.png

======================================================================

第三版本:小球运动的规范化
加入了阻尼运动

#define WIDTH 1024         //屏幕的宽
#define HEIGHT 768         //屏幕的高
#define NUM_MOVERS 800     //小球的数量
#define FRICTION 0.96f    //摩擦力,阻力系数



//定义小球结构
struct Mover
{
    COLORREF color;         //颜色
    float x, y;             //坐标
    float vx, vy;           //速度
    float radius;           //半径
};

//定义全局变量
Mover movers[NUM_MOVERS];      //小球数组,每个数组元素都是一个结构体

void startup()
{
    //设置随机种子
    srand((unsigned int)time(NULL));

    //初始化小球数组
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        movers[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
        movers[i].x = rand() % WIDTH;
        movers[i].y = rand() % HEIGHT;
        movers[i].vx = float(cos(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦,重点!
        movers[i].vy = float(sin(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦
        movers[i].radius = (rand() % 34) / 15.0;
    }
    initgraph(WIDTH, HEIGHT);
    BeginBatchDraw();
}
void show()
{
    clearrectangle(0, 0, WIDTH - 1, HEIGHT - 1);   //清空画面中的全部矩形区域
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        //画小球
        setcolor(movers[i].color);
        setfillstyle(movers[i].color);
        fillcircle(int(movers[i].x + 0.5), int(movers[i].y + 0.5), int(movers[i].radius + 0.5));
        
    }
    FlushBatchDraw();
    Sleep(2);

}


void updateWithoutInput()
{
    for (int i = 0; i < NUM_MOVERS;i++)      //对所有小球进行遍历
    {
        float x = movers[i].x;      //当前小球的坐标
        float y = movers[i].y;
        float vx = movers[i].vx;     //当前小球的速度
        float vy = movers[i].vy;

        //小球运动有一个阻尼,受到摩擦力限制,速度逐渐减小.每次循环,就减小一次
        vx = vx*FRICTION;
        vy = vy*FRICTION;
        //速度的绝对值
        float avgVX = abs(vx);
        float avgVY = abs(vy);
        //两个方向,速度的平均值
        float avgV = (avgVX + avgVY)*0.5f;

        //因为有上面阻尼的作用,如果速度过小,乘以一个0-3的随机数,会以比较大的概率让速度变大
        if (avgVX<0.1 )
        {
            vx = vx* float(rand()) / RAND_MAX * 3;
        }
        if ( avgVY<0.1)
        {
            vy = vy*float(rand()) / RAND_MAX * 3;
        }
        //小球的半径在0.4-3.5之间,速度越大,半径越大
        float sc = avgV*0.45f;
        sc = max(min(sc, 3.5f), 0.4f);
        movers[i].radius = sc;

        //根据 位置+速度,来更新小球的坐标
        float nextX = x + vx;
        float nextY = y + vy;

        //如果小球超过上下左右四个边界,就将位置设为边界处,速度反向
        if (nextX>WIDTH)  //右边边界
        {
            nextX = WIDTH;
            vx = -1 * vx;
        }
        else if (nextX<0 )  //左边边界
        {
            nextX = 0;
            vx = -1 * vx;
        }
        if (nextY>HEIGHT )   //下边边界
        {
            nextY = HEIGHT;
            vy = -1 * vy;
        }
        else if (nextY<0 )      //上边边界
        {
            nextY = 0;
            vy = -1 * vy;
        }

        //更新小球位置,速度的结构体数组
        movers[i].vx = vx;
        movers[i].vy = vy;
        movers[i].x = nextX;
        movers[i].y = nextY;


    }

    
}
void updateWithInput()
{

}
void gameover()
{
    EndBatchDraw();
    closegraph();
}

int main()
{
    startup();
    while (1)
    {
        show();
        updateWithoutInput();
        updateWithInput();
    }
    gameover();
    return 0;
}

===============================================
第四版本:鼠标的吸引力


#define WIDTH 1024         //屏幕的宽
#define HEIGHT 768         //屏幕的高
#define NUM_MOVERS 800     //小球的数量
#define FRICTION 0.96f    //摩擦力,阻力系数



//定义小球结构
struct Mover
{
    COLORREF color;         //颜色
    float x, y;             //坐标
    float vx, vy;           //速度
    float radius;           //半径
};

//定义全局变量
Mover movers[NUM_MOVERS];      //小球数组,每个数组元素都是一个结构体
int mouseX, mouseY;               //当前鼠标坐标


void startup()
{
    //设置随机种子
    srand((unsigned int)time(NULL));

    //初始化小球数组
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        movers[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
        movers[i].x = rand() % WIDTH;
        movers[i].y = rand() % HEIGHT;
        movers[i].vx = float(cos(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦,重点!
        movers[i].vy = float(sin(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦
        movers[i].radius = (rand() % 34) / 15.0;
    }

    //初始化当前鼠标坐标在画布中心;
    mouseX = WIDTH / 2;
    mouseY = HEIGHT / 2;

    initgraph(WIDTH, HEIGHT);
    BeginBatchDraw();
}
void show()
{
    clearrectangle(0, 0, WIDTH - 1, HEIGHT - 1);   //清空画面中的全部矩形区域
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        //画小球
        setcolor(movers[i].color);
        setfillstyle(movers[i].color);
        fillcircle(int(movers[i].x + 0.5), int(movers[i].y + 0.5), int(movers[i].radius + 0.5));
        
    }
    FlushBatchDraw();
    Sleep(2);

}

void updateWithoutInput()
{
    float toDist = WIDTH *0.86;         //吸引距离,如果小球与鼠标的距离在此范围内,就会被吸引

    for (int i = 0; i < NUM_MOVERS;i++)      //对所有小球进行遍历
    {
        float x = movers[i].x;      //当前小球的坐标
        float y = movers[i].y;
        float vx = movers[i].vx;     //当前小球的速度
        float vy = movers[i].vy;

        float dx = x - mouseX;   //计算当前小球和鼠标坐标的距离,可能是整数,也可能是负数
        float dy = y - mouseY;
        float d = sqrt(dx*dx + dy*dy);   //当前小球和鼠标位置的距离
        //下面将dx和dy,归一化,仅仅取其方向,和距离无关
        if (d!=0 )
        {
            dx = dx / d;
            dy = dy / d;
        }
        else
        {
            dx = 0;
            dy = 0;
        }
        //小球距离鼠标<toDist ,在此范围内小球会受到鼠标的吸引
        if (d<toDist )
        {
            //吸引力引起的加速度服务,小球距离鼠标越近引起的加速度越大           d越来越小--》toACC,越来越大。
            float toAcc = (1 - (d / toDist))*WIDTH*0.0014f;
            //由dx,dy归一化方向信息,加速度幅度值为toAcc,得到新的小球速度
            vx = vx - dx*toAcc;
            vy = vy - dy*toAcc;
        }

        //小球运动有一个阻尼,受到摩擦力限制,速度逐渐减小.每次循环,就减小一次
        vx = vx*FRICTION;
        vy = vy*FRICTION;
        //速度的绝对值
        float avgVX = abs(vx);
        float avgVY = abs(vy);
        //两个方向,速度的平均值
        float avgV = (avgVX + avgVY)*0.5f;

        //因为有上面阻尼的作用,如果速度过小,乘以一个0-3的随机数,会以比较大的概率让速度变大
        if (avgVX<0.1 )
        {
            vx = vx* float(rand()) / RAND_MAX * 3;
        }
        if ( avgVY<0.1)
        {
            vy = vy*float(rand()) / RAND_MAX * 3;
        }
        //小球的半径在0.4-3.5之间,速度越大,半径越大
        float sc = avgV*0.45f;
        sc = max(min(sc, 3.5f), 0.4f);
        movers[i].radius = sc;

        //根据 位置+速度,来更新小球的坐标
        float nextX = x + vx;
        float nextY = y + vy;

        //如果小球超过上下左右四个边界,就将位置设为边界处,速度反向
        if (nextX>WIDTH)  //右边边界
        {
            nextX = WIDTH;
            vx = -1 * vx;
        }
        else if (nextX<0 )  //左边边界
        {
            nextX = 0;
            vx = -1 * vx;
        }
        if (nextY>HEIGHT )   //下边边界
        {
            nextY = HEIGHT;
            vy = -1 * vy;
        }
        else if (nextY<0 )      //上边边界
        {
            nextY = 0;
            vy = -1 * vy;
        }

        //更新小球位置,速度的结构体数组
        movers[i].vx = vx;
        movers[i].vy = vy;
        movers[i].x = nextX;
        movers[i].y = nextY;
    }   
}
void updateWithInput()
{
    MOUSEMSG m;      //定义鼠标消息
    while (MouseHit())      //检测当前是否有鼠标消息
    {
        m = GetMouseMsg();
        if (m.uMsg==WM_MOUSEMOVE )    //如果鼠标移动,更新当前鼠标坐标变量。
        {
            mouseX = m.x;
            mouseY = m.y;
        }
    }

}
void gameover()
{
    EndBatchDraw();
    closegraph();
}
int main()
{
    startup();
    while (1)
    {
        show();
        updateWithoutInput();
        updateWithInput();
    }
    gameover();
    return 0;
}

效果图:


image.png

===========================================================================
第五版本,鼠标的击打斥力

#define WIDTH 1024         //屏幕的宽
#define HEIGHT 768         //屏幕的高
#define NUM_MOVERS 800     //小球的数量
#define FRICTION 0.96f    //摩擦力,阻力系数



//定义小球结构
struct Mover
{
    COLORREF color;         //颜色
    float x, y;             //坐标
    float vx, vy;           //速度
    float radius;           //半径
};

//定义全局变量
Mover movers[NUM_MOVERS];      //小球数组,每个数组元素都是一个结构体
int mouseX, mouseY;               //当前鼠标坐标
int isMouseDown;                 //鼠标左键是否按下

void startup()
{
    //设置随机种子
    srand((unsigned int)time(NULL));

    //初始化小球数组
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        movers[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
        movers[i].x = rand() % WIDTH;
        movers[i].y = rand() % HEIGHT;
        movers[i].vx = float(cos(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦,重点!
        movers[i].vy = float(sin(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦
        movers[i].radius = (rand() % 34) / 15.0;
    }

    //初始化当前鼠标坐标在画布中心;
    mouseX = WIDTH / 2;
    mouseY = HEIGHT / 2;

    isMouseDown = 0;      //初始鼠标未按下

    initgraph(WIDTH, HEIGHT);
    BeginBatchDraw();

}
void show()
{
    clearrectangle(0, 0, WIDTH - 1, HEIGHT - 1);   //清空画面中的全部矩形区域
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        //画小球
        setcolor(movers[i].color);
        setfillstyle(movers[i].color);
        fillcircle(int(movers[i].x + 0.5), int(movers[i].y + 0.5), int(movers[i].radius + 0.5));
        
    }
    FlushBatchDraw();
    Sleep(2);

}


void updateWithoutInput()
{
    float toDist = WIDTH *0.86;         //吸引距离,如果小球与鼠标的距离在此范围内,就会被吸引
    float blowDist = WIDTH*0.5;         //击打距离,小球与鼠标的距离在此范围内,都会受到击打的斥力



    for (int i = 0; i < NUM_MOVERS;i++)      //对所有小球进行遍历
    {
        float x = movers[i].x;      //当前小球的坐标
        float y = movers[i].y;
        float vx = movers[i].vx;     //当前小球的速度
        float vy = movers[i].vy;

        float dx = x - mouseX;   //计算当前小球和鼠标坐标的距离,可能是整数,也可能是负数
        float dy = y - mouseY;
        float d = sqrt(dx*dx + dy*dy);   //当前小球和鼠标位置的距离
        //下面将dx和dy,归一化,仅仅取其方向,和距离无关
        if (d!=0 )
        {
            dx = dx / d;
            dy = dy / d;
        }
        else
        {
            dx = 0;
            dy = 0;
        }
        //小球距离鼠标<toDist ,在此范围内小球会受到鼠标的吸引
        if (d<toDist )
        {
            //吸引力引起的加速度服务,小球距离鼠标越近引起的加速度越大           d越来越小--》toACC,越来越大。
            float toAcc = (1 - (d / toDist))*WIDTH*0.0014f;
            //由dx,dy归一化方向信息,加速度幅度值为toAcc,得到新的小球速度
            vx = vx - dx*toAcc;
            vy = vy - dy*toAcc;
        }
        //当鼠标左键按下,并且小球距离鼠标<blowDist,在击打范围内时会受到向外的力。
        if (isMouseDown && d < blowDist)
        {
            //击打力引起的加速度幅度,这个公式表示小球距离鼠标越近击打斥力引起的加速度越大
            float blowAcc = (1 - (d / blowDist)) * 6;
            //0.5f - float(rand()) / RAND_MAX; --->产生-0.5到0.5的随机数,加入一些扰动
            //float(rand()) / RAND_MAX   --->产生0-1的随机数。
            //得到最新的小球速度。

            vx = vx + dx*blowAcc + 0.5f - float(rand()) / RAND_MAX;
            vy = vy + dy*blowAcc + 0.5f - float(rand()) / RAND_MAX;



        }




        //小球运动有一个阻尼,受到摩擦力限制,速度逐渐减小.每次循环,就减小一次
        vx = vx*FRICTION;
        vy = vy*FRICTION;
        //速度的绝对值
        float avgVX = abs(vx);
        float avgVY = abs(vy);
        //两个方向,速度的平均值
        float avgV = (avgVX + avgVY)*0.5f;

        //因为有上面阻尼的作用,如果速度过小,乘以一个0-3的随机数,会以比较大的概率让速度变大
        if (avgVX<0.1 )
        {
            vx = vx* float(rand()) / RAND_MAX * 3;
        }
        if ( avgVY<0.1)
        {
            vy = vy*float(rand()) / RAND_MAX * 3;
        }
        //小球的半径在0.4-3.5之间,速度越大,半径越大
        float sc = avgV*0.45f;
        sc = max(min(sc, 3.5f), 0.4f);
        movers[i].radius = sc;

        //根据 位置+速度,来更新小球的坐标
        float nextX = x + vx;
        float nextY = y + vy;

        //如果小球超过上下左右四个边界,就将位置设为边界处,速度反向
        if (nextX>WIDTH)  //右边边界
        {
            nextX = WIDTH;
            vx = -1 * vx;
        }
        else if (nextX<0 )  //左边边界
        {
            nextX = 0;
            vx = -1 * vx;
        }
        if (nextY>HEIGHT )   //下边边界
        {
            nextY = HEIGHT;
            vy = -1 * vy;
        }
        else if (nextY<0 )      //上边边界
        {
            nextY = 0;
            vy = -1 * vy;
        }

        //更新小球位置,速度的结构体数组
        movers[i].vx = vx;
        movers[i].vy = vy;
        movers[i].x = nextX;
        movers[i].y = nextY;
    }

    
}
void updateWithInput()
{
    MOUSEMSG m;      //定义鼠标消息
    while (MouseHit())      //检测当前是否有鼠标消息
    {
        m = GetMouseMsg();
        if (m.uMsg==WM_MOUSEMOVE )    //如果鼠标移动,更新当前鼠标坐标变量。
        {
            mouseX = m.x;
            mouseY = m.y;
        }
        else if (m.uMsg ==WM_LBUTTONDOWN ) //左键按下
        {
            isMouseDown = 1;
        }
        else if (m.uMsg ==WM_LBUTTONUP )  //左键抬起
        {
            isMouseDown = 0;
        }
    }

}
void gameover()
{
    EndBatchDraw();
    closegraph();
}

int main()
{
    startup();
    while (1)
    {
        show();
        updateWithoutInput();
        updateWithInput();
    }
    gameover();
    return 0;
}

===================================================
第六版本:鼠标的干扰力。实现鼠标移动时对例子的扰动力,类似于在水面上拿树枝搅动,搅动的越快,扰动越大。

#define WIDTH 1024         //屏幕的宽
#define HEIGHT 768         //屏幕的高
#define NUM_MOVERS 800     //小球的数量
#define FRICTION 0.96f    //摩擦力,阻力系数



//定义小球结构
struct Mover
{
    COLORREF color;         //颜色
    float x, y;             //坐标
    float vx, vy;           //速度
    float radius;           //半径
};

//定义全局变量
Mover movers[NUM_MOVERS];      //小球数组,每个数组元素都是一个结构体
int mouseX, mouseY;               //当前鼠标坐标
int isMouseDown;                 //鼠标左键是否按下
int prevMouseX, prevMouseY;      //上次鼠标位置
int mouseVX, mouseVY;            //鼠标的速度, 

void startup()
{
    //设置随机种子
    srand((unsigned int)time(NULL));

    //初始化小球数组
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        movers[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
        movers[i].x = rand() % WIDTH;
        movers[i].y = rand() % HEIGHT;
        movers[i].vx = float(cos(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦,重点!
        movers[i].vy = float(sin(float(i)))*(rand() % 34);       //这个速度变化不是线性变化哦
        movers[i].radius = (rand() % 34) / 15.0;
    }

    //初始化当前鼠标坐标在画布中心;
    mouseX = prevMouseX= WIDTH / 2;
    mouseY = prevMouseY= HEIGHT / 2;

    isMouseDown = 0;      //初始鼠标未按下

    initgraph(WIDTH, HEIGHT);
    BeginBatchDraw();

}
void show()
{
    clearrectangle(0, 0, WIDTH - 1, HEIGHT - 1);   //清空画面中的全部矩形区域
    for (int i = 0; i < NUM_MOVERS;i++)
    {
        //画小球
        setcolor(movers[i].color);
        setfillstyle(movers[i].color);
        fillcircle(int(movers[i].x + 0.5), int(movers[i].y + 0.5), int(movers[i].radius + 0.5));
        
    }
    FlushBatchDraw();
    Sleep(2);

}


void updateWithoutInput()
{
    float toDist = WIDTH *0.86;         //吸引距离,如果小球与鼠标的距离在此范围内,就会被吸引
    float blowDist = WIDTH*0.5;         //击打距离,小球与鼠标的距离在此范围内,都会受到击打的斥力
    float stirDist = WIDTH*0.125;       //扰动距离,小球与鼠标的距离在此范围内,都会受到鼠标的扰动

    //前后两次运行间鼠标移动的距离,即为鼠标的速度
    mouseVX = mouseX - prevMouseX;
    mouseVY = mouseY - prevMouseY;
    //为记录这次鼠标的坐标,更新上次鼠标坐标变量。
    prevMouseX = mouseX;
    prevMouseY = mouseY;


    for (int i = 0; i < NUM_MOVERS;i++)      //对所有小球进行遍历
    {
        float x = movers[i].x;      //当前小球的坐标
        float y = movers[i].y;
        float vx = movers[i].vx;     //当前小球的速度
        float vy = movers[i].vy;

        float dx = x - mouseX;   //计算当前小球和鼠标坐标的距离,可能是整数,也可能是负数
        float dy = y - mouseY;
        float d = sqrt(dx*dx + dy*dy);   //当前小球和鼠标位置的距离
        //下面将dx和dy,归一化,仅仅取其方向,和距离无关
        if (d!=0 )
        {
            dx = dx / d;
            dy = dy / d;
        }
        else
        {
            dx = 0;
            dy = 0;
        }
        //小球距离鼠标<toDist ,在此范围内小球会受到鼠标的吸引
        if (d<toDist )
        {
            //吸引力引起的加速度服务,小球距离鼠标越近引起的加速度越大           d越来越小--》toACC,越来越大。
            float toAcc = (1 - (d / toDist))*WIDTH*0.0014f;
            //由dx,dy归一化方向信息,加速度幅度值为toAcc,得到新的小球速度
            vx = vx - dx*toAcc;
            vy = vy - dy*toAcc;
        }
        //当鼠标左键按下,并且小球距离鼠标<blowDist,在击打范围内时会受到向外的力。
        if (isMouseDown && d < blowDist)
        {
            //击打力引起的加速度幅度,这个公式表示小球距离鼠标越近击打斥力引起的加速度越大
            float blowAcc = (1 - (d / blowDist)) * 6;
            //0.5f - float(rand()) / RAND_MAX; --->产生-0.5到0.5的随机数,加入一些扰动
            //float(rand()) / RAND_MAX   --->产生0-1的随机数。
            //得到最新的小球速度。

            vx = vx + dx*blowAcc + 0.5f - float(rand()) / RAND_MAX;
            vy = vy + dy*blowAcc + 0.5f - float(rand()) / RAND_MAX;

        }

        //若小球距离鼠标<stirDist,在此范围内小球会受到鼠标的扰动
        if (d<stirDist ) 
        {
            //扰动力引起的加速度幅度,小球距离鼠标越近引起的加速度越大,扰动力值更小。
            float mAcc = (1 - (d / stirDist))*WIDTH*0.00026f;
            //鼠标速度越快,引起的扰动力越大。
            vx = vx + mouseVX*mAcc;
            vy = vy + mouseVY*mAcc;

        }

        //小球运动有一个阻尼,受到摩擦力限制,速度逐渐减小.每次循环,就减小一次
        vx = vx*FRICTION;
        vy = vy*FRICTION;
        //速度的绝对值
        float avgVX = abs(vx);
        float avgVY = abs(vy);
        //两个方向,速度的平均值
        float avgV = (avgVX + avgVY)*0.5f;

        //因为有上面阻尼的作用,如果速度过小,乘以一个0-3的随机数,会以比较大的概率让速度变大
        if (avgVX<0.1 )
        {
            vx = vx* float(rand()) / RAND_MAX * 3;
        }
        if ( avgVY<0.1)
        {
            vy = vy*float(rand()) / RAND_MAX * 3;
        }
        //小球的半径在0.4-3.5之间,速度越大,半径越大
        float sc = avgV*0.45f;
        sc = max(min(sc, 3.5f), 0.4f);
        movers[i].radius = sc;

        //根据 位置+速度,来更新小球的坐标
        float nextX = x + vx;
        float nextY = y + vy;

        //如果小球超过上下左右四个边界,就将位置设为边界处,速度反向
        if (nextX>WIDTH)  //右边边界
        {
            nextX = WIDTH;
            vx = -1 * vx;
        }
        else if (nextX<0 )  //左边边界
        {
            nextX = 0;
            vx = -1 * vx;
        }
        if (nextY>HEIGHT )   //下边边界
        {
            nextY = HEIGHT;
            vy = -1 * vy;
        }
        else if (nextY<0 )      //上边边界
        {
            nextY = 0;
            vy = -1 * vy;
        }

        //更新小球位置,速度的结构体数组
        movers[i].vx = vx;
        movers[i].vy = vy;
        movers[i].x = nextX;
        movers[i].y = nextY;
    }

    
}
void updateWithInput()
{
    MOUSEMSG m;      //定义鼠标消息
    while (MouseHit())      //检测当前是否有鼠标消息
    {
        m = GetMouseMsg();
        if (m.uMsg==WM_MOUSEMOVE )    //如果鼠标移动,更新当前鼠标坐标变量。
        {
            mouseX = m.x;
            mouseY = m.y;
        }
        else if (m.uMsg ==WM_LBUTTONDOWN ) //左键按下
        {
            isMouseDown = 1;
        }
        else if (m.uMsg ==WM_LBUTTONUP )  //左键抬起
        {
            isMouseDown = 0;
        }
    }

}
void gameover()
{
    EndBatchDraw();
    closegraph();
}

int main()
{
    startup();
    while (1)
    {
        show();
        updateWithoutInput();
        updateWithInput();
    }
    gameover();
    return 0;
}

相关文章

网友评论

      本文标题:网易C++--书籍练习案例02--互动粒子

      本文链接:https://www.haomeiwen.com/subject/xibhbktx.html