萌新如何学写c++游戏

xuan神 and t神 游戏集
文章
【全网唯一 纯C++动画游戏编程指南】完结!编你自己的游戏!
95 likes, on 2021-08-19 17:14:57, in 文章
编你自己的游戏!
------------- huaxuanwu1111自创游戏编程教学指南
目录:

一、总纲
    (一)编游戏的步骤 
二、游戏框架
    (一)基础框架
    (二)游戏步骤设计
        (1)游戏步骤之胎神之路
    (三)框架总结 
        (1)“实时战斗”游戏
        (2)“回合制”游戏  (附冒险世界链接) 
三、工具函数(初级)--本文重点学习部分!
    (一)光标跳跃 Setpos 
    (二)键盘读取 
        (1)GetAsyncKeyState
        (2)kbhit() + _getch()
    (三)时间暂停 Sleep() (附 SlowDisplay())
    (四)颜色改变
        (1)单字 FOREGROUND
        (2)全屏 system("color") 
    (五)存档读档 ifstream
    (六)工具代码(包括改框,改字体等等)
四、制作动画(C++简单字符画)
    (一)静态点阵画(空心圆,实心圆,椭圆,圆弧,线段)
    (二)动画 
五、工具函数(进阶)
    (一)鼠标函数 
    (二)计时 clock() 
六、高级代码实现 
    (一)物理引擎
        (1)物理引擎之小胎弹弹乐 
        (2)物理引擎之三体星战 
    (二)高效输出系统 
七、结语
声明:本教程提到的所有游戏、代码均为作者原创游戏作品的节选,可博客自取。

一、总纲
编一个好玩的游戏,不需要精湛的技术,不需要漫长的时间,不需要几千行的代码。

你需要的,只是一丝灵感,一点耐心,一些好习惯。

编游戏的步骤:

一、找到灵感啦!

这是很困难的一步,因为一个人的人力很有限,像愤怒的小胎,我不可能像愤怒的小鸟一样去人为的编每一关,只能随机出猪。所以事实上可实现的灵感寥寥无几(我学生物时曾经写过一个关于进化的游戏,但是太复杂了,最终放弃) 一般来说,所有的自编游戏只需要一个核心操作(弹弓瞄准,放置炸弹,躲避弹幕)加上一些随机因素(星星,小怪移动,地图刷新)就可以组合成有无限可能的好游戏。

二、写游戏大纲

大纲一定要写,编游戏就是打比赛,不要只想着你最终的效果(就像写题只想着程序输出了正解的情景),在开始编时想好如何实现它!(连变量名都可以想一想,防止重复) 开始编元气小胎时,很意外的是,无脑小怪的运动方式我想了很久,如果每一个每一刻都走最短路的话,那游戏会卡炸(试过),最后的解决方案是简化游戏地图使贪心+随机算法可以有解。

三、编游戏框架

四、写核心代码

五、修改润色,添加因素

六、改 BUG !!! 完成程序(发布)

二、游戏框架
(一)基础框架

#include<bits/stdc++.h>
#include<windows.h>
#include<conio.h>
using namespace std;
int m[20][20],Z,T,speed;float X,Y;
float Sin(int a) {return sin(3.1416*a/180);}
//------- 定义变量,函数(好习惯:把所有变量一起定义)-------// 
void Start() {cout<<"---- 我是封面 ----";}
//------- 动画函数,封面函数,杂七杂八函数 -------//  
void Go(float t) {Z++;}
//------- 核心代码,会重复使用不断修改,放下面点 -------// 
int main(){
    system("mode con cols=42 lines=21");
//------- 工具代码(初始化随机种子,调整屏幕大小等) -------// 
    St:Start(); 
//------- 开启游戏之前需要做的事(输出封面,调难度等)(St:是 goto函数的语句) -------// 
    X=2,Y=5;memset(m,0,sizeof(m)); 
//------- 初始化 -------// 
    while(1){
        T++;
        cout<<"分数:"<<X<<' '; cout<<"血量:"<<Y<<' ';//游戏中随时改变需要一直输出的量,让你在操作前看到它。
    if(X>=100||Y<=0) break;//判断(如胜利失败)你可否继续操作。
//------- 操作前的回合间步骤,其实它们的运行也只比操作后步骤迟几十毫秒而已 -------// 
        if(GetAsyncKeyState(VK_LEFT)&0x8000) X--;
        if(kbhit()){char c=_getch();if(c==' ') Y--;}
//------- 操作(鼠标,键盘控制)一般要打标记,简单操作(如Y--)直接就做了 -------// 
        Go(1);Go(2);Go(3);Go(4);Go(X);
    if(rand()%100==0) X++;
//------- 操作后的回合间步骤,主要对操作进行反应与改变(移动,输出,随机事件等),见下文 -------// 
        Sleep(speed);
//------- 回合结束暂停(防止 while(1)过快及调整速度,一般为几十毫秒) -------// 
    }
    if(X<100) cout<<"   GAME OVER...";
    else cout<<"LEVEL VICTORY !";
//------- 结束游戏之后需要做的事(结算总分,输出你好菜啊等) -------// 
    goto St;//回到开始(冒号处)
} 
(二)游戏步骤设计

例:胎神之路

    while(1){
        S++;
//------- 好习惯:始终在while中加上计数器 -------// 
        if(Fen>1000) {Win=1;break;}
    if(X<K&&dx<K) break; if(X<K&&dx>K) X=dx,Y=dy;// 若出界(X:小胎高度,K:底部,dx:存档点高度)
//------- 胜利与失败判定 -------//         
        if(K<=5) Slep=20;if(K>5&&K<=10) Slep=15;if(K>10) Slep=10;
//------- 游戏难度增加(高度-->速度) -------//
        if(S%Slep==0) {K++;Fen++;Creat_Map();}
//------- 当进行到 第(Slep,速度)倍数 的回合时,随机生成下一行地图,加分 -------//
        if(GetAsyncKeyState(VK_UP) & 0x8000) TT=0;//跳跃标记,其他操作省略 
//------- 键盘处理部分 -------//
        Map(K+22,K-3,0);
//------- 输出地图 -------//
        Wo(1);
//------- 移动:核心代码(包括弹跳块判断,复活,拾取星星等等) -------//
        Sleep(50);
    }
可以发现游戏步骤实际上千篇一律!游戏框架总结如下:

(1)“实时战斗”游戏

例:海岛奇胎,三体星战,重力消消乐,双人贪吃蛇,超级迷宫,忍者必须胎,幻门疾走,变色小跳龙,胎红胎蓝......(没错,它们基本上全是一个套路)

接下来讲的所有内容,都教的是编这种游戏!

1.输分输图 
2.操作(如键盘) 
3.各种判断(胜负,颜色,状态等) 
4.移动(清除图像,移动,输出新位置图像) 
5.触发反应(开箱,捡星,撞怪等) 
6.回合间暂停。
海岛奇胎的每一个子弹分别实时移动,和城市守卫战完全不是一个类型啦(说是3.0实际上是重打了代码......没错,海岛奇胎其实是一个全新的游戏!(只有数据与设定继承了)--所以更新慢--)
(2)“回合制”游戏

例:城市守卫战,危险游戏,小胎大乱斗,冒险世界 (巨菜同机房Crab打造)

1.输分输状态(气,血...)
2.操作(药,技能...) 
3.前置操作(嘲讽,战吼...)
4.攻前判断(沉默...)+你攻+触发反应(暴击...)+怪死判断+触发反应(亡语...)
5.怪攻+触发反应+你死判断+触发反应
6.后置操作(毒...)
7.不是实时暂停几十毫秒干嘛
另:剧情游戏(例:丧尸危机)

这货就是 while(1)里面的东西巨多,基本上全是判断(第一天怎么怎么,第二天balabala......),毫无技术可言(就是分支语句太多难调),只要大纲写的好就有手就行。

三、工具函数
(本文重点学习部分!)
#include<bits/stdc++.h>
#include<windows.h>
#include<stdio.h>
#include<conio.h>
#include<time.h>
(胎神大大专用头文件集,编游戏的很多时候,一个bits/stdc++.h都是不够用的!)

(一)、光标跳跃

SetPos(): 实时动画游戏中最重要的函数,没有之一。
又名 gotoxy,locate。

void SetPos(int x,int y)
{
    COORD pos;
    pos.X=y*2,pos.Y=x;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
}
这个函数主要在动画里讲,这里只讲基本用法:

例:开始在第三行第六格输出

SetPos(2,5),cout<<"Hello!";
(Setpos中,(0,0) 才是第一行第一格)

注意该函数里有这一行:

    pos.X=y*2,pos.Y=x;
其中x,y是你输入的数。

如果改成

    pos.X=x,pos.Y=y*2;
就会变成 x代表横 y代表纵的“正坐标轴”(小胎弹弹乐,y值从上到下增加)

同理,如果改成

void SetPos(int x,float y)
    y*=2;
    pos.X=y,pos.Y=x;
y就可以是5.5了!(该行第5.5格,第11个空格(半角)处)(简单游戏2.0)

有的时候你会发现你想在屏幕最上方输出一些东西(如盾,血,子弹数,需要额外三行),但是地图函数里已经将(0,0)作为了开始输出的位置,如果一个个将(0,0)(2,3)改成(3,0)(5,3)的话太费时间,这时候!

    pos.X=y*2,pos.Y=x+3;
这样,你就得到了 SetPos(-1,0)(-2,0)(-3,0)三行。(元气小胎)

(二)、键盘读取

(1)流畅获取键盘键按下与否之函数:GetAsyncKeyState(i)&0x8000

(i 一般为数字,但也可输入特殊的VK_UP,VK_LEFT等,可以上网搜)

应用1:获取键盘编号方法

#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
int main()
{
    cout<"按下一个键,我会输出它的键盘编号!";
    while(1) for(int i=0;i<=200;i++)
    if(GetAsyncKeyState(i)&0x8000) cout<<i<<' ';
}
例:

if(GetAsyncKeyState(VK_LEFT)&0x8000) cout<<"左键!人物左移一步!",Y--,step++;
(2)便捷获取键盘键按下与否之函数:kbhit()(获取整个键盘按下与否)+ _getch() (注意下划线,获取一个字符且不在屏幕上输出它)

例:

if(kbhit()){char c=_getch();if(c==' ') cout<<"空格!发射子弹!";Shot();}
便捷之处:不必费心找键盘编号了

但是,kbhit() 的判定会受 Sleep()的影响,(Sleep()时kbhit()不判)即,在休息的那几毫秒中按下键盘不松,休息完后就算一直按着也不会显示键盘按下的信息。

在掘地矿胎中用的是 kbhit(),游戏中的一个炸弹爆出多个炸弹的效果是因为你稍微长按了键盘,导致一个炸弹在另一个炸弹落下前叠于其上(不加“按键无效时间”的后果!)(不信你边走边长按,有时可以连放4个炸弹!)但是由于Sleep()的影响,你的连按会被打断(必须松开键再按一次才可放下一个炸弹,因为kbhit()归零了!)
胎神之路和元气小胎都“重大更新”过,实际上只是将移动函数改用了GetAsyncKeyState。若用两条语句分别实现按左键人物左移,那当长按左键时, GetAsyncKeyState 小人的移速与流畅性明显高于 kbhit 小人。
(三)、时间暂停

例:暂停半秒

    Sleep(500);
应用1:缓慢输出语句

void SlowDisplay(char *p,int x)
{
    while(1)
    {
        if(*p!=0) printf("%c",*p++);
        else break;
    Sleep(x);
    }
}
SlowDisplay("慢慢说出这句话,字与字之间间隔两秒",1000);
注意:一个汉字算两个字符。

有的时候你会发现写了Sleep();控制while(1)速度,但是不同电脑运行程序时回合间的时间间隔明显不同(简单游戏2.0,三体星战),是因为电脑运行回合中间语句的速度不同,想要编每个电脑间隔速度一致的游戏(如音游),见-工具函数(进阶)clock()

(四)、颜色改变 (1)单字 FOREGROUND

void Color(int a)
{
    if(a==0) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
    if(a==1) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE);
    if(a==2) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN);
    if(a==3) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_BLUE);
    if(a==4) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED);
    if(a==5) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_BLUE);
    if(a==6) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN);
}
(胎神大大专用颜色代码,0~6:白蓝绿紫红靛黄)记法:城市守卫战炮台颜色

语句解释1:FOREGROUND_INTENSITY:调亮,只有该语句时显示深灰色(愤怒的小胎黑鸟),浅灰:白色语句种去掉该语句。

语句解释2:_RED_GREEN_BLUE:光线三原色(不是色彩!我记得初中物理有讲)红光 + 绿光 = 黄光,等等。加上INTENSITY,通过删减语句,一共可以调出16种颜色(重力消消乐)。

另:BACKGROUND_为背景颜色。

例:绿底红字:

FOREGROUND_INTENSITY|FOREGROUND_RED|BACKGROUND_INTENSITY|BACKGROUND_GREEN

注意:BACKGROUND_会为空格赋予颜色!(简单游戏2.0)

(2)全屏 system("color XY")

用法: X,Y两个字符,第一个代表背景,第二个代表前景。每个数字可以为以下任何值之一:

0 = 黑色   8 = 灰色
1 = 蓝色   9 = 淡蓝色
2 = 绿色   A = 淡绿色
3 = 湖蓝色 B = 淡浅绿色
4 = 红色   C = 淡红色
5 = 紫色   D = 淡紫色
6 = 黄色   E = 淡黄色
7 = 白色   F = 亮白色
例:

    system("color 0F");  //恢复默认
    system("color 6E");  //胜利时
    system("color 7F");  //失败时
    system("color 4");   //全屏变红字
只有一个字符时,默认为改变全屏字体颜色+黑背景

system函数还有两个好用的:

(1)清屏
    system("cls");
(2)暂停
    system("pause");
(五)、存档读档 ifstream

void Read()
{
    ifstream in("存档.xxx");
    in>>a>>b>>c;    //读入三个值分别赋值给abc
    in.close();
}
void Save()
{
    ofstream out("存档.xxx");    //创建一个txt/in/cpp/balabala...
    out<<a<<' '<<b<<' '<<c<<endl;    //超级普通
    out.close();
}
注意:Save() 会覆盖掉同名文件!

(六)、工具代码

(1)胎神大大经典开头工具代码

int main()
{
    system("mode con cols=80 lines=40");
//------- 改输出框行列数(大小),此语句:改成80空格宽40回车高 -------//
    CONSOLE_CURSOR_INFO cursor_info={1,0}; 
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
//------- 清除光标(就是在你键盘输入时一直闪烁的那个竖线或横线) -------//
    srand((unsigned)time(NULL));
//------- 初始化随机种子 (生成0-a范围随机整数:rand()%a;) -------//
//生成0-a范围随机整数:rand()%a;
    Color(0); 
//------- 默认灰白色,改成亮白色 -------//
    ......(程序正文开始)
(2)改变字体大小(海岛奇胎)

using namespace std;     //这句只是在标明以下语句位置
typedef BOOL (WINAPI *PROCSETCONSOLEFONT)(HANDLE, DWORD);
PROCSETCONSOLEFONT SetConsoleFont;
void Print(int a){HMODULE hKernel32 = GetModuleHandle("kernel32");SetConsoleFont = (PROCSETCONSOLEFONT)GetProcAddress(hKernel32,"SetConsoleFont");SetConsoleFont(GetStdHandle(STD_OUTPUT_HANDLE),a);}
int main()
{
    Print(2);  //1正常2缩小,只支持两种字体大小
    cout<<"I am tiny";
}
四、制作动画(C++简单字符画)
(一)静态点阵画

(1)一些很有用的函数

int to_int(float a) {if(a>0) return int(a+0.5);else if(a<0) return int(-a-0.5);if(a==0) return 0;}
//绝对值四舍五入
float Sin(int a) {return sin(3.1416*a/180);}
float Cos(int a) {return cos(3.1416*a/180);}
//多少度。例:Sin(30)=0.5
float SinP(int a) {return sin(3.1416*a);}
float CosP(int a) {return cos(3.1416*a);}
//多少pai。例:SinP(1)=0
(2)空心圆

空心圆实际上就是一些连成圆的点集罢了

float x=10,y=10;
int num=8,r=2;  //例:以(10,10)为圆心,作由八个点组成的半径为二的圆
int next=360/num;
for(int i=0;i<360;i++)
if(i%next==0)
{
    float X,Y;
    X=x+Sin(i)*r;
    Y=y+2*Cos(i)*r;
    SetPos(X,Y),cout<<".";
} 
注意这一句:

    Y=y+2*Cos(i)*r;
这个2一改,圆就会变成椭圆。

for(int i=0;i<360;i++)   //0-359度检测
范围一改,圆就会变成圆弧。

(2)线段

模拟走路:

    float x0=1,y0=1,x1=8,y1=10;  //例:从(1,1)到(8,10)画线 
    float Ax=x1-x0,Ay=y1-y0,Dis=sqrt(Ax*Ax+Ay*Ay);
    bool left=0;
    if(Ax<0) swap(x1,x0),swap(y1,y0),Ax*=-1,Ay*=-1;
//------- 保证点从上方到下方输出 -------//
    if(Ay<0) left=1;
//------- 左走标记 -------//
    float ax=Ax/Dis*1.0,ay=Ay/Dis*1.0;
//------- 步长,保证每一步不大于 1 -------//
    float i=x0,j=y0;
//------- 从起点开走 -------//
    while(i<=x1&&((j<=y1&&left==0)||(j>=y1&&left==1)))
    {
        SetPos(to_int(i),to_int(j)),cout<<".";   //注意 SetPos中为整数 
        i+=ax;j+=ay;
//------- 走一格 -------//
    }
(3)实心圆

float x0=10,y0=10,R=4;  //例:以(10,10)为圆心,作由半径为四的实心圆 
for(int i=x0-R;i<=x0+R;i++)
//------- 同理,从最上方行开始检测每一行,圆的“边缘点”(行:整数) -------//
{
    float Ay=sqrt(R*R-(i-x0)*(i-x0))*2;
//------- 距离公式逆用! -------//
    for(float j=y0-Ay;j<y0+Ay;j++)
    {
        SetPos(i,to_int(j)),cout<<".";
    }
}
(二)动画

动画其实很简单,就三步:清除图像,移动,输出新位置图像。

例1:旋转圆弧

float x=10,y=10;
int num=8,r=2,next=360/num;
int Angle=0;
while(1)
{
    for(int i=Angle;i<Angle+180;i++)
    if(i%next==0)
    {
        float X,Y;
        X=x+Sin(i)*r;
        Y=y+2*Cos(i)*r;
        SetPos(X,Y),cout<<" ";
    }

    Angle+=20;
    if(Angle>360) Angle-=360;

    for(int i=Angle;i<Angle+180;i++)
    if(i%next==0)
    {
        float X,Y;
        X=x+Sin(i)*r;
        Y=y+2*Cos(i)*r;
        SetPos(X,Y),cout<<".";
    }
    Sleep(500);
}
没了,就这么简单。

五、工具函数(进阶)
(一)、鼠标函数

使用前,先在最前面打上这一段代码:

#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
typedef BOOL (WINAPI *PROCSETCONSOLEFONT)(HANDLE, DWORD);
PROCSETCONSOLEFONT SetConsoleFont;
(1)检测鼠标是否按下:GetAsyncKeyState(没错!正是在下!)

int mouse;
mouse=GetAsyncKeyState(VK_LBUTTON)&0x8000;
if(mouse) cout<<"按了!";      
VK_LBUTTON是鼠标左键的意思,左右键的编号是 1和2(可以用之前那个程序试出来)。

(2)检测鼠标位置:pt.x,pt.y

POINT Windowpos()
{
    POINT pt;
    GetCursorPos(&pt);
    HWND h=GetForegroundWindow();
    ScreenToClient(h,&pt);
    pt.x/=8;pt.y/=16;
    return pt;
}
int main()
{
    while(1)
    {
        POINT pt=Windowpos();
        cout<<"鼠标坐标:"<<pt.x<<' '<<pt.y;
    }
}
(二)、计时 clock() (<time.h>自带)

例:每隔0.5s 输出 hi

int clocknum=0;
while(1)
{
    if(clock()-clocknum>=500) {cout<<"hi ";clocknum=clock();}
}
六、高级代码实现
(一)物理引擎

例1:物理引擎之小胎弹弹乐

void Go(float t)//使用正xy坐标轴  边界:0-20 
{
    //这里的 t代表运行时间,游戏中始终为 1(30ms),但是公式不套常数是好习惯(见前文) 
        bool vxe=0,vye=0;
        if(vx<0) vxe=1,vx*=-1;if(vy<0) vye=1,vy*=-1; 
    vx=fmin(2.0,fmax(0.1,vx)),vy=fmin(2.0,fmax(0.1,vy));
        if(vxe==1) vx*=-1;if(vye==1) vy*=-1;
    //速度调整 之最初:vx,vy(横纵速度的绝对值 防止过大过小) 
    ax=0,ay=g;
    X+=vx*t,Y+=vy*t+0.5*g*t*t;
    //移动 (速度公式) 

    if(Y<0||Y>20){
    //纵坐标越界时(优先级大于横坐标越界)
        X=fmax(0.0,fmin(20.0,X));Y=fmax(0.0,fmin(20.0,Y));
    //出界归位(注意可能同时出界!) 
        vy*=-1;
    //反弹之纵
            if(Y>=20.0){
                if((board-0.3>X||board+Blen+0.3<X)&&WD==0) system("Color C"),Sleep(500),system("Color F"),Sco-=5,WD=WDmax;
    //弹跳版板判定 之 未接住 (闪红光,暂停少许,无敌一小段时间( WD,防止重复扣分!)) 
                else {Sco++;vx=(float)(rand()%50)/20.0-1.25;vy*=0.9+(rand()%40)/200.0;}
    //弹跳版板判定 之 接住 (随机改变速度(vx:-1.25~+1.25,vy:增大 0.9~1.1倍)) 
                if(abs(vx)>=2) vx*=0.9;if(abs(vy)<=1.7) vy*=1.2;if(abs(vy)>=2.2) vy*=0.9;if(abs(vy)<=1) vy=-1;
    //速度调整 之地面 (缓慢调整(例:每次触地 *0.9,变化更自然)) 
        }}

    else if(X<0||X>20){
        X=fmax(0.0,fmin(20.0,X));Y=fmax(0.0,fmin(20.0,Y));
        vx*=-1;
    //反弹之横
         if(abs(vx)>=2) vx*=0.85
    //速度调整 之横反弹:vx,vy(横速度 防止过大,因为过大时难度会极高) 
        ;}

    else vx+=ax*t,vy+=ay*t;
    //不出界则加上加速度, 出界时,速度已经调整过了 

    X=fmax(0.0,fmin(20.0,X));Y=fmax(0.0,fmin(20.0,Y));
    //最后保证 不出界(好习惯) 
}
一定注意!在物理引擎中尽量不要出现常数!

目的1:保证你的公式正确!X+=vx t ,Y+=vy t+0.5 g t * t ; 写错概率很低。

目的2:好调试!物理游戏,一个常数变0.1就会使太阳爆炸(三体星战),所以后期调试主要就是这个数变666,那个加888......把所有常数放在一起调!

例2:物理引擎之三体星战

void Push(int a,int b){
    double Ax=Sun[a].x-Sun[b].x,Ay=Sun[a].y-Sun[b].y;
    double Dis=sqrt(Ax*Ax+Ay*Ay)*1.0;
    if(Dis==0) return;
//------- 距离公式!(Ax: △X) -------//
    double fDis=sqrt((Sun[a].vx-Sun[b].vx)*(Sun[a].vx-Sun[b].vx)+(Sun[a].vy-Sun[b].vy)*(Sun[a].vy-Sun[b].vy));
//------- fDis:速度(矢量)相减取模,指本回合两太阳“追上”的距离 -------//
    else if(Dis<=Sun[a].r+Sun[b].r+fDis){
//-------三体星战中,我们两两考虑相互作用,两个太阳分别所造成的位移相加就是第三个太阳的总位移-------//
//-------所以两两考虑时,两个太阳计算出来的速度与位移均是朝向对方的!-------//
//-------所以, 追上的距离 + 半径 > 距离 ==>追上了!相撞融合-------//
        Sun[a].vx=Sun[b].vx=(Sun[a].vx+Sun[b].vx)/2.0,Sun[a].vy=Sun[b].vy=(Sun[a].vy+Sun[b].vy)/2.0;
//-------融合速度取中间值(动量定理,质量相同情况)-------//
        Sun[a].x=aX-(Ax/Dis)/2.0,Sun[b].x=aX+(Ax/Dis)/2.0;Sun[a].y=aY-(Ay/Dis)/2.0,Sun[b].y=aY+(Ay/Dis)/2.0;
        return;
    }
    if(abs(Ay)<=0.0001) Ay=0.0001;
//------- 万有引力公式中保证分母不为 0 -------//
    double F=Speegless*Sun[a].m*Sun[b].m/(Dis*Dis);//G*m1*m2/r^2 这里的 G 是常数!调整使引力不过大使太阳爆发 
    double fx=0,fy=0,d=abs(Ax/Ay);
    fy=sqrt(F/(1+d*d))*1.0,fx=sqrt(F/(1+d*d))*d;
    if(Ax>0) fx*=-1;if(Ay>0) fy*=-1;
//------- 直角三角形正余弦计算,化总为横纵分速度 (d=tan(夹角)) -------//
    Sun[a].vx+=fx/Sun[a].m;
//------- 牛顿第二定律,省略一部分-------//
    Sun[a].vx=max(-Speedmax,min(Speedmax,Sun[a].vx));
//------- 控制速度,省略一部分 -------//
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值