🎊 【全网唯一 纯C++动画游戏编程指南】完结!编你自己的游戏!

【全网唯一 纯C++动画游戏编程指南】完结!编你自己的游戏!

【全网唯一 纯C++动画游戏编程指南】完结!编你自己的游戏!

TSzza

·

2021-08-19 17:14:57

·

个人记录

编你自己的游戏!

------------- 胎神大大自创游戏编程教学指南

目录:

一、总纲

(一)编游戏的步骤

二、游戏框架

(一)基础框架

(二)游戏步骤设计

(1)游戏步骤之胎神之路

(三)框架总结

(1)“实时战斗”游戏

(2)“回合制”游戏 (附冒险世界链接)

三、工具函数(初级)--本文重点学习部分!

(一)光标跳跃 Setpos

(二)键盘读取

(1)GetAsyncKeyState

(2)kbhit() + _getch()

(三)时间暂停 Sleep() (附 SlowDisplay())

(四)颜色改变

(1)单字 FOREGROUND

(2)全屏 system("color")

(五)存档读档 ifstream

(六)工具代码(包括改框,改字体等等)

四、制作动画(C++简单字符画)

(一)静态点阵画(空心圆,实心圆,椭圆,圆弧,线段)

(二)动画

五、工具函数(进阶)

(一)鼠标函数

(二)计时 clock()

六、高级代码实现

(一)物理引擎

(1)物理引擎之小胎弹弹乐

(2)物理引擎之三体星战

(二)高效输出系统

七、结语

声明:本教程提到的所有游戏、代码均为作者原创游戏作品的节选,可博客自取。

一、总纲

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

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

编游戏的步骤:

一、找到灵感啦!

这是很困难的一步,因为一个人的人力很有限,像愤怒的小胎,我不可能像愤怒的小鸟一样去人为的编每一关,只能随机出猪。所以事实上可实现的灵感寥寥无几(我学生物时曾经写过一个关于进化的游戏,但是太复杂了,最终放弃)

一般来说,所有的自编游戏只需要一个核心操作(弹弓瞄准,放置炸弹,躲避弹幕)加上一些随机因素(星星,小怪移动,地图刷新)就可以组合成有无限可能的好游戏。

二、写游戏大纲

大纲一定要写,编游戏就是打比赛,不要只想着你最终的效果(就像写题只想着程序输出了正解的情景),在开始编时想好如何实现它!(连变量名都可以想一想,防止重复)

开始编元气小胎时,很意外的是,无脑小怪的运动方式我想了很久,如果每一个每一刻都走最短路的话,那游戏会卡炸(试过),最后的解决方案是简化游戏地图使贪心+随机算法可以有解。

三、编游戏框架

四、写核心代码

五、修改润色,添加因素

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

二、游戏框架

(一)基础框架

#include

#include

#include

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<<"分数:"<

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(XK) 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

#include

#include

#include

#include

(胎神大大专用头文件集,编游戏的很多时候,一个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

#include

using namespace std;

int main()

{

cout<"按下一个键,我会输出它的键盘编号!";

while(1) for(int i=0;i<=200;i++)

if(GetAsyncKeyState(i)&0x8000) cout<

}

例:

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<

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

{

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

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

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

#include

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<<"鼠标坐标:"<

}

}

(二)、计时 clock() (自带)

例:每隔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

//弹跳版板判定 之 未接住 (闪红光,暂停少许,无敌一小段时间( 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));

//------- 控制速度,省略一部分 -------//

}

(二)、高效输出系统

高效输出系统的思路很简单,用两个数组存储每一个点输出的字符,每一回合只改变一个数组,然后检查两个数组之间的不同点输出即可,这样不会输出没必要的空格,也不会输出溢出(出界)。(简单游戏2.0)

七、结语

我还依稀记得城市守卫战1.0发布当天,那时我们只有6年级,我几乎用了半年调试它,然后轰动了整个机房。每个人都玩着,谈论着,那一刻我知道我可能永远无缘于普一提一,但是我无悔入OI。

如今我也是高中生了,以往的灵感已消耗殆尽,当死党Crab看到新游戏小胎弹弹乐时,他说,他在至少两个游戏里玩过类似机制,然后他说,我老了。

原来我也会老。

那就传承下去吧。我是一个蒟蒻,我这几年自己摸索出一个个算法,也可能这辈子都打不出一个A*,使用一个图像函数,编一个像样一点的游戏。

所以我写下一切,不期望你们会学会什么,真的写的很差,感谢你们的阅读。

就这样吧,让胎神游戏集恢复更新不定状态,期待你们带来更多。

结束了。谢谢你们。再见。

---------------------------------- THE END ---------------------------------

🎁 相关推荐

髂腰肌的按摩与拉伸
🎯 bat365官方网站

髂腰肌的按摩与拉伸

📅 07-20 👀 1426
局域网ARP防护和ARP病毒的清除(附图)
🎯 bat365官方网站

局域网ARP防护和ARP病毒的清除(附图)

📅 08-09 👀 1733
2018赛季意甲积分榜完整分析:排名变动、球队表现与赛季总结