【全网唯一 纯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(X //------- 胜利与失败判定 -------// 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...