<cite id="ffb66"></cite><cite id="ffb66"><track id="ffb66"></track></cite>
      <legend id="ffb66"><li id="ffb66"></li></legend>
      色婷婷久,激情色播,久久久无码专区,亚洲中文字幕av,国产成人A片,av无码免费,精品久久国产,99视频精品3
      網易首頁 > 網易號 > 正文 申請入駐

      游戲AI行為決策——HTN(分層任務網絡)

      0
      分享至


      【USparkle專欄】如果你深懷絕技,愛“搞點研究”,樂于分享也博采眾長,我們期待你的加入,讓智慧的火花碰撞交織,讓知識的傳遞生生不息!

      這是侑虎科技第1855篇文章,感謝作者狐王駕虎供稿。歡迎轉發分享,未經作者授權請勿轉載。如果您有任何獨到的見解或者發現也歡迎聯系我們,一起探討。(QQ群:793972859)

      作者主頁:

      https://home.cnblogs.com/u/OwlCat

      一、前言

      Hierarchical Task Network(分層任務網絡),簡稱HTN,與行為樹、GOAP一樣,也是一種行為決策方法。在《地平線:零之曙光》、《變形金剛:塞伯坦的隕落》中都有用它來制作游戲敵人的AI。比起其它行為決策方法,HTN有個十分鮮明的特點:推演

      HTN允許我們把要做的事以高度復雜的「復合任務」來表示,而不是單單一個行為。什么意思呢?無論是有限狀態機狀態的轉換,還是行為樹節點的切換,大多時候只是從一個執行動作變為執行另一個動作。而HTN的一次規劃,可以一口氣規劃出包含好幾個動作的「復合任務」,你看到它做出的新動作,也不過是之前就計劃好的一部分。

      這么看來,好像還有點預知未來的味道呢,說得越來越玄乎了,直接來看看它的運行邏輯吧!

      PS:據后續反饋,分享一個有應用HTN的項目的代碼[1](也可以用該gitee倉庫[2])HTN使用的主要部分在該項目的Script/Characters/Enemy部分,有需要的可以參考看看這個HTN的實際使用。

      二、運行邏輯

      HTN的整體結構框架如下:


      別怕,看著復雜而已,相信你能夠理解的:

      1. 任務

      首先,和其它行為決策方法一樣,角色內部有存儲一系列要做的事。在有限狀態機中是「狀態」,行為樹中是「動作節點」,而HTN中是「任務(Task)」。但要注意,HTN的「任務」十分特殊,它不只是單一的動作,可能包含多個動作,總的可以分為三種:「復合任務」、「方法」以及「原子任務」。

      • 原子任務:是最簡單的任務,只是單一的動作,像「奔跑」、「跳躍」等就算是原子任務。通常也不建議把一個原子任務設計得太復雜。

      • 復合任務:只理解為是多個原子任務組合成的,并不完全正確。復合任務是由多個「方法」組合而成的,而每次執行復合任務,只會選擇組成它的眾多「方法」之一來執行,就像行為樹的選擇節點一樣。

      • 方法:方法是HTN讓角色行動豐富的關鍵,一個方法可以由多個「原子任務」或「復合任務」組合而成。在「方法」的幫助下,我們可以自然且清晰地構建豐富的行為。以「砍樹」為例,可以構造成這個樣子:


      方法的執行,會逐一判斷組成的「復合任務」和「原子任務」是否滿足條件,只要有一個不滿足,這個方法便會被放棄,它有點像行為樹中的順序節點。

      這里要多說一嘴,「復合任務」和「方法」只會在HTN的規劃階段被執行。所謂「規劃階段」,就是根據「世界狀態」來決定該做什么事,規劃時會把要做的「復合任務」和「方法」統統分解成一個個「原子任務」。也就是說,最終角色實際執行的都是「原子任務」。

      2. 世界狀態

      在游戲常用的決策行為算法中,只有GOAP和HTN有用到「世界狀態」。其實這是更接近傳統人工智能的設計方式(GOAP和HTN也確實是由傳統人工智能轉變來的),還是以「砍樹」為例,想要讓一個角色去砍樹,他就得知道:哪里有樹、哪里有電鋸、電鋸有多少油……這些做事的前提都可以歸為「世界狀態」的一員,反過來說,世界狀態就是這類「前提條件」的集合,它們共同構成了HTN任務規劃的基礎。

      在規劃階段,角色會復制一份「世界狀態」的副本用于個人判斷并選出可執行的任務,就好像是偵探拿著照片進行腦補推斷一樣。這個過程不會影響真正的「世界狀態」。而在選出了可執行的任務后,就會將它分解成一系列「原子任務」挨個執行。有些(或者說大多數)「原子任務」執行完成后會對「世界狀態」造成一定影響,比如開槍會減少彈藥數,鋸完樹會減少樹木數量等等。但要注意,這里的影響就不再是“腦補”的啦,而是真正改變「世界狀態」的某些值。就像是部隊制定完計劃后,就開始正式行動了。

      3. 總結

      通過上述兩大點,我想已經能大概弄清楚HTN的運行邏輯了吧(如果還是很懵,可以看看這個視頻[3]相關部分的介紹):根據世界狀態來選擇要執行的任務,再將選好的任務分解為一個個原子任務來執行,而原子任務執行完后又會影響世界狀態。一旦分解出的原子任務都執行完了,又或者某個原子任務的執行條件突然不能滿足了,就重新選擇,重復這個步驟。這就是HTN大體的運行邏輯了。

      三、代碼實現

      這次代碼實現同樣參考了Steve Rabin的《Game AI Pro》[4],相比之前我們實現的行為樹,這次所要寫的類不會太多(除去注釋的話就更少了)。

      1. 世界狀態

      世界狀態實現的難點在于:

      1. 狀態數據的類型是多種多樣的,該用什么來統一保存?

      2. 狀態數據會時時變化,如何保證存儲的數據也會同步更新?

      對于問題1,我們可以用 的字典來解決。畢竟C ,Object類是所有數據類型的老祖宗。那問題2呢,假設用這種字典存儲了某個角色的血量,那這個角色就算血量變成0了,字典里存儲的也只是剛存進去時的那個值而不是0。而且反過來,我們修改字典里的這個血量值,也不會影響實際角色的血量……除非,這些值能像屬性一樣……

      這是可以做到的!但要用到兩個字典,一個用來模仿屬性的get,一個用來模仿屬性的set。分別用值類型為System.Action和System.Func的字典就可以了。

      到這里我得再說一下,如果對于上面這幾段話中的一些名詞你有些許疑惑的話,就該再學習一下C,否則你可能不能理解世界狀態類的實現:

      //世界狀態只有一個即可,我們將其設為靜態類 publicstaticclassHTNWorld {     //讀 世界狀態的字典     privatestaticreadonly Dictionary
      
       > get_WorldState;     //寫 世界狀態的字典     privatestaticreadonly Dictionary
      
       > set_WorldState;     static HTNWorld()     {         get_WorldState = new Dictionary
      
       >();         set_WorldState = new Dictionary
      
       >();     }     //添加一個狀態,需要傳入狀態名、讀取函數和寫入函數     public static void AddState(string key, Func
      getter, Action setter)     {         get_WorldState[key] = getter;         set_WorldState[key] = setter;     }     //根據狀態名移除某個世界狀態     public static void RemoveState(string key)     {         get_WorldState.Remove(key);         set_WorldState.Remove(key);     }     //修改某個狀態的值     public static void UpdateState(string key, object value)     {         //就是通過寫入字典修改的         set_WorldState[key].Invoke(value);     }     //讀取某個狀態的值,利用泛型,可以將獲取的object轉為指定的類型     public static T GetWorldState
      
       (string key)     {         return (T)get_WorldState[key].Invoke();     }     //復制一份當前世界狀態的值(這個主要是用在規劃中)     public static Dictionary
      
        CopyWorldState()     {         var copy = new Dictionary
      
       ();         foreach(var state in get_WorldState)         {             copy.Add(state.Key, state.Value.Invoke());         }         return copy;     } }
      
      
      
      
      
      
      

      2. 任務類接口

      「復合任務」、「方法」和「原子任務」它們有共通之處,我們把這些共通之處以接口的形式提煉出來,可以簡化我們在規劃環節的代碼邏輯。

      //用于描述運行結果的枚舉(如果有看上一篇行為樹的話,也可以直接用行為樹的EStatus) public enum EStatus {     Failure, Success, Running,  } public interface IBaseTask {     //判斷是否滿足條件     bool MetCondition(Dictionary
      
       worldState);     //添加子任務     void AddNextTask(IBaseTask nextTask); }
      

      3. 原子任務

      原子任務是一個抽象類,相當于行為樹中的動作節點,用于開發者自定義的最小單元任務。一般就是像「開火」、「奔跑」之類的簡單動作。值得注意的是,這里的條件判斷和執行影響都要分兩種情況,一種是規劃時,一種是實際執行時,因為規劃時我們使用的并不是真正的世界狀態,而是一份模擬的世界狀態副本。

      public abstractclassPrimitiveTask : IBaseTask {     //原子任務不可以再分解為子任務,所以AddNextTask方法不必實現     void IBaseTask.AddNextTask(IBaseTask nextTask)     {         thrownew System.NotImplementedException();     }     ///      /// 執行前判斷條件是否滿足,傳入null時直接修改HTNWorld     ///      /// 用于plan的世界狀態副本     public bool MetCondition(Dictionary
      
       worldState = null)     {         if(worldState == null)//實際運行時         {             return MetCondition_OnRun();         }         else//模擬規劃時,若能滿足條件就直接進行Effect         {             if(MetCondition_OnPlan(worldState))             {                 Effect_OnPlan(worldState);                 returntrue;             }             returnfalse;         }     }     protected virtual bool MetCondition_OnPlan(Dictionary
      
       worldState)     {         returntrue;     }     protected virtual bool MetCondition_OnRun()     {         returntrue;     }     //任務的具體運行邏輯,交給具體類實現     public abstract EStatus Operator();     ///      /// 執行成功后的影響,傳入null時直接修改HTNWorld     ///      /// 用于plan的世界狀態副本     public void Effect(Dictionary
      
       worldState = null)     {         Effect_OnRun();     }     protected virtual void Effect_OnPlan(Dictionary
      
       worldState)     {         ;     }     protected virtual void Effect_OnRun()     {         ;     } }
      
      
      
      

      4. 方法

      方法既可以添加「復合任務」又可以添加「原子任務」作組成的子任務,所以我們用IBaseTask列表來存儲;而方法的滿足與否,要看兩個條件,具體看代碼注釋吧:

      public classMethod : IBaseTask {     //子任務列表,可以是復合任務,也可以是原點任務     public List SubTask {  get; privateset; }     //方法的前提條件     privatereadonly Func
      
       condition;     public Method(Func
      
       condition)     {         SubTask = new List ();         this.condition = condition;     }     //方法條件滿足的判斷=方法本身前提條件滿足+所有子任務條件滿足     public bool MetCondition(Dictionary
      
       worldState = null)     {         /*         再復制一遍世界狀態,用于追蹤每個子任務的Effect。方法有多個子任務,         只要其中一個不滿足條件,那整個方法不滿足條件,之前子任務進行Effect也不算數         因此用tpWorld記錄,待驗證了方法滿足條件后(所有子任務均滿足條件),再復制回worldState         */         var tpWorld = new Dictionary
      
       (worldState);         if (condition())//方法自身的前提條件是否滿足         {             for (int i = 0; i < SubTask.Count; ++i)             {                 //一旦有一個子任務的條件不滿足,這個方法就不滿足了                 if(!SubTask[i].MetCondition(tpWorld))                 {                     returnfalse;                 }             }             //最終滿足條件后,再將各Effect導致的新世界狀態(tpWorld)給worldState             worldState = tpWorld;             returntrue;//如果子任務全都滿足了,那就成了!         }         returnfalse;     }     //添加子任務     public void AddNextTask(IBaseTask nextTask)     {         SubTask.Add(nextTask);     } }
      
      
      
      

      5. 復合任務

      復合任務和「方法」類似,只不過只能添加「方法」作為子任務。

      public classCompoundTask : IBaseTask {     //選中的方法     public Method ValidMethod { get; privateset; }     //子任務(方法)列表     privatereadonly List methods;     public CompoundTask()     {         methods = new List ();     }     public void AddNextTask(IBaseTask nextTask)     {         //要判斷添加進來的是不是方法類,是的話才添加         if (nextTask is Method m)         {             methods.Add(m);         }     }     public bool MetCondition(Dictionary
      
       worldState)     {         for (int i = 0; i < methods.Count; ++i)         {             //只要有一個方法滿足前提條件就可以             if(methods[i].MetCondition(worldState))             {                 //記錄下這個滿足的方法                 ValidMethod = methods[i];                 returntrue;             }         }         returnfalse;     } }
      

      到這里,基本的組件類就全部完成了,對比行為樹那章,代碼量很少對吧?接下來就是有關構造的類了。

      6. 規劃器

      規劃器的要點在于對「復合任務」的分解,這里提一下,一個HTN會保證有一個復合任務作為根任務,就和行為樹的根節點一樣。分解也是由此開始:

      public classHTNPlanner {     //最終分解完成的所有原子任務存放的列表     public Stack FinalTasks {  get; privateset; }     //分解過程中,用來緩存被分解出的任務的棧,因為類型各異,故用IBaseTask類型     privatereadonly Stack taskOfProcess;     privatereadonly CompoundTask rootTask;//根任務     public HTNPlanner(CompoundTask rootTask)     {         this.rootTask = rootTask;         taskOfProcess = new Stack ();         FinalTasks = new Stack ();     }     //規劃(核心)     public void Plan()     {         //先復制一份世界狀態         var worldState = HTNWorld.CopyWorldState();         //將存儲列表清空,避免上次計劃結果的影響         FinalTasks.Clear();         //將根任務壓進棧中,準備分解         taskOfProcess.Push(rootTask);         //只要棧還沒空,就繼續分解         while(taskOfProcess.Count > 0)         {             //拿出棧頂的元素             var task = taskOfProcess.Pop();             //如果這個元素是復合任務             if(task is CompoundTask cTask)             {                 //判斷是否可以執行                 if(cTask.MetCondition(worldState))                 {                     /*如果可以執行,就肯定有可用的方法,                     就將該方法的子任務都壓入棧中,以便繼續分解*/                     var subTask = cTask.ValidMethod.SubTask;                     foreach(var t in subTask)                     {                         taskOfProcess.Push(t);                     }                     /*通過上面的步驟我們知道,能被壓進棧中的只有                     復合任務和原子任務,方法本身并不會入棧*/                 }             }             else//否則,這個元素就是原子任務             {                 //將該元素轉為原子任務,因為原本是IBaseTask類型                 var pTask = task as PrimitiveTask;                 //再將該原子任務加入存放分解完成的任務列表                 FinalTasks.Push(pTask);             }         }     } }

      7. 執行器

      執行器的關鍵在于如何確認一個原子任務是否執行完成,并且要在執行完成后產生影響并切換到下一個原子任務。

      public classHTNPlanRunner {     //當前運行狀態     private EStatus curState;     //直接將規劃器包含進來,方便重新規劃     privatereadonly HTNPlanner planner;     //當前執行的原子任務     private PrimitiveTask curTask;     //標記「原子任務列表是否還有元素、能夠繼續」     privatebool canContinue;     public HTNPlanRunner(HTNPlanner planner)     {         this.planner = planner;         curState = EStatus.Failure;     }     public void RunPlan()     {         //如果當前運行狀態是失敗(一開始默認失?。?        if(curState == EStatus.Failure)         {             //就規劃一次             planner.Plan();         }         //如果當前運行狀態是成功,就表示當前任務完成了         if(curState == EStatus.Success)         {             //讓當前原子任務造成影響             curTask.Effect();         }         /*如果當前狀態不是「正在執行」,就取出新一個原子任務作為當前任務         無論失敗還是成功,都要這么做。因為如果是失敗,肯定在代碼運行到這         之前,已經進行了一次規劃,理應獲取新規劃出的任務來運行;如果是因         為成功,那也要取出新任務來運行*/         if(curState != EStatus.Running)         {             //用TryPop的返回結果判斷規劃器的FinalTasks是否為空             canContinue = planner.FinalTasks.TryPop(out curTask);         }         /*如果canContinue為false,那curTask會為null也視作失?。ㄆ鋵崙撌恰溉?        完成」,但全部完成和失敗是一樣的,都要重新規劃)。所以只有當canContinue && curTask.MetCondition()都滿足時,才讀取當前原子任務的運行狀態,否則就失敗。*/         curState = canContinue && curTask.MetCondition() ? curTask.Operator() : EStatus.Failure;     } }

      差不多所有東西都完成了,為了方便使用,我們和上篇寫行為樹時一樣,也做一個構造器。

      8. 構造器

      構造器會自帶規劃器和執行器,并將任務的創建打包成函數。也和上篇行為樹一樣,用棧的方式描述構建過程,提供一定可視化。

      public partialclassHTNPlanBuilder {     private HTNPlanner planner;      private HTNPlanRunner runner;     privatereadonly Stack taskStack;     public HTNPlanBuilder()     {         taskStack = new Stack ();     }     private void AddTask(IBaseTask task)     {         if (planner != null)//當前計劃器不為空         {             //將新任務作為構造棧頂元素的子任務             taskStack.Peek().AddNextTask(task);         }         else//如果計劃器為空,意味著新任務是根任務,進行初始化         {             planner = new HTNPlanner(task as CompoundTask);             runner = new HTNPlanRunner(planner);         }         //如果新任務是原子任務,就不需要進棧了,因為原子任務不會有子任務         if (task isnot PrimitiveTask)         {             taskStack.Push(task);         }     }     //剩下的代碼都很簡單,我相信能直接看得懂     public void RunPlan()     {         runner.RunPlan();     }     public HTNPlanBuilder Back()     {         taskStack.Pop();         returnthis;     }     public HTNPlanner End()     {         taskStack.Clear();         return planner;     }     public HTNPlanBuilder CompoundTask()     {         var task = new CompoundTask();         AddTask(task);         returnthis;     }     public HTNPlanBuilder Method(System.Func
      
       condition)     {         var task = new Method(condition);         AddTask(task);         returnthis;     } }
      

      我還是來簡單畫圖,示意一下構建棧的運作過程吧:

      • 加入一個復合節點0后:


      • 往這個復0加一個方法作為一個子任務:


      • 如果要向復0再加一個方法,就要調用Back函數,再添加:



      總之,用Back調整棧頂的元素,我們可以自由地控制新任務作為誰的子任務。而且通過縮進可以較直觀的看到HTN的整個結構,例如下面這樣:

      //節選自我某個小游戲里的一個小怪的行動 protected override void Start() {     base.Start();     trigger = Para.HeathValue * 0.5f;     hTN.CompoundTask()             .Method(() => isHurt)                 .Enemy_Hurt(this)                 .Enemy_Die(this)                 .Back()             .Method(() => curHp <= trigger)                 .Enemy_Combo(this, 3)                 .Enemy_Rest(this, "victory")                 .Back()             .Method(() => HTNWorld.GetWorldState
      
       ("PlayerHp") > 0)                 .Enemy_Check(this)                 .Enemy_Track(this, PlayerTrans)                 .Enemy_Atk(this)                 .Back()             .Method(() => true)                 .Enemy_Idle(this, 3f)             .End(); }
      

      上述中的Enemy_Check、Enemy_Atk都是實際開發實現的具體原子行為?,F在再來看,發現還是有問題的,HTN擅長規劃,其實并不擅長時時決策,所以在實際開發時,建議與有限狀態機結合。將受傷、死亡這類需要時時反饋的事交給狀態機,HTN本身也可以放進一個狀態,來進行復雜行為。而不是像我這樣,將受傷、死亡也當成原子任務,因為這樣做就要你為各個行為設計受傷中斷,代碼就會比較繁冗。

      “狀態機+其它”的復合決策模型并不罕見,GOAP也經常以這種形式出現。

      最后分享一些設計原子任務的心得:

      1. 如果一個原子任務有一定的運行過程,可以用一個bool值在Operator函數內部判斷是否完成了動作。

      2. 因為我們的世界狀態是用字符串來讀取的,如果我們想獲取某個士兵的血量該怎么辦?有很多士兵在,該如何區分?可以用Unity的GetInstanceID()獲取唯一的ID+“血量”,組合成字符串來區分,其它類似情況同理。例如:

      HTNWorld.AddState(GetInstanceID() + "currentHp", () => currentHp, (v) => currentHp = (float)v); HTNWorld.AddState(GetInstanceID() + "IsHurt", () => isHurt, (v) => { isHurt = (bool)v; }); HTNWorld.AddState(GetInstanceID() + "IsDie", () => curHp <= 0, (v) => { });

      其實真正要了解HTN還是應當自己上手使用,鄙人也只是結合個人的學習和使用心得寫出了這篇文章。

      參考

      [1] 項目的代碼:

      https://www.alipan.com/s/FbjejATyabd

      [2] gitee倉庫:

      https://gitee.com/OwlCat/some-projects-in-tutorials/tree/master/HTN_Game

      [3] 視頻:

      https://www.bilibili.com/video/BV1iG4y1i78Q/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click&vd_source=c9a1131d04faacd4a397411965ea21f4

      [4] 《Game AI Pro》:

      http://www.gameaipro.com/

      文末,再次感謝狐王駕虎 的分享, 作者主頁:https://home.cnblogs.com/u/OwlCat, 如果您有任何獨到的見解或者發現也歡迎聯系我們,一起探討。(QQ群: 793972859 )。

      近期精彩回顧

      【萬象更新】

      【萬象更新】

      【厚積薄發】

      【學堂上新】

      特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。

      Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

      相關推薦
      熱點推薦
      柬泰沖突,再現“和事佬”中國的身影

      柬泰沖突,再現“和事佬”中國的身影

      心花手中開
      2025-12-23 19:54:15
      輿論升級!小洛熙事件后續:有多名醫生發聲,涉事部門已重新服務

      輿論升級!小洛熙事件后續:有多名醫生發聲,涉事部門已重新服務

      王大嘴評說
      2025-12-23 21:49:34
      “二王”的槍法有多準?跟他同時開槍的民警,受處分后黯然下崗

      “二王”的槍法有多準?跟他同時開槍的民警,受處分后黯然下崗

      徐慍解說
      2025-12-12 12:08:08
      中國力保委內瑞拉,特朗普下令:100枚導彈從天而降,先炸第3國!

      中國力保委內瑞拉,特朗普下令:100枚導彈從天而降,先炸第3國!

      兵器評論
      2025-12-22 10:12:27
      天吶!原來大錢是這么來的!網友:難怪我螺絲打到冒煙都賺不到錢

      天吶!原來大錢是這么來的!網友:難怪我螺絲打到冒煙都賺不到錢

      夜深愛雜談
      2025-12-20 17:40:11
      妄言“擁核”的日本官員身份曝光:系高市早苗親信 負責核軍縮事務

      妄言“擁核”的日本官員身份曝光:系高市早苗親信 負責核軍縮事務

      鳳凰衛視
      2025-12-25 14:06:19
      樊振東歸來后由馬龍執教,林詩棟則叫張繼科執鞭,這個提議靠譜嗎

      樊振東歸來后由馬龍執教,林詩棟則叫張繼科執鞭,這個提議靠譜嗎

      鴻印百合
      2025-12-24 21:32:30
      闞清子做夢沒想到,心中這口惡氣竟讓紀凌塵給出了,岳云鵬沒說錯

      闞清子做夢沒想到,心中這口惡氣竟讓紀凌塵給出了,岳云鵬沒說錯

      攬星河的筆記
      2025-12-25 13:50:26
      簽約3年!北京國安國腳中場確定加盟上海海港,離隊不僅因踢替補

      簽約3年!北京國安國腳中場確定加盟上海海港,離隊不僅因踢替補

      天光破云來
      2025-12-25 10:50:21
      大爆冷!聯盟第一轟然倒下,馬刺雙殺雷霆這一戰!我認清4個現實

      大爆冷!聯盟第一轟然倒下,馬刺雙殺雷霆這一戰!我認清4個現實

      籃球掃地僧
      2025-12-24 15:11:54
      新加坡反了!越南也反了!中日關系緊張的時候,一個公然站隊日本

      新加坡反了!越南也反了!中日關系緊張的時候,一個公然站隊日本

      南權先生
      2025-12-23 16:56:51
      中共中央紀委印發《關于做好2026年元旦春節期間正風肅紀工作的通知》

      中共中央紀委印發《關于做好2026年元旦春節期間正風肅紀工作的通知》

      新京報
      2025-12-24 16:09:07
      寧可斷電也不用中國?越南重啟核電慘遭日本“退單”,這下太尷尬

      寧可斷電也不用中國?越南重啟核電慘遭日本“退單”,這下太尷尬

      泠泠說史
      2025-12-24 17:40:33
      暴跌10.4℃!冷空氣控制浙江,還有中雨、雨夾雪或雪!這兩天溫度最低......

      暴跌10.4℃!冷空氣控制浙江,還有中雨、雨夾雪或雪!這兩天溫度最低......

      魯中晨報
      2025-12-25 09:41:05
      70歲大姨回鄉養老,月工資4千指定我母親伺候,父親:我們不差錢

      70歲大姨回鄉養老,月工資4千指定我母親伺候,父親:我們不差錢

      燕無衣
      2024-10-03 08:24:15
      U22國足能否逆襲?U23亞洲杯小組出線的希望在這里!

      U22國足能否逆襲?U23亞洲杯小組出線的希望在這里!

      魔血獄苼
      2025-12-25 12:53:28
      湖北一大媽跳了20多年廣場舞后,拿100多個金鐲子去賣,說家里還有金項鏈沒拿,我人好,都是別人送的

      湖北一大媽跳了20多年廣場舞后,拿100多個金鐲子去賣,說家里還有金項鏈沒拿,我人好,都是別人送的

      LULU生活家
      2025-12-24 18:51:10
      越反華支持越高?高市早苗90%支持率真相曝光,原來民眾另有所圖

      越反華支持越高?高市早苗90%支持率真相曝光,原來民眾另有所圖

      李健政觀察
      2025-12-25 13:34:27
      215票贊成后馬斯克失望離職,一句話評價中國,給特朗普臨別忠告

      215票贊成后馬斯克失望離職,一句話評價中國,給特朗普臨別忠告

      面包夾知識
      2025-12-25 13:45:51
      已經掉了500多顆了,“星鏈”衛星墜落已成常態

      已經掉了500多顆了,“星鏈”衛星墜落已成常態

      新民晚報
      2025-12-23 13:02:35
      2025-12-25 14:36:50
      侑虎科技UWA incentive-icons
      侑虎科技UWA
      游戲/VR性能優化平臺
      1532文章數 986關注度
      往期回顧 全部

      游戲要聞

      remedy調整發售策略 《控制》新作將同步首發Steam平臺

      頭條要聞

      朱孝天舉報阿信所屬公司:勾結黃牛炒票逃稅、假唱

      頭條要聞

      朱孝天舉報阿信所屬公司:勾結黃牛炒票逃稅、假唱

      體育要聞

      單賽季11冠,羽壇“安洗瑩時代”真的來了

      娛樂要聞

      金莎小19歲男友求婚成功!兩人雪地擁吻

      財經要聞

      美國未來18個月不對中國芯片加額外關稅

      科技要聞

      屠龍少年被"招安"!英偉達平安夜豪擲200億

      汽車要聞

      預售31.3萬元起 全新奧迪Q5L將于1月內上市

      態度原創

      房產
      健康
      教育
      數碼
      公開課

      房產要聞

      硬核!央企??谝痪€江景頂流紅盤,上演超預期交付!

      這些新療法,讓化療不再那么痛苦

      教育要聞

      孩子的好奇心從何而來?

      數碼要聞

      英特爾推出2025Q4版Arc Pro顯卡驅動:核顯可分得更多內存

      公開課

      李玫瑾:為什么性格比能力更重要?

      無障礙瀏覽 進入關懷版 主站蜘蛛池模板: 天天撸网站| a片在线免费观看| 久久婷婷五月综合色一区二区| 国产v综合v亚洲欧美久久| 中文字幕亚洲乱码熟女一区二区| 亚洲高清WWW色好看美女| 久草精品视频在线观看| 欧洲av在线| 国产天堂亚洲国产碰碰| 免费VA国产高清大片在线| 久久黄色网| 从化市| 亚洲国产合集| 亚洲欧美?va天堂人熟伦| 日本国产精品第一页久久| 久久96国产精品久久久| 亚洲欧美一区二区成人片| 平湖市| 欧美一区二区三区在线观看| 精品福利导航| 亚洲成av人片色午夜乱码| 国产精品自在在线午夜免费| 国产产无码乱码精品久久鸭| 午夜福利看片在线观看| 91丝袜在线| 中文字幕人妻无码一区二区三区| 男人的天堂av社区在线| 国产高跟黑色丝袜在线| av明星换脸无码精品区| 99久久精品国产一区二区暴力| 日韩无码AV电影网| 奇米影视7777久久精品| 少妇把腿扒开让我爽爽视频| 思思99热精品在线| 日逼免费视频| b站永久免费看片大全| 无码成人AV在线看免费| 国产视频拍拍拍| 精品亚洲天堂| 女人扒开腿让男人桶到爽| 亚洲av不卡电影在线网址最新|