最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

用游戲內(nèi)置記錄器系統(tǒng)調(diào)試AI | Game AI Pro

2020-05-05 17:39 作者:有木乘舟  | 我要投稿

Debugging AI with Instant In-Game Scrubbing

David Young

6.1 Introduction

? 實時捕獲AI的錯誤信息,對理解發(fā)生了什么邏輯錯誤,以及錯誤產(chǎn)生的根本原因至關(guān)重要。

不幸的是,當你看到AI發(fā)生錯誤的時候,往往意味著已經(jīng)丟失了大量的關(guān)鍵信息。理想情況下,如果當錯誤發(fā)生的時候,整個游戲能重新恢復(fù)到錯誤發(fā)生的時間點,那解決BUG和理解BUG發(fā)生的原因?qū)⒆兊檬州p松。

? 游戲引擎通常使用確定性回放方法(Dickinson 2001)來處理這些問題,該方法使整個游戲狀態(tài)回溯到發(fā)生問題的時間點,然后一遍又一遍重現(xiàn)這些問題。不幸的是,將目前還不是“決定論”的引擎改造成“決定論”不是一件容易的事,需要仔細的維護以防止無意中破壞“決定論”(determinism?不清楚什么意思)。

? 幸運的是,調(diào)試后的AI沒有再出現(xiàn)這些問題。游戲錄制系統(tǒng)提供了記錄所有相關(guān)數(shù)據(jù)所需的功能,并允許游戲模擬以可視化的方式在時間線上移動,以了解錯誤發(fā)生的時間、地點和原因。本章將闡述Activision Treyarch開發(fā)的3A游戲創(chuàng)作中使用的游戲錄制系統(tǒng)的架構(gòu)。

6.2 In-Game Recorder

??在較高的層次上,游戲內(nèi)置記錄器是一個相對簡單的概念:在每個模擬幀完成后,遍歷每個游戲?qū)ο螅⒂涗浽佻F(xiàn)游戲?qū)ο蟮木_視覺狀態(tài)所需的最少數(shù)據(jù)量。要重放錄制的數(shù)據(jù),請暫停游戲模擬,并從單個模擬幀還原每個錄制的游戲?qū)ο蟮乃袖浿茢?shù)據(jù)。為了將記錄系統(tǒng)有效的應(yīng)用在游戲開發(fā)中,還必須考慮其他現(xiàn)實因素。游戲?qū)ο蟮臄?shù)據(jù)記錄不能對游戲運行速度產(chǎn)生影響,系統(tǒng)的內(nèi)存要求必須是固定的,游戲?qū)ο蟮臄?shù)據(jù)記錄必須不會改變?nèi)魏卧镜慕Y(jié)果。

??考慮到這些要求,本章的其余部分將詳細介紹用于創(chuàng)建游戲內(nèi)置錄制系統(tǒng)的每個主要組件,以及使用錄制的調(diào)試信息和導(dǎo)航播放(也稱為拖動播放)來調(diào)試人工智能的實用操作。

6.2.1 Recorder Architecture

??記錄數(shù)據(jù)的最基本的構(gòu)建塊是存儲在記錄的游戲?qū)ο笊系膯蝹€數(shù)據(jù)包。在游戲的每個模擬幀,每個對象構(gòu)造一個記錄數(shù)據(jù)包,存儲位置、方向、聯(lián)合矩陣和可見性等信息。每個模擬幀記錄的所有數(shù)據(jù)包的整體將波動,并集中存儲在記錄數(shù)據(jù)的幀內(nèi)。每個記錄幀都用模擬的絕對時間以及最后一個記錄幀之間的相對時間作為時間戳,以便于快速檢索和準確回放。

??所有被記錄的模擬幀都由一個記錄器管理器存儲和管理,該管理器提供與記錄器系統(tǒng)交互以及更新每個子系統(tǒng)以記錄對象數(shù)據(jù)之間的接口。管理器在內(nèi)部存儲一個固定大小的內(nèi)存緩沖區(qū),該緩沖區(qū)分配和釋放每個數(shù)據(jù)包和每幀記錄器數(shù)據(jù)消耗的內(nèi)存。當存儲器緩沖區(qū)內(nèi)沒有額外的可用存儲器可供分配時,為了分配足夠的存儲器來存儲新幀的分組數(shù)據(jù),將釋放最舊的記錄數(shù)據(jù)幀。

? 序列化記錄器數(shù)據(jù)的責任留給單個記錄處理程序?qū)ο笕崿F(xiàn),這些實現(xiàn)僅根據(jù)為其分類的對象類型來序列化記錄數(shù)據(jù)。在實踐中,只需要幾個種類的記錄處理程序來覆蓋錄制動畫模型、第一人稱視圖模型、玩家角色和玩家攝像機。為了處理記錄數(shù)據(jù)的回放,回放處理程序?qū)ο蟾鶕?jù)其分類對象的類型實現(xiàn)反序列化功能。通常,記錄處理程序和回放處理程序一對一映射,這些處理程序已注冊供記錄器使用。

?系統(tǒng)的最后一個組件處理用戶在記錄回放期間的交互。當啟用記錄器回放時,專用控制器實現(xiàn)將覆蓋系統(tǒng)默認控制器實現(xiàn)??刂破鞣桨柑峁┝烁鞣N功能按鍵,如倒退、快進和單步執(zhí)行記錄數(shù)據(jù)的各個幀。此外,控制器方案允許選擇特定的感興趣對象,來減少不需要的多余調(diào)試信息。?

??圖6.1顯示了記錄器架構(gòu)的每個子系統(tǒng)之間的高層關(guān)系以及系統(tǒng)使用的內(nèi)部數(shù)據(jù)的所有權(quán)。

6.3 Recording Data Primitives

??由于記錄器只是過去信息的可視化,因此在構(gòu)造原始包數(shù)據(jù)時,只需要將重建游戲?qū)ο鬆顟B(tài)所需的最小視覺信息序列化。通常,至少,這段數(shù)據(jù)包括游戲?qū)ο骾d、對象類型、對象的位置,以及在錄像機回放期間對象是否可定位。圖6.2顯示了基于記錄的原語類型的不同類型的記錄器數(shù)據(jù)包信息的示例。

??對圖6.2中特定的包類型?RecorderModelPacket,讓我們分析每個游戲?qū)ο蠖即鎯α四男┬畔ⅰJ紫仁荌D,表示游戲?qū)ο笤谡麄€游戲引擎中的唯一標識符。盡管包中沒有存儲有關(guān)特定模型的信息,但是使用唯一id可以檢索該信息。

? 存儲的下一個字段定義id對應(yīng)的對象類型,并通過引擎對特定游戲?qū)ο筮M行分類。

? 指向下一個分組的指針被存儲在每個記錄器分組中,以形成幀內(nèi)所有分組數(shù)據(jù)的單鏈表。數(shù)據(jù)包的單鏈表被用作記錄幀的內(nèi)部數(shù)據(jù)結(jié)構(gòu),因為單個數(shù)據(jù)包可以有不同的大小,這取決于它們序列化的游戲?qū)ο?。例如,記錄模型?shù)據(jù)取決于特定模型具有的骨骼數(shù)量,盡管游戲?qū)ο箢愋拖嗤煌P偷墓趋罃?shù)量也不同。

? 下一個字段表示是否應(yīng)該選擇模型,以便在錄制器播放期間切換其他錄制的調(diào)試信息。

? 位置字段是不言而喻的,表示對象在世界中的當前位置。

? “角度”字段表示模型的當前方向。

? 最后一個字段表示模型中所有骨骼的當前方向和位置。如果模型沒有骨骼,則此字段保留為空,否則將存儲骨骼矩陣數(shù)組。盡管存儲游戲?qū)ο蟮恼麄€骨骼的詳細表示可能有違直覺,但存儲動畫骨骼矩陣比直接存儲動畫數(shù)據(jù)更可取,以便考慮物理驅(qū)動或反向運動學骨骼約束。

? 除了存儲游戲?qū)ο蟮臄?shù)據(jù)外,記錄器還存儲額外的調(diào)試原語(primitives),它提供了許多使用記錄器系統(tǒng)的調(diào)試功能。公開與特定游戲?qū)ο笙嚓P(guān)聯(lián)的多個調(diào)試原語,如線條、球體、長方體、多邊形和文本原語,可以方便地可視化人工智能系統(tǒng)的底層決策結(jié)構(gòu)。在正常的游戲模擬過程中,只要調(diào)用記錄器中相同的調(diào)試原語,就能在運行時和刷洗記錄器回放(scrubbing through recorder playback)時實現(xiàn)相同調(diào)試功能。

? 雖然以這種方式記錄每個游戲?qū)ο笤陂_發(fā)過程中有好處,但有選擇地僅記錄感興趣的游戲?qū)ο?,可以延長由固定內(nèi)存預(yù)算分配的錄制時間。錄制通道設(shè)備的使用,允許用戶在系統(tǒng)內(nèi)任意時間點選擇特定的對象類型來錄制。? 每個游戲?qū)ο箢愋投伎梢詺w類為屬于一個或多個頻道分類,如AI、動畫、腳本和fx。調(diào)試原語還可以指定它們應(yīng)該屬于哪個通道,以便在調(diào)試特定系統(tǒng)(如動畫或腳本)時適當顯示它們。

6.3.1 Record Handlers? ?

? 由于每種游戲?qū)ο箢愋蛯π蛄谢梢暠硎镜囊笸ǔ6加酗@著的不同,記錄器公開了一個簡單的接口來實現(xiàn)不同的記錄處理程序。記錄處理程序主要在記錄器中提供兩個不同的功能:計算任何特定游戲?qū)ο髮嵗璧膬?nèi)存量,以及在指定的分配內(nèi)存中序列化游戲?qū)ο箢愋?。每個記錄處理程序?qū)嵗来蜗蛴涗浧髯?,該記錄器指定處理程序能夠序列化的游戲?qū)ο蟮念愋汀?/p>

? 當記錄器正在處理游戲?qū)ο笠赃M行序列化時,首先將相應(yīng)的記錄處理程序排隊,以確定構(gòu)造有關(guān)游戲?qū)ο蟮男畔⒌闹夭グ璧膬?nèi)存量。如果內(nèi)存緩沖區(qū)中存在足夠的可用內(nèi)存,記錄器將立即調(diào)用傳入請求內(nèi)存的記錄;否則,記錄器必須首先釋放內(nèi)存緩沖區(qū)中最舊的記錄數(shù)據(jù),直到可以分配請求的大小。

? ? 將序列化功能從記錄器的主要功能中分離出來,允許游戲引擎擴展記錄器對新游戲?qū)ο箢愋偷闹С?,而無需修改記錄器系統(tǒng)本身。特別是,通過創(chuàng)建新的記錄處理程序并在記錄器注冊期間分配記錄處理程序的實例,可以輕松實現(xiàn)新的游戲?qū)ο箢愋汀?/p>

? 但要注意的一個方面是記錄處理程序內(nèi)存系統(tǒng)的不透明性。傳遞到記錄函數(shù)的重放數(shù)據(jù)包內(nèi)存只是從記錄器的內(nèi)存緩沖區(qū)傳遞的未初始化內(nèi)存。記錄器本身只知道如何解釋初始記錄器數(shù)據(jù)包字段:id、type、next、targetable、position和所有記錄數(shù)據(jù)包之間共享的角度,但不知道如何解釋不同數(shù)據(jù)包類型中剩余的附加內(nèi)存。為了正確地解釋額外的內(nèi)存,記錄處理程序必須與回放處理程序協(xié)同工作。

6.4 Recording Simulation Frames

? 來自單個模擬幀的所有記錄器數(shù)據(jù)包存儲在單個記錄器幀結(jié)構(gòu)中。圖6.3顯示了典型記錄器幀實例的數(shù)據(jù)結(jié)構(gòu)。第一個字段id表示記錄器幀的唯一標識符。接下來的兩個字段存儲指向上一個錄制幀以及下一個錄制幀(如果存在)的指針。

??由于幀時間可以變化,且能正確地將記錄幀與特定的時間戳關(guān)聯(lián)在在一起,這在記錄器系統(tǒng)中是至關(guān)重要的,同時存儲模擬的絕對時間以及自上次記錄的幀以來的增量時間,以便播放原汁原味的錄制幀。最后一個字段 packets 指向與幀相關(guān)聯(lián)的記錄器分組數(shù)據(jù)的單鏈接列表的頭部。由于包數(shù)據(jù)的遍歷始終是線性的,因此鏈表足以滿足快速訪問的時間需求。

? 管理記錄器數(shù)據(jù)的每個模擬幀主要集中在當在時間上向前或向后遍歷幀時快速訪問相鄰幀數(shù)據(jù)。每個新記錄的數(shù)據(jù)幀存儲一個指向第一個記錄器數(shù)據(jù)包的頭部指針以及指向附加模擬幀記錄的上一個和下一個地址的指針。使用雙鏈接列表數(shù)據(jù)結(jié)構(gòu)可以在添加或銷毀幀時實現(xiàn)最小的維護,因為新幀總是添加到列表的開頭,而刪除幀則是從列表的結(jié)尾剪除的。

??為了能夠正確地回放記錄的幀,通過存儲游戲世界的絕對時間以及自上次記錄幀以來的增量時間,能確定任意記錄幀表示的游戲世界中的任意特定時刻,并在時間步中計算差異。即使大多數(shù)游戲模擬運行在固定的時間步,一個模擬幀的實際時間量可能超過分配的時間預(yù)算。在每幀中存儲相對增量時間允許以與原始捕獲率相同的速率重放數(shù)據(jù)。

? 即使存儲了絕對時間戳和相對時間戳,為了簡單起見,其他系統(tǒng)與記錄器的交互嚴格基于游戲時間。計算正確的游戲時間利用每幀數(shù)據(jù)中存儲的絕對時間,而向后和向前擦除時間只利用幀中存儲的增量時間。

6.4.1 Recorder Manager

? 管理記錄器系統(tǒng)圍繞著兩個不同的職責,通過標準的輪詢更新循環(huán)記錄模擬幀后的數(shù)據(jù),并在擦洗記錄幀時重放記錄的數(shù)據(jù)。圖6.4顯示了記錄器管理員的成員數(shù)據(jù)和外部接口的高級類圖。

??記錄器管理器中最關(guān)鍵的內(nèi)部任務(wù)之一,就是正確分配和釋放記錄器數(shù)據(jù)包和幀。在內(nèi)部,管理器將內(nèi)存緩沖區(qū)用作循環(huán)緩沖區(qū),其中丟棄最舊的記錄幀,以允許記錄新幀。管理器的初始化從分配一個連續(xù)的內(nèi)存塊開始,以成為緩沖區(qū)內(nèi)部管理的內(nèi)存。在管理器開始記錄游戲?qū)ο髷?shù)據(jù)之前,記錄處理程序和回放處理程序就會開始注冊。

??游戲引擎的整體管理和記錄系統(tǒng)的更新計時由記錄管理器的單個實例處理。在游戲模擬循環(huán)結(jié)束后調(diào)用記錄器管理器的更新函數(shù),以確保在記錄處理程序處理每個游戲?qū)ο笾?,已完成對游戲?qū)ο蟮乃刑幚?。當記錄器管理器處理每個游戲?qū)ο髸r,首先根據(jù)對象類型對游戲?qū)ο筮M行分類,并使用相應(yīng)的記錄處理程序來確定從緩沖區(qū)請求的適當內(nèi)存量。一旦分配了內(nèi)存,記錄器管理器將分配的內(nèi)存和游戲?qū)ο笾苯觽鬟f給記錄處理程序進行序列化。信息包序列化后,管理器將正確地修復(fù)每個數(shù)據(jù)包指針,并將幀的起始指針分配給記錄的第一個信息包。

? 一旦記錄器幀內(nèi)的所有分組數(shù)據(jù)被序列化,管理器將更新當前記錄器數(shù)據(jù)第一幀的對應(yīng)下一幀和上一幀指針,以將新記錄的數(shù)據(jù)幀附加到雙鏈接列表。新的數(shù)據(jù)幀現(xiàn)在成為管理器在請求回放時使用的第一個數(shù)據(jù)幀。

? 附加信息存儲在管理器中,以允許快速訪問最近記錄的數(shù)據(jù)幀和最舊記錄的數(shù)據(jù)幀,以便在釋放幀和分配新幀時清除和更新雙鏈接列表。在錄像機回放期間,正在回放的數(shù)據(jù)的當前幀被單獨存儲,以便快速恢復(fù)游戲中的擦除以及有選擇地向前和向后移動。

6.4.2 Memory Management

??由于記錄器在固定內(nèi)存預(yù)算內(nèi)連續(xù)在后臺運行,每當內(nèi)存緩沖區(qū)無法分配請求的內(nèi)存量時,記錄器管理器將連續(xù)釋放最舊的記錄幀,直到有足夠的可用內(nèi)存來容納新的分配請求。連續(xù)釋放最舊的記錄幀并用新的幀數(shù)據(jù)替換這些幀,在內(nèi)存緩沖區(qū)內(nèi)實現(xiàn)最小的內(nèi)存碎片,從而使緩沖區(qū)充當循環(huán)緩沖區(qū),即使只使用連續(xù)內(nèi)存。

? 圖6.5顯示了當幀被連續(xù)添加到緩沖區(qū)中時的內(nèi)存狀態(tài),直到剩余的可用內(nèi)存量不足以容納額外的一幀記錄器數(shù)據(jù)。

? 在本例中,圖6.6顯示了內(nèi)存緩沖區(qū)如何處理第一幀存儲數(shù)據(jù)(第1幀)的釋放,該幀立即被第3幀的數(shù)據(jù)替換。作為循環(huán)緩沖區(qū),使用此設(shè)置時,唯一無法存儲的部分將位于最后記錄的幀和第一個記錄的幀之間,以及位于內(nèi)存緩沖區(qū)分配的內(nèi)存尾部的不可用內(nèi)存的某些部分。

6.5 Frame Playback

??為了回放錄制的幀,需要考慮一些因素,以防止影響游戲模擬本身。由于恢復(fù)記錄的數(shù)據(jù)(如位置和方向)將直接作用于游戲?qū)ο蟮漠斍盃顟B(tài),因此記錄器必須等到記錄最新幀的分組數(shù)據(jù)結(jié)束后,才能暫停游戲模擬并覆蓋游戲?qū)ο髷?shù)據(jù)。相反,在錄像機退出播放模式之前,必須將上次錄制的幀數(shù)據(jù)重新應(yīng)用到每個游戲?qū)ο螅允姑總€游戲?qū)ο笤趩⒂貌シ拍J街疤幱谙嗤臓顟B(tài)。

??當向記錄器管理器請求回放模式時,除非外部源已請求記錄器及時跳回特定的時間戳,否則模擬將暫停,而無需記錄器執(zhí)行任何其他工作。在這種情況下,當基于時間戳遍歷幀數(shù)據(jù)時,記錄器將從最新記錄的幀數(shù)據(jù)開始,并在數(shù)據(jù)的鏈接列表中向后工作,以找到與所請求的確切時間戳(緊靠在所請求的時間之前的第一個時間戳)匹配的幀,或鏈接列表中最舊的剩余時間戳。

? 在時間上向前和向后擦洗的工作原理與游戲世界的時間完全不同。擦洗可以是向前或向后移動一幀數(shù)據(jù),也可以以與原始記錄完全相同的速率重放數(shù)據(jù)。實時擦除時,記錄器的更新循環(huán)將保持自請求擦除以來的增量時間,并且僅基于每個幀數(shù)據(jù)的累積增量時間連續(xù)向后或向前移動重放幀數(shù)據(jù)。基于原始錄制速率管理時間增量允許重播數(shù)據(jù)幀,而不考慮游戲的增量時間,這可能與每幀記錄器數(shù)據(jù)之間的實際增量時間不同。

6.5.1 Controlling Recorder Playback

??一旦進入回放模式,使用自定義控制器方案覆蓋標準控制器方案將提供操作回放所需的靈活性。尤其是,對于與回放交互而言,至少兩種快速轉(zhuǎn)發(fā)和重繞的操作速度是至關(guān)重要的。能夠以正常的游戲模擬速度播放數(shù)據(jù),可以對動畫工作進行微調(diào),而每次只需將錄制器移一幀,這通常是調(diào)試決策邏輯所必需的。由于行為選擇通常是在特定的模擬幀上確定的,因此所有相關(guān)的記錄調(diào)試原語通常只在單個記錄幀期間繪制。此外,控制器方案用于有選擇地確定在回放期間呈現(xiàn)哪個游戲?qū)ο蟮恼{(diào)試原語,這有助于縮小對任何特定游戲?qū)ο蟮恼{(diào)試范圍。

6.6 Debugging AI

? 在以前的Treyarch中,AI廣泛地使用了一個錄制系統(tǒng)來進行動畫、決策和指導(dǎo)調(diào)試。為了調(diào)試動畫播放和選擇,人工智能的整個動畫樹都會被記錄下來,每個人工智能的動畫貢獻、速率、標準化時間和選擇標準都可用。在調(diào)試動畫彈出窗口、不正確的動畫混合和不正確的動畫選擇時,能按時精確地來回移動是關(guān)鍵。

??使用記錄器調(diào)試決策允許在游戲中記錄人工智能的整個行為樹,該行為樹顯示樹中當前正在執(zhí)行的分支以及其他可能的決策分支中的哪些分支過早終止執(zhí)行。每當一個人工智能表現(xiàn)出不正確的行為或者沒有選擇正確的行為時,立即進入錄像機回放是很簡單的;及時向后拖動,并立即理解行為樹的哪個分支沒有按預(yù)期執(zhí)行。

??使用記錄器調(diào)試與轉(zhuǎn)向相關(guān)的錯誤,通常涉及理解和解決不在預(yù)期中的速度振蕩或錯誤的動畫選擇,這導(dǎo)致人工智能彼此沖突或其他障礙。

6.6.1 Synchronizing External AI Tools

? 除了在游戲中利用記錄器調(diào)試之外,還可以直接在當前時間戳中引入外部工具。使用自定義消息總線系統(tǒng),記錄器將廣播當前時間戳被清除的內(nèi)容,這允許其他工具將外部存儲信息的回放與記錄器的可視回放同步。利用相同的消息總線,記錄器也可以通過其他工具進行外部控制,以進入回放模式并跳轉(zhuǎn)到特定的時間戳。此外,在引擎中添加了一種稱為記錄器斷言的新型斷言,它將暫停游戲模擬,并將攝像機立即設(shè)置為特定的游戲?qū)ο?,通知所有其他外部工具通過記錄器與目標游戲?qū)ο笸健?/p>

6.7 Conclusion

? 想要找出人工智能出問題的根本原因是個很大的挑戰(zhàn),更不用說還得在需求不斷變化、功能不斷迭代的開發(fā)過程中去完成這個任務(wù)。盡管記錄器并不是萬能的,也不能阻止BUG的產(chǎn)生,但它能直接在生產(chǎn)層面上進行調(diào)試,而無需在用戶層面去重現(xiàn)問題,這使得它成為了一個十分重要的生產(chǎn)工具,可以節(jié)省非常多的工時。

??任何調(diào)試信息或文本輸出的記錄,都無法在即時性上與記錄器相比。Treyarch人工智能的過去和現(xiàn)在的發(fā)展,都使用一個記錄器作為中心工具,圍繞這個工具和過程來創(chuàng)建其他的工具和過程,這樣一個系統(tǒng)的好處還在持續(xù)的被挖掘中。

References Dickinson

P. 2001. Instant replay: Building a game engine with reproducible behavior. Gamasutra. http://www.gamasutra.com/view/feature/131466/instant_replay_building_ a_game_.php?

Llopis, N. 2008. Back to the future. Game Developer Magazine. http://gamesfromwithin.com/ back-to-the-future-part-1,http://gamesfromwithin.com/back-to-the-future-part-2 (accessed June 11, 2016).

文章來源:http://www.gameaipro.com/?

如侵犯版權(quán),請聯(lián)系譯者刪除。??

翻譯不易,讀者若需要轉(zhuǎn)載,請注明出處。??

若有錯誤,歡迎指正。

用游戲內(nèi)置記錄器系統(tǒng)調(diào)試AI | Game AI Pro的評論 (共 條)

分享到微博請遵守國家法律
新化县| 浦江县| 全州县| 金溪县| 株洲县| 天峻县| 荣昌县| 灵宝市| 高雄县| 巴中市| 信宜市| 瑞昌市| 沧州市| 墨竹工卡县| 集贤县| 措勤县| 南康市| 大城县| 巧家县| 扶沟县| 武清区| 潼南县| 长武县| 武夷山市| 星座| 正阳县| 桃园县| 安义县| 泰和县| 商洛市| 勃利县| 泸州市| 法库县| 昭通市| 手游| 灵宝市| 宁国市| 临桂县| 古丈县| 金乡县| 大方县|