2D特效動(dòng)畫的播放機(jī)制

一. 簡介

動(dòng)畫在2D游戲里用得十分廣泛, 根據(jù)這些動(dòng)畫的特點(diǎn),我們可以大概歸為3類

1. 粒子動(dòng)畫

這種動(dòng)畫是由幾百甚至上千個(gè)粒子構(gòu)成, 所有粒子都共享一個(gè)紋理, 這些粒子都是從一個(gè)發(fā)射器發(fā)出, 加以一定的隨機(jī)因素, 在不同發(fā)射速度和重力等外力作用下,每個(gè)粒子呈現(xiàn)不一樣的運(yùn)動(dòng)狀態(tài), 大量粒子可以組合成各種各樣不一樣的效果, 比如煙花, 火焰. 粒子動(dòng)畫的實(shí)現(xiàn)一般都會(huì)使用批次渲染和對(duì)象池來保證性能.

2. 骨骼動(dòng)畫

這種動(dòng)畫通常用于表現(xiàn)有多個(gè)動(dòng)作的角色, 它通常是由骨骼(bone)和綁定在骨骼上的蒙皮(skin/mesh)構(gòu)成.

動(dòng)畫師通常在spine(2d)或者3dmax等工具里面對(duì)骨骼動(dòng)作進(jìn)行設(shè)計(jì), 同時(shí)對(duì)蒙皮進(jìn)行編輯.

3. 特效動(dòng)畫

特效動(dòng)畫不需要或者難以使用骨骼進(jìn)行表達(dá), 比如一個(gè)刀光效果或者一閃一閃的星星, 我們可以使用最原始的實(shí)現(xiàn)方式, 對(duì)動(dòng)畫的每一幀都畫一張圖片, 依次連續(xù)展示這些圖片就可以達(dá)到動(dòng)畫效果.

但是這種方法實(shí)現(xiàn)的動(dòng)畫過于浪費(fèi)空間和內(nèi)存. 其中有非常多的特效我們可以通過關(guān)鍵幀動(dòng)畫的方式來實(shí)現(xiàn), 常使用Flash工具進(jìn)行關(guān)鍵幀動(dòng)畫的設(shè)計(jì).

本文中下面只討論關(guān)鍵幀動(dòng)畫的實(shí)現(xiàn).

二. 關(guān)鍵幀動(dòng)畫介紹

1. 動(dòng)畫舉例

我們先來看下面這樣一個(gè)動(dòng)畫:

strength.gif

動(dòng)畫設(shè)計(jì)師進(jìn)行編輯的時(shí)候, 是這樣的:

strength_design.png

設(shè)計(jì)師把動(dòng)畫分成了4層, 每一層里帶有黑色小點(diǎn)的就是關(guān)鍵幀,

  • 底座: 這一層就只有一個(gè)關(guān)鍵幀, 放入了一個(gè)靜態(tài)的底座圖片
  • 鐵錘: 這一層就放了一個(gè)鐵錘, 鐵錘在每個(gè)關(guān)鍵幀里都具備不同的位置和角度, 在動(dòng)畫播放過程中, 在2個(gè)關(guān)鍵幀之間的鐵錘的位置和角度, 自動(dòng)進(jìn)行插值運(yùn)算, 這個(gè)地方一般使用線性插值, 也可以使用更復(fù)雜的貝塞爾曲線插值.
  • 火花2: 前面幾幀是空白的,到后面鐵錘敲打在底座上時(shí), 會(huì)在后面幾幀產(chǎn)生火花, 由于這幾幀火花使用的都是不同的圖片, 而且間隔最多1-2幀,所以這個(gè)地方不需要進(jìn)行插值運(yùn)算
  • 火花1: 同火花2

2. 關(guān)鍵幀動(dòng)畫的好處

從上面的一個(gè)動(dòng)畫分析,我們可以看到關(guān)鍵幀動(dòng)畫的好處:

  • 節(jié)省了資源
  • 動(dòng)畫分層設(shè)計(jì), 邏輯清晰

3. 關(guān)鍵幀動(dòng)畫的適用范圍

我們可以看到 2個(gè)關(guān)鍵幀之間, 元件可以對(duì)下面的幾種屬性進(jìn)行插值計(jì)算從而實(shí)現(xiàn)動(dòng)畫的平滑過渡:

  • 位置(x,y)
  • 旋轉(zhuǎn)和傾斜(rotation/skew)
  • 縮放(scale)
  • 透明度(alpha)
  • 顏色(color-rgb)

如果我們要做的動(dòng)畫不在上面說的這幾種范圍內(nèi)(比如對(duì)元件進(jìn)行Z軸翻轉(zhuǎn)), 那么就不適合使用關(guān)鍵幀動(dòng)畫.

三. 播放機(jī)制的實(shí)現(xiàn)

1. 特效結(jié)構(gòu)圖

從flash編輯器里的動(dòng)畫分層圖, 我們可以直接腦補(bǔ)出以下這張結(jié)構(gòu)圖:

flash_layer.png

2. 播放步驟

1) 創(chuàng)建4層空的容器層

2) 一幀一幀往后解析, 對(duì)于每一個(gè)容器層

  • 容器層當(dāng)前為空時(shí), 如果遇到關(guān)鍵幀則創(chuàng)建該關(guān)鍵幀對(duì)應(yīng)圖片放入
  • 容器層當(dāng)前不為空, 預(yù)先判斷下一個(gè)關(guān)鍵幀內(nèi)容,
    • 如果下一個(gè)關(guān)鍵幀是對(duì)本幀圖片進(jìn)行了屬性修改(5種屬性), 那么根據(jù)當(dāng)前幀位置進(jìn)行插值計(jì)算, 修改本幀圖片的屬性
    • 如果下一個(gè)關(guān)鍵幀是只是更換成另外一張圖片,那么本幀保持不變直到播放到下一個(gè)關(guān)鍵幀時(shí)替換圖片
    • 如果當(dāng)前幀遇到空白幀, 則刪除容器里的所有內(nèi)容

3. cocos2d-x的實(shí)現(xiàn)

對(duì)于容器層我們不需要?jiǎng)?chuàng)建實(shí)際的顯示節(jié)點(diǎn), 我們可以畫出一個(gè)特效動(dòng)畫在某一瞬間的顯示樹結(jié)構(gòu):

cocos_effect.png

四. 性能優(yōu)化

1. 使用紋理集 textureAtlas

我們可以把以上例子中使用到的散圖, 整合到一張大圖上(sprite sheet), 減少多次的io讀文件, 讓動(dòng)畫播放更加流暢, 也為下一步的批次渲染優(yōu)化打下基礎(chǔ).

2. 盡可能的批次渲染

我們知道在opengl進(jìn)行繪圖的時(shí)候, 如果我們幾個(gè)圖形都有一樣的顯示狀態(tài)( 紋理, shader及其uniform參數(shù), blend方式), 那么我們通過一次draw就可以同時(shí)畫出這幾個(gè)圖形.

在cocos2d-x v3.x版本里, 底層會(huì)自動(dòng)做判斷合并多次draw為一次批次渲染, 而在v2.x里, 我們需要自己實(shí)現(xiàn), 一個(gè)小成本的做法就是, 當(dāng)判斷可以批次渲染的時(shí)候, 在原本Node.addChild(sprite)的地方, 給改成 batchNode.addChild(sprite)即可.

渲染樹結(jié)構(gòu)如下:

flash_batch.png

3. 合理使用對(duì)象池

如果特效是長時(shí)間的不斷的循環(huán)播放, 那么我們在remove元件的時(shí)候, 最好不要馬上銷毀, 可以把它放入一個(gè)對(duì)象池里, 需要使用的時(shí)候,重新初始化元件拿出來使用就可以了.

五. 功能擴(kuò)展: 嵌套子特效

為了節(jié)省資源, 動(dòng)畫設(shè)計(jì)師可能會(huì)在某一層里放入以前做過的另外一個(gè)特效, 我們可以簡單的調(diào)整代碼就可以做到嵌套播放, 播放時(shí)的一個(gè)渲染樹結(jié)構(gòu)如下:

flash_subeffect.png

由于子特效很可能使用跟父特效不一樣的紋理, 如果我們?nèi)耘f想使用批次渲染,

我們有2種做法:

  • A. 動(dòng)態(tài)合并特效紋理: 除非我們有太多的draw call需要合并, 不然動(dòng)態(tài)合并紋理的開銷明顯不合算
  • B. 盡可能把使用相同紋理的相鄰層(個(gè)數(shù)超過1個(gè)才有合并的意義)進(jìn)行批次渲染

我們選擇B做法,那么調(diào)整過后, 渲染樹結(jié)構(gòu)如下:

flash_batch2.png

六. 功能擴(kuò)展: 動(dòng)態(tài)子元件

考慮如下場景:

動(dòng)畫師設(shè)計(jì)了個(gè)抽卡動(dòng)畫特效, 他在設(shè)計(jì)的時(shí)候, 卡牌是畫死的, 但是我們在游戲里使用這個(gè)特效的時(shí)候, 需要這個(gè)卡牌可以動(dòng)態(tài)替換成我們要的卡牌.

要做到這個(gè)功能也不麻煩, 需要:

  • 在導(dǎo)出特效的時(shí)候, 需要剔除掉這個(gè)畫死的卡牌圖, 免得浪費(fèi)資源, 同時(shí)對(duì)這個(gè)資源做一個(gè)標(biāo)記, 表示這個(gè)元件需要外部創(chuàng)建
  • 在實(shí)現(xiàn)播放特效的代碼里, 在創(chuàng)建元件的地方(通常我們會(huì)使用工廠模式來實(shí)現(xiàn)), 發(fā)現(xiàn)某元件是需要外部創(chuàng)建的, 那么調(diào)用之前埋入的外部創(chuàng)建器進(jìn)行元件生成

七. 功能擴(kuò)展: 遮罩實(shí)現(xiàn)

考慮以下的動(dòng)畫效果:

mask.gif

動(dòng)畫分2層, 下面一層是背景圖層, 上面一層是一個(gè)圓形遮罩, 圓形遮罩會(huì)做一個(gè)從左到右的移動(dòng), 而只有在圓形覆蓋下的背景區(qū)域才會(huì)顯示出來.

mask_edit.png

opengl渲染管線里, 在fragment shader之后, 寫入frame buffer之前, 可以進(jìn)行stencil test, 它可以剔除不要的像素, 依據(jù)是stencil buffer里對(duì)應(yīng)的取值(1或者0).

對(duì)應(yīng)這個(gè)功能, cocos2d-x里有一個(gè)clippingNode類, 我們可以設(shè)置它的模板(stencil), 那么它里面的子節(jié)點(diǎn), 只有stencil覆蓋范圍內(nèi), 才會(huì)被渲染出來, 這就可以實(shí)現(xiàn)我們的遮罩功能了.

加入ClippingNode之后我們的渲染樹如下:

cocos_stencil.png

到這里為止我們已經(jīng)基本了解動(dòng)畫特效的實(shí)現(xiàn)原理. 下面我們會(huì)對(duì)上面提到的5種屬性做更深入的介紹.

八. 屬性詳細(xì)介紹

我們知道,在opengl的渲染, 本質(zhì)上就是定義頂點(diǎn)的位置, 顏色, 紋理坐標(biāo), 然后進(jìn)行繪制. 這里面頂點(diǎn)的位置(x,y) 是最基本的一個(gè)元素.

在我們實(shí)際的應(yīng)用里, 圖形可以縮放, 旋轉(zhuǎn), 傾斜, 移動(dòng),這些操作,本質(zhì)上是對(duì)頂點(diǎn)進(jìn)行位置的調(diào)整, 從數(shù)學(xué)上, 我們都可以歸結(jié)為對(duì)頂點(diǎn)(x,y) 做矩陣乘法.

1. 縮放(scale)

縮放是最簡單的, 就是把頂點(diǎn)的(x,y) 變成

x = x * scaleX

y = y * scaleY

這樣的一種坐標(biāo)變換, 我們可以用矩陣乘法來表達(dá):
$ \begin{bmatrix}scaleX & 0 \\0 & scaleY\end{bmatrix}\begin{bmatrix}x \\ y \end{bmatrix} = \begin{bmatrix}x*scaleX \\ y*scaleY \end{bmatrix} $

所以, 縮放操作對(duì)應(yīng)的變換矩陣為: $ \begin{bmatrix}scaleX & 0 \\0 & scaleY\end{bmatrix} $

2. 旋轉(zhuǎn)和傾斜(rotation/skew)

旋轉(zhuǎn)和傾斜本身是2種不同的變換, 這里放在這里一起講,是因?yàn)閒lash編輯器里的skew其實(shí)是rotation的變種, 它不同于傳統(tǒng)意義上的skew

1). rotation 旋轉(zhuǎn)

旋轉(zhuǎn)比較好理解, 就是點(diǎn)(x,y) 圍繞某點(diǎn)(通常是原點(diǎn)) 進(jìn)行旋轉(zhuǎn), 關(guān)于點(diǎn)旋轉(zhuǎn)有個(gè)數(shù)學(xué)公式(逆時(shí)針旋轉(zhuǎn)A角度):

x = cosA * x - sinA * y

y = sinA * x + cosA * y

所以我們很容易得出旋轉(zhuǎn)操作的變換矩陣為: $\begin{bmatrix}cosA & -sinA \\ sinA & cosA\end{bmatrix}$

2). 傳統(tǒng)意義的skew 傾斜

這里我們先看一下傳統(tǒng)意義上的skew的含義:

flash_skew.jpeg

圖里黃色虛線框是skew之前的圖形,它原本是一個(gè)長方形,

  • 在x方向上進(jìn)行A角度的skew, 就相當(dāng)長方形的4個(gè)頂點(diǎn)保持y坐標(biāo)不變, 而x = x + y*tanA
  • 在y方向上進(jìn)行A角度的skew, 就相當(dāng)長方形的4個(gè)頂點(diǎn)保持x坐標(biāo)不變, 而y = y + x*tanA

所以我們很容易得出傾斜操作的變換矩陣為: $\begin{bmatrix}1 & tan(skewX) \\ tan(skewY) & 1\end{bmatrix}$

3). flash編輯器里的skew

經(jīng)過一些測試, 我們可以發(fā)現(xiàn)flash里的skew其實(shí)是 rotation的變種.

我們可以推算出它的變換矩陣為: $\begin{bmatrix}cos(skewY) & -sin(skewX) \\ sin(skewY) & cos(skewX)\end{bmatrix}$

也就是當(dāng)skewX = skewY = A的時(shí)候, 你會(huì)發(fā)現(xiàn)它就等于旋轉(zhuǎn)操作

3. 移動(dòng) (dx,dy)

1) 坐標(biāo)系的方向問題

flash編輯器里, y軸正方向是垂直向下的, 而opengl系的引擎比如cocos2d-x, 它的y軸是向上的, 這就需要flash里導(dǎo)出動(dòng)畫信息的時(shí)候, 如果是給cocos2d-x的引擎使用,需要把y坐標(biāo)做一個(gè)翻轉(zhuǎn).

2) 錨點(diǎn)問題

我們看一下上面動(dòng)畫例子里的底座在第一幀的信息:

flash_anchor.jpeg

動(dòng)畫師在編輯元件的時(shí)候, 一般都會(huì)把元件的錨點(diǎn)定在圖片內(nèi)的某個(gè)位置, 這樣方便后面做旋轉(zhuǎn)縮放,flash里元件的(x,y), 其實(shí)就是元件錨點(diǎn)的(x,y).

所以如果在cocos2d-x里, 把圖片創(chuàng)建出來成Sprite之后, 默認(rèn)錨點(diǎn)是在圖片正中間, 需要重新設(shè)置圖片的錨點(diǎn)為當(dāng)時(shí)flash里元件的錨點(diǎn)位置, 這樣才能保證(x,y)是正確的.

3) 變換矩陣

我們知道移動(dòng)之后,

y = y + dy

x = x + dx

我們會(huì)發(fā)現(xiàn)我們無法用之前的2維矩陣來表示這種變換, 我們需要把我們之前的2維矩陣擴(kuò)展成3維:

$$
\begin{bmatrix}1 & 0 & dx \\ 0 & 1 & dy \\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix}x \\ y \\ 1 \end{bmatrix} = \begin{bmatrix}x + dx \\ y + dy \\ 1 \end{bmatrix} $$

同理, 為了統(tǒng)一, 我們回頭把我們上面提到的縮放,旋轉(zhuǎn),傾斜的2維矩陣也擴(kuò)展成3維的, 只需要右下角填充1, 其他位置填充0即可.

到這里為止, 縮放, 旋轉(zhuǎn),傾斜, 平移, 這些操作我們稱之為 仿射變換(Affine Transformation)

4. 多個(gè)矩陣相乘

我們對(duì)一個(gè)頂點(diǎn)同時(shí)做縮放, 旋轉(zhuǎn), 等多個(gè)操作, 其實(shí)相當(dāng)于就是對(duì)這個(gè)頂點(diǎn)做幾次矩陣乘法, 因?yàn)橐话銇碚f圖形會(huì)有多個(gè)頂點(diǎn), 對(duì)每個(gè)頂點(diǎn)都做這個(gè)計(jì)算會(huì)比較浪費(fèi), 我們可以先把這些矩陣先提前相乘, 最后把結(jié)果再統(tǒng)一跟所有頂點(diǎn)做一次矩陣乘法即可.

同時(shí)需要注意, cocos2d-x等引擎都會(huì)有渲染樹的概念, 也就是圖形可以是另外一個(gè)圖形的子節(jié)點(diǎn), 那么在實(shí)際渲染子圖形的時(shí)候, 需要依次取得到根節(jié)點(diǎn)的所有變換矩陣, 全部進(jìn)行相乘, 才可以正確的渲染.

5. 透明度(alpha)

1) alpha用在什么地方

我們可以對(duì)顯示對(duì)象設(shè)置alpha值, 顯示對(duì)象的紋理本身每個(gè)坐標(biāo)對(duì)應(yīng)的像素也有自己的alpha值, 最終2個(gè)alpha值會(huì)相乘得到這個(gè)顯示對(duì)象某點(diǎn)坐標(biāo)的最終alpha值.

alpha值它可以用在alpha test, 或者一些shader中作為某種特殊輸入, 更多的, 它可以用于繪制最后一步上: 寫入frame buffer.

這一步我們叫做blend, blend公式是可以選擇的,

大多數(shù)情況下, 寫入公式為:

src為本圖形某像素點(diǎn)的顏色

dest為目標(biāo)像素點(diǎn)的原來顏色

新顏色 = src.rgb * src.alpha + dest.rgb*(1-src.alpha )

我們可以調(diào)整blend方法來修改這個(gè)默認(rèn)公式.

2) pre-multiply alpha

我們看看上面這個(gè)公式里src.rgb * src.alpha, 我們可以把這個(gè)計(jì)算優(yōu)化掉, 讓圖片在導(dǎo)出的時(shí)候, 就讓它每個(gè)像素的rgb 都乘以alpha(這個(gè)過程叫alpha premultiply), 這樣雖然導(dǎo)出來的圖片會(huì)有點(diǎn)怪, 但是在游戲渲染的時(shí)候, 我們可以告訴它我們這個(gè)紋理已經(jīng)是預(yù)先乘過alpha了, 你在blend的時(shí)候就可以少做一些乘法運(yùn)算了.

通過這個(gè)辦法我們可以提高一些渲染性能.

premultiple alpha還有一些其他的好處, 比如可以統(tǒng)一blend模式方便batch渲染, 顏色線性插值計(jì)算時(shí)不會(huì)因?yàn)閍lpha的差異過大導(dǎo)致出現(xiàn)奇怪的邊緣等問題.

6. 顏色(color)

我們看一下flash編輯器里的顏色編輯:

flash_color.png

顏色效果這里, 有2個(gè)部分, 一個(gè)是RGB分量, 一個(gè)是RGB的絕對(duì)值疊加,

RGB分量部分可以通過給頂點(diǎn)設(shè)置顏色(setColor) 實(shí)現(xiàn),

而RGB絕對(duì)值疊加部分會(huì)有點(diǎn)麻煩, 一般我們需要通過寫一個(gè)fragment shader來進(jìn)行顏色分量的疊加, 而且還需要考慮父節(jié)點(diǎn)的顏色疊加效果 跟 子節(jié)點(diǎn)的顏色疊加效果需要做一個(gè)加法.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末已卸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子贸桶,更是在濱河造成了極大的恐慌凿歼,老刑警劉巖员寇,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羽戒,死亡現(xiàn)場離奇詭異芋酌,居然都是意外死亡极阅,警方通過查閱死者的電腦和手機(jī)胃碾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涂屁,“玉大人书在,你說我怎么就攤上這事〔鹩郑” “怎么了儒旬?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵栏账,是天一觀的道長。 經(jīng)常有香客問我栈源,道長挡爵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任甚垦,我火速辦了婚禮茶鹃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艰亮。我一直安慰自己闭翩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布迄埃。 她就那樣靜靜地躺著疗韵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侄非。 梳的紋絲不亂的頭發(fā)上蕉汪,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音逞怨,去河邊找鬼者疤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛叠赦,可吹牛的內(nèi)容都是我干的驹马。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼眯搭,長吁一口氣:“原來是場噩夢啊……” “哼窥翩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鳞仙,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤寇蚊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后棍好,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仗岸,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年借笙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扒怖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡业稼,死狀恐怖盗痒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤俯邓,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布骡楼,位于F島的核電站,受9級(jí)特大地震影響稽鞭,放射性物質(zhì)發(fā)生泄漏鸟整。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一朦蕴、第九天 我趴在偏房一處隱蔽的房頂上張望篮条。 院中可真熱鬧,春花似錦吩抓、人聲如沸涉茧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽降瞳。三九已至,卻和暖如春蚓胸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背除师。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工沛膳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汛聚。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓锹安,卻偏偏與公主長得像,于是被迫代替她去往敵國和親倚舀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叹哭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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