使用C语言和DX库编写东方风格的STG射击游戏--------第一章--------创建基本界面以及可以移动的角色

这个是这章需要的文件,本章代码下载(建议下载后对照查看,记得配置好dx库以及素材文件)

各文件定义请查看

View Code
define.h : 定义常量
function.h : 函数的声明
GV.h : 全局变量的声明
sturct.h : 结构体的定义

char.cpp : 角色属性函数,例如移动
graph.cpp : 界面载入
ini.cpp : 初始化
key.cpp : 按键定义
load.cpp : 读入数据资源
mian.cpp : 程序主循环

 首先由main.cpp开始看吧,main.cpp的代码如下

 1 #define GLOBAL_INSTANCE 
 2 #include "../include/GV.h"
 3 
 4 //每次更新都必须的函数
 5 int ProcessLoop(){
 6     if(ProcessMessage()!=0)        //处理系统消息
 7         return -1;        
 8     if(ClearDrawScreen()!=0)        //清理画面
 9         return -1;        
10     GetHitKeyStateAll_2();        //获取键盘输入
11     GetHitPadStateAll();         //获取手柄输入
12     return 0;
13 }
14 
15 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){
16     ChangeWindowMode(TRUE);        //设置为窗口模式
17     if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) 
18         return -1;        //初始化dx库与设置背画面
19 
20     //游戏主循环
21     while(ProcessLoop()==0){
22 
23         switch(func_state){
24         case 0:
25             load();         //载入数据
26             first_ini();        //初始化数据
27             func_state=100;
28             break;
29         case 100:
30             calc_ch();   ///角色动画变化
31             ch_move();
32             graph_main();        //把图像数据等画到背画面上
33             break;
34         default:
35             printfDx("未知的func_state\n");
36             break;
37         }
38         //按esc键则退出程序
39         if(CheckStateKey(KEY_INPUT_ESCAPE)==1)
40             break;
41         ScreenFlip();        //把在背画面设置好的画到表画面,也就是显示到显示屏上
42     }
43 
44     DxLib_End();        //终止程序
45     return 0;
46 }

从第一行开始看,查看gv.h可以发现

#ifdef GLOBAL_INSTANCE
#define GLOBAL
#else
#define GLOBAL extern 
#endif

也就是说,如果文件包含了gv.h,就不用把gv.h里面的数据声明为extern了.

 

接下来跳到15行开始,这里是跟MFC框架相似的主函数入口winmain

其中ChangeWindowMode,DxLib_Init,SetDrawScreen为库函数,作用看注释,DxLib_Init与DxLib_End相对应

背画面可以看成是一个舞台的的后台,演员要在后台里面化好妆,准备好才出到前台来表演

ProcessLoop里的函数为每次(每一帧)更新都必须要调用的函数

例如从系统消息队列里面获取消息,清理上一帧的数据,获取键盘按键状态等

这里可以到key.cpp里看GetHitKeyStateAll_2和GetHitPadStateAll的实现

unsigned int stateKey[256];        //键盘按键次数

int GetHitKeyStateAll_2(){
    char GetHitKeyStateAll_Key[256];        //键盘按键状态,被按下的为1
    GetHitKeyStateAll( GetHitKeyStateAll_Key );
    for(int i=0;i<256;i++){
        if(GetHitKeyStateAll_Key[i]==1) 
            stateKey[i]++;
        else                            
            stateKey[i]=0;
    }
    return 0;
}

GetHitKeyStateAll为DX库函数,作用为获取键盘256个键值的状态,被按下的键被标识为1,存储在GetHitKeyStateAll_Key[256]里

按键连发次数被保存在stateKey[256]数组里,连发结束则设置为0

 

GetHitPadStateAll的原理同上,pad_t pad;被定义在sturct.h里面

//添加手柄控制
typedef struct{
    int key[PAD_MAX];
}pad_t;

//手柄按键的标识
typedef struct{
    int left,up,right,down,shot,bom,slow,start,change;
}configpad_t;

其中(在key.cpp可查看)函数input_pad_or_key()为处理键盘与手柄同时按下同一功能键的情况,返回次数较多的一方

 

回到main.cpp里,主循环func_state恒被初始化为0,所以函数必定被执行load与first_ini函数(分别被定义在load.cpp和ini.cpp里)

关于load方法中的LoadDivGraph( "../dat/img/char/0.png" , 12 , 4 , 3 , 73 , 73 , img_ch[0] ) ;

0.png为以下这个,img_ch[0](在gv.h中定义)为一个二维数组的其中一维

其作用为将0.png分成12张图,横4张,纵3张,每张73x73大小,存储在img_ch[0]这个数组里面

first_ini为初始化角色最初出现的位置以及手柄按键

 

往下来到main.cpp的case100中,(calc_ch不明白的话暂时不要管,只知道其作用即可)

//角色动作图像变换以连成动画效果
void calc_ch(){
    ch.cnt++;
    ch.img=(ch.cnt%24)/6;

}

//指定角色的移动
void ch_move(){
    int i,sayu_flag=0,joge_flag=0;
    double x,y,mx,my,naname=1;

    //左右下上四方向的速度
    double move_x[4]={-4.0,4.0,0,0},move_y[4]={0,0,4.0,-4.0};

    //手柄四方向的按键状态
    int inputpad[4];
    inputpad[0]=CheckStatePad(configpad.left); inputpad[1]=CheckStatePad(configpad.right);
    inputpad[2]=CheckStatePad(configpad.down); inputpad[3]=CheckStatePad(configpad.up);

    //角色向左或者向右时的图像变化
    if(CheckStatePad(configpad.left)>0)
        ch.img+=4*2;        //向左移动的角色图像
    else if(CheckStatePad(configpad.right)>0)
        ch.img+=4*1;        //向右移动的角色图像

    //45度斜向移动的话就稍作减速
    for(i=0;i<2;i++)        //检测左右按键的输入
        if(inputpad[i]>0)    //检测到输入的话
            sayu_flag=1;    //将标识设置为1
    for(i=2;i<4;i++)        //检测上下按键的输入,原理同上
        if(inputpad[i]>0)    
            joge_flag=1;
    if(sayu_flag==1 && joge_flag==1)    //如果两个标识都为1的话,也就是斜向行走的话
        naname=sqrt(2.0);    //稍作减速设置,naname在下面用上

    //检测是否有输入从而令角色的移动
    for(int i=0;i<4;i++){        //4个方向检测
        if(inputpad[i]>0){        //有输入的话
            x=ch.x , y=ch.y;        //存储角色座标
            mx=move_x[i];   my=move_y[i];        //赋予移动距离
            //减速移动
            if(CheckStatePad(configpad.slow)>0){        
                mx=move_x[i]/3; my=move_y[i]/3;    //将移动速度设置为原来的3份1
            }
            x+=mx/naname , y+=my/naname;        //角色原座标加上移动的距离
            //防止角色超出边界
            if(!(x<10 || x>FIELD_MAX_X-10 || y<5 || y>FIELD_MAX_Y-5)){
                ch.x=x , ch.y=y;
            }
        }
    }
}

 

graph_main中的两个函数

//角色描绘
void graph_ch(){
    DrawRotaGraphF(ch.x,ch.y,1.0f,0.0f,img_ch[0][ch.img],TRUE);
}

//界面描绘
void graph_board(){
    DrawGraph(  0,  0,img_board[10],FALSE);
    DrawGraph(  0, 16,img_board[11],FALSE);
    DrawGraph(  0,464,img_board[12],FALSE);
    DrawGraph(416,  0,img_board[20],FALSE);
}

DrawRotaGraphF(x座标,y座标,图像缩放的大小,图像的角度,图像数据,是否透明)

DrawGraph(x座标,y座标,图像数据,是否透明)

 

最后的printfDx("未知的func_state\n");为库函数,将出错信息输出到控制台

 运行程序,如无意外将得到如下图所示的程序

 

 



 

 

 

posted on 2012-09-23 16:31  tomboy  阅读(1321)  评论(0编辑  收藏

导航