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));
//------- 控制速度,省略一部分 -------//
}
萌新如何学写c++游戏
于 2024-01-04 17:49:13 首次发布