额外减小 发表于 2023-1-30 02:20:50

看烟花

本帖最后由 额外减小 于 2023-1-30 21:16 编辑

前言



最近几天烟花放的很狂啊...
天天看烟花
于是我想到了制作燃放烟花的程序
友情提示(预防针/心理准备)
没有GUI
就是黑框框
而且还一卡一卡的(因为坐标只能是整数)
但我保证了24帧/秒(楽)

设计意图

通过自主设计程序,实现放烟花的效果,不仅节约了放烟花的资金,还能在练习中提升自己的编程能力!

设计思路

1.依据简单的力学知识,计算烟花坐标随时间变化的函数
2.每帧都有其对应的时间,带入函数求出坐标,在该坐标按指定颜色打印出烟花点('*')即可
3.通过指针传递来初始化次级烟花数组。

可变因素

1.重力加速度({:10_256:} )
2.每秒播放的帧数(默认24)
3.初始参数:起始点、初速度、发射角、颜色参数
4.每个烟花携带次级烟花数目
5.烟花爆炸前的飞行时间

源代码

#define _XOPEN_SOURCE
#define _GNU_SOURCE
#define _BSD_SOURCE
#define _USE_MATH_DEFINES

#include <math.h>
#include <stdio.h>
#include <windows.h>

#define G 9.8//重力加速度                                  /*可变*/
#define sleeptm 42// 保持 24 帧每秒 (加上运行时间)             /*可变:每秒的帧数*/

typedef struct _FPOINT
{
        double x;
        double y;
} FPOINT;

typedef struct Firework
{
        FPOINT startp; //出发点 (正常坐标系,x=Pos_x,y=-Pos_y)
        double startv; //初速度
        double angle; //发射角 , RAD
        int color; //烟花颜色(1-15)
        int petal; //花瓣个数
} FW;


void Pos(int x,int y);
void color(int type);
void getcoord(FPOINT *endp,FPOINT startp,double startv,double angle,int mstime);
int _round(double origin);
double mod(double a,double m,int *i);

FW * launchFATfirework(FW FATHER);
void launchSONfirework(int argc,FW * SONarray);


void Pos(int x,int y)
/*
设置光标位置
运行框左上方:(0,0)
从定位光标开始打印
若换行,变为从行首打印
*/
{
    COORD pos;
    pos.X=x;
    pos.Y=y;
    HANDLE hOutPut;
    hOutPut=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hOutPut,pos);
}

void color(int type)
/*设置颜色*/
{
        if((type>=0)||(type<=15))
        {
                SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),type);
        }
        else
        {
                SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
        }
}

int _round(double origin)
/*四舍五入函数
用于烟花位置打印
(精确度为1)*/
{
        double minus_origin=-origin;
        if(origin>=0)
        {
                if(origin-(int)origin>=0.5)
                {
                        return ((int)origin)+1;
                }
                else
                {
                        return (int)origin;
                }
        }
        else
        {
                if(minus_origin-(int)minus_origin>=0.5)
                {
                        return (int)origin-1;
                }
                else
                {
                        return (int)origin;
                }
        }
}

double mod(double a,double m,int *i)
//广义上取余数函数
//因为求余符号 % 只能用于整数
{
        *i=0;
        if(m<=0)
        {
                return -1;
        }
        if(a>=0)
        {
                while((*i)*m<=a)
                {
                        (*i)++;
                }
                return a-(*i-1)*m;
        }
        else
        {
                while((*i)*m+a<0)
                {
                        (*i)++;
                }
                return a+(*i)*m;
        }
}

void getcoord(FPOINT *endp,FPOINT startp,double startv,double angle,int mstime)
//获取烟花坐标(严格依据运动学定理)
{
        endp->x=startp.x+startv*cos(angle)*mstime/1000;
        endp->y=startp.y+startv*sin(angle)*mstime/1000-G*mstime*mstime/2000000;
}

FW * launchFATfirework(FW FATHER)//发射一级烟花
{
        int mstime=0,explosion,i,count;
        if(mod(FATHER.angle,2*M_PI,&i)<M_PI)
        {
                explosion=(int)(1000*(FATHER.startv*sin(FATHER.angle)/G)); // 当烟花飞行至最高点时爆炸
        }
        else
        {
                explosion=3000;                                  /*可变:一级烟花向下发射时的爆炸时间*/
        }
        FPOINT now;
        while(mstime<=explosion)
        {
                getcoord(&now,FATHER.startp,FATHER.startv,FATHER.angle,mstime);
                Pos(_round(now.x),-_round(now.y));
                color(FATHER.color);
                printf("*");
                Sleep(sleeptm);
                mstime+=sleeptm;
                system("cls");
        }
       
        FW *SONarray=(FW *)malloc(sizeof(FW)*FATHER.petal);
        FW *ptr=SONarray;
       
        for(count=0;count<FATHER.petal;count++,ptr++)
        {
                ptr->startp.x=now.x;
                ptr->startp.y=now.y;
                ptr->startv=15;                                                                   /*可变:二级烟花的初速度*/
                ptr->angle=0.5+2*M_PI/FATHER.petal*count; //设置为均匀放射(相邻两初速度方向夹角相等)
                ptr->color=FATHER.color;
                ptr->petal=0; //每瓣二级烟花附带的三级烟花数量 (一般设为 0 )
        }
       
        return SONarray;
}

void launchSONfirework(int argc,FW * SONarray)
{
        int mstime=0,i,count;
        FPOINT p;
        FW *ptr=SONarray;
        while(mstime<1000)//explosion                                       /*可变:二级烟花爆炸时间*/
        {
                for(count=0;count<argc;count++)
                {
                        getcoord(&p,SONarray.startp,SONarray.startv,SONarray.angle,mstime);
                        Pos(_round(p.x),-_round(p.y));
                        color(SONarray.color);
                        printf("*");
                }
                Sleep(sleeptm);
                mstime+=sleeptm;
                system("cls");
        }
}

int main()
{
        int c;
        FPOINT p={100,-50};                                     /*可变:一级烟花起始点坐标(正常坐标系,x轴向右,y轴向上)*/
        FW father=
        {
                p,
                25,                /*可变:初速度*/
                1.8,               /*可变:发射角(RAD)*/
                4,               /*可变:颜色*/
                5                  /*可变:附带二级烟花数目*/
        };                                                                   /*结构体变量内容可修改*/
       
        FW* son=launchFATfirework(father);

        launchSONfirework(5,son);
       
        free(son);
       
        return 0;
}

效果

简述:较为朴素,没有尾迹,较为卡顿
建议全屏观看

改进

1.知道很多物理(竞?)大佬会说,欸,你看,这个烟花啊,他是要计算空气阻力的。
的确,二级烟花(其实是残渣)的质量较小,空气阻力理论上不可忽略
但是看看阻力的式子:f=kv2(k是多少忘记了,不重要,反正是一常量)
结合G=mg,我个人不觉得能够轻松愉快地解出位置随时间的变化函数。
看着这个微分方程都头大
物/数竞大佬可以帮忙解出关系式,并且对程序作出相应修改,如有完成者奖励1鱼币+1荣誉

2.一般的烟花都带有尾迹,很好看。但我目前没想到如何简便地为烟花添加尾迹。
如有完成者奖励2鱼币+1荣誉

3.提出让烟花更真实的建议并给出源代码的同学 奖励2鱼币+2荣誉(我不要gui谢谢哈,我只想知道用这种黑框框怎样会变得更逼真)

结语

本人系C语言新手,欢迎各位大佬对我的代码提出宝贵建议!{:10_297:}

如果喜欢,别忘了评分{:10_281:}
谢谢!

额外减小 发表于 2023-1-30 02:33:02

@hveagle @sfqxx @高山 @liuhongrun2022 {:10_256:}

额外减小 发表于 2023-1-30 02:35:59

本帖最后由 额外减小 于 2023-1-30 21:16 编辑

针对改进第一条

想到可以通过减小二级烟花对应重力加速度的值来近似它的有阻力运动。
例如
使用      FW* son=launchFATfirework(father,G);
       
#undef G
#define G 0.05

        launchSONfirework(5,son,G);
       

来使得二级烟花下落较缓
当然,拟合的不一定像

具体代码

#define _XOPEN_SOURCE
#define _GNU_SOURCE
#define _BSD_SOURCE
#define _USE_MATH_DEFINES

#include <math.h>
#include <stdio.h>
#include <windows.h>

#define G 9.8//重力加速度                                  /*可变*/
#define sleeptm 42// 保持 24 帧每秒 (加上运行时间)             /*可变:每秒的帧数*/

typedef struct _FPOINT
{
        double x;
        double y;
} FPOINT;

typedef struct Firework
{
        FPOINT startp; //出发点 (正常坐标系,x=Pos_x,y=-Pos_y)
        double startv; //初速度
        double angle; //发射角 , RAD
        int color; //烟花颜色(1-15)
        int petal; //花瓣个数
} FW;


void Pos(int x,int y);
void color(int type);
void getcoord(FPOINT *endp,FPOINT startp,double startv,double angle,double g,int mstime);
int _round(double origin);
double mod(double a,double m,int *i);

FW * launchFATfirework(FW FATHER,double g);
void launchSONfirework(int argc,FW * SONarray,double g);


void Pos(int x,int y)
/*
设置光标位置
运行框左上方:(0,0)
从定位光标开始打印
若换行,变为从行首打印
*/
{
    COORD pos;
    pos.X=x;
    pos.Y=y;
    HANDLE hOutPut;
    hOutPut=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hOutPut,pos);
}

void color(int type)
/*设置颜色*/
{
        if((type>=0)||(type<=15))
        {
                SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),type);
        }
        else
        {
                SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
        }
}

int _round(double origin)
/*四舍五入函数
用于烟花位置打印
(精确度为1)*/
{
        double minus_origin=-origin;
        if(origin>=0)
        {
                if(origin-(int)origin>=0.5)
                {
                        return ((int)origin)+1;
                }
                else
                {
                        return (int)origin;
                }
        }
        else
        {
                if(minus_origin-(int)minus_origin>=0.5)
                {
                        return (int)origin-1;
                }
                else
                {
                        return (int)origin;
                }
        }
}

double mod(double a,double m,int *i)
//广义上取余数函数
//因为求余符号 % 只能用于整数
{
        *i=0;
        if(m<=0)
        {
                return -1;
        }
        if(a>=0)
        {
                while((*i)*m<=a)
                {
                        (*i)++;
                }
                return a-(*i-1)*m;
        }
        else
        {
                while((*i)*m+a<0)
                {
                        (*i)++;
                }
                return a+(*i)*m;
        }
}

void getcoord(FPOINT *endp,FPOINT startp,double startv,double angle,double g,int mstime)
//获取烟花坐标(严格依据运动学定理)
{
        endp->x=startp.x+startv*cos(angle)*mstime/1000;
        endp->y=startp.y+startv*sin(angle)*mstime/1000-g*mstime*mstime/2000000;
}

FW * launchFATfirework(FW FATHER,double g)//发射一级烟花
{
        int mstime=0,explosion,i,count;
        if(mod(FATHER.angle,2*M_PI,&i)<M_PI)
        {
                explosion=(int)(1000*(FATHER.startv*sin(FATHER.angle)/g)); // 当烟花飞行至最高点时爆炸
        }
        else
        {
                explosion=3000;                                  /*可变:一级烟花向下发射时的爆炸时间*/
        }
        FPOINT now;
        while(mstime<=explosion)
        {
                getcoord(&now,FATHER.startp,FATHER.startv,FATHER.angle,g,mstime);
                Pos(_round(now.x),-_round(now.y));
                color(FATHER.color);
                printf("*");
                Sleep(sleeptm);
                mstime+=sleeptm;
                system("cls");
        }
       
        FW *SONarray=(FW *)malloc(sizeof(FW)*FATHER.petal);
        FW *ptr=SONarray;
       
        for(count=0;count<FATHER.petal;count++,ptr++)
        {
                ptr->startp.x=now.x;
                ptr->startp.y=now.y;
                ptr->startv=15;                                                                   /*可变:二级烟花的初速度*/
                ptr->angle=0.5+2*M_PI/FATHER.petal*count; //设置为均匀放射(相邻两初速度方向夹角相等)
                ptr->color=FATHER.color;
                ptr->petal=0; //每瓣二级烟花附带的三级烟花数量 (一般设为 0 )
        }
       
        return SONarray;
}

void launchSONfirework(int argc,FW * SONarray,double g)
{
        int mstime=0,i,count;
        FPOINT p;
        FW *ptr=SONarray;
        while(mstime<1000)//explosion                                       /*可变:二级烟花爆炸时间*/
        {
                for(count=0;count<argc;count++)
                {
                        getcoord(&p,SONarray.startp,SONarray.startv,SONarray.angle,g,mstime);
                        Pos(_round(p.x),-_round(p.y));
                        color(SONarray.color);
                        printf("*");
                }
                Sleep(sleeptm);
                mstime+=sleeptm;
                system("cls");
        }
}

int main()
{
        int c;
        FPOINT p={100,-50};                                     /*可变:一级烟花起始点坐标(正常坐标系,x轴向右,y轴向上)*/
        FW father=
        {
                p,
                25,                /*可变:初速度*/
                1.8,               /*可变:发射角(RAD)*/
                4,               /*可变:颜色*/
                5                  /*可变:附带二级烟花数目*/
        };                                                                   /*结构体变量内容可修改*/
       
        FW* son=launchFATfirework(father,G);
       
#undef G
#define G 0.05

        launchSONfirework(5,son,G);
       
        free(son);
       
        return 0;
}

高山 发表于 2023-1-30 09:30:59

厉害厉害,加油

Eschborn 发表于 2023-1-30 10:52:54

你老家哪里啊,可以放烟花

Mike_python小 发表于 2023-1-30 10:58:01

把你的windows.h去掉!!!!!!{:10_244:}

一点沙 发表于 2023-1-30 12:23:00

碰运气

liuhongrun2022 发表于 2023-1-30 13:02:18

加油!

zhangjinxuan 发表于 2023-1-30 13:17:23

{:10_256:}

sfqxx 发表于 2023-1-30 15:01:57

{:7_146:}

liuzhf 发表于 2023-1-30 15:38:32

感谢鱼c!

kerln888 发表于 2023-1-30 15:41:43

加油!

1molHF 发表于 2023-1-30 16:37:04

{:7_146:}

quark 发表于 2023-1-30 18:59:09

厉害,学习中。

额外减小 发表于 2023-1-30 21:02:18

Mike_python小 发表于 2023-1-30 10:58
把你的windows.h去掉!!!!!!

谢谢提醒
不过有必要发火吗()
{:10_245:}

额外减小 发表于 2023-1-30 21:04:10

Eschborn 发表于 2023-1-30 10:52
你老家哪里啊,可以放烟花

福州
话说不是很多地方都允许了吗?

Mike_python小 发表于 2023-1-30 21:13:34

额外减小 发表于 2023-1-30 21:02
谢谢提醒
不过有必要发火吗()

不是 就调侃一句 我是Mac,所以看到很多好玩的程序(比如你这种,然后一看到“windows.h”就怒火攻心了

Mike_python小 发表于 2023-1-30 21:14:14

额外减小 发表于 2023-1-30 21:02
谢谢提醒
不过有必要发火吗()

卧槽原来可以不用windows.h,我这就去试试

额外减小 发表于 2023-1-30 21:15:05

Mike_python小 发表于 2023-1-30 21:13
不是 就调侃一句 我是Mac,所以看到很多好玩的程序(比如你这种,然后一看到“windows.h”就怒火攻心了

那有休眠一定时间的功能函数吗?
就是在mac里面

Mike_python小 发表于 2023-1-30 21:15:10

额外减小 发表于 2023-1-30 21:02
谢谢提醒
不过有必要发火吗()

很好,我的编译器无法识别“COORD”,“HANDLE”和STD_OUTPUT_HANDLE
页: [1] 2 3
查看完整版本: 看烟花