日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用Managed DirectX编写游戏

發布時間:2024/4/17 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Managed DirectX编写游戏 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉自 ?http://dev.gameres.com/Program/Visual/DirectX/ManagedDirectX9Game_01.htm

?? ? ??http://dev.gameres.com/Program/Visual/DirectX/ManagedDirectX9Game_02.htm

?? ? ??http://dev.gameres.com/Program/Visual/DirectX/ManagedDirectX9Game_03.htm

?? ? ??http://dev.gameres.com/Program/Visual/DirectX/ManagedDirectX9Game_04.htm

3D游戲編程,顯然,這是一個很復雜的主題。首先,讓我們來學習一些編寫任何游戲都會用到的基礎知識。把你的創意轉變為真實的游戲是一種很有趣的經歷。把你的想法雕琢為一個人們可以獲得樂趣的游戲,是每個開發者的目標。

  所有的一切都來源于創意,游戲也是如此。游戲創意可以來自于生活中所有地方。也許你看到了另一個很好玩的游戲,但總覺得稍做修改它將會更好玩。也許你昨晚的夢就能創造一個完美的游戲。無論你的靈感來自于哪里,都必須先有了靈感再編寫游戲。

  當然,對于我們即將要編寫的游戲來說,你將使用我的創意。選擇這幾個游戲是有很多原因的,但主要原因還是和難度有關。無論如何,我們將會盡可能的覆蓋所有你可能遇到的游戲類型。

  第一種類型是益智游戲(puzzle game)。經典的Tetris(俄羅斯方塊)就是這類游戲的代表。幾乎所有學習游戲編程的人都克隆過這個游戲,因此,跳過它,編寫一些特別一點的東西。游戲中將有一個可以在板子上跳動的角色。板子由一系列的立方體構成,類似一個棋盤。每個立方體都有一種特定的顏色,當角色跳到這些立方體時,他將會變為另一種指定的顏色。當所有方塊都和這一關制定的顏色相同時,這一關就算贏了。


設計細節
  當游戲創意浮現出來之后,就應該花一些時間把它寫為游戲方案。并確保對所有可能遇到的問題都有解決方法。對于這個游戲來說,我們列出了一下條目作為方案的細節。

*名字叫做Blockers的益智游戲
*單人游戲
*全3D環境
*基于完成每一關的時間來計算得分
*每一關都由一系列相連的立方體構成,就像建立在棋盤上一樣
*每個立方體都是單獨的實心顏色
*當每一個立方體的顏色都和本關預定的結束顏色相同時,管卡結束
*每個立方體都有一個顏色列表,這個列表最多包含6種顏色,最少2種
*玩家通過調到另一個立方體來移動,同時,所跳到的立方體變為列表中的下一個顏色
*游戲開始時,顏色列表中只有2種顏色
*高級關卡通過增加顏色列表中的顏色來增減難度
*如果玩家通關了,難度將回到一開始的狀態
*如果玩家不能在最大的制定時間完成本關,則游戲結束。

  這是否是一份毫無遺漏的文檔了呢?也許不是吧。但他回答了大部分我們將如何編寫游戲的問題。明白在開發之前,你不可能把所有性都設計好了是很重要的,隨著開發和思考的深入,游戲將會需要越來越多的特性,但是,無論如何都不能貿然在沒有考慮到底想完成些什么特性時就開始編碼,這會導致更大的混亂。

  有了方案,就可以開始設計游戲中的對象模型了。你將有一個神奇的游戲引擎,由他來維護玩家信息、當前關卡、以及渲染的設備。渲染的設備用來完成所有美妙的繪圖工作。玩家總是需要一些直觀的表示,渲染設備會處理好這個工作。

  大多數的游戲都需要當前關卡的信息。當前的時間是最重要的,因為最終的得分和是否結束游戲都是由他來決定,因此,當前的管卡必須可以訪問時間。

  實際的關卡將被儲為文件,保存在程序的media目錄中。當前的關卡必須是所存在關卡中的一個,所以關卡必須能訪問這些文件。實際上,關卡只要追蹤組成每一關的立方體列表就可以了。每一關最少有兩個立方體,當然高級別的管卡中將添加大量立方體。

  雖然編寫游戲不會很容易,但你看,要達到目標,所創建的對象并不多。另外,為了讓游戲有具有可玩性,只要讓高級關卡不會難到使玩家有挫敗感就可以了。沒有比讓玩家苦惱的游戲更糟的東西。如果游戲不好玩,那么就不會有人玩它,就不會成為成功的游戲。


為什么需要3D游戲
  你需要認識到這個游戲(實際上是所有游戲)根本不“需要”作為全三維的場景來渲染。考慮到所有顯示器都是平面的,再好看的3D場景都要映射為2D的圖片。完全可以把游戲中所有可能的場景設置為一組2D的精靈(sprite),當然,這樣需要更多的美工操作。

  假設你安裝了DirectX SDK的任何版本,打開DirectX Sample Broeser,確保只選擇了左邊的C#選項,點擊左上的Direct3D標題,找到Empty Project項目,并安裝它。之后,在VS中打開項目。


  這樣我們就創建了一個“空白”項目。(實際上它還繪制了一些UI控件,但先忽略他們)。但是,這里還沒有任何3D 的物體,這一部分的內容是告訴你為什么我們需要編寫3D游戲,因此,來繪制一些東西吧。

添加幾行代碼來顯示一個緩慢旋轉的茶壺。添加如下變量

private Mesh teapotMesh = null;
private Material teapotMaterial;

接下來創建茶壺和材質,在OnCreateDevice方法的最后添加如下代碼:

teapotMesh = Mesh.Teapot(e.Device);
teapotMaterial = new Material();
teapotMaterial.DiffuseColor = new Colorvalue(1.0f,1.0f,1.0f,1.0f);

為了讓茶壺更好看,接下來添加燈光,在OnResetDevice方法中添加代碼:

e.Device.Lights[0].DiffuseColor = new Colorvalue(1.0f,1.0f,1.0f,1.0f);
e.Device.Lights[0].Direction = new Vector3(0,-1,0);
e.Device.Lights[0].Type = LightType.Directional;
e.Device.Lights[0].Enabled = true;

準備工作到這里就結束了。找到OnFrameRender方法,在BeginScene方法之后添加代碼:

e.Device.Transform.View = camera.ViewMatrix;
e.Device.Transform.Projection = camera.ProjectionMatrix;
e.Device.Transform.World = Matrix.RotationX((float)appTime);
e.Device.Material = teapotMaterial;
teapotMesh.DrawSubset(0);

  運行程序,可以看到一個渲染的茶壺。茶壺在3D世界里有一段悠久的歷史。在那個建模軟件還不發達的時代,獲得一個真實的3D模型相當困難,而茶壺就是最早供人們免費使用的模型之一。另外,茶壺有許多不錯的特性適合于用來做測試:它有圓滑的曲面,可以顯示光影漸變的效果,并且容易識別。


  需要注意的是,雖然茶壺是這個程序里唯一的模型,但我們并沒有使用任何額外資源來省城模型,而是使用了Mesh類的一個靜態方法。我們使用了最少的資源得到了一個不錯的茶壺。

  對于3D版本來說,我們可以從任何角度觀察茶壺。但是假設我們需要在2D世界里實現同樣的效果呢?我們需要為茶壺旋轉的每一個角度保留一張位圖(就是360張圖),如果還想讓茶壺沿任意軸旋轉,那么就需要4664600張不同的位圖。那么對于虛幻競技場這樣的游戲來說,就算他什么都不干,只繪制2D精靈,也至少需要一張DVD來保存數據,同時還要有一大群藝術家花上幾年時間來創建這些東西。

  如果你的藝術家可以創建高質量的3D模型,顯然,你就可以使用相對很少的資源更加自由的創建場景 。可以用許多不同方法來渲染一個簡單的模型,比如不同的燈光、縮放比例、位置、角度等等,而這一切使用2D來實現都是不切實際的。

  3D程序的這種自由性需要強大的處理能力來實現,為了滿足這種處理能力,甚至形成了一整個產業。nVidia和ATI就是這個產業中的領導者。現代的圖形卡有了飛速的創新,甚至在處理能力上超越了CPU。

  現在的圖形卡(支持DirectX9的圖形卡意味著他至少支持shader model2.0)每秒能渲染數百萬三角形。不要擔心,我們稍候會討論關于著色器(shader)的內容。如果你想知道為什么需要那么多三角形,那么你需要知道三角形是構成3D模型的基本多邊型。以下就是線框模式之下的茶壺模型。


  你看,整個茶壺都是由三角形構成。為什么使用三角形呢?因為它是唯一能夠保證共面的閉合多邊形,這樣,在渲染時更容易計算。任何3D圖形都可以用三角形模擬出來。

  至此,我們的游戲需要是3D的嗎?當然不需要,你可以完全使用精靈來編寫游戲,但這還有什么樂趣呢?大部分游戲編成的書籍都只涉及2D部分,但3D世界才是真正讓游戲精彩的地方。我們即將開始三維之旅。


編寫文檔

  接下來,我們將來到編寫游戲最重要的一個環節,編寫文檔。在一行代碼都沒有編寫的情況下,我實在無法強迫你花一點時間來考慮可能遇到的問題。事實上,我遇到的所有年青游戲開發者在開發第一個游戲時都是直接開始編碼。慢慢你才會明白快速的開始編碼會帶來很多麻煩。對于這個游戲,需要解決什么問題?當然,需要一個游戲引擎作為游戲的大腦。一個玩家對象,一個用于渲染的設備,以及一個維護關卡信息的方法。最常見的現實開發文檔就是UML。如下圖所示:


  如果你熟悉UML,那么各熟悉之間的關系應該是很明顯的。如果不熟悉,就需要花點時間來看看這篇文檔在說什么。首先,他把問題分成了組成游戲的幾個邏輯組件。這里,四個對象分別是游戲引擎、玩家、關卡、方塊列表,以及單獨的方塊。每個對象的屬性和方法都列在了對象盒里,這樣就可以快速的對每個對象有一個總攬。

  把圖放到一邊,我們到底需要做些什么呢?顯然,需要有一個控制中心來控制所有操作。在這里,就是游戲引擎。注意看UML圖,游戲引擎包含了用于渲染的device(從InitializeGraphics方法可以看出來)。游戲引擎需要知道以下東西:

*玩家對象
*當前關卡
*游戲是否結束
*如果是,那么玩家是否贏了當前關卡
*如果是,那么玩家是由通關了

  游戲引擎還需要保存其他信息,比如渲染的設備,以及一些其他對象,但這些都是私有方法,因此,不現示在UML中。接下來的對象是玩家,這是一個相當簡單的類,我們只需要知道它的位置,同時,讓他可以渲染它自己就可以了。在我們的引擎里,玩家更像一個抽象的概念而不是實際對象。這個對象主要是為了控制任何現實玩家。

  游戲引擎所需的其它信息都來自于levels對象。實際上這也是一個很簡單的對象,他也只包含了幾個其他對象,比較重要的就是blocks集合。每一個方塊包含了在不同層次如何控制它自己的信息,包括可能的顏色列表,以及是否需要翻顏色。

  好了,有了這最最簡單的文檔,可以開始編碼了。

?

理解框架

  首先創建工程,添加對DirectX程序集的引用。接下來,把sample framework添加到工程中。我們把這些文件放到一個單獨的文件夾中,在解決方案管理器中點擊右鍵---添加---新建文件夾,并把它命名為framework。右鍵點擊新創建的文件夾,選擇添加現有項,導航到SDK的\Samples\Managed\Common目錄下,把每一個文件添加到項目中。

  好了,現在回到我們剛才創建的Form1.cs文件中來,可以看到大部分自動生成的代碼都是用來創建Windows Form應用程序的。因此刪除所有代碼,并添加如下代碼:

using System;
using System.Configuration;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;

public class GameEngine : IDeviceCreation
{
??? /// <summary>
??? /// Entry point to the program. Initializes everything and goes into a
??? /// message processing loop. Idle time is used to render the scene.
??? /// </summary>
??? static int Main()
??? {
??????? using(Framework sampleFramework = new Framework())
??????? {
??????????? return sampleFramework.ExitCode;
??????? }
??? }
}

  這里有三個需要注意的地方 。首先,我們只留下了一個修改過的main方法。由于其他代碼都是窗體設計器為Windows Form程序生成的,所以完全可以刪除它們。其次,代碼現在還不能通過編譯,應為GameEngine類還有兩個接口沒有實現。第三,這段代碼實際上什么也沒做。

  首先我們來實現IDeviceCreation接口,你將通過他來控制枚舉和創建device。在這里,枚舉的含義包括檢查目標機器上有幾塊顯卡、幾個顯示器、可以支持多少種顯示模式、控制刷新率等等。

public bool IsDeviceAcceptable(Caps caps, Format adapterFormat,Format backBufferFormat, bool windowed)
{
??? if (!Manager.CheckDeviceFormat(caps.AdapterOrdinal, caps.DeviceType,adapterFormat, Usage.QueryPostPixelShaderBlending, ResourceType.Textures, backBufferFormat))
??????? return false;
??? if (caps.MaxActiveLights == 0)
??????? return false;
??? return true;
}

public void ModifyDeviceSettings(DeviceSettings settings, Caps caps)
{
??? if ( (caps.DeviceCaps.SupportsPureDevice) && ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) != 0 ) )
??????? settings.BehaviorFlags |= CreateFlags.PureDevice;
}

  第一個方法將在device初始化時調用,用來檢查device最低能支持什么特性(capability)。當sample framework在系統上枚舉設備時,他會對所找到的每一種可能組合調用這個方法。注意看這個方法是如何決定返回值的。他的第一個參數包含了大量關于制定設備的信息,可以幫你決定它是否是你希望創建的device類型。接下來的參數一個是關于后備緩沖的,另一個則是關于設備格式。最后一個參數則是檢查是否支持窗口模式。雖然大多數游戲都是運行在全屏模式,但在窗口模式之下調試程序會方便一些。這個方法默認情況下將返回true,但他還做了兩個特別的檢查。首先,檢查它是否支持alpha混合(創建游戲的用戶界面將用到他)。其次,檢查是否支持動態燈光----沒有燈光物體看起來會很單調而且不真實,所以至少使用一個燈光。

  再來看第二個方法:在創建device之前調用它,來修改創建設備的參數。Setting參數包含了框架為device所制定的設置,你可以自由的修改這些設置。需要特別注意的是sample framework不會驗證這些設置的有效性,因此,你需要自己來驗證。

  在繼續之前,還有一件事情要做。由于sample framework包含一些unsafe的代碼塊,因此,必須允許工程中包含不安全代碼:


現在,可以使用框架來枚舉設備了。首先,為GameEngine類添加一個構造函數,把從main方法中創建的sample framework實例作為參數:

private Framework sampleFramework = null;
public GameEngine(Framework f)
{
??? // Store framework
??? sampleFramework = f;
}

  在調用了sample framework之后,他所做的第一件事就是枚舉系統設備。在工程中,打開之前添加的dxmutenum.cs文件。這個文件包含了枚舉設備的所有代碼。由于知道如何以及為什么枚舉設備是很重要的,我們來仔細看一下這些代碼。

  首先注意到Enumeration是不能被實例化的,塔頂每一個方法和成員也都是靜態的。因為就目前來說,你的硬件圖形設備在運行時不太可以改變,所以枚舉的過程在程序一開始運行一次就可以了。

  大多數的枚舉過程都是在創建device之前,通過sample framework調用Enumerate方法開始的。這個方法所接受的唯一參數,就是我們至今為止在GameEngine類中實現的接口。在枚舉設備狀態組合的時候,需要調用IsDeviceAcceptable方法來判斷這個設備狀態是否應該添加到正確的設備列表中。那么到底是如何來枚舉設備的呢?實際上大多數功能都是通過Manager類來完成的。如果你熟悉普通的DirectX API,那么這個類實際上就是COM接口Idirect3D9的映射。(注:枚舉設備的過程這里不再講解,請看我以前翻譯過的文章)

把所有符合框架要求的顯示模式都保存到一個列表中,最后,通過實現Icomparer接口對他們進行排序。

public class DisplayModeSorter : IComparer
{
??? public int Compare(object x, object y)
??? {
??????? DisplayMode d1 = (DisplayMode)x;
??????? DisplayMode d2 = (DisplayMode)y;
??????? if (d1.Width > d2.Width)
??????????? return +1;
??????? if (d1.Width < d2.Width)
??????????? return -1;
??????? if (d1.Height > d2.Height)
??????????? return +1;
??????? if (d1.Height < d2.Height)
??????????? return -1;
??????? if (d1.Format > d2.Format)
??????????? return +1;
??????? if (d1.Format < d2.Format)
??????????? return -1;
??????? if (d1.RefreshRate > d2.RefreshRate)
??????????? return +1;
??????? if (d1.RefreshRate < d2.RefreshRate)
??????????? return -1;
??????? return 0;
??? }
}

這里的算法很簡單,大家自己看吧。保存了可用的顯示模式之后,調用EnumerateDevices方法。

private static void EnumerateDevices(EnumAdapterInformation adapterInfo, ArrayList adapterFormatList)
{
??? // Ignore any exceptions while looking for these device types
??? DirectXException.IgnoreExceptions();
??? // Enumerate each Direct3D device type
??? for(uint i = 0; i < deviceTypeArray.Length; i++)
??? {
??????? // Create a new device information object
??????? EnumDeviceInformation deviceInfo = new EnumDeviceInformation();

??????? // Store the type
??????? deviceInfo.DeviceType = deviceTypeArray[i];

??????? // Try to get the capabilities
??????? deviceInfo.Caps = Manager.GetDeviceCaps((int)adapterInfo.AdapterOrdinal, deviceInfo.DeviceType);

??????? // Get information about each device combination on this device
??????? EnumerateDeviceCombos( adapterInfo, deviceInfo, adapterFormatList);

??????? // Do we have any device combinations?
??????? if (deviceInfo.deviceSettingsList.Count > 0)
??????? {
??????????? // Yes, add it
??????????? adapterInfo.deviceInfoList.Add(deviceInfo);
??????? }
??? }
??? // Turn exception handling back on
??? DirectXException.EnableExceptions();
}

  觀察一下這段代碼,你應該注意到并且記住兩件事。猜猜是什么?如果答案是DirectXException類方法調用,那么恭喜,答對了。 DirectXException.IgnoreExceptions();方法關閉了Managed DirectX程序集中所有異常拋出。你可能會問這樣做有什么好處,答案是性能的提升。捕捉和拋出異常是系統花費很大的操作,而這段代碼有可能導致多個異常的拋出。你只希望快速的完成枚舉,因此,暫時忽略所有異常,在這段代碼結束的時候,再打開異常機制。雖然這里的代碼很簡單,但你可能會問問什么會導致異常。

  很高興你問到了這個問題,那么我們就來仔細討論一下吧。最常見的問題就是你的設備不支持DirectX 9。有可能你沒有更新驅動程序,或者當前的驅動程序沒有正確安裝。也有可能是你的顯卡太老了無法使用DirectX 9。通常PCI接口的顯卡都不能很好的支持DirectX 9了。

  這段代碼嘗試獲得關于顯卡能力的信息,并為適配器枚舉合適的組合。可用的設備類型有以下幾種:Hardware, Reference, software。

  假設枚舉時找到了合適的設備設置組合,就把他保存到一個列表中。Enumeration類為之后創建device保存了一些列表,觀察EnumerateDeviceCombos方法。注意,如果IsDeviceAcceptable方法返回false,那么就忽略這種設備組合類型。

?

理解sample framework之事件處理

Framework類是sample framework中最重要的類,完成了創建窗體,初始化設備,創建命令行,事件處理(render loop)以及調節各種參數的任務。Framework類包含在dxmut.cs文件中。其中,比較特別的就是事件處理模型(或render loop)。

  為了獲得高性能的渲染以及事件處理機制,framework類在初始化的方法中使用Device.IsUsingEventHandlers = false;關閉了事件處理模型。我們先來看看為什么默認的事件處理機制會導致性能的損失。默認情況下,Managed DirectX中的類在每創建一個資源時,都會為device訂閱一些必然的事件。在最簡單的情況下,每個資源(比如紋理或頂點緩沖)將會訂閱Disposing事件,以及其他一些諸如DeviceLost和DeviceReset的事件。這個步驟在整個對象的生存期都會發生。但是為什么我們在程序中不需要這種行為呢??

主要原因就是需要對這種行為付出一定代價,有些情況下,代價還會很大。我們舉個例子來說明這一點。看看下面這段簡單的偽代碼:

SomeResource res = new SomeResource(device);
device.Render(res);

  這段代碼看起來幾乎是“無害”的。只是創建了一個資源,并且渲染它。當這個對象不再使用時,垃圾回收器應該會智能的清除它。但是,這個想法是完全錯誤的。當創建新資源時,至少需要對device訂閱一個事件,允許device正確的清除它。這種訂閱實際上是一把“雙刃劍”。

  首先,訂閱事件時需要分配(allocation)EventHander對象完成實際的訂閱工作。雖然這種分配的代價很小,但是,我們稍候就會看到,就算是很小的分配也會迅速膨脹。第二,訂閱事件之后,資源和設備之間就有了一個硬連接(hard link)。因此,在垃圾回收器的眼里,這個對象在device的生存期里仍然處于使用狀態,并且沒有取消事件的訂約。設想一下,這代碼如果在渲染每幀的時候都運行一次;再設想一下,你的程序每分鐘需要進行上千次渲染,并且程序已經運行了兩分鐘。結果,你創建了120000個在device生存期間不會被回收的對象,以及120000個事件句柄。所創建的這些對象不但會迅速消耗內存,而且會導致額外的垃圾回收,嚴重影響程序性能。如果你的資源都位于顯存中,那么很快就會耗盡顯存。

  這里,我們還沒有考慮當最后釋放設備時可能發生的情況。在前面的例子中,當釋放device時,首先觸發Disposing事件,此時,有120000個監聽者訂約了這個事件。你是否已經考慮到,調用這個巨大的事件句柄列表會將花費很多時間?實際上這將會花去幾分鐘時間,并且讓用戶認為程序已經處于死鎖狀態。

  因此,最好只在最簡單的Direct3D程序中使用Managed Direct3D內建的事件處理機制。在任何需要考慮內存容量和性能的應用中(比如游戲),都必須避免這些處理過程。

  接下來,我們來看看framework中是如何實現事件處理模型的。實際上,SDK中的事件處理模型也是幾經修改,現在使用的方法最早由Tom Mille 在他的bolg上貼出了具體的實現:

public void MainLoop()
{
??? // Hook the application's idle event
??? System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);
??? System.Windows.Forms.Application.Run(myForm);
}

private void OnApplicationIdle(object sender, EventArgs e)
{
??? while (AppStillIdle)
??? {
??????? // Render a frame during idle time (no messages are waiting)
??????? UpdateEnvironment();
??????? Render3DEnvironment();
??? }
}

private bool AppStillIdle
{
??? get
??? {
??????? NativeMethods.Message msg;
??????? return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
??? }
}

And the declarations for those two native methods members:

[StructLayout(LayoutKind.Sequential)]
public struct Message
{
??? public IntPtr hWnd;
??? public WindowMessage msg;
??? public IntPtr wParam;
??? public IntPtr lParam;
??? public uint time;
??? public System.Drawing.Point p;
}

[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

  這里,通過平臺調用,使用了一些Win32 API。首先,在main方法中訂閱了Application.Idle事件。在程序處理完了所有消息(如果不熟悉消息,那么可以把消息理解為系統定義的一個32位的值,他唯一的定義了一個事件,向Windows發出一個通知,告訴應用程序某個事情發生了。例如,單擊鼠標、改變窗口尺寸、按下鍵盤上的一個鍵都會使Windows發送一個消息給應用程序。)之后,將會觸發Application.Idle事件。我們的目標是讓程序盡可能快,盡可能多的處理消息,同時不打斷wendows消息的輸入。

  在OnApplicationIdle事件處理程序中,使用的了簡單的Win32 API PeekMessage來檢查程序是否有任何未處理的消息。這里使用while循環的原因是保證在處理完所有消息,同時消息隊列還為空時,只觸發一次Application.Idle事件。所以,我沒一直循環,直到有新的消息,然后,跳出循環。普通的.Net WinForm窗體消息句柄將會選擇未處理的消息。

  接下來,我們將使用框架,來顯示一些物體了(源碼請參考SDK中的empty project)。由于框架已經為我們完成了以上工作。我們只需要選擇訂閱那些事件就可以了。Sample framework通過這些事件通知應用程序關于改變設備、用戶輸入以及各種窗口消息。這些事件是可選的,但是,如果你沒有設置,那么框架就不會為你處理相應的事件。在main方法中,創建了GameEngine對象之后,添加代碼:

sampleFramework.Disposing += new EventHandler(blockerEngine.OnDestroyDevice);
sampleFramework.DeviceLost += new EventHandler(blockerEngine.OnLoseDevice);
sampleFramework.DeviceCreated += new DeviceEventHandler(blockerEngine.CreateDevice);
sampleFramework.DeviceReset += new DeviceEventHandler(blockerEngine.OnResetDevice);
sampleFramework.SetWndProcCallback(new WndProcCallback(blockerEngine.OnMsgProc));
sampleFramework.SetCallbackInterface(blockerEngine);
(注意,雖然在SDK October 2005的文檔中還可以查到framework對象的SetKeyboardCallback方法,但實際上這個方法已經被刪除了,老版本的SDK示例中使用了整個方法。)

  這一段代碼作了很多工作,首先,為四個事件訂閱了事件處理程序,分別是創建設備,失去設備,重置設備,銷毀設備。我們將在后面實現這些事件處理程序。SetWndProcCallback方法訂閱了處理windows消息的方法。隨后,使用當前game engine實例作為參數,調用SetCallbackInterface方法。之后,編寫事件處理程序:

private void OnCreateDevice(object sender, DeviceEventArgs e)
{
??? SurfaceDescription desc = e.BackBufferDescription;
}

private void OnResetDevice(object sender, DeviceEventArgs e)
{
??? SurfaceDescription desc = e.BackBufferDescription;
}

private void OnLostDevice(object sender, EventArgs e)
{
}

private void OnDestroyDevice(object sender, EventArgs e)
{
}

public IntPtr OnMsgProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam, ref bool noFurtherProcessing)
{
}

  由于之前的SetCallbackInterface需要接收一個IframeworkCallback的變量作為參數,但是我們的game engine類并沒有實現這個類,所以添加以下代碼:

public class EmptyProject : IFrameworkCallback, IdeviceCreation

實現這個接口所定義的方法:

public void OnFrameMove(Device device, double appTime, float elapsedTime)
{
}
public void OnFrameRender(Device device, double appTime, float elapsedTime)
{
}

哦,框架性的東西總算是弄的差不多了。在SetCallbackInterface之后加上以下代碼:

try
{

??? sampleFramework.SetCursorSettings(true, true);
??? sampleFramework.Initialize( false, false, true );
??? sampleFramework.CreateWindow("haha");
??? sampleFramework.Window.KeyDown += new System.Windows.Forms.KeyEventHandler(blockerEngine.onKeyEvent);
??? sampleFramework.CreateDevice( 0, true, Framework.DefaultSizeWidth, Framework.DefaultSizeHeight, blockerEngine);
??? sampleFramework.MainLoop();

}
#if(DEBUG)
catch (Exception e)
{
??? sampleFramework.DisplayErrorMessage(e);
#else
??? catch
??? {
??????? // In release mode fail silently
#endif
??????? // Ignore any exceptions here, they would have been handled by other areas
??????? return (sampleFramework.ExitCode == 0) ? 1 : sampleFramework.ExitCode; // Return an error code here
??? }

??? return sampleFramework.ExitCode;
}

}

  現在運行程序看看,雖然只是一個藍色的窗口,但是我們背后所搭建的框架已經可以實際應用到一個游戲之中了。為了讓程序開起來有一點點交互,我們還訂閱了鍵盤事件,通過空格鍵可以改變程序的顏色。


使用Managed DirectX創建三維地形

使用Height Map作為輸入
  首先,什么是高度圖(Height Map)呢?所謂高度圖實際上就是一個2維數組。創建地形為什么需要高度圖呢?我們這樣考慮,地形實際上就是一系列高度不同的網格而已,這樣數組中每個元素的索引值剛好可以用來定位不用的網格(x,y),而所儲存的值就是網格的高度(z)。正是由于這個簡單的映射關系,最常見的地形生成方法都使用高度圖作為輸入數據。同時,為了減小數組的尺寸,通常使用Byte類型來保存高度值,因此,地形中最低點將用0表示,而最高點使用255表示(當然,這樣做可能會出現一些問題,比如,地形中大部分區域的高度差別都不大,但是有少數地方高度差特別大時,不過大多數情況下這個系統都能運行的很好)。使用2D Byte數組的另一個好處就是我們高度圖剛好可以用一張灰度位圖(grayscale bitmap) 來表示。對于位圖中的每個像素來說,同樣使用0~~255之間的值來表示一個灰度。這樣,我們又能把不同的灰度映射為高度,并且用像素索引表示不同網格。

  那么如何來創建高度圖呢?有兩種方法:直接使用程序創建2D數組或者使用其他繪圖軟件創建灰度位圖。先來看看兩種方法的優缺點。直接創建數組,通過特定算法填充每個元素的值(只為每個元素賦隨即值是不可行,這樣會導致你的地面看起來極度不真實,不連續的高度值可能創建出很扭曲的地形。),你不需要任何額外的工具就能創建地形。但是,通過這種方法創建的地形基本是隨機的,雖然可以通過調節算法的參數控制大概的地形形狀,卻不能精確控制每個點應該凹下還是凸起。而使用灰度圖,你不必掌握復雜的地形生成算法,可以把3維軟件建好的地形模型渲染為灰度圖,也可以使用通過衛星采樣的圖片作為灰度圖。我們的示例程序將使用后一種方法,不過首先,我們還是來看看完全使用程序生成地形的算法。


使用Midpoint Displacement方法生成高度圖
  這里我們介紹一種比較常用,也比較簡單的地形生成算法,稱為Midpoint Displacement中點偏移算法。使用這個方法,我們先創建一張平坦的高度圖,然后再來升高或降低不同的網格創建隨機地形。為了避免生成的值是完全沒有規則的,我們先把整個平面分為4個正方形區域,接下來重復對這四個正方形進行同樣的分割,同時,調整每個正方形頂點的高度。隨著細份層次的增加,相應減少頂點高度調整的幅度。?


  使用[0,255]之間的浮點值來進行調整,以保證最后能用8位的灰度值來表示所有高度。每一步,都在一個確定范圍內產生一個隨機值來作為頂點偏移值。對于第一步來說,隨機值將在[-128,128]之間(為了方便說明,我們把這個隨機值范圍記為[-delta,delta]產生,并且賦給上圖左邊的A,B,C,D四個頂點。接下來,用虛線把它分為4個小區域,這將創建5個新的頂點。計算每個新頂點所在邊兩個頂點高度的平均值作為這個點的基準值(比如 把點A和B的高度平均值作為點1的基準值),其中,點5的基準值是由四個頂點A,B,C,D的平均值來決定的。再計算[-delta,delta]之間的一個隨機值,對基準值進行偏移,作為這個點的最終值。5個點的值都計算完畢之后,我就調到下一階段,使用同樣的方法,計算個頂點值,如上圖右邊所示。

  為了引導地形的產生,再把delta和一個縮放因子相乘。我們把這個因子稱為roughness,它是一個1~0之間的值,這樣,每個階段都會減小delta的值。

delta = delta * roughness

roughness的值越大,地形起伏就越明顯,而越小,相應的地形也就越平坦。


使用Perlin Noise生成高度圖
  任何沒有討論噪聲函數的程序地形算法都是不完整的。最重要的噪聲函數就是Perlin Noise。他幾乎是現代圖形軟件包生成各種火焰,云彩,奇形怪狀的巖石,以及樹木和大理石表面等許多應用的基礎。這里不對Perlin Noise的理論做詳細介紹,我們主要看看如何使用它為我們的地形添加噪聲。

  Perlin噪聲可以適用于任何維度的空間,但這里我們只討論二維的情況。本質上,2D Perlin噪音就是對每個網格頂點法線的一種插值,來仔細看看這個技術吧。


  首先,使用網格把整個圖片劃分為幾個不同部分。如上圖所示,我們使用了一個4X4的網格來劃分整個圖片。這里,網格的多少控制著噪聲的復雜性。網格越多,噪聲越密集(tiger),類似于電視沒有信號時顯示出的雪花點;而網格越少,噪聲的波形就越明顯,類似于云朵的效果。

  對于每個網格頂點我們都分配一個隨機法線(normal vector)。這些法線實際上就是一些指向不同方向的單位矢量而已。這里,常見的方法是創建一張有256個指向不同方向(形成一個圓周)的向量查找表。然后為每個網格隨機分配一個向量,如上圖所示。

  對于圖片中的每個像素來說,我們先找到包含它的網格單元。然后,再創建4個從網格頂點指向所要計算的像素的方向矢量,如下圖所示。現在,每個網格頂點有2個向量:一個隨機的單位向量以及一個指向像素的方向向量。計算每對向量的點積,把它作為每個網格頂點的梯度高度值(scalar height value)。接下來,混合這4個值決定所計算像素的高度。這里,不同的混合方法可以產生不同效果,最常見的就方法就是通過目標像素與每個頂點位置的權重來計算。


我們將執行3次混合操作。首先需要計算混合權重。使用如下公式:

W = 6t^5 – 15t^4 + 10t^3 (^符號表示冪運算)

其中w表示權重,t根據需要替換為x或y值。這個方法與最早Perlin提出的公式(w = 3t^2 – 2t^3)有些區別。它雖然計算起來比較慢,但效果要好得多。
首先,計算x方向上的權重,使用公式:

V = Ca(w) + Cb(1-w)

  混合網格上邊的兩個頂點。其中Ca和Cb分別為上面兩個頂點的梯度高度值,w是上一個公式計算出的權重值。然后,使用同樣的方法混合下面兩個頂點。最后,使用前兩部混合的結果,以及y方向上的權重再進行一次混合。最后為這個像素計算出的高度值位于[0,1]之間,我們再把它縮放為相應的灰度值。

  舉個例子,假如網格上邊兩個頂點的坐標分別為Ca[2,0]和Cb[8,0],梯度高度值分別為h0和h1,所求像素位置為[4,2],那么兩個頂點指向這個像素的矢量就是:

Vector2 d0(4 -2,2-0)
Vector2 d1(4-8,2-0);

X軸方向的權重就為:

Sx = 6*d0.x^5 – 15d0.x^4 + 10d0.x^3

相應的插值就為:

avgX0 = h0*Sx + h1(1 –Sx)

如果下面兩個頂點的插值為avgX1,則最后的插值就是:

Result = avgX0 * Sy + avg2(1- Sy)

通常情況下,為了獲得真實的地形,會選取不同網格粒度,分別對圖像進行多次Perlin噪音處理,最后把這些處理過的圖加到一起,獲得最終結果。


生成地形
  現在來看看如何把高度圖轉變為為多邊形網格。一開始就說過,把高度圖中像素的x,y值轉換為頂點的x,y值,把像素的顏色值轉換為頂點高度。我們可以把這些值縮放為所需要的尺寸。

  每2X2個像素就對應著2X2個頂點,同時可以組成2個三角形。可以把把頂點數據儲存為一個簡單的(x,y,z)列表,三角形數據儲存為三個索引值一組的頂點列表。這兩個列表之后就轉變為頂點緩沖和索引緩沖。

public class Terrain
{
??? private Device device;
??? private VertexBuffer vb;
??? private IndexBuffer ib;
??? private int numVertices, numIndices, numTriangles;
??? //保存從高度圖中提取的數據
??? float[,] heights;
??? //地形大小
??? private float terrainSize;

??? public unsafe Terrain(Device d,float Min, float Max,float terrainSize)
??? {
??????? device = d;
??????? //加載高度圖
??????? Bitmap heightMap = new Bitmap(@"..\..\heightmap.bmp");
??????? //根據位圖大小創建數組
??????? heights = new float[heightMap.Width,heightMap.Height];
??????? //鎖定數據
??????? BitmapData data = heightMap.LockBits(new Rectangle(0,0,heightMap.Width,heightMap.Height, ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb);
??????? //獲得位圖中第一個像素的地址
??????? byte* p = (byte*) data.Scan0;
??????? //遍歷位圖,獲得最高和最低點的灰度值
??????? byte lowest = 255;
??????? byte hightest = 0;
??????? for(int i=0;i<heightMap.Width;i++)
??????? {
??????????? for(int j=0;j<heightMap.Height;j++)
??????????? {
??????????????? if ( *p < lowest)
??????????????????? lowest = *p;
??????????????? if( *p > hightest)
??????????????????? hightest = *p;
??????????????? //由于每個像素是24位,而指針是8位,所以+3指向下一個像素
??????????????? p += 3;
??????????? }
??????? }
??????? //填充數組,max表示地形最高點的位置,min標志最低點。
??????? p = (byte*) data.Scan0;
??????? for(int i=0;i< heightMap.Width;i++)
??????? {
??????????? for(int j=0; j< heightMap.Height; j++)
??????????? {
??????????????? heights[i,j] = (float)(*p - lowest) / (float)(hightest - lowest) * (Max - Min) + Min;
??????????????? p += 3;
??????????? }
??????? }
??????? heightMap.UnlockBits(data);
??????? //計算頂點,索引,三角形數量
??????? numVertices = heightMap.Width * heightMap.Height;
??????? numIndices = 6 * (heightMap.Width - 1) * (heightMap.Height - 1);
??????? numTriangles = 2 * (heightMap.Width - 1) * (heightMap.Height - 1);
??????? //創建頂點數組
??????? Vector3[] verts = new Vector3[numVertices];
??????? int[] index = new int[numIndices];
??????? int x = 0;
??????? int n = 0;
??????? float dx = terrainSize / (float) heightMap.Height;
??????? float dy = terrainSize / (float) heightMap.Width;
??????? //填充頂點數組
??????? for ( int i = 0; i < heightMap.Height; i ++)
??????? {
??????????? for ( int j = 0; j < heightMap.Width; j ++)
??????????? {?
??????????????? verts[i*heightMap.Width+j] = new Vector3((float)j*dx -terrainSize/2f,heights[j,i],(float)i*dy -terrainSize/2f);?
??????????? }
??????? }
??????? //填充索引數組
??????? for ( int i = 0; i < heightMap.Width-1; i ++)
??????? {
??????????? for ( int j = 0; j < heightMap.Height-1; j ++)
??????????? {
??????????????? x = i * heightMap.Width + j;
??????????????? index[n++] = x;?
??????????????? index[n++] = x+1;
??????????????? index[n++] = x+heightMap.Width+1;
??????????????? index[n++] = x;
??????????????? index[n++] = x+heightMap.Width;
??????????????? index[n++] = x+heightMap.Width+1;
??????????? }
??????? }
??????? //設置頂點以及索引緩沖
??????? vb = new VertexBuffer(typeof(Vector3),numVertices,device,Usage.None,VertexFormats.Position,Pool.Default);
??????? vb.SetData(verts,0,0);
??????? ib = new IndexBuffer(typeof(int),numIndices,device,Usage.None,Pool.Default);
??????? ib.SetData(index,0,0);
??? }

??? public void DrawTerrain()
??? {
??????? device.VertexFormat = VertexFormats.Position;
??????? device.SetStreamSource(0,vb,0);
??????? device.Indices = ib;
??????? device.Transform.World = Matrix.Translation(0,0,0);
??????? device.DrawIndexedPrimitives(PrimitiveType.TriangleList,0,0,numVertices,0,numTriangles);
??? }
}

  好了,看看我們的工作成果吧,還不錯把。源碼中我們使用了一張位圖作為高度圖。

  當然,這只是初級的地形技術而已,我們沒有為地形貼紋理,頂點沒有法線信息,以至于不能使用燈光照亮他,另外,也沒有進行任何LOD處理。下一次,我們將仔細討論這些問題。

轉載于:https://www.cnblogs.com/Blanche/archive/2011/11/02/2233074.html

總結

以上是生活随笔為你收集整理的使用Managed DirectX编写游戏的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

91成人亚洲 | 久久99国产精品 | 高清av网站| 亚洲精品国内 | 久久99精品视频 | 日韩精品一区二区三区在线视频 | 成人黄色毛片 | 日韩久久久久久久久 | 国产精品国产三级国产不产一地 | 久久网站最新地址 | 99久久婷婷国产综合亚洲 | 国产精品免费av | 欧洲亚洲国产视频 | 超碰97免费观看 | 99国产精品视频免费观看一公开 | 视频一区二区在线 | 黄色精品免费 | 国产精品成人免费一区久久羞羞 | 午夜999| 中文字幕一区二区三区久久蜜桃 | www.久久色 | 在线最新av| 免费午夜网站 | 亚洲精品美女在线观看播放 | 婷婷综合成人 | 亚洲欧美日韩国产精品一区午夜 | 国产精品成人国产乱一区 | 精品亚洲免费视频 | 亚洲三级性片 | 精品主播网红福利资源观看 | 中文字幕在线中文 | 操操操操网 | 久久久久在线视频 | 五月天久久婷 | 久久久久久久亚洲精品 | 一区二区三区精品在线 | 五月天丁香 | 国产色婷婷精品综合在线手机播放 | 天天操综 | 亚洲精品免费在线视频 | 国产午夜在线观看视频 | 99久久影院 | 成年人在线观看 | 久久看片| 激情综合国产 | 精品国产一区二区三区久久久蜜臀 | 国内综合精品午夜久久资源 | 亚洲精品视频二区 | 亚洲aⅴ久久精品 | 永久免费的av电影 | 激情视频免费观看 | 久草在在线 | 亚洲九九| 亚洲国产日韩在线 | 国产成人精品一区二区三区在线观看 | 久热电影| 中文字幕人成人 | 亚洲免费专区 | 久久精品在线视频 | 日韩欧美视频免费看 | 久久久亚洲网站 | 色婷婷狠狠| 日本中文字幕一二区观 | 四虎国产精品免费观看视频优播 | 97在线看 | 日本韩国精品一区二区在线观看 | 国产午夜精品免费一区二区三区视频 | 中文字幕人成不卡一区 | 成人久久精品视频 | 99精品网站| 天海翼一区二区三区免费 | 亚洲激情网站免费观看 | 精品国产免费观看 | 日韩a在线观看 | 00av视频 | 99视频免费看 | 99综合视频 | 日韩毛片在线一区二区毛片 | 91视频在线免费看 | 在线一区电影 | 久久免费电影网 | 欧美va天堂va视频va在线 | 狠狠干夜夜爱 | 中文国产在线观看 | 日本久久综合网 | 99久久婷婷| 国产精品美女久久久久久久久 | 亚洲精品国产精品久久99 | 欧美色综合久久 | 久久人人爽人人爽人人片 | 国产成人av在线影院 | 久久久久99精品国产片 | 一级黄色视屏 | 精品一区二区日韩 | 99免费精品 | 成人小视频在线观看免费 | 天天天天天天天操 | 人人爽久久涩噜噜噜网站 | 97小视频 | 欧美色精品天天在线观看视频 | 丁香综合五月 | ww亚洲ww亚在线观看 | 精品国产一二三 | 日日插日日干 | 国产一二区在线观看 | 久久免费视频网 | 99精品欧美一区二区三区黑人哦 | 在线观看资源 | 欧美成人久久 | 国产激情电影综合在线看 | 亚洲高清精品在线 | 992tv在线观看网站 | 欧美日韩午夜爽爽 | 91精品一区国产高清在线gif | 色偷偷888欧美精品久久久 | 色综合久久88色综合天天免费 | 日韩专区中文字幕 | 中文字幕免费高 | 欧美精品久久久久性色 | 欧美成人精品欧美一级乱黄 | 一区二区伦理电影 | 亚洲美女免费精品视频在线观看 | 黄色成人91| 欧美精品在线观看 | 最近免费观看的电影完整版 | 国产精品毛片久久久久久 | 在线观看日本韩国电影 | 免费在线色 | 在线观看中文字幕一区二区 | 91伊人久久大香线蕉蜜芽人口 | 久久tv | 99欧美视频 | 丰满少妇高潮在线观看 | ,午夜性刺激免费看视频 | 99视频在线观看免费 | 美女久久久久久久久久久 | 亚洲午夜精品久久久久久久久 | 丁香激情视频 | www.黄色片网站| www.黄色小说.com | 狠狠躁夜夜躁人人爽超碰97香蕉 | 中文字幕在线播放视频 | 日日夜夜天天综合 | av亚洲产国偷v产偷v自拍小说 | 天天色综合三 | 1024手机看片国产 | 亚洲欧美激情插 | 夜夜嗨av色一区二区不卡 | 亚洲乱码在线观看 | 色婷婷av在线 | 一区二区中文字幕在线播放 | 久久久久免费精品视频 | 国产精品电影一区二区 | 亚洲精品乱码久久久久 | 夜色资源站国产www在线视频 | 特级免费毛片 | 探花视频免费观看 | 国产久草在线 | 久久这里只有精品视频首页 | 日产av在线播放 | 一区二区毛片 | 国产一区电影在线观看 | 天天做夜夜做 | av在线电影免费观看 | 在线观看黄色小视频 | 国产中文字幕网 | 久久天天躁夜夜躁狠狠85麻豆 | 一区二区三区高清不卡 | 日韩啪啪小视频 | 男女男视频 | 99久久99久久精品国产片 | 亚洲美女精品区人人人人 | 日韩在线观看一区二区 | 中文字幕在线观看视频免费 | 日韩欧美电影在线观看 | 欧美ⅹxxxxxx | 一区二区三区在线看 | 在线免费视频你懂的 | 黄色在线观看www | 偷拍福利视频一区二区三区 | 国产亲近乱来精品 | 麻豆国产精品永久免费视频 | 992tv又爽又黄的免费视频 | 超碰97网站| 婷婷六月综合网 | 美女在线国产 | 成人免费在线看片 | 欧美精品二 | 国产成人一级电影 | 久久综合一本 | 亚洲爽爽网 | 四虎成人免费观看 | 在线观看91久久久久久 | 日韩精品在线观看视频 | 97视频网站 | 毛片一级免费一级 | 精品毛片一区二区免费看 | 人人插人人舔 | 国产婷婷视频在线 | 久久久久久久久久久电影 | 久久综合婷婷国产二区高清 | 成片免费| 狠狠躁18三区二区一区ai明星 | 日韩电影在线一区 | 中文字幕在线播放一区二区 | 日本不卡123区 | 亚洲综合在线一区二区三区 | 成人在线观看免费 | 天天视频色 | 欧美一区二区三区在线观看 | 欧美精品第一 | 亚洲爱视频 | 亚洲国产精品电影 | 黄色片视频在线观看 | 国产va在线 | 亚洲黄色在线观看 | 亚洲免费永久精品国产 | 午夜av日韩| 精品国产自在精品国产精野外直播 | 国产麻豆精品久久一二三 | 欧美99热 | 99爱视频| 亚洲mv大片欧洲mv大片免费 | 麻豆精品传媒视频 | 日韩精品视频免费在线观看 | 久热国产视频 | 91视频免费 | 日韩精品一区二区在线观看 | 久久精品国产第一区二区三区 | 天天插天天操天天干 | 91精品视频播放 | 国产精品美女久久久久久久久 | 婷婷色av| 91av手机在线| 亚洲成a人片77777潘金莲 | 激情视频在线高清看 | 亚洲综合在线视频 | 久久精品伊人 | 激情综合啪 | 日韩精品中文字幕在线不卡尤物 | 色婷婷啪啪免费在线电影观看 | 一区二区三区电影大全 | 成人av网站在线观看 | 国产青春久久久国产毛片 | 国产精品一区二区av麻豆 | 激情五月激情综合网 | 日韩18p| 一区三区在线欧 | 又湿又紧又大又爽a视频国产 | 操操操天天操 | 国产精品一区一区三区 | 国产高清在线不卡 | 亚洲精品国偷自产在线99热 | 99精品视频观看 | 在线观影网站 | 久久亚洲专区 | 毛片网站在线看 | 天天操天天曰 | 色av婷婷 | 亚洲婷婷免费 | 精品天堂av | 亚洲精品网页 | 伊人影院99 | 四虎国产精品成人免费4hu | 久久精国产 | 一区二区三区免费在线播放 | 亚洲国产精品传媒在线观看 | 国内精品二区 | 五月天伊人网 | 精品国产电影 | 激情丁香综合 | 国产69精品久久久久99 | 性色av免费观看 | 超碰精品在线观看 | 最新国产精品久久精品 | 一色屋精品视频在线观看 | 丰满少妇在线观看 | 亚洲一区美女视频在线观看免费 | 久碰视频在线观看 | 日韩电影在线一区二区 | 国产伦理剧 | 国产精品一区在线 | 四虎成人免费影院 | 97人人模人人爽人人喊中文字 | 国产精品毛片完整版 | 久草在线中文888 | 成人久久久久久久久久 | 国产国产人免费人成免费视频 | 美女网色 | 日韩中文字幕视频在线观看 | 日韩精品大片 | 中文在线a天堂 | 欧美一区二区免费在线观看 | 在线观看亚洲国产 | 激情婷婷丁香 | 久久久污 | 国产一级性生活 | 亚洲精品在线网站 | 国产va在线| 国产精品久久久久久久久蜜臀 | 激情久久久久久久久久久久久久久久 | 成人国产网址 | 又湿又紧又大又爽a视频国产 | 国产精品一区二区av麻豆 | 在线色吧 | 91在线九色| 亚洲精品玖玖玖av在线看 | 亚洲色图美腿丝袜 | 日韩三级成人 | 欧美日韩视频一区二区三区 | 91成人免费在线 | 在线观看中文字幕第一页 | 激情av五月婷婷 | 久久草在线免费 | 欧美一区免费在线观看 | 国产亚洲高清视频 | 国产999精品视频 | 久久久蜜桃一区二区 | 天天做天天爱天天爽综合网 | 久久午夜电影院 | 黄a在线观看| 亚洲精品久久久久58 | 亚洲欧洲成人精品av97 | 国产美女视频网站 | 麻花豆传媒mv在线观看网站 | 亚洲国产精品成人综合 | 日韩在线观看视频一区二区三区 | 美女久久 | 成人毛片在线观看视频 | 91高清不卡 | 综合色天天 | 日韩一区二区免费视频 | 午夜精品久久久久久久99热影院 | 性色va| 亚洲激精日韩激精欧美精品 | 国产成人精品一区在线 | 91精品在线免费观看 | 天天射网| 国产精品 日本 | 国产精品久久久一区二区三区网站 | 色婷久久| 999久久国精品免费观看网站 | 91精品夜夜 | 亚洲午夜精 | 深爱五月激情网 | 色婷婷福利视频 | 五月天.com | 日本精品中文字幕在线观看 | 在线小视频国产 | 日本精品视频免费 | 日韩欧美在线观看一区二区三区 | 午夜精品久久久99热福利 | 狠狠狠狠狠狠狠狠干 | 亚洲国产视频直播 | 婷婷久久网站 | 久久精品影片 | 911av视频| 欧美a性 | 国内精品在线一区 | 久久九精品 | 99热九九这里只有精品10 | 久久精品99国产精品日本 | 99精品视频播放 | 四虎影视欧美 | 日韩高清av在线 | 日韩欧美在线视频一区二区三区 | 国产精品久久久久久久久久ktv | 99精品热视频 | 美女av电影 | 国产精品人人做人人爽人人添 | 韩国一区视频 | 黄色av影视 | 中文字幕专区高清在线观看 | 人人草在线视频 | 亚洲免费不卡 | 手机成人在线 | 日韩女同一区二区三区在线观看 | 国产又粗又硬又爽的视频 | 国产黄色免费观看 | 精品美女久久久久 | 99在线高清视频在线播放 | 久久久久久久精 | 狠狠色噜噜狠狠狠合久 | av免费网站| 国产91大片| 日韩免费在线视频 | 在线不卡中文字幕播放 | 国产精品乱码久久久久久1区2区 | 天天天色综合a | 91最新网址在线观看 | 五月天久久狠狠 | 在线精品观看 | 黄色资源网站 | 国产中文字幕视频 | 国产高清视频在线观看 | 在线观看视频一区二区三区 | 中文字幕在线免费观看 | 97超碰在线人人 | 涩涩资源网| 五月天亚洲综合小说网 | 日韩xxxbbb| 一区二区三区在线免费 | 在线视频 一区二区 | 亚洲精品国产综合久久 | 国产精品 亚洲精品 | 久久躁日日躁aaaaxxxx | 国产h在线播放 | 狠狠的日日 | 欧美最猛性xxxxx(亚洲精品) | 久久久黄视频 | 午夜一级免费电影 | 久久久久久久久久久免费av | 国产在线污 | 国产欧美精品一区aⅴ影院 99视频国产精品免费观看 | 麻花天美星空视频 | 五月婷婷综合激情网 | 国产精品免费在线 | 国产精品视频在线观看 | 欧美专区国产专区 | 日韩欧美国产精品 | 女人18毛片90分钟 | 国产美女久久 | 国产又粗又猛又黄又爽 | 五月综合久久 | 国产精品久久久久永久免费 | 国产麻豆精品传媒av国产下载 | 伊人色播 | 在线观看mv的中文字幕网站 | 久久久久一区二区三区 | 色丁香色婷婷 | 草久视频在线 | 国产午夜三级一区二区三桃花影视 | 国产视频在线看 | 在线 国产 亚洲 欧美 | 成人黄色在线播放 | 天天爱天天干天天爽 | 久久精品国产免费观看 | 久久国精品 | 99久久精品国产免费看不卡 | 色丁香久久 | 成人影片在线免费观看 | 国内丰满少妇猛烈精品播放 | 婷婷综合久久 | 五月婷婷一级片 | 国产91精品一区二区绿帽 | 天天操天天草 | 国产精品久久99综合免费观看尤物 | 亚洲精品中文字幕视频 | 免费网站看v片在线a | 亚洲伦理一区二区 | 美女视频黄在线 | 精品国产一区二区三区久久久蜜月 | 91av中文 | 一区中文字幕电影 | 91av国产视频 | 国产午夜精品在线 | 91在线你懂的 | 精品国产一区二区三区噜噜噜 | 国产精品扒开做爽爽的视频 | 丁香六月久久综合狠狠色 | 婷婷综合激情 | 97视频在线 | 日本精品在线看 | 国产精品免费看久久久8精臀av | 亚洲国产欧洲综合997久久, | 日韩一区二区三区高清在线观看 | 国产一区二区三区网站 | 国产不卡av在线 | 国产在线观看不卡 | 六月天综合网 | 免费观看久久 | 国产精品wwwwww | 国产黄色片一级三级 | 国产精品免费一区二区 | 亚洲精品理论 | 一区二区中文字幕在线播放 | 五月婷婷导航 | 精品欧美一区二区精品久久 | 国产系列 在线观看 | 欧美日韩精品网站 | 婷婷av网| 免费亚洲成人 | 久久tv | 久久三级视频 | 亚洲成av人片一区二区梦乃 | 日韩av伦理片| 欧美最猛性xxx | 免费看的黄网站 | 欧美黄色免费 | 久久国产综合视频 | 婷婷婷国产在线视频 | 中文字幕欧美日韩va免费视频 | 香蕉视频久久久 | 五月婷激情 | 精品免费久久久久久 | a天堂在线看 | 欧美a级成人淫片免费看 | 99久久精品日本一区二区免费 | 国产精彩在线视频 | 国产视频在线观看一区二区 | 国产成人免费精品 | 亚洲h视频在线 | 精品一区二区亚洲 | 一区二区三区 中文字幕 | 亚洲天天在线日亚洲洲精 | 九九热精品国产 | 国产综合精品一区二区三区 | 中文欧美字幕免费 | 三级黄色大片在线观看 | 国产一级二级三级在线观看 | 91毛片视频| 黄色三级免费看 | av电影免费看 | 国产不卡视频在线 | 精品影院一区二区久久久 | 91精品啪在线观看国产线免费 | 中文字幕在线观看免费观看 | 国产国语在线 | 色婷婷视频在线观看 | 91看片淫黄大片在线播放 | av黄色在线播放 | 国产精品一区二区在线观看 | www.亚洲在线 | 91av在线视频免费观看 | 激情五月播播久久久精品 | 中文字幕综合在线 | 日本久久成人中文字幕电影 | 日本黄色片一区二区 | 欧洲一区二区在线观看 | 成人性生交大片免费观看网站 | 国内精品久久久久国产 | 国产成人在线看 | 人人爽人人香蕉 | 国产视频黄 | 国产日产在线观看 | 国内精自线一二区永久 | 四虎影视av | 蜜臀精品久久久久久蜜臀 | 亚洲精品久久久久999中文字幕 | www.五月天婷婷.com | 国产在线视频资源 | 亚洲综合欧美日韩狠狠色 | 丁香五月亚洲综合在线 | 中文字幕在线日本 | 久久好看免费视频 | 国产99久久久国产精品免费二区 | 一区二区三区四区五区在线视频 | 国产大尺度视频 | 五月天综合网站 | 日韩美在线| 久久激情片 | 国产精品美女久久久久久网站 | 成人免费观看网站 | 天堂资源在线观看视频 | 97色国产 | 中文字幕a∨在线乱码免费看 | 久久手机免费视频 | 在线视频黄 | 五月婷婷操 | 九九色网 | 一区二区三区av在线 | 国产午夜小视频 | 久久乐九色婷婷综合色狠狠182 | 久久久高清免费视频 | 在线中文字幕播放 | 国产做aⅴ在线视频播放 | 美女国产精品 | 久久国产成人午夜av影院潦草 | 天天插天天| 中国一级片在线播放 | 嫩草伊人久久精品少妇av | 韩日av在线 | 亚洲爱爱视频 | 日韩av一卡二卡三卡 | 国产色资源 | 国产小视频在线免费观看视频 | 国产精品久久久久毛片大屁完整版 | 久草在线免费新视频 | 香蕉日日| 综合婷婷丁香 | 免费黄色av | 超碰在线观看99 | 国产精品免费观看网站 | 国产又粗又猛又色又黄视频 | 国产不卡一二三区 | 91手机电视 | av三区在线| 国产不卡精品 | 黄色电影在线免费观看 | 在线看的毛片 | www色,com| 九九日韩 | 福利视频精品 | 久久成人一区 | 久久不卡视频 | 欧美性大战 | 国产色啪| 二区三区精品 | 久久97超碰| 久久久久女人精品毛片九一 | 久久国产网站 | 亚洲国产午夜视频 | 久久久久久久久久网 | 久久精品在线 | 久久国产电影 | 色综合久久久久久久久五月 | 91精品国产亚洲 | 久久精品激情 | 日日婷婷夜日日天干 | 国产美女被啪进深处喷白浆视频 | 日韩午夜在线观看 | 欧美成人在线免费 | 日韩精品在线观看视频 | 97免费在线观看视频 | 欧美另类巨大 | 韩国av免费观看 | 九九久久婷婷 | 国产亚洲精品美女久久 | 成人免费在线观看av | 伊人激情网 | 国产成人一区二区三区 | 81精品国产乱码久久久久久 | 日韩网站在线免费观看 | 亚洲毛片在线观看. | 日韩欧美国产精品 | 天天干夜夜操视频 | 人人狠 | 免费91在线 | 日本黄色特级片 | 黄av资源 | 91新人在线观看 | 日韩精品一区二区三区第95 | 在线探花 | 色中色综合 | 日韩理论片 | 国产资源在线播放 | 99精品在线观看 | 日本在线观看黄色 | 99热这里| aa一级片| 国产二区视频在线观看 | 91日韩在线 | 久久久精品 | 夜夜婷婷 | 奇米7777狠狠狠琪琪视频 | 亚洲三级性片 | 精品主播网红福利资源观看 | 日韩午夜在线观看 | 激情网在线观看 | 激情五月开心 | 久久精品一区八戒影视 | 中文字幕国产精品 | 日韩视频免费在线 | 欧美日韩性视频在线 | 91视频在线播放视频 | 久久精品女人毛片国产 | 国产成人一区二区三区免费看 | 色999在线| 一区二区三区四区五区六区 | 91精品一区国产高清在线gif | 97精品在线观看 | 亚洲色影爱久久精品 | 久久久久国产精品午夜一区 | 日韩电影中文字幕在线观看 | 精品免费久久久久久 | 91精品欧美| 五月婷婷综合网 | 中文字幕日韩高清 | av高清不卡 | 亚洲精品在线观看中文字幕 | 毛片永久免费 | 国产伦精品一区二区三区四区视频 | 国产精品高潮久久av | 免费h视频| 91视视频在线直接观看在线看网页在线看 | 日韩在线视频免费观看 | 亚洲精品网站 | 久草在线久 | 中文字幕久久网 | 欧美精品久久久久久久亚洲调教 | 精品一区二区视频 | 一区中文字幕在线观看 | 亚洲欧美成人在线 | www.香蕉 | 成人免费精品 | 天天草天天操 | 国产精品美 | 五月天堂色 | 久草精品视频 | 免费在线观看成人av | 亚洲人毛片| 九九三级毛片 | 91亚洲精品久久久中文字幕 | 综合网五月天 | 国产精品成人久久 | 亚洲精品美女在线 | 天天干天天拍天天操天天拍 | 国产精品你懂的在线观看 | 日韩va欧美va亚洲va久久 | 日韩va欧美va亚洲va久久 | 国产高清av | 日本黄色一级电影 | 日韩综合视频在线观看 | 国产日产精品一区二区三区四区 | 亚洲黄色高清 | 久久a国产 | 国内精品视频免费 | 久久免费看a级毛毛片 | 在线国产99| 美女网站在线 | 成人精品久久 | 一区 二区 精品 | 亚洲国产久 | 99综合影院在线 | 成人av在线播放网站 | 午夜精品在线看 | 在线久热| 夜夜干天天操 | 欧美性大胆 | 亚洲欧美999 | 亚洲一级片免费观看 | 天天操夜夜摸 | 国产91精品一区二区麻豆网站 | 免费久久99精品国产婷婷六月 | 超碰在线观看97 | 国产黄色观看 | 国产精品视频久久 | 日韩在线免费小视频 | 亚洲一区二区天堂 | 欧美色一色| 一区二区三区四区在线免费观看 | 久久综合成人 | 午夜精品一区二区三区免费视频 | 久久婷婷视频 | 91漂亮少妇露脸在线播放 | 天天干天天拍天天操天天拍 | 国产在线一线 | 久草在线高清视频 | 国产手机在线播放 | 久久艹艹 | 在线欧美日韩 | 天天干天天做天天操 | 国产成人综合在线观看 | 亚洲午夜久久久影院 | 国产成人资源 | 国产一区二区视频在线 | 久久好看免费视频 | 日韩毛片在线一区二区毛片 | 中文字幕在线观看国产 | 欧美激情综合色综合啪啪五月 | 一区视频在线 | 深夜免费福利视频 | 99这里都是精品 | 国产亚洲成人精品 | av一区二区三区在线播放 | 亚洲精品国内 | 午夜视频福利 | www黄com| 欧美成人亚洲成人 | 97中文字幕 | 九九九九免费视频 | 五月激情站 | 日韩最新理论电影 | 91影视成人| 美女激情影院 | www.午夜视频 | 91在线视频免费91 | 久一久久 | av官网在线| 日韩av电影网站在线观看 | 91在线网址 | 国产精品a久久 | 人人爱爱人人 | 五月婷久 | 三级黄色网址 | 日韩高清一区在线 | 国产免费黄视频在线观看 | 日韩在线观看视频中文字幕 | 久久a久久 | 亚洲第一香蕉视频 | 中文字幕日韩电影 | 日本一区二区不卡高清 | 日精品 | 在线日韩中文字幕 | 成人av片免费看 | 欧美日韩在线观看一区 | 午夜精品一区二区三区免费视频 | 91最新网址| 96视频免费在线观看 | 国产免费专区 | 久久久久国产精品免费 | 日本不卡123区| 91在线看片 | 在线观看视频亚洲 | 国产精品亚州 | 亚洲国产精品va在线看黑人动漫 | 国内精品视频免费 | 久久久久国产精品www | 成人一区二区三区中文字幕 | 久久成电影 | 日韩免费看片 | 欧美日韩成人一区 | 黄色一级在线免费观看 | 免费观看v片在线观看 | 亚洲精品五月 | a在线免费 | 在线看国产| 婷婷色在线 | 成人四虎 | 麻豆精品在线视频 | 日本bbbb摸bbbb | 国产伦理剧 | 视频在线日韩 | 国产精品久久久久久久av电影 | 看片黄网站 | 久久久午夜视频 | 天天色影院| 亚洲国产欧美一区二区三区丁香婷 | 日韩在线观看免费 | 天天操天天操天天操天天操 | 国产美女主播精品一区二区三区 | 国产高清在线 | 99久久精品网 | 国产精品久久久亚洲 | 91精品一区二区三区久久久久久 | 亚洲精品乱码久久久久久写真 | 91视频电影 | 久久久综合电影 | 一区二区 不卡 | 亚洲理论片在线观看 | 久久免费视频这里只有精品 | 欧美视屏一区二区 | 精品日韩中文字幕 | 久久免费av | 国产色视频网站 | 丁香婷婷色综合亚洲电影 | 久久久免费播放 | 国产精品久久99 | 久久精品99久久 | 久久这里只有精品首页 | av在线超碰 | 久久久久免费精品视频 | 九九九九九九精品任你躁 | 国产精品视频不卡 | 五月激情丁香 | 玖玖在线播放 | 国产精品 欧美 日韩 | 99在线热播精品免费 | 日韩成人免费观看 | 91av影视| 国产精品一二 | 手机在线黄色网址 | 久久精品7 | 亚洲国产中文在线观看 | 婷婷99| 亚洲精品午夜国产va久久成人 | 国产一区电影在线观看 | 91香蕉视频| 国产精品成人久久久 | 精品国产精品国产偷麻豆 | 国产视频精选 | www视频在线观看 | 九九视频在线播放 | 精品一区二区在线免费观看 | 91一区二区三区久久久久国产乱 | 欧美先锋影音 | 久操97 | 青青久草在线视频 | 久久精品高清视频 | 超碰在线97国产 | 天天综合操 | 一区二区三区 亚洲 | 91精品视频导航 | 五月婷婷中文 | 国产高清精 | 欧美午夜久久 | 日韩精品在线观看av | 91麻豆精品国产91久久久无限制版 | 日韩大陆欧美高清视频区 | 高清有码中文字幕 | www.色五月.com | 国产视频97 | 国产区精品在线 | 狠狠躁日日躁 | 天堂在线一区 | 国产无限资源在线观看 | 欧美一区二区三区在线 | 99视频国产在线 | 狠狠狠狠狠操 | 韩国av免费在线观看 | 日韩三区在线观看 | a级一a一级在线观看 | 久久久久综合视频 | 日韩视频免费在线观看 | 色噜噜日韩精品一区二区三区视频 | 成全在线视频免费观看 | 亚洲欧洲成人精品av97 | 国产黄a三级 | 97爱爱爱 | 草久视频在线观看 | 天天曰夜夜爽 | 欧美成人在线免费 | 999久久a精品合区久久久 | 操处女逼 | 欧美日韩一区二区免费在线观看 | 国产精品2020 | 西西444www大胆高清视频 | 成人99免费视频 | 中文字幕一区2区3区 | 香蕉在线观看视频 | 日日草夜夜操 | 91亚色视频 | 久久国产精品一二三区 | 亚洲资源一区 | 日韩精品在线免费播放 | 伊人首页 | 国产精品久久久一区二区 | 69av视频在线 | 精品久久综合 | 成人av中文字幕在线观看 | 国产综合香蕉五月婷在线 | 精品亚洲一区二区三区 | 人人爽人人乐 | 成人av资源| 黄色在线观看免费网站 | 国产视频首页 | 日日夜夜精品网站 | 免费高清在线观看电视网站 | 亚洲精品动漫成人3d无尽在线 | 国产精品6999成人免费视频 | 日本中文字幕影院 | 深夜视频久久 | 99免费精品 | 最新国产在线 | 国产成人精品一区二区三区网站观看 | 欧美一级视频免费看 | 亚洲涩涩网站 | 精品1区二区 | www.黄色片网站 | 欧美天天干 | 国精产品999国精产品视频 | 婷婷成人在线 | 国偷自产视频一区二区久 | 在线观看香蕉视频 | 97综合网 | 亚洲黄网站| 美女网站视频一区 | 激情综合色综合久久 | 日本黄色特级片 | 精品国产乱码久久久久久三级人 | 美女久久精品 | 欧美激情第八页 | 久久精品国产一区二区三区 | av永久网址| 黄色小说在线观看视频 | 久久99国产一区二区三区 | 国产黄色在线 | 91视频在线观看大全 | 亚洲欧美日本一区二区三区 | 日韩视频一区二区三区在线播放免费观看 | 国产成人精品电影久久久 | 亚洲精品国产精品乱码不99热 | 999抗病毒口服液 | 婷婷网站天天婷婷网站 | 91精品国产福利 | 婷婷色吧| 激情网五月婷婷 | 日韩精品中文字幕在线观看 | 亚洲高清在线观看视频 | 成年人免费在线播放 | h视频在线看 | 一区二区三区四区五区在线视频 | 伊人射| 国产精品免费观看在线 | 亚洲爽爽网 | av综合 日韩 | 91免费视频国产 | 久久综合国产伦精品免费 | 91亚洲永久精品 | 欧美色图东方 | 精品久久久久久久久久久久久久久久久久 | 911精品美国片911久久久 | 日韩av偷拍 | 中文字幕av在线电影 | bbb搡bbb爽爽爽 | 免费日韩视 | 久久久久久久久免费视频 | 国产我不卡 | 激情五月激情综合网 | 中文字幕一区二 | 日本 在线 视频 中文 有码 | 在线va网站| 免费日韩在线 | 免费一级片久久 | 亚洲精品裸体 | 亚洲欧美成人综合 | 亚洲精品国产拍在线 | a级一a一级在线观看 |