Easyx官网上有个分享代码的网站。
然后心血来潮,想起来之前的瓦片地图编辑器
DevC++ easyx实现视口编辑--像素绘图板与贴图系统-CSDN博客
就去试试投稿。然后收到回复说要改格式,写注释,按帖子要求排版。
然后排版完了,有好心人提示几个地方修改,比如说题目英文中文之间空格隔开。
然后继续改,巧的是改着改着就来消息说代码比较多,建议上传项目文件压缩包。
然后就到 visualstdio 上跑一遍,发现可以。
然后再提交,然后手机响了,审核同志来询问这个软件的“应用”。然后提出来软件的应用性不够。
比如说整个png图像当游戏背景显然是不可能的,像素绘制瓦片就不如到PS上绘制了。
虽然发了一个png当跳一跳游戏背景的截图,然后有人就指出来实际游戏里运行的情况不同,提示说去可以看看官网推荐参考案例——FC红白机坦克大战的代码。
然后就谢谢指点。
总之后续又有几句话,强调了应用性,随即提示说背景是个二维数组里了,坦克到哪个数组,数组序号是1,1是砖头,坦克就不能继续前进。说可以导出二维数组。再然后就没有然后了,理解了整个故事,也翻看了坦克大战的数组地图,打算这个寒假重整一下瓦片地图,导出瓦片地图背后的二维数组。和二维数组对应的数字,数字对应的像素贴图。这样应该就能过审了。也可以产生价值了。
但是旧的代码呢?估计是雪藏了,但是又再网上搜索关键字,发现旧文案被转载了,然后想起来按格式改写的代码,觉得还是先整一篇新的吧,原来的篇目当做历史,记录无中生有的变化。也算是还原一下一个普通程序的发展历程。
完整代码
虽然代码确实和DevC++ easyx实现视口编辑--像素绘图板与贴图系统-CSDN博客一样,
但是一想起来转载到DevC++ easyx实现视口编辑--像素绘图板与贴图系统 - 技术栈 (jishuzhan.net)
没有注释的代码多少确实有点不够意思,或者说现在巧了,有个详细注释版本,就更新一下
记录一下一些原来应被改写的代码的原样。
一键整合版
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// main.cpp
#include
#include
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// oripic.h
int w = 2600; // 大地图长
int h = 800; // 大地图宽
IMAGE set(w, h); // 大地图
struct pixlocal
{
short x; // 记录鼠标绘制的坐标,是地图编辑器改线条生长动画时用到。这里只是用于测试鼠标像素
short y;
};
struct skline
{ // 另一个编辑器按绘制顺序打印线条生长动画,这里是 debug 用
int lenth = 0; // 线条长度
int setx, sety; // 锚点,线条参考位置,但是这个像素绘图板没有用到这个锚点
struct pixlocal a[10000]; // 记录鼠标绘制的坐标,是地图编辑器改线条生长动画时用到。这里只是用于测试鼠标像素
} nf, ndnf;
struct drawdesksize
{ // 绿色绘图板的大小设置
int x; // 绿色绘图板左上角坐标
int y;
int a; // 绿色绘图板长宽
int h;
int sizetile = 8; // 像素大小为 8 正方形大小则是 8*8
int smx; // 悬浮窗大小
int smy;
} ddm; // 绿色绘图板参数,其设定完成后,用于计算其他配套网格参数
IMAGE maptile(ddm.a / ddm.sizetile, ddm.h / ddm.sizetile); // 瓦片、和瓦片大小
struct pircle
{ // 拖动悬浮窗
IMAGE img2; // 悬浮窗图像
IMAGE img3; // 悬浮窗底线盖住的图像,用于恢复原来盖住图像
int orilx, orily; // 悬浮窗初始化时的左上角坐标
int nowlx, nowly; // 悬浮窗当前位置的左上角坐标
int a, h; // 悬浮窗长宽
int m1x = 0, m1y = 0; // img3 左上角坐标
bool putflag = 0; // 拖动 flag , 用于悬浮窗拖动
int drawflag = 0; // 瓦片绘制 flag , 用于实现长按鼠标绘制像素
struct skline b; // 线条长度存储,当时用于开发技能动画,但是在这里只是用来测试功能,比如拖动技能图标,实现技能启用关闭,就是这个变量在控制
} save; // 待保存的悬浮窗数据,所以只能有一个
struct picsave
{ // 绿色网格插槽,暂存笔刷
int lx; // 左上角坐标
int ly;
int wide; // 宽高
int h;
int useflag; // 是否被使用
struct skline b; // 配套线条暂存
} a[28]; // 暂存上限 28 个瓦片
// 初始化绘图窗口
void OriPicDesk(int a, int h)
{
initgraph(a, h, EX_SHOWCONSOLE);
setbkcolor(WHITE);
cleardevice(); // 创制绘图界面,背景色设置,以背景色填充
setlinecolor(BLACK);
setlinestyle(PS_SOLID, 1); // 不含实线,矩形的面积为 8*8,含实线 9*9
rectangle(0, 0, a - 2, h - 1); // 确定工作界面范围
setfillcolor(WHITE);
}
// 初始化整个软件窗口大小
void OriDrawDeskSize(struct drawdesksize* ddm, int x, int y)
{
ddm->x = x;
ddm->y = y;
ddm->a = 640;
ddm->h = 640;
ddm->sizetile = 8;
ddm->smx = ddm->x - ddm->a / ddm->sizetile;
ddm->smy = ddm->y - ddm->h / ddm->sizetile;
}
// 初始化整个软件窗口大小
void DrawDesk(struct drawdesksize ddm)
{
int i, j;
int square = ddm.sizetile;
for (i = ddm.x; i <= ddm.x + ddm.a - square; i += square)
{
for (j = ddm.y; j < ddm.y + ddm.h; j += square)
{
fillrectangle(i, j, i + square, j + square);
}
}
rectangle(ddm.smx - 1, ddm.smy - 1, ddm.x, ddm.y);
}
// 初始化白色小图
void OriPirCle(struct pircle* save, struct drawdesksize* ddm)
{
save->orilx = ddm->smx, save->orily = ddm->smy;
save->nowlx = ddm->smx, save->nowly = ddm->smy; // img2 的左上角坐标
save->a = ddm->a / ddm->sizetile;
save->h = ddm->h / ddm->sizetile;
save->m1x = 0, save->m1y = 0; // img3 的左上角坐标
save->putflag = 0;
save->drawflag = 0;
}
// 初始化大地图
void OriSet(IMAGE* set, struct drawdesksize ddm)
{
SetWorkingImage(set);
setbkcolor(YELLOW); // 背景黄色
cleardevice();
setlinecolor(BLACK);
for (int i = 0; i < w; i += ddm.a / ddm.sizetile)
{
for (int j = 0; j < h; j += ddm.h / ddm.sizetile)
rectangle(j + 1, i + 1, j + ddm.a / ddm.sizetile, i + ddm.h / ddm.sizetile); // 网格绘制
}
line(0, 0, 800, 1400);
SetWorkingImage();
}
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// show.h
extern struct skline nf, ndnf;
extern IMAGE maptile;
void DidShow(IMAGE* set, IMAGE maptile, ExMessage m, int show_x, int show_y, int nowlx, int nowly)
{
static int flag = 0;
static int oldx = -8, oldy = -8;
int putx = 0, puty = 0;
if (m.message == WM_LBUTTONDOWN)
{
// 左键批量绘制瓦片到大地图
SetWorkingImage(set);
putx = nowlx + m.x - show_x; // 计算当前在大地图的坐标
puty = nowly + m.y - show_y;
putx = putx - putx % 80; // 计算所在的网格
puty = puty - puty % 80;
putimage(putx, puty, &maptile); // 把 maptile 粘贴到大地图中
SetWorkingImage();
}
else
{
if (putx == oldx && puty == oldy)
{ // 如果和上次的位置相同,则不贴图,避免了重复贴图
}
else
{
SetWorkingImage(set);
putx = nowlx + m.x - show_x; // 计算当前在大地图的坐标
puty = nowly + m.y - show_y;
putx = putx - putx % 80; // 计算所在的网格
puty = puty - puty % 80;
putimage(putx, puty, &maptile); // 把 maptile 粘贴到大地图中
oldx = putx; // 暂存本次绘图左上角坐标
oldy = puty;
SetWorkingImage();
}
}
}
// 视口函数
void Show(IMAGE* set, ExMessage m, int show_x, int show_y, int show_wideth, int show_height)
{
// 视口的图片大小,视口的大小,在窗口的坐标,视口的大小
static int nowlx, nowly, pic_wide, pic_heigh;
static int m1x, m1y;
static bool drawflag, putflag, attachflag;
static IMAGE olds(show_wideth, show_height);
static IMAGE news(show_wideth, show_height);
static int m2x, m2y; // 加速移动设置参数 m2x , m2y , 自动移动设置
static int flag = 1; // 视口初始化,要覆盖到窗口上
if (flag == 1)
{
pic_wide = show_wideth;
pic_heigh = show_height;
m1x = 0;
m1y = 0;
m2x = 0;
m2y = 0;
drawflag = 0;
putflag = 0;
attachflag = 0;
flag = 0;
setlinecolor(BLACK);
rectangle(show_x, show_y, show_x + show_wideth, show_y + show_height);
SetWorkingImage(set);
getimage(&olds, nowlx, nowly, pic_wide, pic_heigh);
SetWorkingImage();
putimage(show_x, show_y, &olds);
}
else
{
if (m.message == WM_LBUTTONDOWN)
{
if (m.ctrl && m.x > show_x && m.xshow_y && m.y < show_y + show_height)
{
// ctrl+左键视口,连续移动大地图
SetWorkingImage(set); // 获取视口里的图片
getimage(&olds, nowlx, nowly, pic_wide, pic_heigh);
m1x = m.x; // 长按开始时的坐标
m1y = m.y;
m2x = m.x;
m2y = m.y;
SetWorkingImage();
putflag = true; // 启动地图移动
}
else if (m.x > show_x && m.xshow_y && m.y < show_y + show_height)
{
// 鼠标左键实现瓦片贴图
DidShow(set, maptile, m, show_x, show_y, nowlx, nowly); // 开始把瓦片贴入大地图
attachflag = true; // 批量绘制启动
}
}
else if (attachflag == true && m.x > show_x && m.xshow_y && m.y < show_y + show_height)
{
// 连续贴图
DidShow(set, maptile, m, show_x, show_y, nowlx, nowly);
if (m.message == WM_LBUTTONUP)
{
attachflag = false; // 长按结束,笔刷涂贴片停止
}
SetWorkingImage(set); // 贴片涂完后台的大地图,从大地图拷贝后,立即在桌面更新显示
getimage(&olds, nowlx, nowly, pic_wide, pic_heigh);
SetWorkingImage();
putimage(show_x, show_y, &olds);
}
else if (putflag == true)
{
// 持续拖动大地图
SetWorkingImage(set); // 进行了位移放大
nowlx = nowlx - 5 * (m.x - m1x); // 位移同鼠标位移方向相反,长度为五倍关系
nowly = nowly - 5 * (m.y - m1y);
m1x = m.x; // 不可改动这两行代码位置
m1y = m.y;
getimage(&olds, nowlx, nowly, pic_wide, pic_heigh);
SetWorkingImage();
putimage(show_x, show_y, &olds);
if (m.message == WM_LBUTTONUP || m.ctrl == 0)
{
putflag = 0; // 长按停止,视口采样停止
}
}
}
}
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// draw.h
extern IMAGE maptile;
// 像素绘图板的小矩形绘制,开发顺序是先有这个 draw.h 代码,然后有 show.h 代码。这两个头文件是继承关系
void Remem(struct ExMessage m, int* lenth, struct drawdesksize ddm)
{
int i = *lenth;
static int oldx = -ddm.sizetile,
oldy = -ddm.sizetile,
a = ddm.sizetile,
h = ddm.sizetile;
if (m.x > oldx && m.xoldy && m.y < oldy + h)
{
// 修正可涂色范围,防止溢出。y 不可等于 640+100,x 等于 640+600-1 是边界小方块的最右侧像素
// 节约运算,不在相同位置重复绘制
}
else
{
int px = 0, py = 0, qx = 0, qy = 0;
int mapqx = 0, mapqy = 0;
px = m.x - ddm.x; // 修正 600 的偏移,601=600+1;0==1%8,600=600+(601-600)%8
py = m.y - ddm.y;
qx = px - px % a; // 取整,用于确定黑色像素矩形位置
qy = py - py % h;
mapqx = qx / a; // 计算对应悬浮窗的像素坐标
mapqy = qy / h;
px = ddm.x + qx; // 相对与绿色绘图区域左上角的坐标
py = ddm.y + qy;
setfillcolor(BLACK);
fillrectangle(px, py, px + ddm.sizetile, py + ddm.sizetile); // 绘制黑色像素矩形
nf.a[i].x = mapqx; // 悬浮窗技能动画数据存储
nf.a[i].y = mapqy;
i++;
*lenth = i;
nf.lenth = *lenth;
printf("%d %d %d\n", i - 1, nf.a[i - 1].x, nf.a[i - 1].y);
putpixel(ddm.smx + nf.a[i - 1].x, ddm.smy + nf.a[i - 1].y, BLACK); // 绘制悬浮窗像素
oldx = px;
oldy = py;
}
}
// 绘制函数,悬浮窗绘制与绿色网格绘制像素
void Draw(ExMessage m, int* lenth)
{
static int flag = 0;
if (m.message == WM_LBUTTONDOWN)
{
Remem(m, lenth, ddm);
// printf("draw function is running 222\n"); // 测试
flag = 1;
}
else if (flag == 1)
{
Remem(m, lenth, ddm);
if (m.message == WM_LBUTTONUP)
{
flag = 0;
}
}
}
// 绘制像素
void DrawMouseCheck(ExMessage m, int* lenth, struct drawdesksize* ddm)
{
if (m.x > ddm->x && m.xx + ddm->a && m.y>ddm->y && m.y < ddm->y + ddm->h)
{
Draw(m, lenth);
// printf("333\n");
}
}
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// flowsave.h
// 用于保存悬浮窗图案,以及对应线条动画数据
void OriFlowSave(int x, int y, int w, int h)
{
setfillcolor(GREEN);
int i;
int num = 0;
for (int j = 0; j < h; j++)
{
for (i = 1; i <= w; i++)
{
a[num].lx = x + 1 + i * 81;
a[num].ly = y + 1 + j * 81;
a[num].wide = 80;
a[num].h = 80;
a[num].useflag = 0;
fillrectangle(a[num].lx, a[num].ly, a[num].lx + a[num].wide, a[num].ly + a[num].h);
num++;
}
}
}
// 选中暂存的瓦片的数据
void ReHave(ExMessage m, int w, int h)
{
int i;
int num = 0;
for (int j = 0; j < w; j++)
{
for (i = 1; i <= h; i++)
{
if (m.x > a[num].lx && m.xa[num].ly && m.y < a[num].ly + a[num].h)
{
if (a[num].useflag == 1)
{
ndnf = a[num].b; // 读取数据,但是 ndnf 没有用到
break;
}
}
num++;
}
}
}
// 选中暂存的瓦片的图案
void PickPen(ExMessage m, int w, int h)
{
int i, j;
int num = 0;
for (j = 0; j < w; j++)
{
for (i = 1; i <= h; i++)
{
if (m.x > a[num].lx && m.xa[num].ly && m.y < a[num].ly + a[num].h)
{
if (a[num].useflag == 1)
{
getimage(&maptile, a[num].lx, a[num].ly, 80, 80);
fillrectangle(20, 80, 100, 160); // 左上角会显示图案,就是选中了
putimage(20, 80, &maptile);
}
}
num++;
}
}
}
// 鼠标放置悬浮窗
void FlowSave(ExMessage m, struct pircle* save, int w, int h)
{
if (m.message == WM_LBUTTONDOWN)
{
PickPen(m, w, h); // 左键选中画笔
}
else if (m.message == WM_LBUTTONUP)
{
int i;
int num = 0;
for (int j = 0; j < w; j++)
{
for (i = 1; i <= h; i++)
{
if (m.x > a[num].lx && m.xa[num].ly && m.y < a[num].ly + a[num].h)
{
if (a[num].useflag == 0)
{ // 找到空白的插槽
a[num].b = nf;
if (save->nowlx != save->orilx && save->nowly != save->orily)
{
putimage(save->nowlx, save->nowly, &save->img3);
}
else
{
getimage(&save->img2, save->orilx, save->orily, save->a, save->h);
}
putimage(a[num].lx, a[num].ly, &save->img2);
save->nowlx = save->orilx; // 悬浮窗复位
save->nowly = save->orily;
save->m1x = 0;
save->m1y = 0;
// printf("%d\n", save->m1x); // 测试
save->putflag = false; // 悬浮窗拖动停止
a[num].useflag = 1;
break;
}
}
num++;
}
}
}
if (m.message == WM_RBUTTONDOWN)
{
ReHave(m, w, h); // 在瓦片地图这里没有用,但是从这里衍生的其他编辑器用到了
}
}
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// movecheck.h
extern int w2, h2;
// 用于检测小地图拖动
void MoveCheck(struct ExMessage m, struct pircle* save)
{
if (save->putflag == 0 && m.message == WM_LBUTTONDOWN)
{
if (save->m1x == 0 && save->m1y == 0 && m.x > save->orilx && m.xorilx + save->a && m.y>save->orily && m.y < save->orily + save->h)
{
getimage(&save->img2, save->orilx, save->orily, save->a, save->h); // 获取原图
getimage(&save->img3, save->orilx, save->orily, save->a, save->h); // 初始化底图
save->m1x = m.x;
save->m1y = m.y;
save->putflag = true;
// printf("movecheck is running 2222\n"); // 测试
}
else if (m.x > save->nowlx && m.xnowlx + save->a && m.y>save->nowly && m.y < save->nowly + save->h)
{
save->putflag = true;
getimage(&save->img2, save->orilx, save->orily, save->a, save->h);
save->m1x = m.x;
save->m1y = m.y;
// printf("movecheck function is running11111\n"); // 测试
}
}
else 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(); // 一次绘图出来,没有屏闪了
if (m.message == WM_LBUTTONUP)
{
save->putflag = 0; // 停止移动悬浮窗
}
}
}
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// keycheck.h
extern IMAGE set;
// 重置绘图版
void Ori(struct drawdesksize ddm)
{
int i, j;
int square = ddm.sizetile;
setfillcolor(WHITE);
setlinecolor(GREEN);
for (i = ddm.x; i <= ddm.x + ddm.a - square; i += square)
{
for (j = ddm.y; j < ddm.y + ddm.h; j += square)
{
fillrectangle(i, j, i + square, j + square); // 像素网格重置
}
}
for (i = ddm.smx; i < ddm.smx + ddm.a / ddm.sizetile; i++)
{
for (j = ddm.smy; j < ddm.smy + ddm.h / ddm.sizetile; j++)
{
putpixel(i, j, WHITE); // 悬浮窗重置
}
}
}
// 小地图轨迹打印,这个就是所谓的 skline 的作用。另一个编辑器实现了线条导出功能
void Pixnf(int lenth, struct drawdesksize ddm)
{
int i = 0;
for (i = 0; i < lenth; i++)
{
putpixel(ddm.smx + nf.a[i].x, ddm.smy + nf.a[i].y, WHITE);
if (i % 55 == 0)
Sleep(1);
}
for (i = 0; i < lenth; i++)
{
putpixel(ddm.smx + nf.a[i].x, ddm.smy + nf.a[i].y, BLACK);
if (i % 55 == 0)
Sleep(1);
}
}
// 按键检测
void KeyCheck(ExMessage m, int* lenth)
{
switch (m.vkcode)
{
case VK_F1: // 重置绘图板
Ori(ddm);
*lenth = 0;
break;
case VK_F2: // 导出为 gamemap0 的 png 图片
saveimage(_T("gamemap.png"), &set);
break;
case VK_F5: // 播放线条动画
static int limf5 = 0;
if (limf5 == 0)
{
Pixnf(*lenth, ddm);
limf5 = 1;
}
else
{
limf5 = 0; // 处理掉的 bug:按 F5 一次,打印两次线条
}
break;
}
}
int x2 = 650, y2 = 100; // 深绿色像素板的左上角坐标
int w2 = 3, h2 = 7; // 绿色网格的列数、行数
int showx3 = 101, showy3 = 100; // 黄色网格视口的左上角坐标
int main()
{
OriPicDesk(1800, 870); // 初始化整个软件窗口大小
OriDrawDeskSize(&ddm, 1000, 100); // 初始化整个软件窗口大小
OriPirCle(&save, &ddm); // 初始化白色小图
OriSet(&set, ddm); // 初始化大地图
OriFlowSave(x2, y2, w2, h2); // 初始化暂存瓦片的绿色网格
DrawDesk(ddm); // 绘制出像素绘图板
ExMessage m;
int lenth = 0; // 用于记录绘制长度,在 debug 时看控制台窗口数据
while (1)
{
m = getmessage(EX_MOUSE | EX_KEY); // 获取鼠标、按键信息
DrawMouseCheck(m, &lenth, &ddm); // 像素绘制检测
KeyCheck(m, &lenth); // 按键检测
MoveCheck(m, &save); // 拖动检测
FlowSave(m, &save, w2, h2); // 暂存检测
Show(&set, m, showx3, showy3, 600, 700); // 大地图绘制
}
closegraph();
return 0;
}
分部版本
main.cpp
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// main.cpp
#include
#include
#include"oripic.h" // 初始化函数集中在这个头文件里了
#include"show.h" // 视口、粘贴瓦片
#include"draw.h" // 绘制瓦片
#include"flowsave.h" // 瓦片暂存
#include"movecheck.h" // 鼠标点击检测
#include"keycheck.h" // 按键按动检测
int x2 = 650, y2 = 100; // 深绿色像素板的左上角坐标
int w2 = 3, h2 = 7; // 绿色网格的列数、行数
int showx3 = 101, showy3 = 100; // 黄色网格视口的左上角坐标
int main()
{
OriPicDesk(1800, 870); // 初始化整个软件窗口大小
OriDrawDeskSize(&ddm, 1000, 100); // 初始化整个软件窗口大小
OriPirCle(&save, &ddm); // 初始化白色小图
OriSet(&set, ddm); // 初始化大地图
OriFlowSave(x2, y2, w2, h2); // 初始化暂存瓦片的绿色网格
DrawDesk(ddm); // 绘制出像素绘图板
ExMessage m;
int lenth = 0; // 用于记录绘制长度,在 debug 时看控制台窗口数据
while (1)
{
m = getmessage(EX_MOUSE | EX_KEY); // 获取鼠标、按键信息
DrawMouseCheck(m, &lenth, &ddm); // 像素绘制检测
KeyCheck(m, &lenth); // 按键检测
MoveCheck(m, &save); // 拖动检测
FlowSave(m, &save, w2, h2); // 暂存检测
Show(&set, m, showx3, showy3, 600, 700); // 大地图绘制
}
closegraph();
return 0;
}
oripic.h
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// oripic.h
int w = 2600; // 大地图长
int h = 800; // 大地图宽
IMAGE set(w, h); // 大地图
struct pixlocal
{
short x; // 记录鼠标绘制的坐标,是地图编辑器改线条生长动画时用到。这里只是用于测试鼠标像素
short y;
};
struct skline
{ // 另一个编辑器按绘制顺序打印线条生长动画,这里是 debug 用
int lenth = 0; // 线条长度
int setx, sety; // 锚点,线条参考位置,但是这个像素绘图板没有用到这个锚点
struct pixlocal a[10000]; // 记录鼠标绘制的坐标,是地图编辑器改线条生长动画时用到。这里只是用于测试鼠标像素
} nf, ndnf;
struct drawdesksize
{ // 绿色绘图板的大小设置
int x; // 绿色绘图板左上角坐标
int y;
int a; // 绿色绘图板长宽
int h;
int sizetile = 8; // 像素大小为 8 正方形大小则是 8*8
int smx; // 悬浮窗大小
int smy;
} ddm; // 绿色绘图板参数,其设定完成后,用于计算其他配套网格参数
IMAGE maptile(ddm.a / ddm.sizetile, ddm.h / ddm.sizetile); // 瓦片、和瓦片大小
struct pircle
{ // 拖动悬浮窗
IMAGE img2; // 悬浮窗图像
IMAGE img3; // 悬浮窗底线盖住的图像,用于恢复原来盖住图像
int orilx, orily; // 悬浮窗初始化时的左上角坐标
int nowlx, nowly; // 悬浮窗当前位置的左上角坐标
int a, h; // 悬浮窗长宽
int m1x = 0, m1y = 0; // img3 左上角坐标
bool putflag = 0; // 拖动 flag , 用于悬浮窗拖动
int drawflag = 0; // 瓦片绘制 flag , 用于实现长按鼠标绘制像素
struct skline b; // 线条长度存储,当时用于开发技能动画,但是在这里只是用来测试功能,比如拖动技能图标,实现技能启用关闭,就是这个变量在控制
} save; // 待保存的悬浮窗数据,所以只能有一个
struct picsave
{ // 绿色网格插槽,暂存笔刷
int lx; // 左上角坐标
int ly;
int wide; // 宽高
int h;
int useflag; // 是否被使用
struct skline b; // 配套线条暂存
} a[28]; // 暂存上限 28 个瓦片
// 初始化绘图窗口
void OriPicDesk(int a, int h)
{
initgraph(a, h, EX_SHOWCONSOLE);
setbkcolor(WHITE);
cleardevice(); // 创制绘图界面,背景色设置,以背景色填充
setlinecolor(BLACK);
setlinestyle(PS_SOLID, 1); // 不含实线,矩形的面积为 8*8,含实线 9*9
rectangle(0, 0, a - 2, h - 1); // 确定工作界面范围
setfillcolor(WHITE);
}
// 初始化整个软件窗口大小
void OriDrawDeskSize(struct drawdesksize* ddm, int x, int y)
{
ddm->x = x;
ddm->y = y;
ddm->a = 640;
ddm->h = 640;
ddm->sizetile = 8;
ddm->smx = ddm->x - ddm->a / ddm->sizetile;
ddm->smy = ddm->y - ddm->h / ddm->sizetile;
}
// 初始化整个软件窗口大小
void DrawDesk(struct drawdesksize ddm)
{
int i, j;
int square = ddm.sizetile;
for (i = ddm.x; i <= ddm.x + ddm.a - square; i += square)
{
for (j = ddm.y; j < ddm.y + ddm.h; j += square)
{
fillrectangle(i, j, i + square, j + square);
}
}
rectangle(ddm.smx - 1, ddm.smy - 1, ddm.x, ddm.y);
}
// 初始化白色小图
void OriPirCle(struct pircle* save, struct drawdesksize* ddm)
{
save->orilx = ddm->smx, save->orily = ddm->smy;
save->nowlx = ddm->smx, save->nowly = ddm->smy; // img2 的左上角坐标
save->a = ddm->a / ddm->sizetile;
save->h = ddm->h / ddm->sizetile;
save->m1x = 0, save->m1y = 0; // img3 的左上角坐标
save->putflag = 0;
save->drawflag = 0;
}
// 初始化大地图
void OriSet(IMAGE* set, struct drawdesksize ddm)
{
SetWorkingImage(set);
setbkcolor(YELLOW); // 背景黄色
cleardevice();
setlinecolor(BLACK);
for (int i = 0; i < w; i += ddm.a / ddm.sizetile)
{
for (int j = 0; j < h; j += ddm.h / ddm.sizetile)
rectangle(j + 1, i + 1, j + ddm.a / ddm.sizetile, i + ddm.h / ddm.sizetile); // 网格绘制
}
line(0, 0, 800, 1400);
SetWorkingImage();
}
show.h
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// show.h
extern struct skline nf, ndnf;
extern IMAGE maptile;
void DidShow(IMAGE* set, IMAGE maptile, ExMessage m, int show_x, int show_y, int nowlx, int nowly)
{
static int flag = 0;
static int oldx = -8, oldy = -8;
int putx = 0, puty = 0;
if (m.message == WM_LBUTTONDOWN)
{
// 左键批量绘制瓦片到大地图
SetWorkingImage(set);
putx = nowlx + m.x - show_x; // 计算当前在大地图的坐标
puty = nowly + m.y - show_y;
putx = putx - putx % 80; // 计算所在的网格
puty = puty - puty % 80;
putimage(putx, puty, &maptile); // 把 maptile 粘贴到大地图中
SetWorkingImage();
}
else
{
if (putx == oldx && puty == oldy)
{ // 如果和上次的位置相同,则不贴图,避免了重复贴图
}
else
{
SetWorkingImage(set);
putx = nowlx + m.x - show_x; // 计算当前在大地图的坐标
puty = nowly + m.y - show_y;
putx = putx - putx % 80; // 计算所在的网格
puty = puty - puty % 80;
putimage(putx, puty, &maptile); // 把 maptile 粘贴到大地图中
oldx = putx; // 暂存本次绘图左上角坐标
oldy = puty;
SetWorkingImage();
}
}
}
// 视口函数
void Show(IMAGE* set, ExMessage m, int show_x, int show_y, int show_wideth, int show_height)
{
// 视口的图片大小,视口的大小,在窗口的坐标,视口的大小
static int nowlx, nowly, pic_wide, pic_heigh;
static int m1x, m1y;
static bool drawflag, putflag, attachflag;
static IMAGE olds(show_wideth, show_height);
static IMAGE news(show_wideth, show_height);
static int m2x, m2y; // 加速移动设置参数 m2x , m2y , 自动移动设置
static int flag = 1; // 视口初始化,要覆盖到窗口上
if (flag == 1)
{
pic_wide = show_wideth;
pic_heigh = show_height;
m1x = 0;
m1y = 0;
m2x = 0;
m2y = 0;
drawflag = 0;
putflag = 0;
attachflag = 0;
flag = 0;
setlinecolor(BLACK);
rectangle(show_x, show_y, show_x + show_wideth, show_y + show_height);
SetWorkingImage(set);
getimage(&olds, nowlx, nowly, pic_wide, pic_heigh);
SetWorkingImage();
putimage(show_x, show_y, &olds);
}
else
{
if (m.message == WM_LBUTTONDOWN)
{
if (m.ctrl && m.x > show_x && m.xshow_y && m.y < show_y + show_height)
{
// ctrl+左键视口,连续移动大地图
SetWorkingImage(set); // 获取视口里的图片
getimage(&olds, nowlx, nowly, pic_wide, pic_heigh);
m1x = m.x; // 长按开始时的坐标
m1y = m.y;
m2x = m.x;
m2y = m.y;
SetWorkingImage();
putflag = true; // 启动地图移动
}
else if (m.x > show_x && m.xshow_y && m.y < show_y + show_height)
{
// 鼠标左键实现瓦片贴图
DidShow(set, maptile, m, show_x, show_y, nowlx, nowly); // 开始把瓦片贴入大地图
attachflag = true; // 批量绘制启动
}
}
else if (attachflag == true && m.x > show_x && m.xshow_y && m.y < show_y + show_height)
{
// 连续贴图
DidShow(set, maptile, m, show_x, show_y, nowlx, nowly);
if (m.message == WM_LBUTTONUP)
{
attachflag = false; // 长按结束,笔刷涂贴片停止
}
SetWorkingImage(set); // 贴片涂完后台的大地图,从大地图拷贝后,立即在桌面更新显示
getimage(&olds, nowlx, nowly, pic_wide, pic_heigh);
SetWorkingImage();
putimage(show_x, show_y, &olds);
}
else if (putflag == true)
{
// 持续拖动大地图
SetWorkingImage(set); // 进行了位移放大
nowlx = nowlx - 5 * (m.x - m1x); // 位移同鼠标位移方向相反,长度为五倍关系
nowly = nowly - 5 * (m.y - m1y);
m1x = m.x; // 不可改动这两行代码位置
m1y = m.y;
getimage(&olds, nowlx, nowly, pic_wide, pic_heigh);
SetWorkingImage();
putimage(show_x, show_y, &olds);
if (m.message == WM_LBUTTONUP || m.ctrl == 0)
{
putflag = 0; // 长按停止,视口采样停止
}
}
}
}
draw.h
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// draw.h
extern IMAGE maptile;
// 像素绘图板的小矩形绘制,开发顺序是先有这个 draw.h 代码,然后有 show.h 代码。这两个头文件是继承关系
void Remem(struct ExMessage m, int* lenth, struct drawdesksize ddm)
{
int i = *lenth;
static int oldx = -ddm.sizetile,
oldy = -ddm.sizetile,
a = ddm.sizetile,
h = ddm.sizetile;
if (m.x > oldx && m.xoldy && m.y < oldy + h)
{
// 修正可涂色范围,防止溢出。y 不可等于 640+100,x 等于 640+600-1 是边界小方块的最右侧像素
// 节约运算,不在相同位置重复绘制
}
else
{
int px = 0, py = 0, qx = 0, qy = 0;
int mapqx = 0, mapqy = 0;
px = m.x - ddm.x; // 修正 600 的偏移,601=600+1;0==1%8,600=600+(601-600)%8
py = m.y - ddm.y;
qx = px - px % a; // 取整,用于确定黑色像素矩形位置
qy = py - py % h;
mapqx = qx / a; // 计算对应悬浮窗的像素坐标
mapqy = qy / h;
px = ddm.x + qx; // 相对与绿色绘图区域左上角的坐标
py = ddm.y + qy;
setfillcolor(BLACK);
fillrectangle(px, py, px + ddm.sizetile, py + ddm.sizetile); // 绘制黑色像素矩形
nf.a[i].x = mapqx; // 悬浮窗技能动画数据存储
nf.a[i].y = mapqy;
i++;
*lenth = i;
nf.lenth = *lenth;
printf("%d %d %d\n", i - 1, nf.a[i - 1].x, nf.a[i - 1].y);
putpixel(ddm.smx + nf.a[i - 1].x, ddm.smy + nf.a[i - 1].y, BLACK); // 绘制悬浮窗像素
oldx = px;
oldy = py;
}
}
// 绘制函数,悬浮窗绘制与绿色网格绘制像素
void Draw(ExMessage m, int* lenth)
{
static int flag = 0;
if (m.message == WM_LBUTTONDOWN)
{
Remem(m, lenth, ddm);
// printf("draw function is running 222\n"); // 测试
flag = 1;
}
else if (flag == 1)
{
Remem(m, lenth, ddm);
if (m.message == WM_LBUTTONUP)
{
flag = 0;
}
}
}
// 绘制像素
void DrawMouseCheck(ExMessage m, int* lenth, struct drawdesksize* ddm)
{
if (m.x > ddm->x && m.xx + ddm->a && m.y>ddm->y && m.y < ddm->y + ddm->h)
{
Draw(m, lenth);
// printf("333\n");
}
}
flowsave.h
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// flowsave.h
// 用于保存悬浮窗图案,以及对应线条动画数据
void OriFlowSave(int x, int y, int w, int h)
{
setfillcolor(GREEN);
int i;
int num = 0;
for (int j = 0; j < h; j++)
{
for (i = 1; i <= w; i++)
{
a[num].lx = x + 1 + i * 81;
a[num].ly = y + 1 + j * 81;
a[num].wide = 80;
a[num].h = 80;
a[num].useflag = 0;
fillrectangle(a[num].lx, a[num].ly, a[num].lx + a[num].wide, a[num].ly + a[num].h);
num++;
}
}
}
// 选中暂存的瓦片的数据
void ReHave(ExMessage m, int w, int h)
{
int i;
int num = 0;
for (int j = 0; j < w; j++)
{
for (i = 1; i <= h; i++)
{
if (m.x > a[num].lx && m.xa[num].ly && m.y < a[num].ly + a[num].h)
{
if (a[num].useflag == 1)
{
ndnf = a[num].b; // 读取数据,但是 ndnf 没有用到
break;
}
}
num++;
}
}
}
// 选中暂存的瓦片的图案
void PickPen(ExMessage m, int w, int h)
{
int i, j;
int num = 0;
for (j = 0; j < w; j++)
{
for (i = 1; i <= h; i++)
{
if (m.x > a[num].lx && m.xa[num].ly && m.y < a[num].ly + a[num].h)
{
if (a[num].useflag == 1)
{
getimage(&maptile, a[num].lx, a[num].ly, 80, 80);
fillrectangle(20, 80, 100, 160); // 左上角会显示图案,就是选中了
putimage(20, 80, &maptile);
}
}
num++;
}
}
}
// 鼠标放置悬浮窗
void FlowSave(ExMessage m, struct pircle* save, int w, int h)
{
if (m.message == WM_LBUTTONDOWN)
{
PickPen(m, w, h); // 左键选中画笔
}
else if (m.message == WM_LBUTTONUP)
{
int i;
int num = 0;
for (int j = 0; j < w; j++)
{
for (i = 1; i <= h; i++)
{
if (m.x > a[num].lx && m.xa[num].ly && m.y < a[num].ly + a[num].h)
{
if (a[num].useflag == 0)
{ // 找到空白的插槽
a[num].b = nf;
if (save->nowlx != save->orilx && save->nowly != save->orily)
{
putimage(save->nowlx, save->nowly, &save->img3);
}
else
{
getimage(&save->img2, save->orilx, save->orily, save->a, save->h);
}
putimage(a[num].lx, a[num].ly, &save->img2);
save->nowlx = save->orilx; // 悬浮窗复位
save->nowly = save->orily;
save->m1x = 0;
save->m1y = 0;
// printf("%d\n", save->m1x); // 测试
save->putflag = false; // 悬浮窗拖动停止
a[num].useflag = 1;
break;
}
}
num++;
}
}
}
if (m.message == WM_RBUTTONDOWN)
{
ReHave(m, w, h); // 在瓦片地图这里没有用,但是从这里衍生的其他编辑器用到了
}
}
movecheck.h
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// movecheck.h
extern int w2, h2;
// 用于检测小地图拖动
void MoveCheck(struct ExMessage m, struct pircle* save)
{
if (save->putflag == 0 && m.message == WM_LBUTTONDOWN)
{
if (save->m1x == 0 && save->m1y == 0 && m.x > save->orilx && m.xorilx + save->a && m.y>save->orily && m.y < save->orily + save->h)
{
getimage(&save->img2, save->orilx, save->orily, save->a, save->h); // 获取原图
getimage(&save->img3, save->orilx, save->orily, save->a, save->h); // 初始化底图
save->m1x = m.x;
save->m1y = m.y;
save->putflag = true;
// printf("movecheck is running 2222\n"); // 测试
}
else if (m.x > save->nowlx && m.xnowlx + save->a && m.y>save->nowly && m.y < save->nowly + save->h)
{
save->putflag = true;
getimage(&save->img2, save->orilx, save->orily, save->a, save->h);
save->m1x = m.x;
save->m1y = m.y;
// printf("movecheck function is running11111\n"); // 测试
}
}
else 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(); // 一次绘图出来,没有屏闪了
if (m.message == WM_LBUTTONUP)
{
save->putflag = 0; // 停止移动悬浮窗
}
}
}
keycheck.h
// 程序:瓦片地图绘图板
// 作者:无脑
// 编译环境:DevC++ easyx4mingw_20220901
// 编写日期:2024-1-2
// keycheck.h
extern IMAGE set;
// 重置绘图版
void Ori(struct drawdesksize ddm)
{
int i, j;
int square = ddm.sizetile;
setfillcolor(WHITE);
setlinecolor(GREEN);
for (i = ddm.x; i <= ddm.x + ddm.a - square; i += square)
{
for (j = ddm.y; j < ddm.y + ddm.h; j += square)
{
fillrectangle(i, j, i + square, j + square); // 像素网格重置
}
}
for (i = ddm.smx; i < ddm.smx + ddm.a / ddm.sizetile; i++)
{
for (j = ddm.smy; j < ddm.smy + ddm.h / ddm.sizetile; j++)
{
putpixel(i, j, WHITE); // 悬浮窗重置
}
}
}
// 小地图轨迹打印,这个就是所谓的 skline 的作用。另一个编辑器实现了线条导出功能
void Pixnf(int lenth, struct drawdesksize ddm)
{
int i = 0;
for (i = 0; i < lenth; i++)
{
putpixel(ddm.smx + nf.a[i].x, ddm.smy + nf.a[i].y, WHITE);
if (i % 55 == 0)
Sleep(1);
}
for (i = 0; i < lenth; i++)
{
putpixel(ddm.smx + nf.a[i].x, ddm.smy + nf.a[i].y, BLACK);
if (i % 55 == 0)
Sleep(1);
}
}
// 按键检测
void KeyCheck(ExMessage m, int* lenth)
{
switch (m.vkcode)
{
case VK_F1: // 重置绘图板
Ori(ddm);
*lenth = 0;
break;
case VK_F2: // 导出为 gamemap0 的 png 图片
saveimage(_T("gamemap.png"), &set);
break;
case VK_F5: // 播放线条动画
static int limf5 = 0;
if (limf5 == 0)
{
Pixnf(*lenth, ddm);
limf5 = 1;
}
else
{
limf5 = 0; // 处理掉的 bug:按 F5 一次,打印两次线条
}
break;
}
}