分享
 
 
 

用DirectDraw编写动画程序

王朝other·作者佚名  2006-12-17
窄屏简体版  字體: |||超大  

用DirectDraw编写动画程序

用DirectDraw编写动画程序 用DirectDraw编写动画程序

一.理论篇

说起DirectDraw也许大多数人还不知其为何物,但一提到DirectX恐怕每一个Computer Fan 和Game Fan都再耳熟不过了。(什么!你没听说过DirectX!?Oh,My god!来人哪,拉下去重责五十大板!)DirectX又叫Game SDK,它最大的特点是直接对硬件的抽象层(HAL)进行操作,利用这个特点可以制作出高性能的Windows游戏。具体信息请见http://www. microsoft.com/directx/default.asp。

DirectDraw就是DirectX5的6个组件之一。DirectX5的其它5个组件分别是:

Direct3D:提供了3D硬件接口。

DirectSound:立体声和3D声音效果,同时管理声卡的内存。

DirectPlay:支持开发多人网络游戏,并能处理游戏中网络之间的通信问题。

DirectInput:为大量的设备提供输入支持。

DirectSetup:自动安装DirectX驱动程序。

而DirectDraw则是DirectX的基石,DirectX的其它组件都是建立在它的基础之上的。DirectDraw使用页面切换的方法实现动画,它不仅可以访问系统内存,还可以访问显示内存,这是以往的Windows程序员所不能的。另外,我们利用DirectDraw还可以生成、移动、剪切、转换、合成图像数据,从而编写出各种“炫丽多彩”图形的应用程序。

介绍了这么一大堆DirectX的理论,你的头是不是已经有些大了呢?心中一定在嘀咕:“哼,原来是个江湖骗子,光说不练。”各位看官不要着急,不把这些基础理论搞懂就去看下面的程序,你肯定是“洋鬼子看戏——目瞪口呆”。

首先,让我们先了解一下DirectDraw的三个重要概念。

1.表面

在用DirectDraw编写程序时,我们先要创建若干个图形数据缓冲区,并把这些图形数据装入其中,再进行转换、拉伸、挎贝等操作,并且还可以显示这些缓冲区中的图形数据,这些缓冲区就称为表面。

表面可以分为几类。

主表面(primary surface)是用户在屏幕上可以看到的,它是显示内存的一部分。所有DirectDraw程序都有主表面,而且只有一个。它在DirectDraw表面对象之前就已经存在了,因此不能改变它的尺寸、格式和位置。主表面有一个很重要的特性——翻转。页面翻转用于程序中,可以产生相当平滑、不闪烁的动画。一个可以翻转的主表面实际上是两个表面,一个是可见的,一个是不可见的。不可见的表面称为后备缓冲区。当发生表面翻转时,后备缓冲区就成为可见的,而以前的可见表面则成为后备缓冲区。

还有一种表面叫离屏表面(off_screen surface),它是不能直接见到的。离屏表面作为存储缓冲区,有助于表面之间的互相切换,它的大小是可以改变的。

主表面和离屏表面都分为有调色板的和无调色板的这两类。像素深度为8位(256色)的表面称为有调色板的表面;而像素深度为16位(64K色)、24位(16M色)的像素表面称为无调色板的表面,它们存储实际的色彩值(RGB值)。在本文下面的程序中,我们使用24位表面即无调色板的表面。

2. Bltting

Bltting是用于复制图形的语言,可以将图像从一处拷贝到另一处。例如大家所熟悉的CDC类(设备描述表类)的BitBlt()就是具有这样功能的函数。在DirectDraw中,典型的blt操作是将离屏表面的内容拷贝到一个后备缓冲区,而一般的blt操作调用一个源表面和一个目标表面,把源表面的内容拷贝到目标表面中,不仅可以整体拷贝源表面,而且还可以拷贝源表面内的任何矩形区域到目标表面的任何位置。blt还支持透明拷贝,就是指表面中的某一像素在blt过程中可以不予以拷贝,而这个像素值是由色彩键码(DDCOLOR KEY )决定的。

DirectDraw中有三个支持blt的函数,它们是Blt()、BltBatch()、BltFast()。Blt( )用得最多,BltFast()的速度比Blt()要快,但功能却很有限,例如不支持拉伸、剪切等操作。

还有一个函数BltSurface(),它是DirectWin类的一个成员函数,Blt()、BltFast()更具有适应性,并且使用起来更加简单。例如,当我们把源表面拷贝到目标表面外时需要裁剪,而BltFast()不支持裁剪。这时我们使用BltSurface()函数,它在内部使用Blt()和 BltSurface()函数,并根据情况自动执行裁剪。

3.色彩键码

DirectDraw 可以把某种颜色或某个范围的颜色指定为一个颜色值,这个颜色值是由DDCOLORKEY结构即色彩键码说明的,DDCORLORKEY结构说明如下:

typedef struct _DDCOLORKEY{

DWORD dwColorSpaceLowValue; //颜色范围的低端

DWORD dwColorSpaceHighValue; //颜色范围的高端

} DDCOLORKEY;

当我们对表面进行拷贝操作时,表面中哪些像素不被拷贝是由色彩键码决定的。例如当DDCOLORKEY结构的两个分量都为零时,表面内所有置为零的像素都不能被拷贝。又例如,当表面是24位RGB模式时,若想指定RGB=(120,120,120)像素不被拷贝,则应该:

DDCOLORKEY ddck;

ddck.dwColorSpaceLowValue=RGB(120,120,120);

ddck.dwColorSpaceHighValue=RGB(120,120,120);

surf→SetColorKey(DDCKEY_SRCBLT,&ddck);

其中SetColorKey()函数是把色彩键码赋给表面surf。这样,在对表面surf的 blt操作期间RGB值为(120,120,120)的像素不能被拷贝。

二.实践篇

古语云:“君欲善其事,必先利其器”,在编写DirectDraw应用程序之前,我们先要准备好以下工具:

Windows95、Windows98或WindowsNT4.0

DirectX 驱动程序(最好是DirectX3.0以上版本)

DirectX SDK

Visual C++ 5.0

Direct SDK包括开发基于DirectX应用程序所需的全部文件,全部安装需要80几兆的硬盘空间。其实你只需安装必需的头文文件(.h文件)和库文件(.lib文件)就行了。

安装完DirectX SDK,需要通知Visual C++ DirectX SDK的路径。具体做法是:在VC的编译环境中,依次把Tools-Options-Directories中的Show Directories for一栏中的include files和library files中分别填入SDK的inc和lib目录,如图1所示。

再准备两幅bmp格式的位图,要求24位(16M色),其中background.bmp作背景,如图2;另一幅bird.bmp作为子画面,如图3。bird.bmp是由4幅小画面组成的,从左到右,从上到下,分别为动画的1至4帧。这样做的目的是避免过多位图文件带来的不必要的麻烦。还要注意一点的是子画面的背景要为黑色(RGB=(0,0,0)),因为在下面的程序中,色彩键码把黑色设为透明色。

好,一切准备就绪,让我们开始吧!

进入VC5的编程环境,File-New-Project,选择DirectDraw AppWizard,输入项目名Fly,按下Ok,以后每一步都按其缺省值即可,这样AppWizard就会自动创建一个项目Fly,属性如下:

APPLICATION STYLE

Full-Screen

SETTINGS

640x480

16-bit

CLASS NAMES

FlyApp

FlyWin

CONTENT

Bitmap

这时,按“Ctrl+F5”编译运行,你就会看到一个“三角形”在屏幕上撞来撞去。

在FlyWin.h中添加代码,如下所示:

#ifndef FLYWIN_H

#define FLYWIN_H

#include “DirectDrawWin.h”

class FlyWin : public DirectDrawWin

{

public:

FlyWin();

protected:

//{{AFX_MSG(FlyWin)

afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

afx_msg void OnTimer(UINT nIDEvent);

afx_msg void OnDestroy();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

private:

int SelectDriver();

int SelectInitialDisplayMode();

BOOL CreateCustomSurfaces();

void DrawScene();

void RestoreSurfaces();

private:

void CopySurface(LPDIRECTDRAWSURFACE ts,

LPDIRECTDRAWSURFACE ss,

int x,int y);

void SplitSurface(LPDIRECTDRAWSURFACE ts,

LPDIRECTDRAWSURFACE ss,

CRect& srcc,

int x,int y);

private:

LPDIRECTDRAWSURFACE bksurf, //定义指向背景画面表面的指针

flysurf, //指向总子画面表面的指针

storesurf,//指向存储背景区域表面的指针

flysurf1, //指向第一帧子画面表面的指针

flysurf2, //指向第二帧子画面表面的指针

flysurf3, //指向第三帧子画面表面的指针

flysurf4, //指向第四帧子画面表面的指针

cursurf; //指向当前移动的子画面表面的指针

//背景画面指background.bmp;总子画面指bird.bmp

int x,y,nPreX,nPreY;//定义子画面移动的坐标

int nIncX,nIncY;// 定义子画面移动的像素数

int nCount; //定义计数器

DWORD fw,fh; //定义总子画面的宽和高

};

#endif

其中,CopySurface()函数起保存被子画面覆盖掉的背景区域的作用。参数ts是指向目标表面的指针,ss是指向源表面的指针,x、y是源表面上需要拷贝的矩形区域左上角的坐标。

SplitSurface()函数用于分离一个大的总子画面表面为4帧小子画面表面。参数ts是指向目标表面的指针,ss是指向源表面的指针,srcc是源表面上需要选取的矩形区域,x、y要拷贝到目标表面上的左上角坐标。

其实当我们看了下面FlyWin.cpp中这两个函数的原型后,细心的朋友也许会发现,两个函数完全可以写成一个函数。而本文特意写成两个函数,为的是程序的清楚易读,不易引起混淆。

下一步在FlyWin.cpp中添加代码,如下所示:

#include “Headers.h”

#include “resource.h”

#include “DriverDialog.h”

#include “FlyWin.h”

#pragma comment (lib,“ddraw.lib”)

#pragma comment (lib,“dxguid.lib”)

const DWORD desiredwidth=640;

const DWORD desiredheight=480;

const DWORD desireddepth=16;

const int nMoveSpeed=100; //设置动画速度为100毫秒/帧

const int nMoveIncX=8; //设置子画面沿X轴移动8个像素

const int nMoveIncY=8; //设置子画面沿y轴移动8个像素

BEGIN_MESSAGE_MAP(FlyWin, DirectDrawWin)

//{{AFX_MSG_MAP(FlyWin)

ON_WM_KEYDOWN()

ON_WM_CREATE()

ON_WM_TIMER()

ON_WM_DESTROY()

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

FlyWin::FlyWin()

{

x=nPreX=300; //子画面初始位置为(300,150)

y=nPreY=150;

nIncX=nMoveIncX;

nIncY=nMoveIncY;

nCount=1;

//设置计数器

}

BOOL FlyWin::CreateCustomSurfaces()

{

// create your surfaces here...

//创建背景表面,并由指针bksurf指向这个表面

bksurf=CreateSurface(“background.bmp”);

//创建一个总子画面(由4帧子画面组成)的表面

//由指针flysurf指向这个表面.

flysurf=CreateSurface(“bird.bmp”);

//得到总子画面表面的宽(fw),高(fh)

GetSurfaceDimensions(flysurf,fw,fh);

//创建4帧宽为fw/2,高为fh/2的子画面的表面,分别由

//指针flysurf1,flysurf2,flysurf3,flysurf4指向它们。

//现在这4个子表面还是空的。

flysurf1=CreateSurface(fw/2,fh/2);

flysurf2=CreateSurface(fw/2,fh/2);

flysurf3=CreateSurface(fw/2,fh/2);

flysurf4=CreateSurface(fw/2,fh/2);

//清除4帧子画面表面的内容,使表面内所有像素为0,

//即表面是透明的。

ClearSurface(flysurf1,0);

ClearSurface(flysurf2,0);

ClearSurface(flysurf3,0);

ClearSurface(flysurf4,0);

//定义色彩键码,指明黑色(RGB=(0,0,0))为透明色

//即在blt操作期间不拷贝黑色像素。

DDCOLORKEY ddck;

ddck.dwColorSpaceLowValue=0;

ddck.dwColorSpaceHighValue=0;

//将色彩键码赋给背景表面,总子画面表面和4个子画面表面

bksurf→SetColorKey(DDCKEY_SRCBLT,&ddck);

flysurf→SetColorKey(DDCKEY_SRCBLT,&ddck);

flysurf1→SetColorKey(DDCKEY_SRCBLT,&ddck);

flysurf2→SetColorKey(DDCKEY_SRCBLT,&ddck);

flysurf3→SetColorKey(DDCKEY_SRCBLT,&ddck);

flysurf4→SetColorKey(DDCKEY_SRCBLT,&ddck);

//定义总子画面表面上的4个矩形区域,即为4个子画面表面区域

CRect r1(0,0,fw/2-1,fh/2-1);

CRect r2(fw/2,0,fw-1,fh/2-1);

CRect r3(0,fh/2,fw/2-1,fh-1);

CRect r4(fw/2,fh/2,fw-1,fh-1);

//把总子画面表面上的4个矩形区域分别拷贝到4个子画面表面

//即实现分离总子画面表面

SplitSurface(flysurf1,flysurf,r1,0,0);

SplitSurface(flysurf2,flysurf,r2,0,0);

SplitSurface(flysurf3,flysurf,r3,0,0);

SplitSurface(flysurf4,flysurf,r4,0,0);

//创建一个存储表面,由指针storesurf指向它

//用以存储子画面移动时所覆盖的背景区域

storesurf=CreateSurface(fw/2,fh/2);

CopySurface(storesurf,bksurf,x,y);

//初始化当前子画面表面

cursurf=flysurf1;

return TRUE;

}

void FlyWin::DrawScene()

{

//绘制背景表面和第一帧子画面表面

//把背景表面拷贝到后备缓冲区

BltSurface(backsurf,bksurf,0,0,TRUE);

//把当前的子画面表面拷到后备缓冲区

BltSurface(backsurf,cursurf,x,y,TRUE);

//页面翻转,使后备缓冲区的内容可见

primsurf→Flip(0,DDFLIP_WAIT);

//再次把背景表面拷贝到后备缓冲区

BltSurface(backsurf,bksurf,0,0,TRUE);

}

void FlyWin::RestoreSurfaces()

{

// reclain lost surfaces with the DirectDrawSurface Restore() function

// depending on the surface's function, it may be necessary to restore

// surface content as well

if(bksurf→IsLost()) //如果bksurf

丢失

{

bksurf→Restore(); //恢复内存

LoadSurface(bksurf,“background.bmp”); //恢复表面内容

}

if(flysurf→IsLost())//如果flysurf丢失

{

flysurf→Restore(); //恢复内存

LoadSurface(flysurf,“bird.bmp”); //恢复表面内容

}

}

int FlyWin::SelectDriver()

{

int numdrivers=GetNumDrivers();

if (numdrivers==1)

return 0;

CArray drivers;

for (int i=0;i

{

LPSTR desc, name;

GetDriverInfo( i, 0, &desc, &name );

drivers.Add(desc);

}

DriverDialog dialog;

dialog.SetContents( &drivers );

if (dialog.DoModal()!=IDOK)

return -1;

return dialog.GetSelection();

}

int FlyWin::SelectInitialDisplayMode()

{

DWORD curdepth=GetDisplayDepth();

int i, nummodes=GetNumDisplayModes();

DWORD w,h,d;

if (curdepth!=desireddepth)

ddraw2→SetDisplayMode( 640, 480, curdepth, 0, 0 );

for (i=0;i

{

GetDisplayModeDimensions( i, w, h, d );

if (w==desiredwidth && h==desiredheight && d==desireddepth)

return i;

}

for (i=0;i

{

GetDisplayModeDimensions( i, w, h, d );

if (d==desireddepth)

return i;

}

return 0;

}

void FlyWin::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)

{

if (nChar==VK_ESCAPE) //按ESC退出

PostMessage( WM_CLOSE );

DirectDrawWin::OnKeyDown(nChar, nRepCnt, nFlags);

}

int FlyWin::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (DirectDrawWin::OnCreate(lpCreateStruct) == -1)

return -1;

// TODO: Add your specialized creation code here

SetTimer(1,nMoveSpeed,NULL); //设置定时器

return 0;

}

void FlyWin::OnTimer(UINT nIDEvent)

{

// TODO: Add your message handler code here and/or call default

int nWhich=nCount%4+1;

nCount++;

//判断当前表面为4个子表面中的哪一个

switch(nWhich)

{

case 1:

cursurf=flysurf1;

break;

case 2:

cursurf=flysurf2;

break;

case 3:

cursurf=flysurf3;

break;

case 4:

cursurf=flysurf4;

break;

default:

break;

}

//处理移动的子画面

x+=nIncX;

y+=nIncY;

//如子画面移动到屏幕下方或上方,应碰撞返回

if(y>=(int)(480-fh/2)||y<=0)

{

nIncY=-nIncY;

y+=nIncY;

}

//如子画面移动到屏幕右方或左方,应碰撞返回

if(x>=(int)(640-fw/2)||x<=0)

{

nIncX=-nIncX;

x+=nIncX;

}

//把当前表面拷贝到后备缓冲区

BltSurface(backsurf,cursurf,x,y,TRUE);

//执行页面翻转,使后备缓冲区的内容可见

primsurf&rarr;Flip( 0, DDFLIP_WAIT );

//把备份的背景区域拷贝到后备缓冲区,以便恢复曾被

//覆盖的背景

BltSurface(backsurf,storesurf,nPreX,nPreY);

//存储下一帧的子画面移动时所覆盖的背景区域于storesurf

CopySurface(storesurf,bksurf,x,y);

nPrex=x;

nPrey=y;

DirectDrawWin::OnTimer(nIDEvent);

}

void FlyWin::OnDestroy()

{

DirectDrawWin::OnDestroy();

// TODO: Add your message handler code here

KillTimer(1); //取消定时器

}

void FlyWin::CopySurface(LPDIRECTDRAWSURFACE ts,

LPDIRECTDRAWSURFACE ss,

int x,int y)

{

if(!ts || !ss ) return;

DDSURFACEDESC tdesc,sdesc;

//初始化sdesc,tdesc为零

ZeroMemory(&sdesc,sizeof(sdesc));

ZeroMemory(&tdesc,sizeof(tdesc));

sdesc.dwSize=sizeof(sdesc);

tdesc.dwSize=sizeof(tdesc);

//得到源表面ss,目标表面ts的描述信息

HRESULT r=ss&rarr;GetSurfaceDesc(&sdesc);

if(r!=DD_OK) return;

r=ts&rarr;GetSurfaceDesc(&tdesc);

if(r!=DD_OK) return;

//定义源表面上的矩形区域,大小大等于目标表面的面积

CRect rc(x,y,x+tdesc.dwWidth,y+tdesc.dwHeight);

//超界处理

if(x+tdesc.dwWidth>(int)sdesc.dwWidth) return;

if(y+tdesc.dwHeight>(int)sdesc.dwHeight) return;

//拷贝源表面ss到目标表面ts

r=ts&rarr;BltFast(0,0,ss,&rc, DDBLTFAST_SRCCOLORKEY|DDBLTFAST_WAIT);

if(r!=DD_OK) return;

}

void FlyWin::SplitSurface(LPDIRECTDRAWSURFACE ts,

LPDIRECTDRAWSURFACE ss,

CRect& srcc,

int x,

int y)

{

if(!ts||!ss) return;

DDSURFACEDESC tdesc,sdesc;

ZeroMemory(&sdesc,sizeof(sdesc));

ZeroMemory(&tdesc,sizeof(tdesc));

sdesc.dwSize=sizeof(sdesc);

tdesc.dwSize=sizeof(tdesc);

HRESULT r=ts&rarr;GetSurfaceDesc(&tdesc);

if(r!=DD_OK) return;

r=ss&rarr;GetSurfaceDesc(&sdesc);

if(r!=DD_OK) return;

//超界处理

if(x+srcc.Width()>(int)tdesc.dwWidth) return;

if(y+srcc.Height()>(int)tdesc.dwHeight) return;

//把源表面srcc的矩形区域拷贝到目标表面左上角坐标为(x,y)处

r=ts&rarr;BltFast(x,y,ss,&srcc,

DDBLTFAST_SRCCOLORKEY|DDBLTFAST_WAIT);

if(r!=DD_OK) return;

}

DDSURFACEDESC为表面描述结构,用以保存表面的所有信息,它的成员变量dwSize为结构的大小而且必须要被初始化;dwHeight为表面的高度;dwWidth为表面的宽度。用函数GetSurfaceDesc()可以获取上述描述信息。

DirectDraw首先调用CreateCustomSurface()函数,我们将在这个函数中创建和准备程序所用到的表面。DrawSence()函数负责更新屏幕,因此我们用它来显示背景画面。我们主要的工作都在OnTimer()函数里,首先判断当前子画面为4帧子画面中的哪一帧,然后进行页面翻转、显示子画面和复制背景区域等操作。

BltFast()函数用于表面之间的拷贝,它的前两个参数指出blt的目标表面位置,第三个参数是指向源表面的指针,第四个参数是源表面上被拷贝的矩形区域,最后的参数是DDBLTFAST_SRCCOLORKEY和DDBLTFAST_WAIT,第一个标志用来激活源表面的色彩键码;第二个标志表示只有结束blt操作,BltFast()函数才返回。

Flip()函数用于页面翻转操作,使backsurf表面的内容可见。它有两个参数。第一个参数是个表面指针,它在用来翻转有多个后备缓冲区的表面时才有用;第二个参数是DDFLIP_WA

IT标志,用来指示函数只有在表面完成之后才能返回。

当程序运行时,一个表面所在的内存可能会被另一个应用程序所占用,这形成了&ldquo;表面丢失&rdquo;。这时DirectDraw会调用RestoreSurface()函数来恢复丢失的表面。但是,如果你的显卡不是老掉牙了,这种情况一般是不会发生的。所以这里我只对两个主要表面bksurf、flysurf进行了恢复处理,而不再管其它的表面。

另外,函数SelectDriver()和SelectInitialDisplayMode()是AppWizard自动创建的。SelectDriver()选择一个显示驱动程序,SelectInitialDisplayMode()选择一个初始显示模式。

三、结束语

话已至此,你对DirectDraw编程应有一个大致的了解了吧!用DirectDraw编程其实很简单,说白了其实就是一句话:&ldquo;几个表面之间拷来拷去&rdquo;。只不过这其间可是大有文章可作的哟!(成都张巍)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有