howzyao 发表于 2022-10-8 18:23:38

扫盲贴 大家都来0门槛设计出自己的游戏.

/*

新框架设计逻辑一览:

一 球,拍,统一称为"道具",各道具使用各自的数组一一对应.

二 main之前:
1,声明(宏定义方式)图形窗口宽度和高度 gw gh
2,声明字符数组,用于存放png文件路径的文字变量: char name
3,声明消息的变量:msg
4,声明存msg中的值的变量:mousex mousey mouselb mouserb
5,声明道具数组:ball paddle
6,声明道具的控制变量数组:ballx bally ballstatu paddlex paddley paddlestatu
7,声明并定义道具数组的初始化函数: initball(); intipaddle();
8,声明并定义消息取值函数:getmsg();
9,声明并定义透明绘图函数: putPNG(int,int,IMAGE,IMAGE,int);

1,声明初始化函数:init()
2,声明并定义init();并在其中使用上述声明,完成贴图载入和相应的变量数组初始化
3,声明并定义update();每次循环中使用putPNG更新画面贴图位置
4,声明用户输入input();

main之前的声明和变量的2点动态原理:
1) 消息取值函数getmsg() 依赖全局(全局高于main函数体)来接受msg
   其中使用了"无阻塞取得消息函数": peekmessage()
   peekmessage的参数只有1个,且按引用入参: &msg
   并赋值给4个变量:mousex mousey mouselb mouserb
   分别是:分别存放鼠标x轴,y轴,左键按下,右键按下的值
2) input(),其中将使用4个变量: mousex mousey mouselb mouserb
   取得的4个鼠标的值来操作paddle的位置,进而影响ball的位置
   每次循环只响应1次鼠标的取值以保持输入一致进而在input()中
   实现的所有"动作函数"都能有一个"统一的数值基准点"
   动作函数可以自由定义如下:
   a.全屏色差判断,使用getpexle(x,y)来实现是否碰到
   b.局部点重全判断,使用 右边:x+getwidth >= 右边:x 来检测是否碰到等
   c.按实际事物来编写碰撞,碰撞检测是程序的核心

三 main之中:
1,init()初始化绘图窗,初始化一切道具
2,while( getmsg() )消息变量赋值,同时返回0可让while退出
3,input()用来执行上行getmsg()中检索到的4项鼠标值
4,使用绘图缓冲区: BigenBatchDraw() 写入图形但并不贴图到屏幕
5,update中使用 putPNG 来绘图放置到绘冲区间,实现每次循环绘制1张全新的图片
update中的道具位置,依赖于input()中获得的4项变量的变化,而发生变化
6,可以增加变量在绘图区间设置if分支,实现分支设计,重新开始,过关等:
7,完成用户输入后,更新(缓冲区的)贴图,直到执行EndBatchDraw(),把缓冲写到画面
下面是while中的顺序:

//获得消息,可退出(暂停并调用菜单再询问是否退出)且得到鼠标的4个值
while( getmsg() )

//先按init()中的排列,更新一次,若无输入,保持init()的画面不动
if( 有生命 )
BigenBatchDraw();
update();   //更新一次,依靠鼠标的4个值,直接和间接操作(影响)道具
EndBatchDraw();
sleep(10);//休眠10毫秒让windows处理其它的事

//使用鼠标的4个值,input()中应当包括所有的动作函数如:按左键跳起等等
input();


//动作函数中,还应当包括: 检查生命值和关卡是否通关
if 挂了
生命--
重新开始    //restart();分两种:一是重新画,一是记录残局画出来
else
结束      //无命可用,通关(失败画面) lost();

if 过关
关卡++
重新开始   //restart();按关卡值设定不同画面或显示要素
else
结束       //无关可过,通关(成功画面) success();

//以上,个人觉得,只要会打出hello world的坛友且能来鱼C里,就能看懂吧?
//应当看得懂吧?若开始看不懂,可以按下面的做,跑起来后,断点之,再看即可.
//实在不行,就发贴吧.应当是其它懂的坛友,一句话就能解决的事.
//业务简介完毕
//-------------------------------------------------------------
//
送给所有和自己一样的初学者

写在前面的话:

    基于编程学习的枯燥,除基础知识外,需要人为地设置"乐趣",来实现人的
自激行为,可以促使在实现的过程中,不得不去查资料,从而提升对编程的认识
深度,这个过程既能减少学习的难度,又能增加学习的兴趣,套用一位老朋友的
话说:无论学多学少,只要兴趣还在,就好.

    原本,这是一个没有做完的系列教程,叫: "从中场入门C++"还未来得及从
"轻重缓急"上整理,也有可能会对初学朋友造成"揠苗助长"的副作用,但我觉想,
既然能来到鱼C,就一定是很有编程爱好的朋友,至少和自己一样吧,所以还是有
信心把原本简单的道理,也通过此篇实例,讲的简单化,大众化,容易接受化.

    原本想把此做成系列教程发到CSDN的,后来发现CSDN越来越不用心做事,
越来越"广告化",所以,我想,即使发到CSDN上,所产生的社会效果也不会很大,因
为本人就是一个不愿意为了下载某个要用的东西而去不择手段的"充值再充值"大
搞拿来主义.有用的东西固然有价值,但作为初学者,我想,还是应该更多地去追求
所以然,至少要知道个大概,至少这个方向不会是错误的.

    看到了鱼C大佬点赞了,受到感动是其一,其二是前几次的版主的回复,也让我
感到的论坛的活力,所以就把这些不是技术的技术送绘坛里吧.也算是自己没有充
鱼币买会员,作为为论坛尽一点绵力,尽一点人事.

    为方便大家的讨论,现将自己理解的一些编程技术,借着这个"新框架""放上来,
有更多的坛友能加入到"对像化"的认识中来,从而讯速提高自己对C++的认识,还能
保持学习的兴趣就是本人的初衷.

本程序的使用前提有四条: (不改,只能如标题写字,测试一下看能不能跑起来的-_-:))
一:
安装包:
code block 17.12_with_MinGW_gcc4.9.2(tdm-1)
(官网下载历史版本即可,若下不到,可发贴索要,我再发网盘)

二:
头文件和库文件:
easyx.h
graphics.h (随easyx.h同时发布的版本才行)
libeasyx.a

3个文件很小,安装好code block后:

2个头文件easyx.h和graphics.h放到这里:
C:\Program Files\CodeBlocks\MinGW\include

1个库文件libeasyx.a放到这里:
C:\Program Files\CodeBlocks\MinGW\lib

三:
素材:
1个球(本体白色),1个遮罩球,两者构成透明绘图的效果,需要先放置遮罩到内存,再放
置本体图形,2者必须同像素大小即可实现完美显示.

1个球拍(本体灰+红),1个球拍遮罩,同上

四:
创建好控制台hell world惯例程序后,执行一次,输出hello world到dos窗口.
然后,找到自己的"工程目录",拿自己的控制台工程目录所示:
F:\e0
是我的"工程目录"路径.
里面会有2个目录:bin obj此暂不用管它,还有4个文件: xxx是你自己的命名:
xxx.project file
xxx.depend
xxx.layout
main.cpp(若不改,默认的话就是这个文件名)
此时,应代表hello world执行成功一次了.

然后,为了能正常使用拷入到include和lib中的easyX头和库,要设置如下:
找到以下标签:
Project -> Project options -> Linker settings点开
看向左边的Debug和Release,先点开Debug,再看向右边的"Other linker options"
空框,里面写入-leasyx后,点击OK,此时已设置好easyX在Debug运行中的链接器.
再重复一次上面的动作,设置Release,OK即可.

    本着简洁好懂和抛砖引玉的想法,避免在开头就折腾不清设置,折腾不清画面等
问题,特别地讲一下上述素材,4张素材一并和头,库文件上传到附件,见目录res中的
4个图片.搞清楚了本例后,相信大家就有能力开发更新的框架和绘出更犀利的原创素
材,来把自己的游戏做到尽善尽美了.到时候别忘了来坛里交流一下,共同提高.

    现在把res目录复制到 F:\e0中即可复制以下的代码,跑起来.
若还有什么意外的情况,请跟贴留言,我尽量解答

2022.10.7 howzyao

*/


再上源码:

#include <graphics.h> //easyX版graphics.h (使用了mfc)
#include <conio.h>   //使用kbhit
#include <stdio.h>//使用sprintf

//图形窗口宽度和高度
#define gw 640
#define gh 480

//加载png文件路径的文字变量
#define namen 64
char name;

//消息取值,以便一次循环下来,使用统一的值(直接使用消息太快,会导至道具失控)
ExMessage msg;
int mousex;
int mousey;
int mouselb;
int mouserb;

//球个数,球,球遮罩(为实现边界透明),坐标,状态,初始化
//能出现在图形窗中可控地显示的最大球数 balln个
//为了兼容putPNG,只好设为数组,当前仅普通球,fireball绘图中...
#define balln 16
IMAGE ball;
IMAGE ballmask;
int   ballx;
int   bally;
int   ballstatu;
void initball()
{
    sprintf(name,"F:/e0/res/ball000.png");
    loadimage(&ball,name);
    sprintf(name,"F:/e0/res/ball001.png");
    loadimage(&ballmask,name);

    for(int i=0;i<balln;i++)
    {
      ballx=0;
      bally=0;
      ballstatu=0;
    }
}

//拍个数,拍,拍遮罩(为实现边界透明),坐标,状态,初始化
#define paddlen 1
IMAGE paddle;
IMAGE paddlemask;
int   paddlex;
int   paddley;
int   paddlestatu; //若有几个拍时,只能其中一个为1,其它为0
void initpaddle()
{
    for(int i=0;i<paddlen;i++)
    {
      sprintf(name,"F:/e0/res/paddle00%d.png",i);
      loadimage(&paddle,name);
      sprintf(name,"F:/e0/res/paddle00%d.png",i);
      loadimage(&paddlemask,name);
      paddlex=0;
      paddley=0;
      paddlestatu=0;
    }
}

//道具摆放函数,实现透明无边界:先放道具遮罩,再放道具实体
void putPNG(int x,int y,IMAGE *mask,IMAGE *actor,int I)
{
    putimage(x,y,&mask,SRCAND);
    putimage(x,y,&actor,SRCPAINT);
}

//消息赋值和退出 (无论用户有没有输入,msg都有消息 getch退出用途除外)
int getmsg()
{
    peekmessage(&msg);
    mousex = msg.x;
    mousey = msg.y;
    mouselb = msg.lbutton;
    mouserb = msg.rbutton;
    flushmessage();
    FlushMouseMsgBuffer();

    if(kbhit())
    {
      switch (getch())
      {
      case 27:
            return 0;
      }
    }
    return 1;
}

//初始化图形窗口 所有道具初始化函数放这里集中管理
void init()
{
    initgraph(gw,gh);

    HWND hwnd;
    hwnd = GetHWnd();
    SetWindowText(hwnd,"e0新版架构(慢移鼠标左键按下可写字)    esc退出");

    //初始化球
    initball();

    putPNG(100,100,ballmask,ball,0); //一个球出现在画面

    ballstatu=1; //状态==1

    //初始化拍
    initpaddle();

    putPNG(getmaxx()/2 - paddle.getwidth()/2, //一个拍出现在画面
         getmaxy() - paddle.getheight()-5,
         paddlemask,paddle,0);

    paddlestatu=1; //状态==1

    //初始其它道具
    //...
}

//集中管理道具,因为每次绘图,需要最低时间复杂度,采用单次遍历
void update()
{
    //测试: 更新球xy座标 测试,仅能写字,写字是动作,应当放入input()中
    for(int i=0;i<balln;i++)
    {
      if(ballstatu==1 && mouselb)
      {
            putPNG(mousex-ball.getwidth()/2,
                   mousey-ball.getheight()/2,ballmask,ball,i);
      }
    }

    //更新拍x座标
    //...

    //更新其它道具x,y坐标
    //...
}

//用户输入需要根据消息变量实现,不能失控
void input()
{
    //mousex;mousey,mouselb,mouserb; 4项值的利用
}

//结束画面,并关闭init中的图形窗口,以免退出后,内仍被存占用
void ended()
{
    cleardevice();
    sprintf(name,"已退出 按任意键关闭窗口");
    outtextxy(getmaxx()/2 - textwidth(name)/2,
            getmaxy()/2 - textheight(name)/2,
            name);
    getch();
    closegraph();
    delete name;
}

int main()
{
    init();

    while( getmsg() )
    {
      input();

      BeginBatchDraw();

      update();

      EndBatchDraw();

      Sleep(5);
    }

    ended();

    return 0;
}

再上必要的附件:




最后,希望贴子不要沉,大家都来0门槛设计出自己的游戏.

howzyao 发表于 2022-10-8 18:24:33

索性补上设置图示:




howzyao 发表于 2022-10-8 21:56:45

本帖最后由 howzyao 于 2022-10-8 21:58 编辑

刚测试win8.1 x64下的运行,需要让windows自动释放而不能使用closegraph()
否则报错.
64位下,使用最新版code block时,去这里下载附件,附件内有使用说明.
https://fishc.com.cn/thread-218941-1-1.html
页: [1]
查看完整版本: 扫盲贴 大家都来0门槛设计出自己的游戏.