Godot3游戲引擎入門之十二:Godot碰撞理論以及KinematicBody2D的兩個(gè)方法

godot_cover.jpg

一、前言

這篇文章是為后續(xù)小游戲的開(kāi)發(fā)做理論鋪墊的。嗯,我們前面已經(jīng)陸陸續(xù)續(xù)討論了很多 Godot 中的一些基礎(chǔ)元素、基本功能,最后也順理成章地完成了兩個(gè)小 Demo :

這兩個(gè)游戲?qū)嶋H上并沒(méi)有很大的區(qū)別,都是簡(jiǎn)單地進(jìn)行上下左右移動(dòng)并完成一些特定功能,所以我打算接下來(lái)做一個(gè)更常見(jiàn),更流行的 2D 游戲: 2D Platformer Game ,即所謂的平臺(tái)游戲!在開(kāi)啟這個(gè)游戲之前,我們先一起來(lái)討論平臺(tái)游戲中涉及到的最重要的一些游戲理論知識(shí): 2D 碰撞檢測(cè)理論。

這些理論涉及到了我們之前討論過(guò)的 Area2D 節(jié)點(diǎn)以及 KinematicBody2D/RigidBody2D/KinematicBody2D 節(jié)點(diǎn),以及相關(guān)碰撞圖層和碰撞圖層掩碼知識(shí)。另外,本文還會(huì)詳細(xì)講述 KinematicBody2D 在游戲中常用的兩個(gè)重要方法: move_and_collide() 以及 move_and_slide() 的區(qū)別和聯(lián)系。本文內(nèi)容參考了 KidsCanCode 的一篇文章: Godot 3.0: Using KinematicBody2D ,然后結(jié)合我自己的一些探索實(shí)踐完成。 :smiley:

Godot 3.0: Using KinematicBody2D

主要內(nèi)容:碰撞理論以及正確使用 KinematicBody2D 節(jié)點(diǎn)
閱讀時(shí)間: 15 分鐘
永久鏈接: http://liuqingwen.me/blog/2018/12/30/introduction-of-godot-3-part-12-talk-about-collision-and-move-and-collide-vs-move-and-slide-in-kinematicbody2d/
系列主頁(yè): http://liuqingwen.me/blog/introduction-of-godot-series/

二、正文

本篇目標(biāo)

  1. 四個(gè)節(jié)點(diǎn)回顧: KinematicBody2D/RigidBody2D/StaticBody2D/Area2D
  2. 碰撞圖層和碰撞圖層掩碼: Collision Layers/Collision Masks
  3. 兩個(gè)重要方法的區(qū)別和聯(lián)系: move_and_collide/move_and_slide

三個(gè)物理節(jié)點(diǎn)

在 Godot 中有三個(gè)常用的 2D 節(jié)點(diǎn),它們具有碰撞檢測(cè)與反饋的功能,這三個(gè)節(jié)點(diǎn)的基礎(chǔ)區(qū)別在我之前的文章中已經(jīng)討論過(guò):Godot3游戲引擎入門之五:上下左右移動(dòng)動(dòng)畫(huà)(下),搬用之前的表格,他們之間的關(guān)系和應(yīng)用場(chǎng)景大致如下:

節(jié)點(diǎn)名 StaticBody2D RigidBody2D KinematicBody2D
節(jié)點(diǎn)名稱 靜態(tài)碰撞節(jié)點(diǎn)( 2D ) 剛體節(jié)點(diǎn)( 2D ) 運(yùn)動(dòng)學(xué)節(jié)點(diǎn)( 2D )
基本特性 自動(dòng)碰撞檢測(cè),位置固定不變 自動(dòng)碰撞檢測(cè),產(chǎn)生碰撞響應(yīng):有線速度、角速度等 參與碰撞檢測(cè),無(wú)自動(dòng)響應(yīng),完全由代碼控制移動(dòng)
使用場(chǎng)景 一般用于固定的墻壁、地面等 一般用于受外界影響而產(chǎn)生運(yùn)動(dòng)的物體,比如球體、隕石等 主要用于由代碼控制的帶物理屬性的玩家

我們?cè)?Godot 編輯器中按 <kbd>F4</kbd> 查找 API ,可以看到這三個(gè)節(jié)點(diǎn)都是直接繼承于 PhysicsBody2D 的,說(shuō)明它們都是物理節(jié)點(diǎn),而 PhysicsBody2D 又繼承于 CollisionObject2D 具有碰撞檢測(cè)功能。另外, CollisionObject2D 節(jié)點(diǎn)還有另外一個(gè)子類,這個(gè)子類也是我們之前提過(guò)并在游戲中應(yīng)用過(guò)的 Area2D 節(jié)點(diǎn)。關(guān)于這四個(gè)節(jié)點(diǎn)的應(yīng)用我舉幾個(gè)常見(jiàn)例子:

  • StaticBody2D 能應(yīng)用于所有游戲,作為墻壁、地面、障礙物等固定物
  • RigidBody2D 比如像憤怒的小鳥(niǎo)、割繩子、太空飛船游戲的主角等
  • KinematicBody2D 幾乎所有的前后左右移動(dòng)、跳躍的平臺(tái)游戲玩家或者敵人
  • Area2D 常見(jiàn)于游戲中的可收集元素或者標(biāo)記,比如金幣、樓梯、關(guān)口或者特殊區(qū)域等

這幾個(gè)節(jié)點(diǎn)我們?cè)谇懊娴奈恼轮卸加鲆?jiàn)過(guò),也有不少例子,它們的使用方法大家應(yīng)該都會(huì)了。這里,關(guān)于剛體 RigidBody2D 我暫時(shí)不會(huì)介紹很多,大家可以參考這篇文章: Godot 3.0: Rigid Bodies ,介紹的內(nèi)容比較全面。

重要說(shuō)明: Godot 3.1 版本中對(duì)于 StaticBody2D 以及 RigidBody2D 的摩擦力屬性( friction )和彈性屬性( bounce )的設(shè)置沒(méi)有出現(xiàn)在屬性面板中,而需要在新增的 Physic Material Override 屬性下新建一個(gè) PhysicMaterial 間接進(jìn)行設(shè)置即可,實(shí)質(zhì)上區(qū)別影響并不大。

碰撞形狀和圖層

所有的物理碰撞節(jié)點(diǎn)都需要至少一個(gè)碰撞形狀才會(huì)有碰撞效果。這個(gè)碰撞形狀你可以直接在屬性面板中添加,也可以在編輯器中暫時(shí)“置空”,轉(zhuǎn)而在代碼中動(dòng)態(tài)生成,這都是可以的。沒(méi)有碰撞形狀的碰撞節(jié)點(diǎn)不會(huì)參與游戲中的碰撞交互,碰撞形狀主要分為: CollisionShape2DCollisionPolygon2D 兩種,應(yīng)用非常簡(jiǎn)單,在場(chǎng)景中表現(xiàn)為藍(lán)色區(qū)域,游戲運(yùn)行后并不會(huì)顯示,可以在 Debug 調(diào)試中打開(kāi)顯示效果,在之前的文章中已經(jīng)詳細(xì)討論過(guò)。

這里重點(diǎn)要提到的概念是碰撞圖層以及碰撞圖層掩碼。在使用碰撞圖層之前,你必須在 Godot 項(xiàng)目設(shè)置中對(duì)你所需要的圖層進(jìn)行添加并合理命名:

godot_12_projectsettings_of_layers.png

如果你熟悉 iOS 游戲開(kāi)發(fā)框架 SpriteKit 的話,那么這兩個(gè)概念對(duì)你來(lái)說(shuō)很簡(jiǎn)單,這里我們先列舉一下它們的定義:

  • Layer 即圖層,在代碼中為 collision_layer ,它表示物體所處的碰撞圖層,一個(gè)物體一般處于一個(gè)圖層中
  • Mask 即掩碼,在代碼中為 collision_mask ,它表示當(dāng)前物體所關(guān)心的其他的碰撞圖層,可包含多個(gè)圖層

碰撞圖層很好理解,類似 PhotoShop/GIMP/Krita 這些圖片處理軟件中的圖層概念,用于把不同的內(nèi)容分離開(kāi)來(lái),游戲中碰撞節(jié)點(diǎn)一般處于某一個(gè)特定的碰撞圖層中。那么掩碼又是啥意思呢?它實(shí)質(zhì)代表的意義是這個(gè)物體需要與哪些圖層進(jìn)行碰撞檢測(cè),所以一個(gè)節(jié)點(diǎn)的掩碼可以包含多個(gè)圖層,如果對(duì)方所處的碰撞圖層不在你的掩碼范圍內(nèi),那么就不會(huì)與之發(fā)生碰撞檢測(cè),有點(diǎn)拗口,舉個(gè)例子你就能明白,給節(jié)點(diǎn)設(shè)置圖層的方法以及各自所屬的圖層如下:

godot_12_set_layer_and_mask.png
游戲物體 碰撞圖層 圖層掩碼
玩家 1 2, 3
敵人 2 1 (or 0)
金幣 3 1 (or 0)

在這種場(chǎng)景設(shè)置下,很顯然,玩家掩碼為 2(enemy) 和 3(coin) ,那么玩家會(huì)檢測(cè)與敵人或者金幣之間的碰撞,敵人和金幣的掩碼設(shè)置都是 1(player) ,所以它們分別也會(huì)檢測(cè)與玩家之間發(fā)生的碰撞,但是敵人與金幣、敵人與敵人、金幣與金幣、玩家與玩家之間則都不會(huì)互相發(fā)生任何碰撞檢測(cè)!

重點(diǎn)說(shuō)明:圖層和掩碼都可以不勾選,也就是完全刪除,如果這里敵人或者金幣刪除全部的掩碼,即設(shè)置圖層掩碼為 0 ,那么是不是敵人就不能檢測(cè)到與玩家之間的碰撞了呢?其實(shí)并不是!他們依然能互相檢測(cè)到與對(duì)方的碰撞,這是因?yàn)橥婕业难诖a中包含了敵人,只要雙方有一個(gè)設(shè)置了與對(duì)方可以發(fā)生碰撞檢測(cè)的掩碼,那么雙方即可相互檢測(cè)到與對(duì)方之間發(fā)生的碰撞!

既然如此,那么假設(shè)有這種需求:“游戲中的玩家只檢測(cè)敵人或者金幣,而金幣或者敵人不需要去檢測(cè)玩家”,那能否實(shí)現(xiàn)呢?其實(shí)在 PhysicsBody2D 的三個(gè)節(jié)點(diǎn)中還真沒(méi)辦法,但是這里我們可以不考慮使用 KinematicBody2D 等節(jié)點(diǎn),轉(zhuǎn)而使用 Area2D 節(jié)點(diǎn)就可以實(shí)現(xiàn)了,需要注意該節(jié)點(diǎn)的兩個(gè)屬性:

  • Monitoring 是否能主動(dòng)檢測(cè)其他碰撞體
  • Monitorable 是否能被其他碰撞體檢測(cè)到
godot_12_monitoring_and_monitorable.png

Area2D 這兩個(gè)屬性一直是新手容易忽略的,弄清楚了這兩個(gè)屬性的概念,你就可以關(guān)閉敵人或者金幣(使用 Area2D 節(jié)點(diǎn))的 monitoring 屬性,關(guān)閉后它們不會(huì)主動(dòng)檢測(cè)與其他碰撞節(jié)點(diǎn)的碰撞,同時(shí),在我們的 Demo 中玩家還是可以檢測(cè)到他們。另一方面,如果要讓某個(gè)金幣不被玩家檢測(cè)到,就像海底撈月,看得見(jiàn)摸不著的效果,那么可以設(shè)置其 monitorable 為關(guān)閉即可!嗯,還是用實(shí)際 Demo 來(lái)體會(huì)一下效果吧:

godot_12_collision_response.gif

OK ,明白了圖層和掩碼對(duì)游戲的開(kāi)發(fā)幫助非常大,你完全可以自己寫(xiě)一個(gè) Demo 嘗試一下,或者下載我的源碼一探究竟吧,偷偷告訴你:這很重要!哈哈! :sunglasses:

兩個(gè)重要的方法

接下來(lái)我們的重點(diǎn)是 KinematicBody2D 節(jié)點(diǎn)的兩個(gè)常用方法,因?yàn)樯婕暗轿锢砼鲎驳拇蟛糠钟螒蛑校婕叶际鞘褂?KinematicBody2D 節(jié)點(diǎn)制作的,而它又有兩個(gè)非常重要的碰撞處理方法,所以我們必須重點(diǎn)“針對(duì)”它們!

首先,在正常的游戲場(chǎng)景中,對(duì)于 KinematicBody2D 幾何學(xué)碰撞體節(jié)點(diǎn)的移動(dòng)實(shí)現(xiàn),我們主要有以下三種方式:

  1. position 屬性設(shè)置,即控制位置,完全手動(dòng)檢測(cè)碰撞
  2. move_and_collide() 方法調(diào)用,移動(dòng)并自動(dòng)檢測(cè)碰撞
  3. move_and_slide() 方法調(diào)用,移動(dòng)并自動(dòng)檢測(cè)碰撞,支持滑動(dòng)

對(duì)于第一種方式,直接操作 position 位置屬性,一般在有碰撞體的游戲中很少這么“武斷”地使用,即使你的游戲是沒(méi)有任何碰撞體,你這個(gè)時(shí)候你也沒(méi)必要選擇 KinematicBody2D 節(jié)點(diǎn),直接使用 Area2D 節(jié)點(diǎn)就好。那么,接下來(lái)我們主要討論另外兩種方式,包括它們的定義,區(qū)別與聯(lián)系以及應(yīng)用場(chǎng)合等。

1. 相關(guān)聯(lián)系

第一個(gè):很顯然,他們必須都在 _physics_process(delta) 方法中調(diào)用,因?yàn)樵摲椒ǖ膬?nèi)部會(huì)對(duì)物理引擎進(jìn)行相關(guān)處理,前面我們已經(jīng)討論過(guò),最好不要在 _process(delta) 中使用這兩個(gè)方法,避免出現(xiàn)異常情況,這也是新手容易犯的錯(cuò)誤之一。

第二個(gè):這兩個(gè)方法在某場(chǎng)景中是完全可以互相取代的,只需要對(duì)碰撞行為作出對(duì)應(yīng)的處理即可。舉個(gè)例子,下面兩個(gè)代碼段的效果表現(xiàn)會(huì)完全相同:

var collision = move_and_collide(velocity * delta)
if collision:
    velocity = velocity.slide(collision.normal)
velocity = move_and_slide(velocity)

效果圖如下:

Godot 3.0: Using KinematicBody2D

2. 兩者區(qū)別

第一個(gè),從上面的代碼中我們能很明顯地看出來(lái),在使用這兩個(gè)方法時(shí),需要傳遞 KinematicBody2D 物體的速度作為參數(shù);而這個(gè)速度在 move_and_collide() 方法中需要乘以幀間隔 delta ,但是在 move_and_slide() 方法中并不需要,這是因?yàn)榇朔椒ㄔ趦?nèi)部已經(jīng)幫我們自動(dòng)處理好了,無(wú)需手動(dòng)相乘。

第二個(gè),根據(jù)上一條以及前面的代碼,我們可以總結(jié)出在通用性方面,明顯 move_and_collide() 更加通用,可以處理運(yùn)動(dòng)過(guò)程中任何碰撞情況,但是需要手動(dòng)編碼,處理起來(lái)也稍難;而 move_and_slide() 方法則更加特別,這對(duì)于新手朋友來(lái)說(shuō)也會(huì)感覺(jué)相對(duì)簡(jiǎn)單。

第三個(gè),使用方式不同,也就是說(shuō)這兩個(gè)方法的簽名是完全不一樣的:

方法 move_and_collide move_and_slide
返回值類型 KinematicCollision2D :包含碰撞相關(guān)信息 Vector2 :移動(dòng)碰撞后的實(shí)際速度
參數(shù)1 rel_vec :Vector2 類型,表示實(shí)際速度,記住需要乘以 delta linear_velocity :Vector2 類型,表示速度,不需要乘以 delta
參數(shù)2 ? infinite_inertia : 3.1 版本新增參數(shù),默認(rèn)為 true floor_normal : Vector2 類型,表示地面法線方向,默認(rèn)值 (0, 0)
參數(shù)3 ? exclude_raycast_shapes : 3.1 版本新增參數(shù),默認(rèn)為 true slope_stop_min_velocity :最小速度,斜坡上速度小于該值則停止滑動(dòng),默認(rèn) 5
參數(shù)4 ? test_only : 3.1 版本新增參數(shù),默認(rèn)為 false max_bounces :停止運(yùn)動(dòng)前最大碰撞次數(shù) ,數(shù)值過(guò)低可能會(huì)直接終止運(yùn)動(dòng),默認(rèn) 4
參數(shù)5 - floor_max_angle :能移動(dòng)的最大斜坡角度,弧度計(jì),默認(rèn)值 0.785398 即 45°
參數(shù)6 - ? infinite_inertia : 3.1 版本新增參數(shù),默認(rèn)為 true

說(shuō)明:上面有一些標(biāo)注了 ? 的參數(shù)是 Godot 3.1 版本中新增的參數(shù),暫時(shí)沒(méi)有詳細(xì)的文檔說(shuō)明,等待正式版以及文檔發(fā)布后會(huì)有詳述,另外 3.1 版本新增了一個(gè) move_and_slide_with_snap() 方法,值得關(guān)注。

兩個(gè)方法中,上面列舉的一些參數(shù)都還是很好理解的,對(duì)于一般場(chǎng)合下,方法的默認(rèn)參數(shù)都?jí)蛴?。其中,參?shù) floor_max_angle 表示最大斜坡角度,低于該角度的斜坡都被認(rèn)為是“可滑行”的地面,另外參數(shù) floor_normal 表示地面的法線,可以結(jié)合 KinematicBody2D 的幾個(gè)方法: is_on_floor() (在地面上), is_on_wall() (在墻壁上),以及 is_on_ceiling() (在天花板上)搭配使用。

第四個(gè),通過(guò)上表可以看出來(lái),如果我們需要檢查玩家碰撞后的反饋信息,我們可以使用 move_and_collide() 方法的返回值即可,如果使用 move_and_slide() 能不能即使獲取相關(guān)信息呢?當(dāng)然也可以,稍微繁瑣,我們可以使用 KinematicCollision2D get_slide_collision(int slide_idx) 方法以及 int get_slide_count() 正確處理即可,示例代碼如下:

func _physics_process(delta):
    var velocity = move_and_slide(velocity)
    var count = get_slide_count()
    for i in range(count):
        var collision = get_slide_collision(i)
        var collider = collision.collider
        print('collider ', i + 1, ': ', collider.name)

3. 應(yīng)用實(shí)踐

這兩個(gè)方法確實(shí)有點(diǎn)繞,別急,先搞清楚他倆的相似點(diǎn)以及不同點(diǎn),然后我們就可以在不同場(chǎng)合中靈活使用了。 :grin:

1. 最基本的移動(dòng)

觀察下圖這個(gè)示例,使用 move_and_collide() 方法在沒(méi)有遇到障礙物時(shí)一切正常,但是遇到墻壁后,如果玩家的速度方向與墻壁表面相交,就會(huì)出現(xiàn)卡住的奇怪現(xiàn)象。究其原因,這是因?yàn)槲覀冸m然使用了碰撞移動(dòng)方法,但是遇到碰撞后的反饋并沒(méi)有手動(dòng)處理,所以 move_and_collide() 這個(gè)方法在玩家遇到碰撞時(shí)只能停下而表現(xiàn)出“卡注”的現(xiàn)象。

godot_12_move_and_slide.gif

解決這個(gè)問(wèn)題的方法很簡(jiǎn)單,把方法替換為 move_and_slide() 即可。該方法不僅能正確處理碰撞反饋,還能告訴你在發(fā)生碰撞后物體的實(shí)際運(yùn)行速度,即方法的返回值,在本 Demo 中你可以通過(guò)打開(kāi) use real velocity 這個(gè)開(kāi)關(guān)查看碰撞后物體運(yùn)動(dòng)的實(shí)時(shí)速度。

那么,是不是所有的 KinematicBody2D 節(jié)點(diǎn)的移動(dòng)都應(yīng)該直接使用 move_and_slide() 方法呢?當(dāng)然不是!一起來(lái)看第二個(gè)示例。

2. 碰撞反彈效果

考慮下這個(gè)場(chǎng)景,我們有一個(gè)用剛體(不反彈)做成的彈力球,這個(gè)球在碰撞到墻壁后能彈回去,但是墻壁是靜態(tài)物體也沒(méi)有彈性,這個(gè)時(shí)候如果使用 move_and_slide() 方法那么彈球遇到墻壁就會(huì)停止或者直接沿著墻壁下滑啦,如何處理呢?很顯然,我們需要一點(diǎn)代碼!

這就是 move_and_collide() 方法的用武之地了,我們可以利用這個(gè)方法的返回值進(jìn)行相關(guān)處理,返回值是一個(gè) KinematicCollision2D 類型的碰撞結(jié)果,其中包含我們所需要的數(shù)據(jù),比如碰撞體的表面方向,即碰撞體的法線方向(垂直方向),把彈力球的速度按法線方向?qū)⑵?strong>反射,那么球就能順利反彈了!

代碼可以參考上文,圖效果可以看下:

godot_12_collide.gif

3. 2D 平臺(tái)游戲

平臺(tái)游戲應(yīng)用非常廣泛,常見(jiàn)的跳躍類型的游戲很多都是 Platform Game :左右移動(dòng),上下跳躍。平臺(tái)游戲場(chǎng)景中靜態(tài)物體元素比較多,一般有地面,墻壁,還有斜坡等,那么玩家在這些平臺(tái)上如何移動(dòng)呢?其實(shí)核心代碼非常簡(jiǎn)單,這不得不歸功于 Godot 提供給我們的 move_and_slide() 方法的了!這種情況在 Unity 中就稍顯麻煩了,你還得使用射線( Raycast )處理與墻壁、地面等的碰撞檢測(cè),而 Godot 中對(duì)于小游戲而言,一個(gè)方法就能解決所有問(wèn)題,聽(tīng)起來(lái)是不是有點(diǎn)小激動(dòng)?別急,先看代碼:

func _physics_process(delta):
    var collision = self.move_and_collide(velocity * delta)
    if collision:
        velocity = velocity.bounce(collision.normal)
        if collision.collider.has_method('hit'):
            collision.collider.hit()

當(dāng)然, Godot 中也有射線節(jié)點(diǎn),后面會(huì)提到,下面是一個(gè)普通的 move_and_slide() 方法的應(yīng)用示例:

godot_12_platformer.gif

三、總結(jié)

枯不枯燥?煩不煩所?哈哈,啰嗦了一大堆, Godot 的強(qiáng)大和方便之處領(lǐng)悟到了嗎?哈哈,如果還不能馬上體會(huì),你可以到我的 Github 倉(cāng)庫(kù)下載源碼,然后打開(kāi)每一個(gè)場(chǎng)景,使用快捷鍵 <kbd>F6</kbd> 單獨(dú)運(yùn)行場(chǎng)景查看效果;如果本文對(duì)你來(lái)說(shuō)很簡(jiǎn)單,那么是不是迫不及待地想寫(xiě)一個(gè)平臺(tái)游戲練練手啦?

Just do it, man!

好了,總結(jié)一下本文的相關(guān)理論知識(shí)吧:

  1. 三種 2D 物理節(jié)點(diǎn)以及 Area2D 節(jié)點(diǎn)的回顧
  2. 碰撞層和碰撞掩碼理論知識(shí)
  3. KinematicBody2D 兩個(gè)方法詳述
  4. 簡(jiǎn)單的應(yīng)用場(chǎng)景分析

本篇的 Demo 以及相關(guān)代碼已經(jīng)上傳到 Github ,地址: https://github.com/spkingr/Godot-Demos , 2018 年最后一篇文章,原創(chuàng)不易,希望大家喜歡,我們 2019 年見(jiàn)! :smile:

參考文檔:
官方 API 文檔: http://docs.godotengine.org/en/3.0/classes/class_kinematicbody2d.html?highlight=kinematicbody2d
Godot 3.0: Using KinematicBody2D : http://kidscancode.org/blog/2018/02/godot3_kinematic2d/
我的博客地址: http://liuqingwen.me
我的博客即將同步至騰訊云+社區(qū),邀請(qǐng)大家一同入駐: https://cloud.tencent.com/developer/support-plan?invite_code=3sg12o13bvwgc
歡迎關(guān)注我的微信公眾號(hào):

IT自學(xué)不成才

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容