中心事件模型鳖粟、統(tǒng)一輸入管理

單例基類

// file name : BaseManager.cs
using UnityEngine;
using UnityEngine.Assertions;
public class BaseManager<T> where T : new()
{
    private static T _instance;
    public static T Ins
    {
        get
        {
            if (_instance == null)
            {
                _instance = new T();
            }
            return _instance;
        }
    }
}

public class BaseManagerMonobehviour<T> : MonoBehaviour where T:MonoBehaviour
{
    private static T _instance;
    public static T Ins
    {
        get
        {
            if (_instance == null)
            {
                Assert.IsTrue(FindObjectsOfType<T>().Length <= 1);      
                _instance = FindObjectOfType<T>();
            }

            if (_instance == null)
            {
                Debug.LogWarning("Base Manager have create a gameobject named " + nameof(T));
                GameObject hc = Instantiate(new GameObject(nameof(T)));
                _instance = hc.AddComponent<T>() as T;
            }
            return _instance;
        }
    }
}

有兩種單例的寫法

  • 不需要繼承 MonoBehaviour 的
  • 需要繼承 MonoBehaviour 的

對于不需要繼承 MonoBehaviour 的,也就是不參與 Unity 的腳本執(zhí)行過程仿畸,也就是只需要被外部調(diào)用示损,不需要自己執(zhí)行,比如說下面的中心事件管理類就是這樣犯眠。
這種方式可以搭配公共 Mono 來實現(xiàn)跟下面的一樣的效果按灶,也就是參與到 Unity 腳本執(zhí)行周期的單例類,但個人覺得更加麻煩筐咧,因為還需要在某個其他的地方把這個沒有繼承自 MonoBehaviour 的一個幀更新方法注冊進(jìn)公共 Mono 的 Update 里面鸯旁,所以暫時不討論。

對于需要繼承 MonoBehaviour 的量蕊,也就是需要參與到腳本執(zhí)行過程或者還需要在編輯器中給某個變量賦值的铺罢。因為是單例,所以場景中只需要有一個残炮,先查找場景中的該腳本的個數(shù)韭赘,如果大于 1 ,那么直接 GG势就,如果等于 1 泉瞻,那么就是它了,如果等于 0苞冯,那么會新建一個空物體瓦灶,然后把該腳本掛上去,但這樣會有一個問題抱完,需要在場景中賦值的腳本運行后就會報空指針,更嚴(yán)重的問題是不能在 OnDestroy 中引用這個單例刃泡,不然會在結(jié)束運行的時候報錯(雖然并不影響什么巧娱,但看著很不舒服)

Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)
The following scene GameObjects were found:
InputMrg

就是說你在 Destory 中創(chuàng)建了新物體。你可能不會直接在 Destory 中 new烘贴,但是只要在其中引用了這個單例實際上就會創(chuàng)建禁添,因為單例被銷毀的之后被判斷為 null,然后就進(jìn)入第二個判斷桨踪,創(chuàng)建了新的 GameObject老翘。

關(guān)于經(jīng)常運行之后才發(fā)現(xiàn) Inspector 面板上有 null 的情況,建議所有需要在場景中賦值的物體都在 OnValidate (其實我覺得在 OnDrawGizmos 中更好)中設(shè)置斷言,如下所示:

public class BreathMaskCheckChapter : MonoBehaviour, IChapter
{

    public GameObject Mask;
    public Transform MaskPreparePosition;
    public GameObject CanvasStartOrReStudy;

    private void OnValidate()
    {
        Assert.IsNotNull(Mask);
        Assert.IsNotNull(MaskPreparePosition);
        Assert.IsNotNull(CanvasStartOrReStudy);
    }

這樣不需要運行就可以在編輯器中檢測這三個值是否為空铺峭。

事件中心

將所有的事件都用字符串索引的方式注冊進(jìn)事件中心墓怀,然后再用事件中心統(tǒng)一觸發(fā),這樣可以在一定程度上解耦合卫键,輸入事件管理的解耦合主要也是依賴于此傀履。

//file name : EventCenter.cs
using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
public interface IEventInfo
{
    //這是一個空接口
}
public class EventInfo<T> : IEventInfo
{
    public UnityAction<T> actions;

    public EventInfo(UnityAction<T> action)
    {
        actions += action;
    }
}

public class EventInfo : IEventInfo
{
    public UnityAction actions;

    public EventInfo(UnityAction action)
    {
        actions += action;
    }
}

public class EventCenter : BaseManager<EventCenter>
{
    //字典中,key對應(yīng)著事件的名字莉炉,
    //value對應(yīng)的是監(jiān)聽這個事件對應(yīng)的委托方法們(重點圈椎稣恕:們)
    private Dictionary<string, IEventInfo> eventDic
        = new Dictionary<string, IEventInfo>();

    //添加事件監(jiān)聽
    //第一個參數(shù):事件的名字
    //第二個參數(shù):處理事件的方法(有參數(shù)(類型為T)的委托)
    public void AddEventListener<T>(string name, UnityAction<T> action)
    {
        //有沒有對應(yīng)的事件監(jiān)聽
        //有的情況
        if (eventDic.ContainsKey(name))
        {
            (eventDic[name] as EventInfo<T>).actions += action;
        }
        //沒有的情況
        else
        {
            eventDic.Add(name, new EventInfo<T>(action));
        }
    }

    public void AddEventListener(string name, UnityAction action)
    {
        //有沒有對應(yīng)的事件監(jiān)聽
        //有的情況
        if (eventDic.ContainsKey(name))
        {
            (eventDic[name] as EventInfo).actions += action;
        }
        //沒有的情況
        else
        {
            eventDic.Add(name, new EventInfo(action));
        }
    }

    //通過事件名字進(jìn)行事件觸發(fā)
    public void EventTrigger<T>(string name, T info)
    {
        //有沒有對應(yīng)的事件監(jiān)聽
        //有的情況(有人關(guān)心這個事件)
        if (eventDic.ContainsKey(name))
        {
            //調(diào)用委托(依次執(zhí)行委托中的方法)
            //?是一個C#的簡化操作
            (eventDic[name] as EventInfo<T>).actions?.Invoke(info);
        }
    }

    public void EventTrigger(string name)
    {
        //有沒有對應(yīng)的事件監(jiān)聽
        //有的情況(有人關(guān)心這個事件)
        if (eventDic.ContainsKey(name))
        {
            //調(diào)用委托(依次執(zhí)行委托中的方法)
            //絮宁?是一個C#的簡化操作
            (eventDic[name] as EventInfo).actions?.Invoke();
        }
    }

    //移除對應(yīng)的事件監(jiān)聽
    public void RemoveEventListener<T>(string name, UnityAction<T> action)
    {
        if (eventDic.ContainsKey(name))
        {
            //移除這個委托
            (eventDic[name] as EventInfo<T>).actions -= action;
        }
    }

    public void RemoveEventListener(string name, UnityAction action)
    {
        if (eventDic.ContainsKey(name))
        {
            //移除這個委托
            (eventDic[name] as EventInfo).actions -= action;
        }
    }

    //清空所有事件監(jiān)聽(主要用在切換場景時)
    public void Clear()
    {
        eventDic.Clear();
    }
}

這樣乍一看沒什么問題梆暮,在外面的調(diào)用方式如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;

public class NewBehaviourScript : MonoBehaviour
{
    private void Start()
    {
        //EventCenter.Ins.AddEventListener<int>("Test1", Test1);
        EventCenter.Ins.AddEventListener("Test1", Test2);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            EventCenter.Ins.EventTrigger<int>("Test1",1);
        }
        
    }

    private void Test1(int i)
    {
        Debug.Log("Test1" + " " + i);
    }

    private void Test2()
    {
        Debug.Log("Test2");
    }
}

對于有參數(shù)的調(diào)用方式如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;

public class NewBehaviourScript : MonoBehaviour
{
    private void Start()
    {
        EventCenter.Ins.AddEventListener<int>("Test1", Test1);
        //EventCenter.Ins.AddEventListener("Test1", Test2);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            EventCenter.Ins.EventTrigger<int>("Test1",1);
        }
        
    }

    private void Test1(int i)
    {
        Debug.Log("Test1" + " " + i);
    }

    private void Test2()
    {
        Debug.Log("Test2");
    }
}

這兩段代碼幾乎是一樣的,為什么我不在一段里面直接說完呢绍昂?是因為如果順著執(zhí)行這兩句的話啦粹,是錯的。一開始 eventDic["Test1"] 的 value 是有參的治专,然后第二次執(zhí)行的時候卖陵,下面的 as 轉(zhuǎn)化的結(jié)果其實是 null,所以就會報空指針異常张峰。

報錯原因

稍微改一下泪蔫,將無參、各個不同的參數(shù)分開來存放

using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
public interface IEventInfo
{
    //這是一個空接口
}
public class EventInfo<T> : IEventInfo
{
    public UnityAction<T> actions;

    public EventInfo(UnityAction<T> action)
    {
        actions += action;
    }
}

public class EventInfo : IEventInfo
{
    public UnityAction actions;

    public EventInfo(UnityAction action)
    {
        actions += action;
    }
}

public class EventCenter : BaseManager<EventCenter>
{
    private Dictionary<string, List<IEventInfo>> eventDic
        = new Dictionary<string, List<IEventInfo>>();

    public void AddEventListener<T>(string name, UnityAction<T> action)
    {
        if (eventDic.ContainsKey(name))
        {
            foreach(var d in eventDic[name])
            {
                if(d is EventInfo<T>)
                {
                    (d as EventInfo<T>).actions += action;
                    return;
                }
            }
        }
        else
        {
            eventDic.Add(name, new List<IEventInfo>());
        }
        eventDic[name].Add(new EventInfo<T>(action));
    }


    public void AddEventListener(string name, UnityAction action)
    {
        if (eventDic.ContainsKey(name))
        {
            foreach (var d in eventDic[name])
            {
                if (d is EventInfo)
                {
                    (d as EventInfo).actions += action;
                    return;
                }
            }
        }
        else
        {
            eventDic.Add(name, new List<IEventInfo>());
        }
        eventDic[name].Add(new EventInfo(action));
    }

    public void EventTrigger<T>(string name, T info)
    {
        if (eventDic.ContainsKey(name))
        {
            foreach (var d in eventDic[name])
            {
                if (d is EventInfo<T>)
                {
                    (d as EventInfo<T>).actions?.Invoke(info);
                }

                if(d is EventInfo)
                {
                    (d as EventInfo).actions?.Invoke();
                }
            }
        }
    }

    public void EventTrigger(string name)
    {
        if (eventDic.ContainsKey(name))
        {
            foreach (var d in eventDic[name])
            {
                if (d is EventInfo)
                {
                    (d as EventInfo).actions?.Invoke();
                    return;
                }
            }
        }
    }

    public void RemoveEventListener<T>(string name, UnityAction<T> action)
    {
        if (eventDic.ContainsKey(name))
        {
            foreach (var d in eventDic[name])
            {
                if (d is EventInfo<T>)
                {
                    (d as EventInfo<T>).actions -= action;
                    return;
                }
            }
        }
    }

    public void RemoveEventListener(string name, UnityAction action)
    {
        if (eventDic.ContainsKey(name))
        {
            foreach (var d in eventDic[name])
            {
                if (d is EventInfo)
                {
                    (d as EventInfo).actions -= action;
                    return;
                }
            }
        }
    }

    //清空所有事件監(jiān)聽(主要用在切換場景時)
    public void Clear()
    {
        eventDic.Clear();
    }
}

測試代碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;

public class NewBehaviourScript : MonoBehaviour
{
    private void Start()
    {
        EventCenter.Ins.AddEventListener<int>("Test1", Test1);
        EventCenter.Ins.AddEventListener("Test1", Test2);
        EventCenter.Ins.AddEventListener<GameObject>("Test1", Test3);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            EventCenter.Ins.EventTrigger<int>("Test1",1);
        }

        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            EventCenter.Ins.EventTrigger("Test1");
        }

        if (Input.GetKeyDown(KeyCode.Alpha3))
        {
            EventCenter.Ins.EventTrigger<GameObject>("Test1",new GameObject("miao"));
        }

    }

    private void Test1(int i)
    {
        Debug.Log("Test1" + " " + i);
    }

    private void Test2()
    {
        Debug.Log("Test2");
    }
    private void Test3(GameObject o)
    {
        Debug.Log("Test3" + " " + o.ToString());
    }
}

當(dāng)按 1 的時候喘批,會調(diào)用 Test1Test2撩荣;當(dāng)按 2 的時候,會調(diào)用 Test2饶深;當(dāng)按 3 的時候餐曹,會調(diào)用 Test2Test3
也就是有參的觸發(fā)時會順帶觸發(fā)無參的敌厘。
最后說一下最好不要在代碼中直接出現(xiàn) string台猴,可以使用一個靜態(tài)類來存放所有的 string。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public static class ConstName
{
    // sounds
    public readonly static string sound_descript_guoShai= "GuoShai";
    public readonly static string sound_descript_liangShai = "LiangShai";
    public readonly static string sound_descript_maDuo = "MaDuo";

    // EventCenter Names
    public readonly static string breathMask_descript_reture = "BreathMask_Reture";
    public readonly static string breathMask_check_startOperate = "BreathMask_Start_Operate";

    // pico SDK Tree
    public readonly static string pico_sdk_controllerManager = "ControllerManager";
    public readonly static string pico_sdk_pvrController = "PvrController";
    public readonly static string pico_sdk_handPosition = "handPosition";

    // Resources
    public readonly static string resources_hand_right= "Prefabs/Hands/hand_right";
    public readonly static string resources_hand_left= "Prefabs/Hands/hand_left";

    // Hand Animation Name
    public readonly static string hand_state_idle = "Idle";
    public readonly static string hand_state_grab = "GrabLarge";
}

輸入事件管理

可以通過中心事件來統(tǒng)一管理輸入事件俱两。因為是做 Pico 開發(fā)饱狂,所有需要檢測兩套輸入,因為需要集合不重復(fù)宪彩,所以可以直接使用 HashSet休讳。如果后面需要再監(jiān)聽別的輸入事件也只需要修改 InputMrg ,對于外部已經(jīng)調(diào)用過的代碼完全不需要修改尿孔。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InputMrg : BaseManagerMonobehviour<InputMrg>
{
    public enum KeyType
    {
        Keyborad,
        Pico
    };
    public class InputDate
    {
        public KeyType type { get; private set; }
        public KeyCode keyCodeType;
        public Pvr_UnitySDKAPI.Pvr_KeyCode picoKeyCodeType;
        public int picoKeyHand;

        public InputDate(KeyCode k)
        {
            type = KeyType.Keyborad;
            keyCodeType = k;
        }

        public InputDate(Pvr_UnitySDKAPI.Pvr_KeyCode k,int hand)
        {
            type = KeyType.Pico;
            picoKeyCodeType = k;
            picoKeyHand = hand;
        }
    }

    
    private readonly HashSet<KeyCode> ListKeyCode = new HashSet<KeyCode>();
    private readonly HashSet<Pvr_UnitySDKAPI.Pvr_KeyCode> ListPicoKeyCode = new HashSet<Pvr_UnitySDKAPI.Pvr_KeyCode>();

    private void Update()
    {
        foreach (var k in ListKeyCode)
        {
            if (Input.GetKeyDown(k))
            {
                EventCenter.Ins.EventTrigger<InputDate>(GetKeyDownEventName(k),new InputDate(k));
            }

            if (Input.GetKeyUp(k))
            {
                EventCenter.Ins.EventTrigger<InputDate>(GetKeyUpEventName(k),new InputDate(k));
            }
        }

        foreach (var k in ListPicoKeyCode)
        {
            if (Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(0, k))
            {
                EventCenter.Ins.EventTrigger<InputDate>(GetKeyDownEventName(k, 0),new InputDate(k,0));
            }

            if (Pvr_UnitySDKAPI.Controller.UPvr_GetKeyDown(1, k))
            {
                EventCenter.Ins.EventTrigger<InputDate>(GetKeyDownEventName(k, 1),new InputDate(k,1));
            }

            if (Pvr_UnitySDKAPI.Controller.UPvr_GetKeyUp(0, k))
            {
                EventCenter.Ins.EventTrigger<InputDate>(GetKeyUpEventName(k, 0),new InputDate(k,0));
            }

            if (Pvr_UnitySDKAPI.Controller.UPvr_GetKeyUp(1, k))
            {
                EventCenter.Ins.EventTrigger<InputDate>(GetKeyUpEventName(k, 1),new InputDate(k,1));
            }
        }
    }

    public string GetKeyDownEventName(KeyCode key)
    {
        ListKeyCode.Add(key);
        return "key_down_" + key.ToString();
    }

    public string GetKeyDownEventName(Pvr_UnitySDKAPI.Pvr_KeyCode key, int handId)
    {
        ListPicoKeyCode.Add(key);
        return "pico_key_down_" + key.ToString() + '_' + handId;
    }

    public string GetKeyUpEventName(KeyCode key)
    {
        ListKeyCode.Add(key);
        return "key_up_" + key.ToString();
    }

    public string GetKeyUpEventName(Pvr_UnitySDKAPI.Pvr_KeyCode key, int handId)
    {
        ListPicoKeyCode.Add(key);
        return "pico_key_up_" + key.ToString() + '_' + handId;
    }

}

外部調(diào)用方式俊柔,你可以像下面這樣用有參數(shù)的回調(diào)函數(shù):

        EventCenter.Ins.AddEventListener<InputMrg.InputDate>(InputMrg.Ins.GetKeyDownEventName(KeyCode.B), EventActionSideButtonDown);
        EventCenter.Ins.AddEventListener<InputMrg.InputDate>(InputMrg.Ins.GetKeyUpEventName(KeyCode.B), EventActionSideButtonUp);
        EventCenter.Ins.AddEventListener<InputMrg.InputDate>(InputMrg.Ins.GetKeyDownEventName(Pvr_UnitySDKAPI.Pvr_KeyCode.Right, 1), EventActionSideButtonDown);
        EventCenter.Ins.AddEventListener<InputMrg.InputDate>(InputMrg.Ins.GetKeyUpEventName(Pvr_UnitySDKAPI.Pvr_KeyCode.Right, 1), EventActionSideButtonUp);
        EventCenter.Ins.AddEventListener<InputMrg.InputDate>(InputMrg.Ins.GetKeyDownEventName(Pvr_UnitySDKAPI.Pvr_KeyCode.Left, 0), EventActionSideButtonDown);
        EventCenter.Ins.AddEventListener<InputMrg.InputDate>(InputMrg.Ins.GetKeyUpEventName(Pvr_UnitySDKAPI.Pvr_KeyCode.Left, 0), EventActionSideButtonUp);```

然后在回調(diào)函數(shù)里面去判斷是否進(jìn)行觸發(fā):

    private void EventActionSideButtonDown(InputMrg.InputDate date)
    {
        if (m_grabbingObject.IsGrabbing)
        {
            if (date.type == InputMrg.KeyType.Keyborad)
            {
                EventActionSideButtonDown1();
            }
            else if (date.type == InputMrg.KeyType.Pico)
            {
                if (m_grabbingObject.IsGrabbingHand == date.picoKeyHand)    // 是正在抓著的手按得側(cè)邊鍵
                {
                    EventActionSideButtonDown1();
                }
            }
        }
    }

你也可以直接使用無參數(shù)的回調(diào)函數(shù):

        EventCenter.Ins.AddEventListener(InputMrg.Ins.GetKeyDownEventName(KeyCode.Space), KeyActionTriggerDown);
        EventCenter.Ins.AddEventListener(InputMrg.Ins.GetKeyDownEventName(Pvr_UnitySDKAPI.Pvr_KeyCode.TRIGGER, handId), KeyActionTriggerDown);

參考

https://blog.csdn.net/m0_46378049/article/details/106180451

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筹麸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子雏婶,更是在濱河造成了極大的恐慌物赶,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尚骄,死亡現(xiàn)場離奇詭異块差,居然都是意外死亡,警方通過查閱死者的電腦和手機倔丈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門憨闰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人需五,你說我怎么就攤上這事鹉动。” “怎么了宏邮?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵泽示,是天一觀的道長。 經(jīng)常有香客問我蜜氨,道長械筛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任飒炎,我火速辦了婚禮埋哟,結(jié)果婚禮上谓娃,老公的妹妹穿的比我還像新娘苗傅。我一直安慰自己帮毁,他們只是感情好忱叭,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著以故,像睡著了一般逮栅。 火紅的嫁衣襯著肌膚如雪跨释。 梳的紋絲不亂的頭發(fā)上照筑,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天吹截,我揣著相機與錄音,去河邊找鬼凝危。 笑死饭弓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的媒抠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咏花,長吁一口氣:“原來是場噩夢啊……” “哼趴生!你這毒婦竟也來了阀趴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤苍匆,失蹤者是張志新(化名)和其女友劉穎刘急,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浸踩,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡叔汁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了检碗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片据块。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖折剃,靈堂內(nèi)的尸體忽然破棺而出另假,到底是詐尸還是另有隱情,我是刑警寧澤怕犁,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布边篮,位于F島的核電站,受9級特大地震影響奏甫,放射性物質(zhì)發(fā)生泄漏戈轿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一阵子、第九天 我趴在偏房一處隱蔽的房頂上張望思杯。 院中可真熱鬧,春花似錦款筑、人聲如沸智蝠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杈湾。三九已至,卻和暖如春攘须,著一層夾襖步出監(jiān)牢的瞬間漆撞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工于宙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留浮驳,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓捞魁,卻偏偏與公主長得像至会,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子谱俭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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