这次就着上上上篇的悬浮窗代码DevC++ easyx实现图片拖动,一种悬浮窗实现原理与完整代码-CSDN博客
继续实现效果。
基本背景是搓出来图片拖动了,然后想把图片暂存到另一块。再细说背景的背景就是之前提到Unity复刻瓦片地图,想着整合一个铅笔绘制功能,就是绘制瓦片和瓦片粘贴都在同一个程序里运行。正是这一系列文案所追溯的故事------做一个绘图板,连同绘图,贴片。当时实现了图片缩小,然后要把这个缩小的图片的数据放到网格里面。这个网格作为备选的贴片。这就是文案所去描述的故事,只不过现在回想起来,发现以前推动代码翻新过程中,有了比较有意思的意识到矛盾的新情况。
这次直接上最终代码,然后直接解释代码是怎么来的。
cpp
#include<stdio.h>
#include<conio.h>
#include<graphics.h>
#include<windows.h>
struct picsave {
int lx;
int ly;
int rx;
int ry;
int useflag=0;
} a[10];
struct pircle {
IMAGE img2;
IMAGE img3;
const int orilx=100,orily=100;
int nowlx=100,nowly=100;
const int a=100,h=100;
// 原有图片的左上角坐标
int m1x=0,m1y=0;
int putflag=0;
int drawflag=0;
} save;
void draw(struct ExMessage m,struct pircle *save) {
if(save->drawflag==true) {
putpixel(m.x,m.y,RGB(255,155,4));
}
switch(m.message) {
case WM_LBUTTONDOWN:
if(m.x>save->nowlx&&m.x<save->nowlx+save->a&&m.y>save->nowly&&m.y<save->nowly+save->h&&m.ctrl) {
// 检测不在图片里面
} else {
printf("m.x = %d\tm.y = %d\t%d\t%d\n",m.x,m.y,save->m1x,save->m1y);
save->drawflag=true;
// printf("drawflag = %d\n",save->drawflag);
}
break;
case WM_LBUTTONUP:
save->drawflag=false;
// printf("%d\n",save->drawflag);
break;
}
}
void check(struct ExMessage m,struct pircle *save,struct picsave a[]) {
printf("putflag = %d\n",save->putflag);
printf("%d %d\n",m.x,m.y);
printf("%d %d\n",save->nowlx,save->nowly);
if(save->putflag==true) {
BeginBatchDraw();
putimage(save->nowlx,save->nowly,&save->img3);
save->nowlx=save->nowlx+m.x-save->m1x;
save->nowly=save->nowly+m.y-save->m1y;
save->m1x=m.x;
save->m1y=m.y;
getimage(&save->img3,save->nowlx,save->nowly,save->a,save->h);
putimage(save->nowlx,save->nowly,&save->img2);
EndBatchDraw();
// 一次绘图出来,没有屏闪了
}
switch(m.message) {
case WM_LBUTTONDOWN:
if(m.x>save->nowlx&&m.x<save->nowlx+save->a&&m.y>save->nowly&&m.y<save->nowly+save->h) {
save->putflag=true;
// 启动批复制粘贴
getimage(&save->img2,save->nowlx,save->nowly,save->a,save->h);
save->m1x=m.x;
save->m1y=m.y;
}
break;
case WM_LBUTTONUP:
save->putflag=0;
// int i;
// for(i=0; i<6; i++) {
//
// if(a[i].useflag==0) {
// if(m.x>a[i].lx&&m.x<a[i].rx&&m.y>a[i].ly&&m.y<a[i].ry) {
// BeginBatchDraw();
//
// putimage(save->nowlx,save->nowly,&save->img3);
//
// save->nowlx=save->nowlx+m.x-save->m1x;
// save->nowly=save->nowly+m.y-save->m1y;
// save->m1x=m.x;
// save->m1y=m.y;
// getimage(&save->img3,save->orilx,save->orily,save->a,save->h);
//
// putimage(a[i].lx,a[i].ly,&save->img2);
//
// EndBatchDraw();
//
// save->nowlx=save->orilx;
// save->nowly=save->orily;
// save->m1x=0;
// save->m1y=0;
//
// a[i].useflag=1;
// break;
// }
// }
// }
// 实现一次性存档
break;
}
}
void flow(ExMessage m,struct picsave a[],struct pircle *save) {
if(save->putflag==0&&m.message==WM_LBUTTONUP) {
int i;
for(i=0; i<6; i++) {
if(a[i].useflag==0) {
if(m.x>a[i].lx&&m.x<a[i].rx&&m.y>a[i].ly&&m.y<a[i].ry) {
BeginBatchDraw();
putimage(save->nowlx,save->nowly,&save->img3);
// save->nowlx=save->nowlx+m.x-save->m1x;
// save->nowly=save->nowly+m.y-save->m1y;
// save->m1x=m.x;
// save->m1y=m.y;
getimage(&save->img3,save->orilx,save->orily,save->a,save->h);
putimage(a[i].lx,a[i].ly,&save->img2);
EndBatchDraw();
save->nowlx=save->orilx;
save->nowly=save->orily;
save->m1x=0;
save->m1y=0;
a[i].useflag=1;
break;
}
}
}
}
}
int main() {
initgraph(640,640,EX_SHOWCONSOLE);
setbkcolor(WHITE);
cleardevice();
setfillcolor(BLACK);
setlinecolor(BLACK);
fillrectangle(100,100,200,200);
// 这是初始图片左上角位置,以后都是新图片左上角的位置
int i;
// setlinecolor(WHITE);
setfillcolor(GREEN);
int q=100;
for(i=1; i<5; i++) {
a[i].lx=q;
a[i].ly=400;
a[i].rx=q+100;
a[i].ry=500;
q=q+100;
fillrectangle(a[i].lx,a[i].ly,a[i].rx,a[i].ry);
}
// save.drawflag=1;
ExMessage m;
while(1) {
m=getmessage(EX_MOUSE|EX_KEY);
if(save.drawflag==false||save.drawflag==true) {
draw(m,&save);
}
if(save.putflag==true||save.putflag==false) {
check(m,&save,a);
flow(m,a,&save);
}
}
closegraph();
return 0;
}
由于鼠标检测实现原理是,通过鼠标坐标m.x m.y和鼠标事件如左键按下,右键按下,实际上延续这个问题就是坐标的问题,不断穷举坐标的各种相关、各种不知道相关不相关的判断。
想象中的需求经过几次描述出来就是,图片到了网格,图片就被网格吸收。
换成坐标来说,图片的坐标进了网格,图片就停在网格里。
但是想象一下图片只是黏在网格上,而不是正好嵌入,就单纯的坐标检测是否重叠也不好想象代码。
这里是参考UNITY,想起来以鼠标为主,就是鼠标在哪个网格,图片就嵌进去哪个网格,这样也能接受,那如此这样,只要能实现检测鼠标的位置就可以了。正好之前的代码check负责拖动,只要拖动结束的时候直接检测就行了,这样代码位置也解决了。于是就直接在check函数里的case LW_LBUTTONUP:这个分支下去写,就是check()函数那一大片注释处。只要全部取消,就能直接使用那部分代码实现检测。
后来是想着,由于地图实现拖动了,图片也实现的网格吸收,就想着独立出来这部分吸收代码,希望以后能够直接使用,或者减少函数长度,一个函数一个功能,减少脑子别扭难受的情况。于是就分离出来flow()函数,这就是专门处理图片嵌入网格存储的。flow()有一堆空白图片数组,然后每个数组的一项单元里,都有这个项对应的坐标,每一个网格的左上角坐标就都在这个项目里面。
然后运行成功,但是,鼠标一碰到网格图片就自己进去了。
这个BUG的效果是无论鼠标点没点击,只要鼠标拖动过图片,然后鼠标移动到网格上,就会有图片贴上去。
然后当时不知道哪里出问题了,唯一确定的是代码没复制粘贴整合成新函数的时候是正常的,于是就检测数据,相当于把有关拖动的变量内容都打印出来,先是在flow()里面增加打印的坐标信息语句,发现随着鼠标移动,就会一直打印鼠标坐标,而按照原计划,是只有鼠标松动的时候才进行检测,相当于打印鼠标只有在鼠标松开,而不是鼠标一移动就打印。flow函数失去了限制功能。
然后扩大范围,相当于发现if(save->putflag==0)失效了,之前想着不是鼠标松开,上一个函数直接就把save->putflag变成0,但是实际上这样却失败了,最后只能再就加上重复的&&m.message==LW_LBUTTONUP,才解决问题,但是没有理解bug。明明上个函数也已经用过一次m.message==LW_LBUTTONUP了。
现在来看,原来是鼠标松开,实现了flag变化,但是flag变化后是长期的,而鼠标松开是一瞬间相等。显然这样一看才理解,作用时间不一样导致了函数运行情况不同。也就是说,根本是没有意识到范围,默认中范围就已经不包含了一瞬间的变化,和长期状态的影响。只能通过矛盾来主动意识到意料之外,强行拓宽思考范围。
当然,文案不只是说为了解决BUG,可以看出来,刚才的BUG就是意外实现了鼠标拖动批量绘制,而这其实这也是以后像素绘画的原理。但是由于在这里能够主动意识到矛盾,对矛盾开发一下,自然而然就是另一个文案了。实际上无中生有的BUG和无中生有的解决方案,其实是同根同源的。他们都脱离过主动意识,而是从一些没来得及,或者以为不相关的情况里自然而然的穷举产生的。
人和BUG和谐共生可还行?但是一般都是BUG得不到理解,BUG也放不到合适的地方去应用emm。
理解BUG可以成为灵感的来源,但是理解bug比解决bug还要费劲,几乎把整个代码用人脑跑一边才能理解问题现象,确实代价比较大。
注意到if(save->putflag==0)和if(save->putflag==0&&m.message==WM.LBUTTONUP)的区别。
第一个BUG完事儿。
另一个BUG是在函数flow分离之前发生的。但是由于代码是复制粘贴,把原来函数的限制条件也复制粘贴过来,其实问题一样。
先看BUG效果
黑色方块变绿色了
纹理和第一幅图的比较像
答案解密:
注意旁边浅蓝色的代码
注意到坐标信息是原图的坐标
相当于又把原图复制粘贴到img3上。
按照惯例,前三章博客里img3含义都是底图,
这里也是一样,描述出来就是,原图变成了底图,
原来根本原因就是悬浮窗的底图把图片放置位置的网格复制了,
然后图片恢复到原处,借用悬浮窗先恢复原图,结果成了原图位置恢复网格纹理
所以需要清空img3,或者说不能清空的话,就把原来的图复制粘贴上。
这就是所谓复位------初始化所解决的矛盾的另一种表现形式。
初始化可以说是在旧数据矛盾不明显下的解决方案,因为不能解释旧数据是什么,
而复位,可以说是在旧数据矛盾明显下的解决方案,因为知道旧数据是上一次图片,而不需要使用这上一次图片,或者说不得不使用粘贴功能,但是可以更改粘贴的内容,把粘贴原来的旧图片变成粘贴相同的图片。
复位就是同样是图片,但是图片里面的数据清除。
而初始化,则是这个存数据的地方,曾经是存过图片的,但是这个数据现在已经不用来存图片,可以存其他的数据,但是里面存的图片数据还没清楚,就需要初始化,在不知道里面存的是什么的情况下直接清除数据,或者用新数据覆盖旧的乱七八糟的数据。
这就是从相同图片变量复位,同一个内存的重写,实现对危险数据的清除;到不同变量类型,使用相同内存,对内存重写数据,所描述出的初始化。
这就是复位和初始化的关系。