游戏编程之DirectX的修炼:三(DirectX的初始化:下)
来源:互联网 发布:阿里云 80端口 编辑:程序博客网 时间:2024/05/18 02:50
哇卡卡卡,哈哈哈,这节咋们就来学习初始化DirectX的具体代码和细节吧。
还几次上节说的我们的初始化主要有两个步骤,我们再看下图。
是否心里已经明白我们大概要做什么了那?
1. 好,首先来解决初始化的第一步,获得IDirect3DDevice9这个接口。
IDirect3DDevice9这个接口又叫设备接口,设备,什么设备?当然就是我们的硬件了。回想之前我们创建窗口的时候,我们说要有一个自己的窗口,需要一些最基本的信息来告诉我们窗口长啥样子,同理,我们在创建IDirect3DDevice9这个设备接口时候我们也要知道一些基本的信息,才能让我们知道这个设备接口大概是什么样子(这里的样子指的是它的属性,并不是具体外观),而这些基本信息就是我们的硬件信息了,因为我们说过DirectX是可以直接和硬件打交道的对吧。那么,也就是说。创建一个设备接口,我们需要获取硬件信息,然后把这些消息告知我们的设备接口,我们的设备接口就算完成了。
总结起来就是两步:
1.1:获取硬件信息
1.2:将信息告知我们的设备接口,创建设备接口
没问题把,获得设备接口就这么两步,那我们先走第一步:获得硬件信息
首先,想要获取,硬件信息,我们需要创建一个LPDIRECT3D9接口,创建这个接口不需要任何条件,我们通过这个接口调用GetDeviceCaps这个函数,就能获得硬件信息,同时我们也是通过LPDIRECT3D9接口调用CreateDevice函数最终创建我们的IDirect3DDevice9设备接口的。稍后便会讲到。我们先看一下用LPDIRECT3D9获得硬件信息的代码。
LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);//创建接口对象获取硬件信息并为后面的设备对象做准备if (NULL == pD3D) { return E_FAIL; }D3DCAPS9 caps;//用于存储设备信息的结构体int vp = 0;//显卡支持的顶点运算模式if (FAILED(pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps))){//填入显卡序号,设备类型(硬件,软件)return E_FAIL;}if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;//硬件顶点运算模式elsevp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;//软件顶点运算模式
我们看一下这一段代码,首先第一句话,我们使用了一个Direct3DCreate9函数来创建了一个LPDIRECT3D9接口,Direct3DCreate9这个函数只有一个参数,这个参数的意义是我们当前的DirectX版本,保证我们包含的头文件能正确调用相应的.dll库。如果失败,说明版本协商有问题。
然后是我们定义了一个D3DCAPS9型的结构体来保存我们的硬件信息。大家看到了我们还定义了一个整形的vp变量,这个变量是用来保存我们硬件是否支持硬件顶点运算模式,如果支持,那么后面我们将告诉我们的设备接口,让它在渲染的时候使用硬件顶点运算模式,如果不行就只能使用软件顶点运算模式了。最后的那句if else语句就是用来判断是否支持硬件顶点运算模式,当然,硬件顶点运算模式是要比软件顶点运算模式快很多的。这也是我们判断的原因。
最后就是中间的GetDeviceCaps函数了,这个函数就是用来获取硬件信息的。
HRESULT GetDeviceCaps( [in] UINT Adapter,//显卡序号 [in] D3DDEVTYPE DeviceType,//设备类型 [out] D3DCAPS9 *pCaps//保存硬件信息的结构体);
第一个参数一般填写默认的:D3DADAPTER_DEFAULT
第二个参数,我们也一般填写硬件设备类型:D3DDEVTYPE_HAL
最后一个参数就是我们需要保存信息的结构体,当我们调用完这个函数以后,设备信息就存到caps里面去了。ok,取得硬件信息就是这么多了。
1.2:将信息告知我们的设备接口,创建设备接口
我们已经取得了,硬件信息,是不是马上可以开始创建设备接口了哪?是的,但是在创建设备接口之前,我们还需要设置设备接口的另一些详细信息。而幸运的是,这些信息都包含在一个结构体内,还记得我们填窗口填写的WINDCLASSEX吗?这次我们要为设备填写一个叫做D3DPRESENT_PARAMETERS的结构体,这个结构体非常重要!
typedef struct _D3DPRESENT_PARAMETERS_{ UINT BackBufferWidth;//交换链后台缓存宽度 UINT BackBufferHeight;//交换链后台缓存高度 D3DFORMAT BackBufferFormat;//后台缓存格式,他的参数是D3DFORMAT的成员之一 UINT BackBufferCount;//后台缓存个数 D3DMULTISAMPLE_TYPE MultiSampleType;//多重采样格式 DWORD MultiSampleQuality;//多重采样类型 D3DSWAPEFFECT SwapEffect;//将后台缓存内容复制到前台的方式. HWND hDeviceWindow;//我们的窗口句柄 BOOL Windowed;//是否全屏 BOOL EnableAutoDepthStencil;//自动深度缓存,如果为真,需要设置下一个参数。 D3DFORMAT AutoDepthStencilFormat;//深度缓存的像素格式 DWORD Flags;//附加属性 /* FullScreen_RefreshRateInHz must be zero for Windowed mode */ UINT FullScreen_RefreshRateInHz;//全屏模式时候的刷新率 UINT PresentationInterval;前台缓存与后台缓存的最大交换速率。在D3DPRESENT中取值}好了这就是这个可以为我们的设备接口做出巨大贡献的结构体,可能大家第一次看到会觉得很生疏,如果大家有看不懂的地方,请不要在意,因为你不懂的,后面都会慢慢讲的。大家只需要记住这是个重要的结构体就行。接下来看看它的赋值
D3DPRESENT_PARAMETERS d3dpp;//一个非常重要的结构体ZeroMemory(&d3dpp, sizeof(d3dpp));//用零填充d3dpp这块内存d3dpp.BackBufferWidth = WINDOW_WEIGHT;//缓冲区宽度d3dpp.BackBufferHeight = WINDOW_HIGHT;//缓冲区高度d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;//缓冲区保存像素格式D3DFORMAT//++d3dpp.BackBufferCount = 1;//后台缓存的个数d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;//启用多重采样的类型d3dpp.MultiSampleQuality = 0;//多重采样类型d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//如何将后台缓存复制到前台缓存中d3dpp.hDeviceWindow = hwnd;d3dpp.Windowed = true;//使用窗口模式还是全屏模式d3dpp.EnableAutoDepthStencil = true;//是否自动管理深度缓存d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;//深度缓存像素格式D3DFORMAT中取值d3dpp.Flags = 0;//附加属性,通常为0d3dpp.FullScreen_RefreshRateInHz = 0;//全屏模式时候的屏幕刷新率d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;//前后台交换的最大频率可以在D3DPRESENT取值
好了 硬件信息有了,结构体也有啦,那就开始创建我们的设备接口把
pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,hwnd, vp, &d3dpp, &g_pd3dDevice)pD3D->Release();
好吧 创建是非常简单的,一个CreateDevice函数就搞定了,调用这个函数后我们就可以用设备接口IDirect3DDevice9搞事情了。不过在此之前我们还是介绍下CreateDevice
HRESULT CreateDevice( [in] UINT Adapter,//显卡序号,一般填0,D3DADAPTER_DEFAULT=0 [in] D3DDEVTYPE DeviceType,//设备类型,当然是硬件 [in] HWND hFocusWindow,//窗口句柄 [in] DWORD BehaviorFlags,//顶点运算模式 [in, out] D3DPRESENT_PARAMETERS *pPresentationParameters,//千辛万苦填的接口体 [out, retval] IDirect3DDevice9 **ppReturnedDeviceInterface//指向我们的设备接口的指针,这个参数就是我们的设备接口.);
好啦 到此为止我们Direct3D的初始化第一部分创建设备接口IDirect3DDevice9就讲完了,我们把完整代码贴出来
LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);//创建接口对象获取硬件信息并为后面的设备对象做准备if (NULL == pD3D) { return E_FAIL; }D3DCAPS9 caps;//用于存储设备信息的结构体int vp = 0;//显卡支持的顶点运算模式if (FAILED(pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps))){//填入显卡序号,设备类型(硬件,软件)return E_FAIL;}if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;//硬件顶点运算模式elsevp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;//软件顶点运算模式D3DPRESENT_PARAMETERS d3dpp;//一个非常重要的结构体ZeroMemory(&d3dpp, sizeof(d3dpp));//用零填充d3dpp这块内存d3dpp.BackBufferWidth = WINDOW_WEIGHT;//后台缓冲区宽度d3dpp.BackBufferHeight = WINDOW_HIGHT;//后台缓冲区高度d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;//缓冲区保存像素格式D3DFORMATd3dpp.BackBufferCount = 1;//后台缓存的个数d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;//多重采样的类型d3dpp.MultiSampleQuality = 0;//多重采样的类型d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//如何将后台缓存复制到前台缓存中d3dpp.hDeviceWindow = hwnd;d3dpp.Windowed = true;//使用窗口模式还是全屏模式d3dpp.EnableAutoDepthStencil = true;//是否自动管理深度缓存d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;//深度缓存像素格式D3DFORMAT中取值d3dpp.Flags = 0;//附加属性,通常为0d3dpp.FullScreen_RefreshRateInHz = 0;//全屏模式时候的屏幕刷新率,窗口时为0在AdapterModes枚举中取值d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;//前后台交换的最大频率可以在D3DPRESENT取值if (FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,hwnd, vp, &d3dpp, &g_pd3dDevice)))return E_FAIL;pD3D->Release();//LPDIRECT3D9接口对象的使命完成释放return S_OK;
以上就是初始化第一部分的全部代码了,记得当我们使用完LPDIRECT3D9这个接口时,要把他Release掉。
2.使用我们的设备接口进行渲染并且在主事件循环中不断渲染
由于现在我们只是在初始化D3D,所以我们的不会在平面上渲染任何东西,所以这部分的代码也非常简单,就是使用我们的IDirect3DDevice9调用几个函数就可以搞定,然后把他们放入我们的主事件循环中不断地渲染就好啦,那我们来看看这几个函数把。
void Render(){g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0, 0);//清空后台缓存g_pd3dDevice->BeginScene();//开始绘制g_pd3dDevice->EndScene();//结束绘制g_pd3dDevice->Present(NULL, NULL, NULL, NULL);//交换与显示}
首先还是来看看咋们是第一个函数Clear,这个函数的主要作用就是设置我们清空了后台缓冲之后的一些属性。
HRESULT Clear( [in] DWORD Count,//指向一个矩形数组地数量,与下一个函数有关,如果下一个函数为NULL,则这里为零,反之,下一个参数不是NULL,这里就不能写零 [in] const D3DRECT *pRects,//指向一个矩形结构数组地指针 [in] DWORD Flags,//需要清空地缓冲区,它可以是模板缓冲,颜色缓冲,和深度缓冲三个值地任意组合,用|连接。 [in] D3DCOLOR Color,//清空缓冲区后每个像素地颜色值 [in] float Z,//清空深度缓冲后地每个像素地深度值 [in] DWORD Stencil//清空模板缓冲后地每个像素地模板值);
好后面的三个函数都非常简单
BeginScene代表着开始绘制
EndScene代表着结束绘制
而我们以后需要在屏幕上绘制东西时的代码函数都在BeginScene和EndScene之间
当所有的绘制完成之后我们就调用Present将绘制的内容显示出来,这个函数也非常简单四个参数都填NULL就ok了!
既然渲染的函数都写好了,那就赶紧将我们的渲染函数放到主循环函数中开始渲染把。是的没错,但是在那之前我们还有最后一个问题需要处理,什么问题?就是关于我们主事件循环的问题,我们先贴一下主事件循环的代码。
MSG msg = {0};//定义megwhile(GetMessage(&msg, NULL, 0, 0)) //使用getmessage获得消息{TranslateMessage(&msg);//转换消息DispatchMessage(&msg);//发消息给程序,然后交给os调用窗口处理函数}之前我们说过,当我们生成窗口之后,OS就会给我们的程序生成一个消息队列,而我们的GetMessage函数则通过While循环不断获取消息,但是大家有没有想过,一旦消息队列为空咋办?会咋办哪?其实,一旦消息队列为空,则GetMessage 函数会一直停在那里等待消息,而不会继续执行下面的代码,试想一下,假如你在玩儿一个游戏,一旦你停止按键盘或者鼠标,整个游戏画面就不再更新,这将是多么可怕的一件事情,所以。我们希望不论消息队列中有没有消息,我们的主循环函数都不会阻塞,并且不断地渲染我们的画面。所以这时候我们需要请出PeekMessage这个函数来代替GetMessage函数。
那么PeekMessage有什么优点哪?它唯一的优点就是,当我们的消息队列中没有消息的时候,它会返回一个值继续往下执行,不会一直等待消息队列中有消息才返回。下面我们看看将主事件循环改为PeekMessage之后时什么样
MSG msg = { 0 };//定义并初始化 megwhile (msg.message != WM_QUIT)//消息循环{if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) //使用peekmessage获得消息{TranslateMessage(&msg);//转换消息DispatchMessage(&msg);//分发消息给程序,然后交给os调用窗口处理函数}else{Render();}}好了,我们可以看到由于PeekMessage不会一直等待消息队列的消息,所以当消息队列没有消息的时候,我们就让它执行我们的渲染函数,这样我们的画面就会一直更新,还有一点就是PeekMessage的前四个参数和Get Message一样,只有最后多一个参数,这个参数是告诉PeekMessage在查看消息队列中的消息时,是否要将查看的消息移出消息队列。好吧,最后我们把一个完整的Direct3D初始化程序贴出来吧!
//======================================================================================================////————————————————————————程序说明———————————————————————////程序名称:DirectInitDemo//2017.9.5 淡一抹夕霞//======================================================================================================////==========================================////——————头文件部分——————————//#include"windows.h"#include<d3d9.h>#include<d3dx9.h>//==========================================////==========================================////——————库文件部分——————————//#pragma comment(lib,"d3dx9.lib")#pragma comment(lib,"d3d9.lib") //==========================================////==========================================////———--———全局变量——————————//LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;//定义一个direct3D设备对象int Color=0;//用于更改清空后台缓存时的颜色//==========================================////==========================================////——————全局函数声明———————--—//LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);//窗口处理函数HRESULT D3Dinit(HWND hWnd, HINSTANCE hInstance);//d3d初始化函数void Render();//d3d渲染函数//==========================================////===========================================================================================////—————--------------------—程序的主函数WinMain———-----------------------------——-////===========================================================================================//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ /////////////////////////////////////// // // //-----------设计窗口部分------------// // // ///////////////////////////////////////WNDCLASSEX wndclassex = { 0 };//创建一个窗口类,并且记得初始化wndclassex.cbSize = sizeof(wndclassex);//节数大小wndclassex.style = CS_VREDRAW | CS_HREDRAW;//样式标记wndclassex.lpfnWndProc = WndProc;//指向窗口事件处理函数的指针wndclassex.hInstance = hInstance;//应用程序实例句柄wndclassex.cbClsExtra = 0;//额外的类信息wndclassex.cbWndExtra = 0;//额外的窗口信息wndclassex.hIcon = LoadIcon(NULL, IDI_APPLICATION);//加载ico图标wndclassex.hCursor = ::LoadCursor(NULL, IDC_ARROW);//指定窗口类的光标句柄wndclassex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);//为成员指定一个灰色画刷wndclassex.lpszMenuName = NULL;//要加入窗口的菜单名wndclassex.lpszClassName = L"WINDOW_CLASS";//窗口类名///////////////////////////////////////// ////-----------注册窗口部分------------//// /////////////////////////////////////////RegisterClassEx(&wndclassex);//向我们的"政府申请注册"///////////////////////////////////////// ////-----------创建窗口部分------------// // // ///////////////////////////////////////HWND hWnd = CreateWindowEx(NULL, L"WINDOW_CLASS",L"MyDirectDemo",//直接调用创建函数就好了WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,800, 600, NULL, NULL, hInstance, NULL);////////////////////////////////////////////////////////////////////////////////ShowWindow(hWnd, nCmdShow);//显示窗口UpdateWindow(hWnd);//更新窗口 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////在此初始化Direct3DD3Dinit(hWnd, hInstance);//////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////// // // //------------主循环部分-------------// // // ///////////////////////////////////////MSG msg = { 0 };//定义并初始化 megwhile (msg.message != WM_QUIT)//消息循环{if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) //使用peekmessage或者getmessage获得消息{TranslateMessage(&msg);//转换消息DispatchMessage(&msg);//分发消息给程序,然后交给os调用窗口处理函数}else{Render();}}return msg.wParam;}/////////////////////////////////////////////////// ////------------窗口消息处理函数部分-------------//// ///////////////////////////////////////////////////LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){PAINTSTRUCT paintStruct;//定义一个paintstruct记录一些绘制信息HDC hdc;//设备环境句柄switch (message)//开始处理{case WM_PAINT://重绘消息 更新客户区hdc = BeginPaint(hWnd, &paintStruct);//指定窗口进行绘图准备,并在ps结构中保存相关信息EndPaint(hWnd, &paintStruct);//窗口绘图过程结束break;case WM_KEYDOWN://键盘按下消息if (wParam == VK_ESCAPE)//如果是escDestroyWindow(hWnd);//销毁窗口,发送WM_DESTROY消息break;case WM_DESTROY://销毁消息PostQuitMessage(0);//向os申请终止请求。break;default:return DefWindowProc(hWnd, message, wParam, lParam);//默认的窗口过程处理函数}return 0;}HRESULT D3Dinit(HWND hWnd, HINSTANCE hInstance){LPDIRECT3D9 pD3D = Direct3DCreate9(D3D_SDK_VERSION);//创建接口对象获取硬件信息并为后面的设备对象做准备if (NULL == pD3D) { return E_FAIL; }D3DCAPS9 caps;//用于存储设备信息的结构体int vp = 0;//显卡支持的顶点运算模式if (FAILED(pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps))){//填入显卡序号,设备类型(硬件,软件)return E_FAIL;}if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;//硬件顶点运算模式elsevp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;//软件顶点运算模式D3DPRESENT_PARAMETERS d3dpp;//一个非常重要的结构体ZeroMemory(&d3dpp, sizeof(d3dpp));//用零填充d3dpp这块内存d3dpp.BackBufferWidth = 800;//缓冲区宽度d3dpp.BackBufferHeight =600;//缓冲区高度d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;//缓冲区保存像素格式D3DFORMAT//+d3dpp.BackBufferCount = 1;//后台缓存的个数d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;//多重采样的类型d3dpp.MultiSampleQuality = 0;//多重采样的质量水平d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//如何将后台缓存复制到前台缓存中d3dpp.hDeviceWindow = hWnd;d3dpp.Windowed = true;//使用窗口模式还是全屏模式d3dpp.EnableAutoDepthStencil = true;//是否自动管理深度缓存d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;//深度缓存像素格式d3dpp.Flags = 0;//附加属性,通常为0d3dpp.FullScreen_RefreshRateInHz = 0;//全屏模式时候的屏幕刷新率,d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;//前后台交换的最大频率if (FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,hWnd, vp, &d3dpp, &g_pd3dDevice)))return E_FAIL;pD3D->Release();//LPDIRECT3D9接口对象的使命完成 释放return S_OK;}void Render(){g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(Color, 0, 255), 1.0, 0);g_pd3dDevice->BeginScene();g_pd3dDevice->EndScene();g_pd3dDevice->Present(NULL, NULL, NULL, NULL);Color += 1;if (Color >= 255)Color = 0;}
好的,大家可以复制代码运行一下看看,是不是觉得很闪?因为我设置了个变量每次清空后台缓存时都将颜色的R值加1;
好吧,这一节就到这里,如果有不明白的,相信大家都明白,大家心中对渲染或许还有疑问,但是没关系,后面会详细介绍。当然有什么建议可以留言哦。感谢!
- 游戏编程之DirectX的修炼:三(DirectX的初始化:下)
- 游戏编程之DirectX的修炼:三(DirectX的初始化:上)
- 游戏编程之DirectX的修炼:一(编程环境和DirectX安装)
- 游戏编程之DirectX的修炼:二(创建属于自己的windows窗口程序:下)
- 游戏编程之DirectX的修炼:二(创建属于自己的windows窗口程序:上)
- 游戏编程之DirectX的修炼:零(随便写点什么)
- DirectX游戏编程的实例.
- 游戏编程之三 DirectX SDK简介
- 【DirectX 2D游戏编程基础】DirectX环境的搭建
- 【DirectX 2D游戏编程基础】DirectX精灵的创建
- 【阅读笔记之三】《DIRECTX.9.0.3D游戏开发编程基础》:Direct3D初始化
- [DirectX游戏开发笔记之二] 最简单的DirectX
- DirectX的初始化
- 0、开始 DirectX 的游戏编程
- DirectX 游戏编程之开篇
- DirectX游戏编程学习(一)初始化Direct3D
- 菜鸟DirectX之DirectX初始化
- DirectX 游戏编程之游戏框架
- Echarts2 自定义悬浮框提示色
- PHP + Mysql 登录功能防止SQL注入的一个办法
- Kotlin 学习之类和继承
- app上架appstore流程
- 详解小程序事件对象中的参数
- 游戏编程之DirectX的修炼:三(DirectX的初始化:下)
- python 多线程join()
- SQL行列转换
- 数组去重
- [shell] tr用法
- 京东2016研发工程师编程题
- 继承类要实现接口父类的所有方法吗
- 微信小程序 the server responded with a status of 404 错误解决方法
- Div#432B Arpa and an exam about geometry