栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++游戏编程教程(二)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++游戏编程教程(二)

上一篇博客,我们讲了SDL环境的配置和基本程序框架。这篇博客,我本打算讲一下Game类的代码,但突然想到很多API都没有介绍,所以这篇博客先来介绍一下SDL的基础知识和游戏运行框架,Game类的实现下篇博客再讲。

基本的SDL 类型、API和宏 SDL基本结构

SDL提供了一些结构体或类型,用来保存游戏的相关信息。下面我们就来看一下常用的结构体:
SDL_Window
窗口结构体。它保存一个窗口的信息。这个结构体的具体实现并不重要,我们不会用到里面的成员,只会通过函数对它的指针进行操作。
SDL_Renderer
渲染器结构体。它保存一个渲染器的信息。这个结构体的具体实现并不重要,我们不会用到里面的成员,只会通过函数对它的指针进行操作。
SDL_Rect
描述一个矩形。x,y为左上角坐标,w,h为宽和高。

typedef struct SDL_Rect
{
    int x, y;
    int w, h;
} SDL_Rect;

SDL_Event
结构体定义:

typedef union SDL_Event
{
    Uint32 type;                    
    SDL_CommonEvent common;         
    SDL_WindowEvent window;         
    SDL_KeyboardEvent key;          
    SDL_TextEditingEvent edit;      
    SDL_TextInputEvent text;        
    SDL_MouseMotionEvent motion;    
    SDL_MouseButtonEvent button;    
    SDL_MouseWheelEvent wheel;      
    SDL_JoyAxisEvent jaxis;         
    SDL_JoyBallEvent jball;         
    SDL_JoyHatEvent jhat;           
    SDL_JoyButtonEvent jbutton;     
    SDL_JoyDeviceEvent jdevice;     
    SDL_ControllerAxisEvent caxis;      
    SDL_ControllerButtonEvent cbutton;  
    SDL_ControllerDeviceEvent cdevice;  
    SDL_QuitEvent quit;             
    SDL_UserEvent user;             
    SDL_SysWMEvent syswm;           
    SDL_TouchFingerEvent tfinger;   
    SDL_MultiGestureEvent mgesture; 
    SDL_DollarGestureEvent dgesture; 
    SDL_DropEvent drop;             

    
    Uint8 padding[56];
} SDL_Event;

这个共用体非常重要,它保存了一个消息的信息。其中type为消息类型,其它都是消息的一些信息,这里不作具体介绍。下面是type的取值:

typedef enum
{
    SDL_FIRSTEVENT     = 0,     

    
    SDL_QUIT           = 0x100, 

    
    SDL_APP_TERMINATING,        
    SDL_APP_LOWMEMORY,          
    SDL_APP_WILLENTERBACKGROUND, 
    SDL_APP_DIDENTERBACKGROUND, 
    SDL_APP_WILLENTERFOREGROUND, 
    SDL_APP_DIDENTERFOREGROUND, 

    
    SDL_WINDOWEVENT    = 0x200, 
    SDL_SYSWMEVENT,             

    
    SDL_KEYDOWN        = 0x300, 
    SDL_KEYUP,                  
    SDL_TEXTEDITING,            
    SDL_TEXTINPUT,              

    
    SDL_MOUSEMOTION    = 0x400, 
    SDL_MOUSEBUTTONDOWN,        
    SDL_MOUSEBUTTONUP,          
    SDL_MOUSEWHEEL,             

    
    SDL_JOYAXISMOTION  = 0x600, 
    SDL_JOYBALLMOTION,          
    SDL_JOYHATMOTION,           
    SDL_JOYBUTTONDOWN,          
    SDL_JOYBUTTONUP,            
    SDL_JOYDEVICEADDED,         
    SDL_JOYDEVICEREMOVED,       

    
    SDL_ConTROLLERAXISMOTION  = 0x650, 
    SDL_CONTROLLERBUTTONDOWN,          
    SDL_CONTROLLERBUTTONUP,            
    SDL_CONTROLLERDEVICEADDED,         
    SDL_CONTROLLERDEVICEREMOVED,       
    SDL_CONTROLLERDEVICEREMAPPED,      

    
    SDL_FINGERDOWN      = 0x700,
    SDL_FINGERUP,
    SDL_FINGERMOTION,

    
    SDL_DOLLARGESTURE   = 0x800,
    SDL_DOLLARRECORD,
    SDL_MULTIGESTURE,

    
    SDL_CLIPBOARDUPDATE = 0x900, 

    
    SDL_DROPFILE        = 0x1000, 

    
    SDL_RENDER_TARGETS_RESET = 0x2000, 

    
    SDL_USEREVENT    = 0x8000,

    
    SDL_LASTEVENT    = 0xFFFF
} SDL_EventType;
SDL基本API

现在,我们需要了解一下SDL的一些常用函数的用法。

基本函数

SDL_Init

extern DECLSPEC int SDLCALL SDL_Init(Uint32 flags);

作用:初始化SDL。
参数:flags指定初始化类型。它可以是以下值(如有多个用"|"连接):

#define SDL_INIT_TIMER          0x00000001
#define SDL_INIT_AUDIO          0x00000010
#define SDL_INIT_VIDEO          0x00000020  
#define SDL_INIT_JOYSTICK       0x00000200  
#define SDL_INIT_HAPTIC         0x00001000
#define SDL_INIT_GAMECONTROLLER 0x00002000  
#define SDL_INIT_EVENTS         0x00004000
#define SDL_INIT_NOPARACHUTE    0x00100000  
#define SDL_INIT_EVERYTHING ( 
                SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | 
                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEConTROLLER 
            )

返回值:成功返回0,失败返回非0。
SDL_GetTicks

extern DECLSPEC Uint32 SDLCALL SDL_GetTicks(void);

作用:获取自SDL_Init以来的毫秒数,如果超过49天,会从头开始。
参数:无。
返回值:自SDL_Init以来的毫秒数。
SDL_GetError

extern DECLSPEC const char *SDLCALL SDL_GetError(void);

作用:获取上一次的错误。
参数:无。
返回值:描述错误的字符串。
SDL_Quit

extern DECLSPEC void SDLCALL SDL_Quit(void);

作用:退出由SDL_Init加载的库。这个函数用在游戏退出时。
参数:无。
返回值:无。

窗口函数

SDL_CreateWindow

extern DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title,
                                                      int x, int y, int w,
                                                      int h, Uint32 flags);

作用:创建一个SDL游戏窗口。
参数:title为窗口标题,x,y为窗口相对于左上角的位置,w,h为窗口宽度和高度,flags为窗口风格,详见此处。
返回值:创建的窗口的指针。通过这个指针,可以对窗口进行相应操作。失败返回nullptr。
SDL_DestroyWindow

extern DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window * window);

作用:销毁一个窗口。
参数:window为要销毁的窗口指针。
返回值:无。

渲染器函数

SDL_CreateRenderer

extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window,
                                               int index, Uint32 flags);

作用:创建一个2D渲染器。
参数:window为要创建渲染器的窗口,index一般为-1,flags一般为0。详见此处。
返回值:创建的渲染器的指针。通过这个指针,可以进行绘图。失败返回nullptr。
SDL_SetRenderDrawColor

extern DECLSPEC int SDLCALL SDL_SetRenderDrawColor(SDL_Renderer * renderer,
                                           Uint8 r, Uint8 g, Uint8 b,
                                           Uint8 a);

作用:设置渲染器绘制颜色。
参数:renderer为要设置的渲染器,r,g,b,a为颜色的rgba分量。
返回值:成功返回0,失败返回非0。
SDL_RenderClear

extern DECLSPEC int SDLCALL SDL_RenderClear(SDL_Renderer * renderer);

作用:将渲染器清空为设置的颜色。
参数:renderer为要清空的渲染器。
返回值:成功返回0,失败返回非0。
SDL_RenderPresent

extern DECLSPEC void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);

作用:把绘制的元素显示到屏幕上。
参数:renderer为要刷新的渲染器。
返回值:无。
SDL_RenderFillRect

extern DECLSPEC int SDLCALL SDL_RenderFillRect(SDL_Renderer * renderer,
                                               const SDL_Rect * rect);

作用:使用指定的颜色填充一个矩形。
参数:renderer为渲染器,rect指定矩形的坐标。
返回值:成功返回0,失败返回非0。
SDL_DestroyRenderer

extern DECLSPEC void SDLCALL SDL_DestroyRenderer(SDL_Renderer * renderer);

作用:销毁一个渲染器。
参数:renderer为要销毁的渲染器。
返回值:无。

获取信息函数

SDL_PollEvent

extern DECLSPEC int SDLCALL SDL_PollEvent(SDL_Event * event);

作用:从消息队列中取出一个消息。
参数:event为消息结构体的指针,用来存储取出的消息。
返回值:如果有消息返回非0,否则返回0。
SDL_GetKeyboardState

extern DECLSPEC const Uint8 *SDLCALL SDL_GetKeyboardState(int *numkeys);

作用:获取键盘状态。
参数:numkey一般为nullptr。
返回值:一个数组,表示键盘状态,其中1为该键被按下,0为没被按下。
示例:

const Uint8* keyState = SDL_GetKeyboardState(NULL);
if (keyState[SDL_SCANCODE_RIGHT])//右箭头被按下
	mPlaneDir += 1;
if (keyState[SDL_SCANCODE_LEFT])//左箭头被按下
	mPlaneDir -= 1;

其中数组下标取值如下:

typedef enum
{
    SDL_SCANCODE_UNKNOWN = 0,

    
    

    SDL_SCANCODE_A = 4,
    SDL_SCANCODE_B = 5,
    SDL_SCANCODE_C = 6,
    SDL_SCANCODE_D = 7,
    SDL_SCANCODE_E = 8,
    SDL_SCANCODE_F = 9,
    SDL_SCANCODE_G = 10,
    SDL_SCANCODE_H = 11,
    SDL_SCANCODE_I = 12,
    SDL_SCANCODE_J = 13,
    SDL_SCANCODE_K = 14,
    SDL_SCANCODE_L = 15,
    SDL_SCANCODE_M = 16,
    SDL_SCANCODE_N = 17,
    SDL_SCANCODE_O = 18,
    SDL_SCANCODE_P = 19,
    SDL_SCANCODE_Q = 20,
    SDL_SCANCODE_R = 21,
    SDL_SCANCODE_S = 22,
    SDL_SCANCODE_T = 23,
    SDL_SCANCODE_U = 24,
    SDL_SCANCODE_V = 25,
    SDL_SCANCODE_W = 26,
    SDL_SCANCODE_X = 27,
    SDL_SCANCODE_Y = 28,
    SDL_SCANCODE_Z = 29,

    SDL_SCANCODE_1 = 30,
    SDL_SCANCODE_2 = 31,
    SDL_SCANCODE_3 = 32,
    SDL_SCANCODE_4 = 33,
    SDL_SCANCODE_5 = 34,
    SDL_SCANCODE_6 = 35,
    SDL_SCANCODE_7 = 36,
    SDL_SCANCODE_8 = 37,
    SDL_SCANCODE_9 = 38,
    SDL_SCANCODE_0 = 39,

    SDL_SCANCODE_RETURN = 40,
    SDL_SCANCODE_ESCAPE = 41,
    SDL_SCANCODE_BACKSPACE = 42,
    SDL_SCANCODE_TAB = 43,
    SDL_SCANCODE_SPACE = 44,

    SDL_SCANCODE_MINUS = 45,
    SDL_SCANCODE_EQUALS = 46,
    SDL_SCANCODE_LEFTBRACKET = 47,
    SDL_SCANCODE_RIGHTBRACKET = 48,
    SDL_SCANCODE_BACKSLASH = 49, 
    SDL_SCANCODE_NonUSHASH = 50, 
    SDL_SCANCODE_SEMIcolon = 51,
    SDL_SCANCODE_APOSTROPHE = 52,
    SDL_SCANCODE_GRAVE = 53, 
    SDL_SCANCODE_COMMA = 54,
    SDL_SCANCODE_PERIOD = 55,
    SDL_SCANCODE_SLASH = 56,

    SDL_SCANCODE_CAPSLOCK = 57,

    SDL_SCANCODE_F1 = 58,
    SDL_SCANCODE_F2 = 59,
    SDL_SCANCODE_F3 = 60,
    SDL_SCANCODE_F4 = 61,
    SDL_SCANCODE_F5 = 62,
    SDL_SCANCODE_F6 = 63,
    SDL_SCANCODE_F7 = 64,
    SDL_SCANCODE_F8 = 65,
    SDL_SCANCODE_F9 = 66,
    SDL_SCANCODE_F10 = 67,
    SDL_SCANCODE_F11 = 68,
    SDL_SCANCODE_F12 = 69,

    SDL_SCANCODE_PRINTSCREEN = 70,
    SDL_SCANCODE_SCROLLLOCK = 71,
    SDL_SCANCODE_PAUSE = 72,
    SDL_SCANCODE_INSERT = 73, 
    SDL_SCANCODE_HOME = 74,
    SDL_SCANCODE_PAGEUP = 75,
    SDL_SCANCODE_DELETE = 76,
    SDL_SCANCODE_END = 77,
    SDL_SCANCODE_PAGEDOWN = 78,
    SDL_SCANCODE_RIGHT = 79,
    SDL_SCANCODE_LEFT = 80,
    SDL_SCANCODE_DOWN = 81,
    SDL_SCANCODE_UP = 82,

    SDL_SCANCODE_NUMLOCKCLEAR = 83, 
    SDL_SCANCODE_KP_DIVIDE = 84,
    SDL_SCANCODE_KP_MULTIPLY = 85,
    SDL_SCANCODE_KP_MINUS = 86,
    SDL_SCANCODE_KP_PLUS = 87,
    SDL_SCANCODE_KP_ENTER = 88,
    SDL_SCANCODE_KP_1 = 89,
    SDL_SCANCODE_KP_2 = 90,
    SDL_SCANCODE_KP_3 = 91,
    SDL_SCANCODE_KP_4 = 92,
    SDL_SCANCODE_KP_5 = 93,
    SDL_SCANCODE_KP_6 = 94,
    SDL_SCANCODE_KP_7 = 95,
    SDL_SCANCODE_KP_8 = 96,
    SDL_SCANCODE_KP_9 = 97,
    SDL_SCANCODE_KP_0 = 98,
    SDL_SCANCODE_KP_PERIOD = 99,

    SDL_SCANCODE_NonUSBACKSLASH = 100, 
    SDL_SCANCODE_APPLICATION = 101, 
    SDL_SCANCODE_POWER = 102, 
    SDL_SCANCODE_KP_EQUALS = 103,
    SDL_SCANCODE_F13 = 104,
    SDL_SCANCODE_F14 = 105,
    SDL_SCANCODE_F15 = 106,
    SDL_SCANCODE_F16 = 107,
    SDL_SCANCODE_F17 = 108,
    SDL_SCANCODE_F18 = 109,
    SDL_SCANCODE_F19 = 110,
    SDL_SCANCODE_F20 = 111,
    SDL_SCANCODE_F21 = 112,
    SDL_SCANCODE_F22 = 113,
    SDL_SCANCODE_F23 = 114,
    SDL_SCANCODE_F24 = 115,
    SDL_SCANCODE_EXECUTE = 116,
    SDL_SCANCODE_HELP = 117,
    SDL_SCANCODE_MENU = 118,
    SDL_SCANCODE_SELECT = 119,
    SDL_SCANCODE_STOP = 120,
    SDL_SCANCODE_AGAIN = 121,   
    SDL_SCANCODE_UNDO = 122,
    SDL_SCANCODE_CUT = 123,
    SDL_SCANCODE_COPY = 124,
    SDL_SCANCODE_PASTE = 125,
    SDL_SCANCODE_FIND = 126,
    SDL_SCANCODE_MUTE = 127,
    SDL_SCANCODE_VOLUMEUP = 128,
    SDL_SCANCODE_VOLUMEDOWN = 129,




    SDL_SCANCODE_KP_COMMA = 133,
    SDL_SCANCODE_KP_EQUALSAS400 = 134,

    SDL_SCANCODE_INTERNATIONAL1 = 135, 
    SDL_SCANCODE_INTERNATIONAL2 = 136,
    SDL_SCANCODE_INTERNATIONAL3 = 137, 
    SDL_SCANCODE_INTERNATIONAL4 = 138,
    SDL_SCANCODE_INTERNATIONAL5 = 139,
    SDL_SCANCODE_INTERNATIONAL6 = 140,
    SDL_SCANCODE_INTERNATIONAL7 = 141,
    SDL_SCANCODE_INTERNATIONAL8 = 142,
    SDL_SCANCODE_INTERNATIONAL9 = 143,
    SDL_SCANCODE_LANG1 = 144, 
    SDL_SCANCODE_LANG2 = 145, 
    SDL_SCANCODE_LANG3 = 146, 
    SDL_SCANCODE_LANG4 = 147, 
    SDL_SCANCODE_LANG5 = 148, 
    SDL_SCANCODE_LANG6 = 149, 
    SDL_SCANCODE_LANG7 = 150, 
    SDL_SCANCODE_LANG8 = 151, 
    SDL_SCANCODE_LANG9 = 152, 

    SDL_SCANCODE_ALTERASE = 153, 
    SDL_SCANCODE_SYSREQ = 154,
    SDL_SCANCODE_CANCEL = 155,
    SDL_SCANCODE_CLEAR = 156,
    SDL_SCANCODE_PRIOR = 157,
    SDL_SCANCODE_RETURN2 = 158,
    SDL_SCANCODE_SEPARATOR = 159,
    SDL_SCANCODE_OUT = 160,
    SDL_SCANCODE_OPER = 161,
    SDL_SCANCODE_CLEARAGAIN = 162,
    SDL_SCANCODE_CRSEL = 163,
    SDL_SCANCODE_EXSEL = 164,

    SDL_SCANCODE_KP_00 = 176,
    SDL_SCANCODE_KP_000 = 177,
    SDL_SCANCODE_THOUSANDSSEPARATOR = 178,
    SDL_SCANCODE_DECIMALSEPARATOR = 179,
    SDL_SCANCODE_CURRENCYUNIT = 180,
    SDL_SCANCODE_CURRENCYSUBUNIT = 181,
    SDL_SCANCODE_KP_LEFTPAREN = 182,
    SDL_SCANCODE_KP_RIGHTPAREN = 183,
    SDL_SCANCODE_KP_LEFTBRACE = 184,
    SDL_SCANCODE_KP_RIGHTBRACE = 185,
    SDL_SCANCODE_KP_TAB = 186,
    SDL_SCANCODE_KP_BACKSPACE = 187,
    SDL_SCANCODE_KP_A = 188,
    SDL_SCANCODE_KP_B = 189,
    SDL_SCANCODE_KP_C = 190,
    SDL_SCANCODE_KP_D = 191,
    SDL_SCANCODE_KP_E = 192,
    SDL_SCANCODE_KP_F = 193,
    SDL_SCANCODE_KP_XOR = 194,
    SDL_SCANCODE_KP_POWER = 195,
    SDL_SCANCODE_KP_PERCENT = 196,
    SDL_SCANCODE_KP_LESS = 197,
    SDL_SCANCODE_KP_GREATER = 198,
    SDL_SCANCODE_KP_AMPERSAND = 199,
    SDL_SCANCODE_KP_DBLAMPERSAND = 200,
    SDL_SCANCODE_KP_VERTICALBAR = 201,
    SDL_SCANCODE_KP_DBLVERTICALBAR = 202,
    SDL_SCANCODE_KP_colon = 203,
    SDL_SCANCODE_KP_HASH = 204,
    SDL_SCANCODE_KP_SPACE = 205,
    SDL_SCANCODE_KP_AT = 206,
    SDL_SCANCODE_KP_EXCLAM = 207,
    SDL_SCANCODE_KP_MEMSTORE = 208,
    SDL_SCANCODE_KP_MEMRECALL = 209,
    SDL_SCANCODE_KP_MEMCLEAR = 210,
    SDL_SCANCODE_KP_MEMADD = 211,
    SDL_SCANCODE_KP_MEMSUBTRACT = 212,
    SDL_SCANCODE_KP_MEMMULTIPLY = 213,
    SDL_SCANCODE_KP_MEMDIVIDE = 214,
    SDL_SCANCODE_KP_PLUSMINUS = 215,
    SDL_SCANCODE_KP_CLEAR = 216,
    SDL_SCANCODE_KP_CLEARENTRY = 217,
    SDL_SCANCODE_KP_BINARY = 218,
    SDL_SCANCODE_KP_OCTAL = 219,
    SDL_SCANCODE_KP_DECIMAL = 220,
    SDL_SCANCODE_KP_HEXADECIMAL = 221,

    SDL_SCANCODE_LCTRL = 224,
    SDL_SCANCODE_LSHIFT = 225,
    SDL_SCANCODE_LALT = 226, 
    SDL_SCANCODE_LGUI = 227, 
    SDL_SCANCODE_RCTRL = 228,
    SDL_SCANCODE_RSHIFT = 229,
    SDL_SCANCODE_RALT = 230, 
    SDL_SCANCODE_RGUI = 231, 

    SDL_SCANCODE_MODE = 257,    

    

    
    

    SDL_SCANCODE_AUDIonEXT = 258,
    SDL_SCANCODE_AUDIOPREV = 259,
    SDL_SCANCODE_AUDIOSTOP = 260,
    SDL_SCANCODE_AUDIOPLAY = 261,
    SDL_SCANCODE_AUDIOMUTE = 262,
    SDL_SCANCODE_MEDIASELECT = 263,
    SDL_SCANCODE_WWW = 264,
    SDL_SCANCODE_MAIL = 265,
    SDL_SCANCODE_CALCULATOR = 266,
    SDL_SCANCODE_COMPUTER = 267,
    SDL_SCANCODE_AC_SEARCH = 268,
    SDL_SCANCODE_AC_HOME = 269,
    SDL_SCANCODE_AC_BACK = 270,
    SDL_SCANCODE_AC_FORWARD = 271,
    SDL_SCANCODE_AC_STOP = 272,
    SDL_SCANCODE_AC_REFRESH = 273,
    SDL_SCANCODE_AC_BOOKMARKS = 274,

    

    
    

    SDL_SCANCODE_BRIGHTNESSDOWN = 275,
    SDL_SCANCODE_BRIGHTNESSUP = 276,
    SDL_SCANCODE_DISPLAYSWITCH = 277, 
    SDL_SCANCODE_KBDILLUMTOGGLE = 278,
    SDL_SCANCODE_KBDILLUMDOWN = 279,
    SDL_SCANCODE_KBDILLUMUP = 280,
    SDL_SCANCODE_EJECT = 281,
    SDL_SCANCODE_SLEEP = 282,

    SDL_SCANCODE_APP1 = 283,
    SDL_SCANCODE_APP2 = 284,

    

    

    SDL_NUM_SCANCODES = 512 
} SDL_Scancode;

介绍了这些函数,就足够做一个简单的小游戏了。

SDL基本宏

SDL_TICKS_PASSED

#define SDL_TICKS_PASSED(A, B)  ((Sint32)((B) - (A)) <= 0)

作用:判断时间A是否经过了时间B。

游戏运行框架

在这里,我先简单介绍一下游戏的更新机制。游戏进行主要由三部分组成:

  1. 处理输入;
  2. 更新游戏;
  3. 生成输出。

在代码中,这三个功能分别由三个函数实现,而在一个主函数里,要循环执行这三个函数。这也是游戏与其他程序不同的一点:需要频繁更新。处理输入就是对输入做出处理,这里的输入包括多方面,如键盘、鼠标、控制杆的操作,还有联机游戏服务器发来的信息等;更新游戏则是对游戏的各种数据做出更新,但并不显示到屏幕上;生成输出时,需要输出当前的信息,如绘制游戏界面,向服务器发送信息等等。


今天就说到这里,谢谢大家支持,下期再见

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/290219.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号