行為樹(behavior tree)

簡單解釋一下這顆行為樹的功能。unreal的行為樹的執(zhí)行流是從上到下弧哎,從左到右,每個節(jié)點執(zhí)行之后都會有相應(yīng)的返回值true或者false, 返回之后控制權(quán)轉(zhuǎn)移給當(dāng)前節(jié)點的父節(jié)點稚虎,來確定下一步的執(zhí)行:

行為樹在執(zhí)行時的第一個入口是ROOT節(jié)點撤嫩,所有的行為樹都會有此root節(jié)點, 當(dāng)root節(jié)點執(zhí)行完成之后蠢终,會重新開始一次執(zhí)行序攘,類似于無終止條件的循環(huán)。

進(jìn)入root節(jié)點之后寻拂,進(jìn)入Ai State這個Selector節(jié)點程奠,依次從左到右執(zhí)行他的三個子節(jié)點,當(dāng)任一節(jié)點執(zhí)行返回true的時候則不再執(zhí)行后續(xù)的子節(jié)點祭钉,直接返回true給父節(jié)點瞄沙。這個節(jié)點的邏輯就是讓被控制的Entity進(jìn)入追逐玩家狀態(tài)還是進(jìn)入巡邏狀態(tài)。

chase player節(jié)點是一個sequence慌核,這個節(jié)點被一個decorator節(jié)點修飾距境,導(dǎo)致只有在has line of sight返回true的時候才能進(jìn)入執(zhí)行,如果decorator返回false則執(zhí)行流回到Ai State節(jié)點垮卓。它的三個子節(jié)點會從左到右依次執(zhí)行垫桂,用來控制Entity去追打玩家的具體步驟:

Rotate to face BB entry,朝向目標(biāo)

BTT_ChasePlayer設(shè)置自己進(jìn)入追擊狀態(tài)粟按,追擊速度為500

Move To诬滩,移動到敵人位置

所以這個狀態(tài)內(nèi),被控制的Entity會首先朝向敵對目標(biāo)钾怔,然后設(shè)置自己為速度500的追逐狀態(tài)碱呼,追逐到目標(biāo)之后,返回true, 如果其中任意一個節(jié)點返回false宗侦,則后續(xù)子節(jié)點不再執(zhí)行愚臀,當(dāng)前節(jié)點也返回false。

Patrol節(jié)點是一個不帶decorator的Sequence矾利,他執(zhí)行的時候也是從左到右執(zhí)行三個子節(jié)點:

BTT_FindRandomPatrol設(shè)置自己為以自己為中心的隨機(jī)巡邏狀態(tài)姑裂,巡邏速度為125,巡邏半徑為1000男旗,獲取半徑上的一個隨機(jī)點

Move To移動上一個節(jié)點確定的位置

Wait等待4-5秒

所以這個Patrol的執(zhí)行內(nèi)容就是舶斧,以125的速度走到以自身為中心的半徑1000的圓的任意一點,走到之后等待4-5秒察皇,然后返回茴厉。

最后的等待1s是為了兩個狀態(tài)都無法進(jìn)入的時候的fallback泽台,避免root節(jié)點空跑占用cpu。

行為樹定義

整個行為樹就類似于我們寫的一個函數(shù)調(diào)用矾缓,他的樹形結(jié)構(gòu)就類似于傳說中的圖形化編程怀酷。一顆行為樹最終運行時,還依賴于他的執(zhí)行環(huán)境嗜闻,例如范圍內(nèi)有沒有目標(biāo)就可以讓這棵樣例行為樹控制的Entity呈現(xiàn)出不同的表現(xiàn)蜕依。這些行為樹的運行環(huán)境,我們可以抽象為一個key value的容器琉雳,叫做黑板Blackboard样眠, 代表行為樹的內(nèi)部存儲的所有參數(shù)。當(dāng)一顆行為樹在特定的黑板環(huán)境中運行時翠肘,行為樹的控制權(quán)不斷地在樹形結(jié)構(gòu)中轉(zhuǎn)移檐束,類似于程序計數(shù)器Program Counter。運行時某一特定時刻的擁有控制權(quán)的節(jié)點集合則定義為行為樹的格局Configuration锯茄。

因此一顆行為樹的運行時描述厢塘,包括如下三個方面:

行為樹的自身結(jié)構(gòu)茶没,所有節(jié)點的邏輯關(guān)系和相關(guān)參數(shù):Structure

行為樹的執(zhí)行環(huán)境肌幽,一個鍵值對的集合:Blackboard

行為樹的活動節(jié)點集合:Configuration

行為樹的結(jié)構(gòu)是以樹的形式來組織的,每個節(jié)點都有相印的節(jié)點類型抓半,配合相關(guān)參數(shù)承載不同的功能喂急。在上面的行為樹樣例中,我們可以對相關(guān)的節(jié)點進(jìn)行歸類:

黑色的調(diào)度控制節(jié)點(composition node)笛求,包括Root,Patrol,Chase Player

紫色的行為表現(xiàn)節(jié)點(action node)廊移, 包括Rotate to face BB entry,BTT_ChasePlayer,Wait,Move To,BTT_FindRandomPatrol, 他們可以當(dāng)作函數(shù)來調(diào)用探入,通過傳入特定的參數(shù)去調(diào)用這些行為節(jié)點狡孔,來完成特定任務(wù)。

藍(lán)色的修飾器節(jié)點(decorator node)蜂嗽,用來作為特定節(jié)點的前置條件苗膝。

上面的這幾類節(jié)點類型都是來自于unreal,在不同的行為樹之中植旧,對于節(jié)點的劃分也是各有不同辱揭。總的來說病附,一個行為樹的結(jié)構(gòu)描述都具有如下幾個部分:

行為樹是一個樹結(jié)構(gòu)问窃,根節(jié)點就是root節(jié)點,作為行為樹的入口完沪,節(jié)點類型為Root,每個行為樹有且只有一個Root類型節(jié)點域庇;

所有的葉子節(jié)點的類型一定是Action,同時Action類型的節(jié)點一定不能作為非葉子節(jié)點來使用。

非葉子節(jié)點也稱為組合節(jié)點Composition听皿,可以有一個或多個子節(jié)點咕别,root節(jié)點一定只有一個子節(jié)點

Action節(jié)點類型和Composition的節(jié)點類型可以做進(jìn)一步的細(xì)分

每種類型的的組合節(jié)點能擁有的子節(jié)點數(shù)量與節(jié)點類型有關(guān)

一個節(jié)點的所有子節(jié)點是一個有序列表

有些節(jié)點可以附加特定參數(shù)來執(zhí)行,有些節(jié)點則不需要參數(shù)

一顆行為樹可以以葉子節(jié)點的形式被另外一顆行為樹進(jìn)行調(diào)用写穴,就相當(dāng)于一棵樹掛接到了另外一棵樹上一樣

行為樹的運行

在明確了行為樹的定義之后惰拱,行為樹的控制表現(xiàn)還依賴于它在特定環(huán)境下的執(zhí)行路徑。為了推理行為樹的執(zhí)行路徑啊送,我們需要對行為樹的運行規(guī)則做規(guī)定偿短。這里我們把一個節(jié)點標(biāo)記為N, 他的子節(jié)點列表標(biāo)記為N.sons,第i個子節(jié)點為N.sons[i],他的父節(jié)點標(biāo)記為N.parent馋没, 節(jié)點的運行標(biāo)記為N.run()昔逗,運行完成之后返回true或者false,代表節(jié)點執(zhí)行成功或者失敗篷朵。

對于Action節(jié)點來說勾怒,因為他是葉子節(jié)點, 不帶控制功能声旺,所以他是不影響執(zhí)行流的笔链。能影響執(zhí)行流的節(jié)點只能是Composition節(jié)點。在具體的行為樹節(jié)點類型定義中腮猖,常見的Composition節(jié)點細(xì)分見下:

Sequence節(jié)點鉴扫,他的執(zhí)行流程就是順序執(zhí)行所有的子節(jié)點,當(dāng)一個子節(jié)點執(zhí)行結(jié)果為false的時候終止執(zhí)行并返回false澈缺,如果沒有子節(jié)點返回false則返回true坪创。他的run函數(shù)定義如下:

boolrun(){for(std::uint32_ti=0;i<sons.size();i++){if(!sons[i].run()){returnfalse;}}returntrue;}

Select節(jié)點,他的執(zhí)行流程就是順序執(zhí)行所有的子節(jié)點姐赡,當(dāng)一個子節(jié)點執(zhí)行結(jié)果為true的時候終止執(zhí)行并返回true莱预,如果沒有子節(jié)點返回true則返回false。他的run函數(shù)定義如下:

boolrun(){for(std::uint32_ti=0;i<sons.size();i++){if(sons[i].run()){returntrue;}}returnfalse;}

IfElse節(jié)點项滑,他擁有三個子節(jié)點依沮,當(dāng)?shù)谝粋€子節(jié)點返回true的時候執(zhí)行第二個子節(jié)點并返回此子節(jié)點的返回值,否則執(zhí)行第三個節(jié)點并返回這個節(jié)點的返回值杖们。他的run函數(shù)定義如下:

boolrun(){if(sons[0].run()){returnsons[1].run();}else{returnsons[2].run();}}

While節(jié)點悉抵, 他有兩個子節(jié)點,當(dāng)?shù)谝粋€子節(jié)點執(zhí)行返回true的時候摘完,執(zhí)行第二個子節(jié)點然后重新開始執(zhí)行流程姥饰,如果第一個子節(jié)點返回false則執(zhí)行完成,并返回true孝治。他的run函數(shù)定義如下:

boolrun(){while(sons[0].run()){sons[1].run();}returntrue;}

Root節(jié)點列粪,他只有一個子節(jié)點审磁,當(dāng)子節(jié)點返回的時候,返回子節(jié)點的返回值:

boolrun(){returnsons[0].run();}

Probility節(jié)點岂座,他根據(jù)內(nèi)部存儲的權(quán)重參數(shù)态蒂,每次執(zhí)行時隨機(jī)選擇特定子節(jié)點執(zhí)行,并返回執(zhí)行結(jié)果:

boolrun(){std::uint32_tidx=random.choice();//根據(jù)權(quán)重選擇節(jié)點索引returnsons[idx].run();}

針對Sequence類型费什,還有一些衍生類型:

AlwaysSequence, 順序執(zhí)行所有子節(jié)點钾恢,不管子節(jié)點的返回值,執(zhí)行完所有子節(jié)點之后返回true鸳址;

RandomSequence, 每次執(zhí)行的時候都以隨機(jī)序列去執(zhí)行所有的子節(jié)點瘩蚪,任一子節(jié)點在執(zhí)行時返回false則中斷執(zhí)行并返回false,否則返回true

在上面的節(jié)點類型分類中稿黍,我們并沒有看到decorator節(jié)點的定義疹瘦,但是我們可以通過Sequence節(jié)點模擬出來,只需要將decorator里面的判斷函數(shù)作為Action節(jié)點去執(zhí)行巡球,對于被任意修飾器修飾的節(jié)點都可以轉(zhuǎn)換為含有修飾器判斷節(jié)點和具體執(zhí)行節(jié)點的Sequence節(jié)點進(jìn)行替換言沐。在Unreal中,他優(yōu)先采用decorator方式的理由如下:

在行為樹的標(biāo)準(zhǔn)模型中酣栈,條件語句是任務(wù)葉節(jié)點险胰,除了成功和失敗,它不會執(zhí)行任何其他操作钉嘹。雖然沒有什么可以阻止您執(zhí)行傳統(tǒng)的條件語句任務(wù)鸯乃,但是強(qiáng)烈建議使用我們的裝飾器(Decorator)系統(tǒng)處理條件語句鲸阻。

使條件語句成為裝飾器而非任務(wù)有幾個顯著的優(yōu)點跋涣。

首先,條件語句裝飾器使行為樹UI更直觀鸟悴、更易于閱讀陈辱。由于條件語句位于它們所控制的分支樹的樹根,如果不滿足條件語句细诸,您可以立即看到行為樹的哪個部分是“關(guān)閉的”沛贪。而且,由于所有的樹葉都是操作任務(wù)震贵,因此更容易看到行為樹對實際操作的排序利赋。在傳統(tǒng)模型中,條件語句位于樹葉之間猩系,因此您需要花費更多的時間來確定哪些樹葉是條件語句媚送,哪些樹葉是操作。

條件語句裝飾器的另一個優(yōu)點是寇甸,很容易讓這些裝飾器充當(dāng)行為樹中關(guān)鍵節(jié)點的觀察者(等待事件)塘偎。這個功能對于充分利用行為樹的事件驅(qū)動性質(zhì)至關(guān)重要疗涉。

行為樹的驅(qū)動方式

在標(biāo)準(zhǔn)行為樹中,節(jié)點的運行是由tick-driven的吟秩,每間隔一段時間開始從root節(jié)點開始執(zhí)行咱扣。當(dāng)特定外部事件需要響應(yīng)的時候,有時會按需調(diào)用root節(jié)點的執(zhí)行涵防。由于這個行為樹在執(zhí)行的時候闹伪,對于上次的執(zhí)行結(jié)果是無記憶的,所以Entity的狀態(tài)機(jī)要處理好各種追擊壮池、攻擊祭往、受擊、巡邏狀態(tài)的強(qiáng)制切換火窒,避免表現(xiàn)異常硼补。最壞情況下一次執(zhí)行會遍歷所有的節(jié)點,如果tick間隔過小熏矿,則行為樹執(zhí)行會消耗大量cpu已骇。同時如果一段時間內(nèi)執(zhí)行的路徑結(jié)果都相同,行為樹就相當(dāng)于空跑浪費cpu票编。所以在標(biāo)準(zhǔn)行為樹模型里面褪储,如何動態(tài)的選擇tick間隔是優(yōu)化的重點。

為了解決這種tick間隔帶來的問題慧域,行為樹的模型演進(jìn)出了基于事件驅(qū)動(event-driven)的行為樹鲤竹。這里行為樹的更新不再是基于tick,而是基于任務(wù)的完成和外部事件的trig。同時每個Action節(jié)點開始有了狀態(tài)昔榴,他的執(zhí)行可能不再是調(diào)用之后立即返回歉糜,而是開始了一個需要一定時間才能執(zhí)行的過程,當(dāng)過程執(zhí)行結(jié)束之后才返回執(zhí)行結(jié)果阔籽。同時纲缓,任意的一個過程現(xiàn)在都需要支持中斷操作,以支持外部環(huán)境的改變引發(fā)的更高優(yōu)先級任務(wù)的處理仰禽。

以追逐目標(biāo)這個例子來說:

在tick驅(qū)動的行為樹中氮墨,我們需要定期從根節(jié)點執(zhí)行,查詢我們是否已經(jīng)離目標(biāo)點足夠近吐葵,如果足夠近則執(zhí)行已經(jīng)到達(dá)目標(biāo)的分支规揪,否則執(zhí)行追逐邏輯。到發(fā)起追逐到追逐完成期間温峭,可能多次執(zhí)行行為樹猛铅。

在事件驅(qū)動的行為樹中,一旦進(jìn)入了Move To節(jié)點诚镰,則會發(fā)起一個尋路過程奕坟,同時節(jié)點標(biāo)記為running狀態(tài)祥款。在尋路到目標(biāo)之后過程返回,控制權(quán)移交到當(dāng)前節(jié)點的父節(jié)點月杉,然后進(jìn)行下一步的操作刃跛。一個完整的流程不涉及到行為樹的其他節(jié)點,相對tick驅(qū)動的行為樹來說苛萎,行為樹的決策消耗大大降低了桨昙。

在尋路過程中,目標(biāo)可能已經(jīng)死亡或者傳送了導(dǎo)致目標(biāo)丟失腌歉,此時我們需要終止當(dāng)前過程的執(zhí)行蛙酪。在事件驅(qū)動的行為樹中,為了實現(xiàn)對外部事件的響應(yīng)功能翘盖,常見的可選方案有如下兩個:

為過程添加前置條件桂塞,在過程執(zhí)行期間定期檢查前置條件是否滿足,如果不滿足則中斷當(dāng)前過程的執(zhí)行并返回false馍驯。

為行為樹添加Parallel節(jié)點和WaitEvent節(jié)點阁危,

Parallel節(jié)點執(zhí)行時,會順序執(zhí)行所有的子節(jié)點汰瘫,而不會阻塞在子節(jié)點的過程調(diào)用上狂打,如果任一子節(jié)點返回,則所有的其他節(jié)點都會被打斷混弥, 同時Parallel節(jié)點返回true趴乡。為了支持這個結(jié)構(gòu),我們需要對原有的行為樹調(diào)用結(jié)構(gòu)進(jìn)行修正蝗拿,因為這里暫時不再給出他的run函數(shù)定義晾捏。

WaitEvent節(jié)點執(zhí)行時,會注冊對特定事件的回調(diào)蛹磺,然后阻塞不返回粟瞬。當(dāng)行為樹接收到特定事件之后,對應(yīng)的回調(diào)句柄被調(diào)用萤捆,相關(guān)的WaitEvent節(jié)點返回true。

通過在Parallel節(jié)點下同時掛載多個節(jié)點俗批,就可以達(dá)到在執(zhí)行特定過程的時候?qū)ν獠渴录M(jìn)行響應(yīng)的功能俗或。

在Unreal和本人所在的項目組都采取的是Parallel方案,但是引入Parallel方案也帶來了新的問題岁忘,就是策劃可能配置出多個過程同時進(jìn)行的Parallel節(jié)點辛慰。試想一下同時開啟兩個對不同目標(biāo)點的尋路所帶來的后果,Entity的狀態(tài)表現(xiàn)會非常的糟糕干像。Parallel結(jié)構(gòu)里面不能同時開啟多個持續(xù)性過程帅腌,一般來說是一個主要目標(biāo)過程附加一些WaitEvent或者WaitTimer的阻塞過程驰弄,這些附加的阻塞過程不會干擾主要目標(biāo)過程,他們的執(zhí)行也是一些輔助性的工作速客。

所以在Unreal中戚篙,特別提到了Simple Parallel節(jié)點。

簡單平行節(jié)點只有兩個子項:一個必須是單個任務(wù)節(jié)點(擁有可選的裝飾器)溺职,另一個可以是完整的子樹岔擂。可以將簡單平行節(jié)點理解為“執(zhí)行A的同時浪耘,也在執(zhí)行B"乱灵。例如“攻擊敵人,同時也朝敵人移動七冲⊥匆校“從基本上而言,A是主要任務(wù)澜躺,B是在等待A完成期間的次要任務(wù)或填充任務(wù)状原。?

行為樹與狀態(tài)機(jī)

但是如果遇到了需要終止當(dāng)前主要任務(wù)的事件,則Parallel結(jié)構(gòu)也是不夠用的苗踪。例如在巡邏過程中遇到敵人颠区,我們需要立即進(jìn)入戰(zhàn)斗狀態(tài),此時需要中斷當(dāng)前任務(wù)的執(zhí)行來開啟新的任務(wù)通铲。類似的還有在不斷的放技能過程中如果發(fā)現(xiàn)自己的血量低于了特定百分比則進(jìn)入狂暴狀態(tài)毕莱。為了處理這種緊急事件的打斷,我組的實現(xiàn)方案是在行為樹的上層加一個狀態(tài)機(jī)來進(jìn)行管理颅夺。

狀態(tài)機(jī)有一個默認(rèn)狀態(tài)朋截,在每個狀態(tài)中執(zhí)行特定任務(wù)的行為樹,同時狀態(tài)與狀態(tài)之間有一個基于事件的跳轉(zhuǎn)表吧黄。當(dāng)Entity的AI接受到一個外部事件的時候:

當(dāng)前狀態(tài)所執(zhí)行的行為樹優(yōu)先處理這個事件部服,查看當(dāng)前阻塞的WaitEvent的節(jié)點是否有對此事件的監(jiān)聽。如果有拗慨,則行為樹來處理這個事件

如果行為樹沒有對這個事件進(jìn)行處理廓八,則狀態(tài)機(jī)來查看當(dāng)前狀態(tài)下是否有對于這個事件的新狀態(tài)跳轉(zhuǎn)。如果有對應(yīng)的跳轉(zhuǎn)赵抢,終止當(dāng)前行為樹的執(zhí)行剧蹂,跳轉(zhuǎn)到對應(yīng)的狀態(tài),開啟新狀態(tài)下的行為樹的執(zhí)行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烦却,一起剝皮案震驚了整個濱河市宠叼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌其爵,老刑警劉巖冒冬,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伸蚯,死亡現(xiàn)場離奇詭異,居然都是意外死亡简烤,警方通過查閱死者的電腦和手機(jī)剂邮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乐埠,“玉大人抗斤,你說我怎么就攤上這事≌筛溃” “怎么了瑞眼?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長棵逊。 經(jīng)常有香客問我伤疙,道長,這世上最難降的妖魔是什么辆影? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任徒像,我火速辦了婚禮,結(jié)果婚禮上蛙讥,老公的妹妹穿的比我還像新娘锯蛀。我一直安慰自己,他們只是感情好次慢,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布旁涤。 她就那樣靜靜地躺著,像睡著了一般迫像。 火紅的嫁衣襯著肌膚如雪劈愚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天闻妓,我揣著相機(jī)與錄音菌羽,去河邊找鬼。 笑死由缆,一個胖子當(dāng)著我的面吹牛注祖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播犁功,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氓轰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浸卦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤案糙,失蹤者是張志新(化名)和其女友劉穎限嫌,沒想到半個月后靴庆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡怒医,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年炉抒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稚叹。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡焰薄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扒袖,到底是詐尸還是另有隱情塞茅,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布季率,位于F島的核電站野瘦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏飒泻。R本人自食惡果不足惜鞭光,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泞遗。 院中可真熱鬧惰许,春花似錦、人聲如沸史辙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽髓霞。三九已至卦睹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間方库,已是汗流浹背结序。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留纵潦,地道東北人徐鹤。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像邀层,于是被迫代替她去往敵國和親返敬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容