99国产精品欲av蜜臀,可以直接免费观看的AV网站,gogogo高清免费完整版,啊灬啊灬啊灬免费毛片

網易首頁 > 網易號 > 正文 申請入駐

社區分享 | 有限狀態機的實現與使用(附代碼與項目)

0
分享至

這篇文章轉載自 Unity 社區開發者狐王加護,記錄了作者對有限狀態機的理解與實現方式,包含示例項目與代碼。狐王加護在 Unity 中國開發者社區持續更新技術內容中, 點擊閱讀原文,前往 狐王加護的社區主頁 ,閱讀更多干貨文章~

有限狀態機在游戲制作中十分常見,它既可以作為玩家角色的控制框架,純代碼控制動畫的播放,免去動畫間的“連連看”;也可以制作簡單的 AI,甚至還可以搭配其它 AI 決策方式做出更復雜易用的 AI 控制……本文僅是個人對有限狀態機的理解,與大家一同交流有限狀態機的使用。

有限狀態機的介紹

有限狀態機(finite-state machine,縮寫:FSM),本身是一種數學計算模型,用于有限幾個「狀態」的動作與它們之間的轉換。大概長這樣:


此物在 Unity 中亦有記載——那就是動畫控制器,它也是一種有限狀態機,只不過各個狀態都是動畫片段,它們之間的轉化的條件是參數。


一個狀態機中,只能同時處于一個狀態。而且,一個狀態中不能用相同條件轉移到不同狀態,因為這樣違背了「同時處于一個狀態」這點,例如下面這樣:


「狀態」并不是具體的,只要你有辦法定義,它可以是別的任何東西;而狀態轉換的條件更是可以小到變量、大到函數。

有限狀態機有個非常重要的特點:下一個狀態只能從當前狀態轉換,這就使得控制的邏輯變得清晰。游戲開發中,我們就可以將角色的一個行為作為一種「狀態」,一些條件判斷作為轉換的依據。

代碼實現有限狀態機

狀態

首先我們定義有限狀態機中的「狀態」,如前文所言,「狀態」可以是很多東西,但通常都少不了以下內容:

  • 進入該狀態時會執行一次的邏輯

  • 處于該狀態時會不斷執行的邏輯

  • 退出該狀態(轉移到其它狀態)時會執行一次的邏輯

故而,我們可以這樣將它們以接口的方式定義:

public interface IFSMState
{
///

/// 進入該狀態時執行的
///
void Enter();

///

/// 相當于用Unity生命周期中的Update,用于邏輯更新
///
void LogicalUpdate();

///

/// 狀態結束時(即轉移出時)執行的
///
void Exit();
}

只要繼承了這個接口,就可以作為一種「狀態」。什么?你說你的角色還會用到FixedUpdate、 OnAnimatorIK 等其它的「不斷更新」的函數,該如何在「狀態」中增加這些邏輯?

其實我們所寫的雖為接口,但并不能直接作為根本,我是說具體狀態并非是直接繼承這個接口實現的,考慮到實際中,所謂處于該狀態時會不斷執行的邏輯可能不止一種,所以我們要用一個繼承了這個接口的類作為基類狀態(在「示例」部分會展示這一點)。

我們并不需要對轉換條件單獨寫一個類,轉換條件可以直接寫在諸如 LogicalUpdate 這類函數中,自行判斷切換(示例中有體現)。

狀態機

狀態機的設計需要考慮以下問題:

  • 能方便地增加與查找各個狀態

  • 能方便的切換狀態

  • 能很好地執行狀態的邏輯(即狀態進入、退出、持續執行的那些邏輯)

對于第一個問題,我們可以使用字典存儲狀態,這樣就方便增加與查找。但該用什么作為字典的鍵值呢?首先,我們知道狀態機中的各個狀態是沒有重復的(兩個相同的狀態也沒什么意義好吧),或許可以給各個狀態起個名字用作鍵值,當然也可以自定義枚舉變量。但這些都要額外多些變量,莫不如就用狀態本身的類型(System.Type),故而我們可以這么寫:

using System.Collections.Generic;

public class FSM where T : IFSMState
{
//狀態表
public Dictionary StateTable{ get; protected set; }

public FSM()
{
StateTable = new Dictionary ();
}

//添加狀態
public void AddState(T state)
{
StateTable.Add(state.GetType(), state);
}
}

接著,該看看如何切換了。已知狀態機時刻只能處以一個狀態,那么我們就定義一個「當前狀態」,切換便是這個變量的變化:

using System.Collections.Generic;

public class FSM where T : IFSMState
{
public Dictionary StateTable{ get; protected set; } //狀態表

protected T curState; //當前狀態

public FSM()
{
StateTable = new Dictionary ();
curState = default;
}

public void AddState(T state)
{
StateTable.Add(state.GetType(), state);
}

public void ChangeState(System.Type nextState)
{
curState = StateTable[nextState];
}
}

假設有個狀態類叫 Player_Run 且已經添加到狀態表里了,那么要從當前狀態切換到 Player_Run,就直接這樣調用即可:

MyFSM.ChangeState(typeof(Player_Run));

最后,我們的狀態機還必須具備處理當前狀態邏輯的能力。

首先是比較特殊的進入、退出邏輯,它們都是在特殊時刻執行一次。這并不難,在狀態機切換狀態時處理下即可——在切換時,當前狀態觸發「退出」邏輯、新的狀態觸發「進入」邏輯:

public void ChangeState(System.Type nextState)
{
curState.Exit();
curState = StateTable[nextState];
//因為此時curState變成了新的狀態,故觸發Enter邏輯
//即為 新狀態進入
curState.Enter();
}

接下來便是那些需要「不斷執行」的邏輯了,其實就是一個包裝,我們只需調用狀態機的 OnUpdate 就能讓「當前狀態」的對應邏輯調用了。

public void OnUpdate()
{
curState.LogicalUpdate();
}

總結上述內容,一個完整的狀態機類如下所示:

using System.Collections.Generic;

public class FSM where T : IFSMState
{
public Dictionary StateTable{ get; protected set; } //狀態表
protected T curState; //當前狀態

public FSM()
{
StateTable = new Dictionary ();
curState = default;
}

public void AddState(T state)
{
StateTable.Add(state.GetType(), state);
}

//設置狀態機的第一個狀態時使用,因為一開始的curState還是空的
//故不需要 curState.Exit()
public void SwitchOn(System.Type startState)
{
curState = StateTable[startState];
curState.Enter();
}

public void ChangeState(System.Type nextState)
{
curState.Exit();
curState = StateTable[nextState];
curState.Enter();
}

public void OnUpdate()
{
curState.LogicalUpdate();
}
}

也許你心中還有一些疑問,看我猜的準不準:

為什么狀態機是作為普通的類,而不是繼承 MonoBehavior?

合情合理的問題(我自己也用過繼承 MonoBehavior 的狀態機,畢竟 FSM.OnUpdate() 想要不斷執行,也要在 Unity 生命周期函數中的 Update 里調用。那還不如直接繼承 MonoBehavior,這樣直接在 Update 中調用 curState.LogicalUpdate()。而不這么做是因為:如果一個物體掛載了這樣一個繼承了 MonoBehavior 的狀態機,那它就只能是一個狀態機了。


大家應該都知道, Unity 中的動畫狀態機是分層級,這使得角色的各個部位可以執行不同的動畫。例如,下半身播放行走動畫,上半身播放射擊動畫,從而做到邊射擊邊移動。考慮到可能需要一個腳本中使用多個狀態機,故而將它作為普通的類。

狀態有很多持續執行的邏輯,但并不是都適 合在 Update 中 調用怎么辦?

這個也和之前設計「狀態」時的做法一樣,我們實現的這 個 FSM 也并非直接使用,最妥當的做法還是根據「狀態」進行繼承擴充,例如,我的狀態設計動畫 IK,有些需要在生命周期中 的 OnAnimatorIK 調 用的邏輯,我們就可以這樣繼承:

public class IK_FSM :  FSM  where T : IFSMState, IAnimIKState
{
public void OnAnimatorMove()
{
curState.AnimatorMove();
}
public void OnAnimatorIK(int layerIndex)
{
curState.AnimatorIKUpdate(layerIndex);
}
}

示例

項目鏈接:

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

我們實現以下這樣的行為切換規則用以實踐有限狀態機:玩家在站立時,可切換到下蹲或跳躍(落地后站立);在下蹲后會一直蹲著,觸發主動站起來;蹲著時不能跳躍,且可以選擇揮拳;當玩家揮拳時可以選擇停止,且如果不是蹲著就不能揮拳。

這可以用兩個狀態機表示,一個控制大動作間的切換,一個負責手臂動作的切換:

首先我們定義一個掛載在角色身上用于控制的 PlayerController 腳本,它包含一個控制動畫的動畫機,以及先前提到的兩個有限狀態機;還有幾個屬性讀取按鍵狀態,控制狀態的轉換條件的觸發:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
public Animator animator; //動畫機
public PlayerFSM FSM_0; //大動作的狀態機
public PlayerFSM FSM_1; //單獨控制手臂動作的狀態機

//按下S鍵準備下蹲
public bool IsTryDown => Input.GetKey(KeyCode.S);

//按下W鍵準備起立
public bool IsTryUp => Input.GetKey(KeyCode.W);

//按下空格鍵準備跳躍
public bool IsTryJump => Input.GetKey(KeyCode.Space);

//按下A鍵準備拳擊
public bool IsTryPunch => Input.GetKey(KeyCode.A);

//按下D鍵停止拳擊
public bool IsTryStopPunch => Input.GetKey(KeyCode.D);

private void OnEnable()
{
FSM_0 = new PlayerFSM();
FSM_1 = new PlayerFSM();
}

private void Start()
{

}

private void Update()
{
FSM_0.OnUpdate();
FSM_1.OnUpdate();
}
}

接著,定義玩家狀態基類,如前所述它將繼承 IFSMState 接口,而由于每個狀態都有對應的動畫要播放,故而我們可以為每個狀態都配備一個動畫名字或動畫哈希,以便進入到該狀態時,用動畫機播放。這其實有點像代碼控制了 Unity 動畫控制器,只不過附帶了些額外邏輯。這是比較常見的做法,使得我們省去了動畫機中各個動畫切換間的連線。

using UnityEngine;

public class PlayerState : IFSMState
{
protected readonly int animHash; //動畫片段的哈希
protected PlayerController agent;

//傳入agent主要是為了獲取其中的狀態機,animName是狀態播放的動畫的名字
public PlayerState(PlayerController agent, string animName)
{
this.agent = agent;
animHash = Animator.StringToHash(animName);
}

//默認一進入狀態就播放對應動畫
public virtual void Enter()
{
//animator.CrossFade函數可以實現動畫切換時的混合效果
agent.animator.CrossFade(animHash, 0.1f);
}

public virtual void Exit()
{
;
}

public virtual void LogicalUpdate()
{
;
}
}

然后是玩家狀態機,完成目前的任務并不需要額外函數,但考慮到手臂的狀態切換條件與大動作有關,所以我們將 curState 即「當前狀態」用屬性的方式公開,方便讀取狀態機的當前狀態:

public class PlayerFSM : FSM

 { public PlayerState CurState => curState; } 

一切準備就緒,可以實現具體狀態了:

  • Player_Idle 視為「站立」

  • Player_Jumping 視為「跳躍」

  • Player_Down 視為「下蹲」

  • Player_Down_Idle 視為「蹲著」

  • Player_Up 視為「起立」

  • Player_DoNothing 視為「無事」

  • Player_Punch 視為「揮拳」

先來看看「站立」,根據需求,站立可以轉換成兩種狀態——蹲下與跳躍:

public class Player_Idle : PlayerState
{
public Player_Idle(PlayerController agent, string animName) : base(agent, animName)
{
}

public override void LogicalUpdate()
{
if(agent.IsTryDown)
{
agent.FSM_0.ChangeState(typeof(Player_Down));
}
else if(agent.IsTryJump)
{
agent.FSM_0.ChangeState(typeof(Player_Jumping));
}
}
}

再來看看「蹲下」,下蹲只可以轉換成「蹲著」,而且理應是蹲下動畫播放完成后就變為「蹲著」:

public class Player_Down : PlayerState
{
public Player_Down(PlayerController agent, string animName) : base(agent, animName)
{
}

public override void LogicalUpdate()
{
var curInfo = agent.animator.GetCurrentAnimatorStateInfo(0);
if(curInfo.normalizedTime > 0.98f && curInfo.shortNameHash == animHash)
{
agent.FSM_0.ChangeState(typeof(Player_Down_Idle));
}
}
}

注意,由于是使用 CrossFade 混合過渡動畫,所以只是判斷當前播放進度歸一化時間還不夠,還需確認當前動畫名字或哈希是否與需要轉換到的動畫匹配。

因為沒有其它邏輯,所以其余的狀態都與這兩個相差不大:

public class Player_Down_Idle : PlayerState
{
public Player_Down_Idle(PlayerController agent, string animName) : base(agent, animName)
{
}

public override void LogicalUpdate()
{
if(agent.IsTryUp)
{
agent.FSM_0.ChangeState(typeof(Player_Up));
}
}
}

public class Player_Jumping : PlayerState
{
public Player_Jumping(PlayerController agent, string animName) : base(agent, animName)
{
}

public override void LogicalUpdate()
{
var curInfo = agent.animator.GetCurrentAnimatorStateInfo(0);
if(curInfo.normalizedTime > 0.98f && curInfo.shortNameHash == animHash)
{
agent.FSM_0.ChangeState(typeof(Player_Idle));
}
}
}

public class Player_Up : PlayerState
{
public Player_Up(PlayerController agent, string animName) : base(agent, animName)
{
}

public override void LogicalUpdate()
{
var curInfo = agent.animator.GetCurrentAnimatorStateInfo(0);
if(curInfo.normalizedTime > 0.98f && curInfo.shortNameHash == animHash)
{
agent.FSM_0.ChangeState(typeof(Player_Idle));
}
}
}

接下來便是第二個狀態機了,也一樣簡單,只不過要注意,此時控制的應當是 FSM_1 而且動畫機的 CrossFade 或 Play 應當用于層級 1 而非默認的層級 0:

public class Player_DoNothing : PlayerState
{
public Player_DoNothing(PlayerController agent, string animName) : base(agent, animName)
{
}

public override void Enter()
{
//用于層級1,不用CrossFade是因為DoNothing是個空動畫片段,無需過渡
agent.animator.Play(animHash, 1);
}

public override void LogicalUpdate()
{
//讀取了FSM_0的狀態并進行判斷,如果「蹲著」且試圖揮拳才進入「揮拳」
if(agent.FSM_0.CurState is Player_Down_Idle && agent.IsTryPunch)
{
agent.FSM_1.ChangeState(typeof(Player_Punch));
}
}
}

public class Player_Punch : PlayerState
{
public Player_Punch(PlayerController agent, string animName) : base(agent, animName)
{
}

public override void Enter()
{
agent.animator.CrossFade(animHash, 0.1f, 1);
}

public override void LogicalUpdate()
{
if(agent.FSM_0.CurState is not Player_Down_Idle || agent.IsTryStopPunch)
{
agent.FSM_1.ChangeState(typeof(Player_DoNothing));
}
}
}

最后,在 PlayerController 中為兩個狀態機,添加各自狀態:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
public Animator animator; //動畫機
public PlayerFSM FSM_0; //第一層狀態機
public PlayerFSM FSM_1; //第二層狀態機

public bool IsTryDown => Input.GetKey(KeyCode.S);
public bool IsTryUp => Input.GetKey(KeyCode.W);
public bool IsTryJump => Input.GetKey(KeyCode.Space);
public bool IsTryPunch => Input.GetKey(KeyCode.A);
public bool IsTryStopPunch => Input.GetKey(KeyCode.D);

private void OnEnable()
{
FSM_0 = new PlayerFSM();
FSM_0.AddState(new Player_Idle(this, "Idle"));
FSM_0.AddState(new Player_Down(this, "Down"));
FSM_0.AddState(new Player_Down_Idle(this, "Down_Idle"));
FSM_0.AddState(new Player_Up(this, "Up"));
FSM_0.AddState(new Player_Jumping(this, "Jumping"));

FSM_1 = new PlayerFSM();
FSM_1.AddState(new Player_DoNothing(this, "DoNothing"));
FSM_1.AddState(new Player_Punch(this, "Punching"));
}

private void Start()
{
FSM_0.SwitchOn(typeof(Player_Idle));
FSM_1.SwitchOn(typeof(Player_DoNothing));
}

private void Update()
{
FSM_0.OnUpdate();
FSM_1.OnUpdate();
}
}

這些動畫名字當然是根據動畫機里的:


最終效果符合預期:

  • FSM_0


  • FSM_1


其他應用

目前我們主要討論的是純粹使用有限狀態機在角色控制上的應用,其實它也很容易與其它決策方式進行融合。以 HTN(分層任務網絡) 為例,HTN 可以為角色 AI 規劃出未來的行為序列并逐一執行,但在實際執行時,也常會因外部原因而中斷。

例如,HTN 規劃出了一個小兵的行動為:前往兵器庫,拾取武器,返回城墻,巡邏。但鑒于小兵是比較低級的怪,如果受到攻擊,無論他在執行上述哪一部,都應當打斷并重新規劃。這樣就必須在每次執行前的條件中添加“沒有受傷”:

public class Enemy_Patrol : EnemyTask
{
……

protected override bool MetCondition_OnPlan(Dictionary

 worldState) { //沒檢查到敵人且沒受傷時方可巡邏 return !manager.CheckEnemy() && !(bool)worldState[isHurtStr]; } protected override bool MetCondition_OnRun() { //同上 return !manager.CheckEnemy() && !HTNWorld.GetWorldState

 (isHurtStr); } …… } 


而一想到很多的行為其實在受到攻擊時都應當被打斷,這樣添加額外條件判斷屬實繁瑣。當然,這時純粹用 HTN 決策時的問題,我們而將有限狀態機與 HTN 結合的話就簡單很多了,結構如下:

非常小巧的有限狀態機,但能將這種意外的中斷從 HTN 中分離出來。類似的構思其實也不少,像首個使用了 GOAP 作為敵人 AI 的游戲《F.E.A.R》,他們是用 GOAP 規劃出合適的行為序列,再交給有限狀態機去執行行為。

結尾

有限狀態機是比較基礎的行為決策方式,但又不限于行為決策,像游戲進程的控制,開始游戲,暫停游戲,退出游戲,重來游戲……也可以視為一個個狀態并用狀態機管理。只要能將問題抽象成狀態間的轉換,都可以嘗試用有限狀態機解決,會使得邏輯更加清晰。更多用法還得從實踐中去學習啦!

Unity 中文社區持續征集內容投稿,歡迎與 Unity 官方分享你的技術筆記、項目 demo、行業經驗、有趣案例,加入社區建設,繁榮內容生態,帶領百萬 Unity 中文開發者一同學習。

投稿方式:

方式一:在 Unity 中文社區首頁(https://developer.unity.cn/)創建個人賬號,點擊【寫文章】,發表文章;

方式二:聯系郵箱 learn-cn@unity.cn , 投稿技術內容。

Unity 官方微信

第一時間了解Unity引擎動向,學習進階開發技能

每一個“在看”,都是我們前進的動力

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

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-05-06 14:28:06
中國大使參加摩洛哥國王宴會,士兵闖入開槍掃射,眾人倒在血泊中

中國大使參加摩洛哥國王宴會,士兵闖入開槍掃射,眾人倒在血泊中

百年歷史老號
2024-04-02 19:37:39
越南推動十年內實現全民免費醫療

越南推動十年內實現全民免費醫療

李東海評論
2025-05-07 15:47:33
斷電斷糧斷燃油,以軍24小時瘋狂摧毀:也門胡塞打服了,要求停火

斷電斷糧斷燃油,以軍24小時瘋狂摧毀:也門胡塞打服了,要求停火

近史博覽
2025-05-07 16:29:24
廣廈VS北京G2時間確定,許利民祭出三塔戰術,孫銘徽別再搞心態了

廣廈VS北京G2時間確定,許利民祭出三塔戰術,孫銘徽別再搞心態了

體育大學僧
2025-05-08 07:43:16
饒毅傷害了一位杰出女性,而且傷害了三次

饒毅傷害了一位杰出女性,而且傷害了三次

清暉有墨
2025-05-07 14:33:43
李嫣疑亮相謝霆鋒演唱會,和Lucas同框無交流,嘴巴太有辨識度

李嫣疑亮相謝霆鋒演唱會,和Lucas同框無交流,嘴巴太有辨識度

古希臘掌管月桂的神
2025-05-06 09:30:15
莫斯科高層建筑密集部署280個防空單元,確保閱兵式萬無一失

莫斯科高層建筑密集部署280個防空單元,確保閱兵式萬無一失

環球熱點快評
2025-05-05 19:19:11
玉石風波后,胖東來再回應:顧客如需退貨,不扣任何費用

玉石風波后,胖東來再回應:顧客如需退貨,不扣任何費用

界面新聞
2025-05-08 13:04:10
“水龍卷”橫掃!珠海多人拍到!本周末天氣要注意

“水龍卷”橫掃!珠海多人拍到!本周末天氣要注意

環球網資訊
2025-05-08 14:35:29
以少勝多!巴外長證實殲10C已手下留情,法國官員的回應辛酸無奈

以少勝多!巴外長證實殲10C已手下留情,法國官員的回應辛酸無奈

涵豆說娛
2025-05-08 10:46:25
官宣退出!世乒賽衛冕冠軍缺席,王楚欽柳暗花明,三大賽首冠有戲

官宣退出!世乒賽衛冕冠軍缺席,王楚欽柳暗花明,三大賽首冠有戲

侃球熊弟
2025-05-08 00:45:03
印度承認數架戰機被擊落,罕見用中文公布戰況,莫迪察覺到了什么

印度承認數架戰機被擊落,罕見用中文公布戰況,莫迪察覺到了什么

小笛科技
2025-05-08 10:52:10
美國專家給中國戰機下定論:殲-35和殲-20遲早會被淘汰

美國專家給中國戰機下定論:殲-35和殲-20遲早會被淘汰

健身狂人
2025-05-04 00:01:13
安徽女教師嫁非洲醫生,同居三月無意發現丈夫真實身份,慘遭活埋

安徽女教師嫁非洲醫生,同居三月無意發現丈夫真實身份,慘遭活埋

罪案洞察者
2025-05-08 13:55:47
女子給領導買藥想進屋,外賣小哥在一旁吃瓜,還趁機要了小費

女子給領導買藥想進屋,外賣小哥在一旁吃瓜,還趁機要了小費

唐小糖說情感
2025-05-08 09:11:01
俄方沒想到:凍結俄幾千億的西方,連中方船只都不敢上,乖乖放行

俄方沒想到:凍結俄幾千億的西方,連中方船只都不敢上,乖乖放行

阿芒娛樂說
2025-05-08 09:25:41
皇馬名宿古蒂:巴薩幾乎整年都在玩火,想進歐冠決賽必須丟更少的球

皇馬名宿古蒂:巴薩幾乎整年都在玩火,想進歐冠決賽必須丟更少的球

雷速體育
2025-05-07 20:39:07
驚爆!新娘索要28.8萬下車費后續,新郎拒絕道歉,堅決解除婚約

驚爆!新娘索要28.8萬下車費后續,新郎拒絕道歉,堅決解除婚約

觀察鑒娛
2025-05-07 11:13:47
婆婆微信告訴我端午聚餐,我回懟道:30萬的飯太貴了,不吃

婆婆微信告訴我端午聚餐,我回懟道:30萬的飯太貴了,不吃

櫻桃講堂
2025-05-06 18:39:43
2025-05-08 14:56:49
Unity incentive-icons
Unity
Unity中國官方帳戶
2300文章數 6718關注度
往期回顧 全部

科技要聞

鴻蒙電腦正式亮相!華為:布局五年

頭條要聞

巴基斯坦外長:印度發動襲擊后 巴印國安顧問有過接觸

頭條要聞

巴基斯坦外長:印度發動襲擊后 巴印國安顧問有過接觸

體育要聞

威少兩戰37分:屢陷沖突將對手打出鼻血

娛樂要聞

黃圣依離婚后發現,母親同樣令她窒息

財經要聞

57政策解讀:力度空前的系統性穩增長舉措

汽車要聞

純電增程雙動力 阿維塔12 2025款上市26.99萬元起

態度原創

健康
藝術
旅游
房產
公開課

唇皰疹和口腔潰瘍是"同伙"嗎?

藝術要聞

故宮珍藏的墨跡《十七帖》,比拓本更精良,這才是地道的魏晉寫法

旅游要聞

熱聞|清明假期將至,熱門目的地有哪些?

房產要聞

刺激!這波大利好,新老買房客,都贏麻了!

公開課

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

無障礙瀏覽 進入關懷版 主站蜘蛛池模板: 涿鹿县| 涞源县| 宁国市| 团风县| 怀仁县| 峨眉山市| 屯昌县| 松溪县| 娱乐| 罗山县| 元谋县| 冕宁县| 安多县| 鲜城| 特克斯县| 大洼县| 资阳市| 深水埗区| 巧家县| 南漳县| 富源县| 青铜峡市| 隆昌县| 二连浩特市| 长葛市| 六枝特区| 绥江县| 南和县| 汝阳县| 博白县| 自治县| 孟村| 库车县| 祁门县| 深圳市| 会泽县| 炎陵县| 都江堰市| 离岛区| 东安县| 双城市|