從去年三月份到現在,《匣》斷斷續續的也經歷一年多的開發,這幾天劇情繫統也算是穩定下來了,後面應該不會再進行大概了,一開始還搞了個節點編輯器,結果後來整個劇情跳轉邏輯改了,編輯器也沒用了。後面有時間還是想把編輯器搞起來,否則配表真的是太麻煩了。
簡介
《匣》是一款劇情向的2d遊戲,所有整個劇情繫統是基於文字內容的,並不需要什麼人物的行爲。基於這一點,整個劇情繫統主體分爲下面三部分
- StoryManager
- StoryData/StoryHandler
- TriggerData/TriggerHandler
StoryManager
這個管理器並不直接管理劇情,它既不存儲劇情數據,也不定義某個劇情的表現形式。當你傳了一個劇情數據或者是觸發器數據給它時,它會根據數據類型選擇對應的Handler再具體去做某件事。系統的核心部分是沒有對數據進行管理的,所以數據怎麼存儲可以由外界定義。
StoryHandler
按照我們的設計需要,劇情被分爲了普通,CG,顧客等幾種,每種劇情都有着自己的表現方式。所有就有個了StoryHandler(劇情處理器)的概念。每種類型的劇情都對應一個處理器。處理器核心代碼只有一個接口和一個泛型基類。泛型基類的方法只有OnHandleStory是其派生類必須實現的,並且要定義好劇情處理完成的時機去調用callback。其他方法一般不用重寫。
public interface IStoryHandler
{
/// <summary>
/// 對應的故事類型
/// </summary>
Type StoryDataType { get; }
/// <summary>
/// 處理劇情數據
/// </summary>
/// <param name="storyDataBase">劇情</param>
/// <param name="callback">完成後的回調</param>
void Handle(StoryDataBase storyDataBase, Action callback = null);
}
public abstract class StoryHandlerBase<T> : IStoryHandler where T : StoryDataBase
{
public Type StoryDataType => typeof(T);
/// <summary>
/// 處理劇情數據
/// </summary>
/// <param name="storyDataBase">劇情</param>
/// <param name="callback">完成後的回調</param>
public void Handle(StoryDataBase storyDataBase, Action callback = null)
/// <summary>
/// 開始故事前檢查是否滿足觸發條件
/// </summary>
/// <param name="storyData"></param>
/// <returns></returns>
protected virtual bool OnCheckStory(T storyData)
/// <summary>
/// 處理故事
/// </summary>
/// <param name="storyData"></param>
/// <param name="callback">故事結束後的回調</param>
protected abstract void OnHandleStory(T storyData, Action callback);
/// <summary>
/// 故事結束後處理一些事情,比如後續劇情
/// </summary>
/// <param name="storyData"></param>
protected virtual void OnStoryEnd(T storyData)
}
TriggerHandler
一個劇情結束後,可能會觸發其他劇情,但觸發方式不一樣,有的可能會立即觸發,有的可能會隔幾天,等等。這樣一來,就需要多種觸發器來定義一個劇情被觸發的方式。
觸發器的核心代碼和劇情處理器基本一致,並且更簡單一些,也是一個接口和一個泛型基類。
在匣開發過程中,並不是每一種觸發器數據都對應一個處理器。有些觸發器可能會在別的地方處理。這一點是同劇情處理器不一樣(如果某種類型的劇情沒有定義對應的處理器,該數據在傳給StoryManager後會拋出一個異常)
/// <summary>
/// 故事觸發處理器
/// </summary>
public interface ITriggerHandler
{
Type TriggerDataType { get; }
void Handle(ITriggerData triggerData);
}
/// <summary>
/// 故事觸發數據的處理器基類
/// </summary>
/// <typeparam name="T">觸發器類型</typeparam>
public abstract class StoryTriggerHandlerBase<T> : ITriggerHandler where T : ITriggerData
{
public Type TriggerDataType => typeof(T);
public void Handle(ITriggerData triggerData)
protected abstract void HandleTriggerData(T triggerData);
}
總結
有了這些基礎代碼之後,再對應去實現所需要的劇情表現形式,可以實現一個相對比較豐富的文字劇情繫統。