【编程开发】C++ 打砖块开发日志

【编程开发】C++ 打砖块开发日志

\[\text{这是一篇写给自己看的日记,非制作教程} \]

【编程开发】用 EasyX 写 UI 开发一个好看好玩的小游戏:打砖块

因为开始写文章的时候 \(\text{UI}\) 已经完成很大一部分了(虽然丑得和没有一样),没有留下多少历史截图,只能强行还原到记忆中的样子,至于代码.....谔谔....懒得改回去了。

还有些踩过的坑记不清楚就直接跳过不扯了。

2020 年 2 月 ?日

之前在学习 \(\text{EasyX}\) 的时候,觉得库里面有一些自带的函数用起来很不方便,于是自己搞了一个头文件,按照平时的使用习惯写了一些常用函数。但这些还远远不够,后续又添加了各种各样的东西。

开坑啦!不管三七二十一,先尝试做一个开始界面吧!

看了看 \(4399\) 上的打砖块 ,经过一系列比对,决定把界面设置成 \(480 \times 820\),并在最下方取出 \(y \in [720,819]\) 的区域作为 \(\text{debug}\) 区域,方便中间查错(一开始忽略了界面像素点的问题,居然天真的以为 \(820\) 也可以用,实际上可用区域应该是 \([0 \sim 479,0 \sim 819]\))。

然后是背景图,找了一张 \(\text{Ririko}\) 老师我大天朝的画师,不支持一波?)的插画,并调整了图片亮度。
[背景PID: 79395649]
(\(\text{PID: 79395649}\))

调好后我突发奇想整了两种暗度的背景图片,主页用较亮 的那一份,其他页面用较暗的。

然后是安上标题作者开始游戏游戏说明 两个选项(厌倦打 \(\text{Latex}\) 了,直接给游戏里的所有东西上高亮),发现不知道从何下手,于是在草稿纸上规划了一下四个格子的方位。

现在问题又来了:绘制出一个怎样的选项。

实际上前期制作的大部分时间基本上都花在了制作开始页面上,主要工作如下:

\((1).\) 构造出一个适合自己使用习惯的绘图函数库作为头文件

\((2).\) 为后续所有可能出现的界面设计出大体类似的框架供使用

\((3).\) 一改 \(\text{OI}\) 码风(平时做题时写的代码极为紧♂凑,但要是做大工程也这样搞的话眼睛会直接爆炸的)

\((4.)\) 为了适应后续的使用需求 ,头文件反复进行增添删改

\((4)\) 的耗时应该是是最多的了,不断思考如何才能写出适用面尽量广、尽量统一化的函数,真的很累很累。但我又不能不这样做,否则的话后续的修改会牵一发而动全身,\(\text{debug}\) 时肯定不轻松。当然这只是我自己的猜测,实际上会不会出现这种情况也不好说,毕竟写小游戏涉及算法不多,简单的模拟一般不会出太多 \(\text{bug}\)Flag 高高立起)。

由于过程过于繁琐(疯狂踩坑,不断地出现各种问题和疑惑),就不一一叙述了(其实就是犯老年痴呆了,踩过的坑一个都记不起来)。

写好后就可以直接把框架应用于其他页面了,直接一波复制粘贴,并完成 碰撞箱判定点检测页面跳转,然后是按下 \(\text{ESC}\) 返回,看上去似乎效果不错。

考虑到页面跳来跳去可能会很乱,于是我做了个页面跳转的逻辑树状图:

但是,到这里还没完,志向远大的我还要做出 \(\text{hit}\)\(\text{nohit}\) 的不同(就是当检测到鼠标指针在选项碰撞箱以内时就改变选项框颜色),于是乎......又是一场恶战。


2020 年 2 月 22 日

\(\text{update:}\) 完成了开始界面以及各个界面的实现模板。

开始界面基本完成,成果如下(主区域和 \(\text{debug}\) 区域之间那条黑线是一开始没有搞清楚像素点排布规律而空出来的部分,没想到我之前居然忽略了这个问题。而上面那张图片是之后截的,这个问题在那时已经修正了,所以看不到黑线):

(由于 \(\text{gif}\) 录制软件是临时找的,而且为了压缩大小进行了阉割,所以看起来画质比较 \(\text{la ji}\),实际效果是没有这么差的)

在此时我神奇地码到了 \(660\) 行(这次刚起步啊崴 QAQ):

(右下角那只不开心的 \(\text{Neptune}\) 请自行忽略)

从这两份代码中可以感受到我真的很尽力的在改善码风了,平时要是出现类似下面这种宽松的写法肯定会浑身不舒服(只要一看到代码中间的空行就会忍不住想把它删掉)。

inline void sakura(){balabala}

int main(){balabala}

2020 年 2 月 23 日

\(\text{update:}\)选择关卡界面增添了关卡选项,设计了小球样式。

打了接近一天的 \(\text{OSU}\),然后继续敲代码。

又把之前写的选项代码复制了一遍,计算了格子方位和数量,成功绘出了选择关卡界面,如下:

下一步做小球样式。

感觉半径为 \(9\) 的圆画出来大小比较合适,于是研究了一番 \(\text{EasyX}\) 库里的 \(\text{circle}\) 函数,发现其像素点排布如下(唉、我终于开始在意像素点的排布了):

懒得设计算法了,直接暴力枚举像素点:

for(Re i=7;i<=11;++i)pan[i][0]=1;
for(Re i=5;i<=13;++i)pan[i][1]=1;
for(Re i=3;i<=15;++i)pan[i][2]=1;
for(Re i=2;i<=16;++i)pan[i][3]=pan[i][4]=1;
for(Re i=1;i<=17;++i)pan[i][5]=pan[i][6]=1;
for(Re i=0;i<=18;++i)pan[i][7]=pan[i][8]=pan[i][9]=pan[i][10]=pan[i][11]=1;
for(Re i=1;i<=17;++i)pan[i][12]=pan[i][13]=1;
for(Re i=2;i<=16;++i)pan[i][14]=pan[i][15]=1;
for(Re i=3;i<=15;++i)pan[i][16]=1;
for(Re i=5;i<=13;++i)pan[i][17]=1;
for(Re i=7;i<=11;++i)pan[i][18]=1;

然后从免费素材网上白嫖了一张 \(\text{3D}\) 小球图片,修成 \(19 \times 19\)。总感觉哪里怪怪的啊(像是套了个胖次)。改亮度后看起来好一点了:,但还是很丑。

算了,先把他画出来再说吧。依旧是暴力枚举(获取需要的像素点):

inline void getbkimg(){
    for(Re i=0;i<=18;++i)
        for(Re j=0;j<=18;++j)
            if(pan[i][j])
                P[++cnt]={i,j},ballpimg[cnt]=getimage(i,j,i,j);
}

inline void print(){
    Re x=Ox-r,y=Oy-B.r;
    for(Re i=1;i<=cnt;++i)
    	putimage({x+P[i].x,y+P[i].y},ballpimg[i]);
}

感觉怪怪的。。。。。

无论怎么改都丑得要死,几乎快要放弃挣扎的时候,再次突发奇想:我直接弄个由内到外的黑白渐变看看?

\(\text{woc}\)!这么帅!\(\text{i}\)\(\text{i}\) 了。

至于之前的小球样式也没有废弃,丢在一边好了(这使我萌发出了新的灵感:设计多种不同样式的小球。当然,这都是后话了,目前就用这两个好了,等后续开发吧)


2020 年 2 月 24 日

早上学习了 \(\text{prufer}\) 序列并做了两道炒鸡大水题。下午从表哥那里嫖到了个他自己买的 \(\text{vpn}\),速度飞起,于是逛了一下午的 \(\text{Youtube}\) 。晚上好像抽出时间写了一点点,但记不清了。


2020 年 2 月 25 日

\(\text{update:}\) 调整了游戏页面中菜单的位置,研究新的配色,优化了代码,添加选关界面逻辑判断。

原本是准备今天做小球运动以及反弹判定的,但在这之前却出现了意外,感觉游戏界面(见下图)上方的黑色生命框和亮青色菜单选项混在一起丑的一 \(\text{p}\) 啊啊啊啊啊!!我受不了,我现在就要改配色!

考虑到自己的艺术细胞可能不太够用,于是向 \(\text{Silent_E}\) 发起了求助:

(我带泥萌打)

经过一系列激♂烈的讨论,最终还是没想出什么好的设计(其实主要是在尝试红配棕,但是太暗了所以都不好看),失败品这里只放一个,就不一一列举了(太多了,会显得文章特别长):

吃午饭时再次突发奇想:我如果换一张色调单一一些的图片会不会好点呢?

发现即使是没有进行调整的原图放上去也比前面的好看:

[背景PID: 72052573]
\(\text{PID: 72052573}\)

此时在换背景的过程中出现了意外:因为代码复制时忘记改细节,导致背景图片全部使用了亮色调的,但是选项框背景却使用了暗色调。

虽然是个意外,但这样的效果却出奇的好,于是我又进行了一系列魔改,成果如下:

在调色的过程中,因为要不断地尝试各种各样的颜色,而几个的选项都散布在各个地方,改来改去很不方便,于是我对代码进行了优化:整合选项,统一使用一个结构体。

下午重新设计好选项样式后,又添加了选择关卡界面的判断:通过一关后解锁下一关,当尝试进入未解锁关卡时提示 \(\text{FBI Warning!!!}\)

\(25\) 日晚开始写这篇文章。

其实今天下午本来是要学习的,但由于那个突然出现的灵感,又荒废了一个下午。。。。。


2020 年 2 月 27 日

沉迷于多项式算法无法自拔。 ~~

\(\text{update:}\) 晚上对日志进行了重新排版,并单独捞出来作为一篇文章。


2021年 10 月 12 日

\(\text{update:}\) 截止目前,完成了玩家小球的检测移动,优化图像闪烁问题。

大一了。

从两周前的某堂无聊计算机课开始,我又重拾了这项工程。

记得当时半途弃坑的其中一个原因是:小球碰撞检测太难。

这个问题一直困扰着我,每当想要继续写时就会因为这个问题而退缩。

至到今天,我突然意识到,用浮点数储存坐标进行数学计算,然后取整绘图呀...先前一直在想如何判好一个锯齿状圆的碰撞转向...

我真傻,真的。

此外,图像动起来后,反复刷新产生的闪烁现象较为明显,需使用BeginBatchDraw();FlushBatchDraw();


2021年 10 月 15 日

\(\text{update:}\) 添加小球与四个方向的碰撞判定,优化绘图速度,添加死亡动画游戏暂停功能。

写了很长一段代码:

inline void reverse_dir(Re flagx,Re falgy){//翻转运动方向
	if(flagx)Dir.x=-Dir.x,DB_Dir.x=-DB_Dir.x;
	if(falgy)Dir.y=-Dir.y,DB_Dir.y=-DB_Dir.y;
}
inline int judge_wall(Range &R1,DB_Point &DB_P,DB_Point &DB_P_){//判断是否撞墙
	if(DB_P_.y>=R1.y2)return -2;//碰下【dead】
	Re p1=dcmp(DB_P_.x-R1.x1);//p1<=0 碰左壁
	Re p2=dcmp(DB_P_.y-R1.y1);//p2<=0 碰上壁
	Re p3=dcmp(DB_P_.x-R1.x2);//p3>=0 碰右壁
	if(p1<=0&&p2<=0){//碰左 且 碰上
		if(p1==0&&p2==0)DB_P_.x=R1.x1,DB_P_.y=R1.y1,reverse_dir(1,1);//角落碰撞(同时碰左壁和上壁)
		else{
			LD t1=(R1.x1-DB_P.x)/DB_Dir.x,t2=(R1.y1-DB_P.y)/DB_Dir.y;
			if(dcmp(t1-t2)<0)DB_P_.x=R1.x1, reverse_dir(1,0);//先碰左壁
			else DB_P_.y=R1.y1, reverse_dir(0,1);//先碰上壁
		}
	}
	else if(p3>=0&&p2>=0){//碰右 且 碰上
		DB_P_.x=max(DB_P_.x,1.0*R1.x1),DB_P_.y=max(DB_P_.y,1.0*R1.y1);
		DB_P_.x=min(DB_P_.x,1.0*R1.x2),DB_P_.y=min(DB_P_.y,1.0*R1.y2);
	}
	else if(p1<=0)DB_P_.x=R1.x1,reverse_dir(1,0);//碰左
	else if(p2<=0)DB_P_.y=R1.y1, reverse_dir(0,1);//碰上
	else if(p3>=0)DB_P_.x=R1.x2,reverse_dir(1,0);//碰右
}
inline int updata(Range R0,Re speed0=1){//更新小球位置
	if(speed0!=speed)calc_DB_Dir(speed0);//改变速度
	Range R1=Range(R0.x1+r,R0.y1+r,R0.x2-r,R0.y2-r);
	DB_Point DB_P_old=DB_P;DB_P+=DB_Dir;
			//DB_P_.x=max(DB_P_.x,R1.x1),DB_P_.y=max(DB_P_.y,R1.y1);//P.x-r>=gamebox.x1,P.y-r>=gamebox.y1
			//DB_P_.x=min(DB_P_.x,R1.x2),DB_P_.y=min(DB_P_.y,R1.y2);//P.x+r<=gamebox.x2,P.y+r<=gamebox.y2
	Re cmd=judge_wall(R1,DB_P_old,DB_P);//判断是否撞墙
	if(cmd==-2)return -2;//【dead】

	Point P_=Point(floor(DB_P.x+eps),floor(DB_P.y+eps));
	if(P_!=P)refresh(P_);//更新图像
	return 0;
}

但此时却出现了一个很麻烦的问题:这里更新小球状态的 updata函数只要一开就卡得飞起。

没办法,只好尝试着能不能卡下常了(保精度的dcmp函数第一个被下刀....)。

然而,并没有什么卵用。

调了一波,发现问题在于小球刷新图像时高频使用putimagegetimage函数:

#define bs style[styletype]
inline void print_background(){
	Re x=P.x-r,y=P.y-r;
	for(Re i=1;i<=bs.cnt;++i)
		putimage({x+bs.P[i].x,y+bs.P[i].y},background_img[i]);
}
inline void print(){
	if(styletype==1){
		Re x=P.x-r,y=P.y-r;
		for(Re i=1;i<=bs.cnt;++i)
			putimage({x+bs.P[i].x,y+bs.P[i].y},bs.ballpimg[i]);
	}
	else if(styletype==2){
		for(Re r=9-1;r>=1;--r){
			Re tmp=int(255.0*(9-r+1)/9.0);
			drawcircle({P.x,P.y},r,RGB(tmp,tmp,tmp));
			drawcircle_({P.x,P.y},r,RGB(tmp,tmp,tmp));
		}
	}
}

inline void playgame_ball_getimage(){
	Re x=P.x-r,y=P.y-r;
	for(Re i=1;i<=bs.cnt;++i)
		background_img[i]=getimage(Point(x+bs.P[i].x,y+bs.P[i].y));
}
#undef bs

emm...一个一个摸像素点确实是有点蠢,背景图的话,其实可以直接扣一个矩阵下来。不过小球的话就没法避免枚举了。

试着优化了一下,没想到跑得飞快:

inline void playgame_ball_getimage(){ background_img=getimage(P.x-r,P.y-r,P.x+r,P.y+r); }
inline void print_background(){ putimage(P.x-r,P.y-r,background_img); }

但如果是第一种小球(枚举法输出图像),还是会慢一些,不过,通过调整小球运动速度可以做到大致相等。

此外,添加死亡动画游戏暂停功能

inline void dead_anime(Re HP){//【死亡动画】
	for(Re O=1;O<=5;++O){//闪烁次数
		HPB[HP].print_background(),game_ball.print_background();
		print(gamebox,_T("【Oh yeah~ You dead!!!】"),red,40,200);
		Sleep(200);
		HPB[HP].print(),game_ball.print();
		print(gamebox,_T("【Oh yeah~ You dead!!!】"),darkgray,40,200);
		Sleep(200);
	}
}
inline void game_pause(){//【游戏暂停】
	printdebug(_T("【游戏暂停】输入任意键以继续..."),white,28,100);
	system("pause"),refresh_debug_level_info();
}

2021年 10 月 19 日

今天开始尝试思考小球砖块

这东西并不好做,而且越想越麻烦越想越复杂。这是一个难点,网上很多人写的都很水。但我是个完美主义者,希望尽量地去实现一个真实物理引擎。

小球碰平面当然是镜面反射,但也有一个问题:程序实现的时候,是每隔一段时间更新小球位置,判断是否碰撞当然是需要判断图形是否有交叉区域(甚至小球可能刷新后的位置直接就穿到了砖块里面去)。

然后我在网上搜了一下,发现了一个叫做“分离轴定理”的黑科技。具体见:

另外,在查找资料的时候,意外发现了一个小 \(\text{trick}\),游戏要流畅运行大概需要 \(\text{60}\) 帧每秒,也就是每秒进行 \(\text{60}\) 次刷新,那么每一次刷新就需要控制在 \(\text{16ms}\) 以内。这东西看起来很显然,但事实上我此前都从未想到加以利用:根据这个刷新所需的运行时间大致区间来调整延迟。

小测试了一波,每两次刷新的间隔大约是 \(11\sim 18ms\)(算上固定 \(10ms\)\(\text{Sleep}\)),撞墙的时候最高可以达到 \(25ms\),也就是大约 \(40\sim 90\) 帧的亚子,目前来看大致合格吧。不过当后面各种检测越来越多并加上砖块刷新后就需要调整了。有了这个标准之后调 \(\text{Sleep}\) 参数会舒服许多。

回到碰撞检测的第二种情况:小球碰砖块尖角。

一开始我的想法比较 \(\text{naive}\),转换成圆心位置判定嘛,然后碰了尖角之后反弹原路返回。

如图(灰色为砖块),首先把本次刷新前的圆心位置和刷新后的圆心位置连起来,与外围的补充边界求交点,得到发生碰撞时的位置,如果是在蓝色区域就镜面反弹,如果红色就按照原来进入的方向反弹回去。

但想来想去总感觉哪儿有点问题,于是去问室友,emm...果不其然,被狠狠地教育了一波。

他提供的解释大概是这样:碰撞的那一瞬间,在碰撞点处做圆的切线,然后将“运动的曲线(圆弧)去撞点(砖块尖角)”转化为“运动的点(砖块尖角)去碰切面(圆的切线)”。(相对运动转换参照物嘛)

一语点醒梦中人。

后来我仔细思考了一下,实际情况应该长这样:

对于第一种情况,原路返回。

对于第二种情况,碰撞前的速度和碰撞后的速度嘤该关于 \(O_2A\) 对称(转换参照物后,就相当于是点 \(A\) 在切面上发生了镜面反弹)。其中 \(O_2\) 为发生碰撞时圆心的位置,\(A\) 为砖块尖角。

哎...太麻烦了,还是写简单点吧...我屈服了行吧....

来自大佬室友的吐槽:你这打砖块整个真实物理引擎玩家不得急死?尖角一碰就掉下去了玩个锤子啊,别人都是一碰就上去,到你这一碰就往下飞...


2021年 10 月 26 日

注意到一个问题:如果小球运动轨迹为垂直向上下左右,需要人为创造偏转一个角度,否则可能会在一个地方反复横跳。

发现求交点有点麻烦,以前的算几板子里没有直线与圆求交的封装代码(似乎三角剖分里面有,但比较乱)。


2021年 11 月 2 日

\(\text{update:}\) 完善小球发射时的运动方向判定;增加线与圆求交点;增加小球碰玩家的部分判定:上壁及左右壁镜面反弹,左上及右上两角原路返回。

挖坑:左上及右上两角反弹的方式待定;关于上壁反弹:给玩家设定“左”,“中”,“右”三个区域,中间镜面反弹,左右按照一定规律偏转角度。


2021年 11 月 9 日

\(\text{update:}\) 添加道具效果:伸长

发现一件很恐怖的事情。
之前我写了小球碰玩家左右壁的判定,也就是说,允许小球在玩家区域稍作逗留。

那么,当我更新玩家位置的时候,就需要判定玩家撞小球。仔细想想发现这玩意痛苦地一p.....

琢磨了好久,晚上回寝室猛然发现,玩家碰小球这一部分其实很简单,因为玩家移动只有单纯的左右平移,所以只需要计算小球左右两边能拦截玩家的位置即可。


2021年 11 月 12 日

\(\text{update:}\) 部分完成玩家移动时被小球拦截的现象。

当然,问题并没有完全解决:可能会出现玩家追着小球跑,小球却不与其发生碰撞(运动方向向外时显然撞不了。向里时也可能出现,因为我存储玩家坐标没有用实数,于是小球与玩家中间会有一些间隙,如果玩家不停地追,就会一直保持这个间隙,导致无法碰撞)
所以,还需要实现:玩家移动时被小球拦截,触发小球碰撞改变运动轨迹。


2021年 11 月 16 日

\(\text{update:}\) 微改小球碰撞变轨方案;完成玩家被拦截后触发小球碰撞变轨;增加道具效果:加速减速

inline int judge_player(Player &py,DB_Point &st,DB_Point &ed){//【碰撞检测】判断与玩家的碰撞情况
    DB_Point CP;Re flag=0;LD dis_CPP=2e9;
			
    DB_Point A=st,B=ed;

    if(dcmp(ed.y+r-py.R.y1)<0)return 0;//小剪枝:如果运动轨迹的终点还没到玩家区域

    DB_Point up_A=DB_Point(py.R.x1,py.R.y1-r),up_B=DB_Point(py.R.x2,py.R.y1-r);
    if(GMT::pan_cross_LL(A,B,up_A,up_B)){//1.上线段
        CP=GMT::cross_LL(A,B,up_A,up_B); flag=UP_LINE; dis_CPP=GMT::Len(CP-st);
    }

    DB_Point left_A=DB_Point(py.R.x1-r,py.R.y1),left_B=DB_Point(py.R.x1-r,py.R.y2);
    if(GMT::pan_cross_LL(A,B,left_A,left_B)){//2.左线段
        DB_Point tmp_CP=GMT::cross_LL(A,B,left_A,left_B); LD tmp_dis=GMT::Len(tmp_CP-st);
        if(tmp_dis<dis_CPP)CP=tmp_CP,flag=LEFT_LINE,dis_CPP=tmp_dis;
    }

    DB_Point right_A=DB_Point(py.R.x2+r,py.R.y1),right_B=DB_Point(py.R.x2+r,py.R.y2);
    if(GMT::pan_cross_LL(A,B,right_A,right_B)){//3.右线段
        DB_Point tmp_CP=GMT::cross_LL(A,B,right_A,right_B); LD tmp_dis=GMT::Len(tmp_CP-st);
        if(tmp_dis<dis_CPP)CP=tmp_CP,flag=RIGHT_LINE,dis_CPP=tmp_dis;
    }

    DB_Point tmp_CP;Re pan;
			
    pan=cross_LH(A,B,DB_Point(py.R.x1,py.R.y1),r,LEFT_UP_ARC,tmp_CP);//5.左上圆弧
    if(pan){
        LD tmp_dis=GMT::Len(tmp_CP-st);
        if(tmp_dis<dis_CPP)CP=tmp_CP,flag=LEFT_UP_ARC,dis_CPP=tmp_dis;
    }

    pan=cross_LH(A,B,DB_Point(py.R.x2,py.R.y1),r,RIGHT_UP_ARC,tmp_CP);//6.右上圆弧
    if(pan){
        LD tmp_dis=GMT::Len(tmp_CP-st);
        if(tmp_dis<dis_CPP)CP=tmp_CP,flag=RIGHT_UP_ARC,dis_CPP=tmp_dis;
    }
    if(flag){ ed=CP,reverse(flag); return flag; }//发生碰撞,改变运动轨迹
    else return 0;//未发生碰撞
}

发现玩家碰小球还是很是别扭——没有设定加速度方案。
但这玩意儿实现起来....不用想就知道很毒瘤,放弃ing...

在测试加速减速效果时,试出了一种特殊情况,把我给整不会了...

(蓝色箭头为小球运动方向)

小球被卡在角落里惹...

虽然,经过测试后发现程序崩溃的原因不在这里,但这提醒了我:要是小球卡住不动咋办TAT


2021年 11 月 19 日

\(\text{No update.}\)

问题是真她喵的多。前面为了简化判定,禁止了垂直移动/水平移动的情况(通过添加偏移量的形式),于是乎,穿墙现象再一次出现...

不过,说到底,在正常游戏中,极低的玩家高度很难造成这种问题,还有前面的一些问题也是,几乎可以说是在做没有用处的工作(除非有熊孩子玩家恶意卡我bug...)。

如何解决?
小球垂直/水平移动 解禁。

那么,代价呢?
出现大量周期运动卡死的情形。

所以,我选择舍小我为大我,忽略这个问题。(希望不要有熊孩子,不要有熊孩子,不要有熊孩子


2021年 11 月 23 日

\(\text{update:}\) 微调小球颜色;加入砖块信息。

发现一个让人头疼的bug,调了很久才发现是因为对之前自己预设的绘图函数不熟悉,使用的时候少了个参数...

哎,也难怪,时间间隔太久,很多东西都忘了。


2021年 11 月 30 日

\(\text{update:}\) 完善砖块信息;实现小球砖块及消灭砖块功能;添加通关动画

发现'drawtext‘里的垂直居中参数居中了个锤子,md,还得自己来调,烦呐。

注意到小球在运动时可能会截到砖块作为背景,即出现小球背景和砖块背景互相嵌套覆盖的情况(导致砖块消失后产生残留色块),仔细想了想,发现这东西竟然异常难搞,自闭...


2021年 12 月 1 日

\(\text{update:}\) 修复背景图相互覆盖 \(\text{bug}\) ;修复碰撞检测重大 \(\text{bug}\) ;调整小球玩家的碰撞方案;调整背景模糊度;添加开发者模式关卡制作功能;开发部分关卡

昨天注意的问题想了很久,最后发现只需要在砖块消失前后添加一点小步骤就可以了:消失前一瞬间让小球被小球背景所覆盖,然后让砖块消失,再获取小球的新背景并输出小球。

//砖块受到攻击
print_background();//砖块变化前先抠掉小球
brick[bk_Rid].vanish();//砖块变化
playgame_ball_getimage();//重新获取小球背景图像
print();//重新输出小球

测试的时候出现了很混乱的状况,各种奇怪数据乱跳,调了一个多小时,发现写栈的时候,同一个点被重复加入了几百次,各种越界起飞...

此外,终于调整了小球碰玩家方案:
正中央一半的区域为镜面反射,左右两边依赖于与中心区域的距离调整偏转角,最小 \(15^{\circ}\) 最大 \(75^{\circ}\),左右两个尖角直接使用 \(15^{\circ}\) 偏角。

if(flag){//发生碰撞,改变运动轨迹
    if(flag==2||flag==3){ ed=CP,reverse(flag); return flag; }//(镜面反弹) 2.左线段 3.右线段
    if(flag==1){//1.上线段
        if(dcmp(CP.x-py.center_L)>=0&&dcmp(CP.x-py.center_R)<=0){ ed=CP,reverse(flag); return flag; }//(镜面反弹) 中心区域
        LD rad;
        if(CP.x<py.center_L)rad=toPi(75.0-60.0*(py.center_L-CP.x)/(py.lenth/2.0)),Dir_ratio.x=-cos(rad);//(特殊反弹) 左特区
        if(CP.x>py.center_R)rad=toPi(75.0-60.0*(CP.x-py.center_R)/(py.lenth/2.0)),Dir_ratio.x= cos(rad);//(特殊反弹) 右特区
        Dir_ratio.y=-sin(rad);
        calc_DB_Dir(speed);
        ed=CP; return flag;
    }
        if(flag==5||flag==6){//5.左上圆弧 6.右上圆弧
        LD rad=toPi(15.0);//碰圆弧直接15°最小偏转角
        if(flag==5)Dir_ratio.x=-cos(rad);//(特殊反弹) 5.左上圆弧
        if(flag==6)Dir_ratio.x= cos(rad);//(特殊反弹) 6.右上圆弧
        Dir_ratio.y=-sin(rad);
        calc_DB_Dir(speed);
        ed=CP; return flag;
    }
}
else return 0;//未发生碰撞

然后,还做了一些其他调整,写了一个关卡制作页面,做了前 \(6\) 关。

到目前为止,最核心的游戏机制就完成地差不多了。

后面还有各种各样的小球buff玩家buff砖块硬度特殊关卡设计等等一系列东西都属于玩法扩展了。
再就是最重要的:设计好看的 \(\text{UI}\)...


2021年 12 月 2 日

\(\text{update:}\) 新增关卡\(18\)玩家中心镜面反弹区域调整为长度的 \(\frac{1}{4}\) ;优化选项框代码框架,增加贴图模式;添加开始游戏贴图、游戏说明贴图

学着使用 \(\text{photoshop}\) 绘贴图,摸索了好久好久,发现这东西简直不要太牛逼。

话说,如果加入大量贴图的话,之前写的代码框架可能会废掉好几百行...


2021年 12 月 3 日

\(\text{update:}\) 增加返回开始界面贴图;增加大标题贴图;调整游戏延迟。

\(\text{photoshop}\) 真好玩!

发现史诗级重大bug!
她喵的,延迟时间呈她喵的类周期变化!时快时慢,低的时候15ms高的时候40ms...
...人麻了...

实在是找不出问题来,没办法,只好硬调延迟,试了无数种方案,最后一碰巧不知道咋回事突然就成了,稳定在9-22ms 。似乎和删掉了 Sleep(5) 有关。

LD temp=gettime(),tp=gettime();
while(1){
    LD nowt=gettime();
    if((nowt-tp)*1000<15)continue;
    else tp=nowt;
    char cmd=updata(0);
    if(cmd=='E')return 'E';//refresh();//按下[ESC]或者[菜单]
    else if(cmd=='S')game_pause();//按space【暂停】
    else if(cmd=='D')return 'D';//自然死亡 或 按[D]开挂【死亡】;
    else if(cmd=='P')return 'P';//按[P]开挂,直接【过关】
    else if(cmd=='G'){//按G进入开挂操作界面
        int G_cmd=G_controller();
        if(G_cmd=='P')return 'P';//按[P]开挂,直接【过关】
        else if(G_cmd=='D')return 'D';//按[D]开挂,直接【死亡】
    }
//Sleep(5);
debug.delaytime(gettime()-temp);temp=gettime();
//debug.coordinate();
//debug.brick_styletype();
}

后面还要加音效,到时候估计又是一波延迟大战,哎...任重道远啊...


2021年 12 月 4 日

\(\text{update:}\) 添加游戏说明文本;添加文件超链接方案;修复部分 \(\text{bug}\) ;调整大标题贴图;新增菜单贴图;新增血量框贴图。

做发布准备,写了个游戏说明,并在游戏内设置超链接。
超链接的实现当然是用 \(\text{cmd}\) 指令start C:\Users\....
但这个指令必须使用文件绝对路径。
解决方案是先用_getcwd_dbg函数获取当前运行程序路径,然后接上想要设置的相对路径就可以了。

inline void cmd_openfile(char *filepath){//打开相对路径filepath下的文件(转换STR或者CH好像会有编码问题,先咕一下,用char凑合凑合)
	char _cmd[1000]="start ";
	_getcwd_dbg(_cmd+6, sizeof(_cmd),_NORMAL_BLOCK,NULL,NULL);//获取当前运行程序绝对路径
	Re len=std::strlen(_cmd);
	for(Re i=0;filepath[i];++i)_cmd[len++]=filepath[i];//在后面接入相对路径
	_cmd[len++]=0,system(_cmd);
}

发现一个很诡异的 \(\text{bug}\),载入进去的图片输出来只有一半。

一段一段的排查,发现是数组越界引起的(之前调整初始上界的时候不小心搞忘调小了一个值)。
哎,数组越界一生之敌啊。


2021年 12 月 5 日

\(\text{update:}\) 添加关卡选项背景贴图;打砖块v1.0发布

记得有人说,如果不打包成一个单文件顺便就会降很多逼格。之前用py有尝试过,不过后来py坏掉惹。要打包只能依靠VC,好像...有点麻烦?先咕着。
另外还有很多很多没做的东西,总之这个摸鱼项目就先暂时告一段落吧。
准备开新坑搞一波大的,这几天爆肝进度也是为了尽快搞定然后转战。
算是去追梦吧。
梦圆之后再回来继续添加内容(也可能中途摸鱼做一点,或者有人发现 \(\text{bug}\) 逼迫我回来修)。

posted @ 2021-11-16 19:44  辰星凌  阅读(728)  评论(4编辑  收藏  举报