[Unity3D] BMFont 簡易生成工具

對網(wǎng)上大佬的代碼進行了易用性修改桌硫,僅WINDOWS可用玻褪。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;


public class BmfInfo
{
    public string filePath;
    public string fileName;
    public int outWidth = 256;
    public int outHeight = 256;
}

public class BMFontTools : EditorWindow
{
    private string configDirPath;

    private string configFlag = "# imported icon images";

    //字體文件生成路徑
    private string fontDirPath;
    private Vector2 scrollPos;
    private string currentSelect = string.Empty;

    private struct FontInfo
    {
        //圖片路徑
        public string ImgPath;

        //字符
        public char charInfo;
    }

    private List<FontInfo> _fontInfoList = new List<FontInfo>();

    private readonly List<BmfInfo> bmfInfoList = new List<BmfInfo>();

    private int xAdvanceOffset;

    private void Awake()
    {
        configDirPath = Path.Combine(Application.dataPath, "../Tools/BMFont");
        fontDirPath = Path.Combine(Application.dataPath, "AllRes/Digit");
        InitConfigInfo();
    }

    private void InitConfigInfo()
    {
        bmfInfoList.Clear();
        List<string> pathList = new List<string>(Directory.GetFiles(configDirPath, "*.bmfc"));
        for (int i = 0; i < pathList.Count; i++)
        {
            BmfInfo info = new BmfInfo();
            info.fileName = Path.GetFileNameWithoutExtension(pathList[i]);
            info.filePath = pathList[i];
            string[] tempLines = File.ReadAllLines(info.filePath);
            foreach (string tempLine in tempLines)
            {
                if (tempLine.StartsWith("outWidth="))
                {
                    string infoTemp = tempLine.Split('#')[0];
                    int width = int.Parse(infoTemp.Replace("outWidth=", string.Empty));
                    info.outWidth = width;
                }
                else if (tempLine.StartsWith("outHeight="))
                {
                    string infoTemp = tempLine.Split('#')[0];
                    int height = int.Parse(infoTemp.Replace("outHeight=", string.Empty));
                    info.outHeight = height;
                }
            }

            bmfInfoList.Add(info);
        }

        if (!IsNullOrEmpty(bmfInfoList))
        {
            currentSelect = bmfInfoList[0].filePath;
            _fontInfoList = AnalysisConfig(currentSelect);
        }
    }

#if UNITY_EDITOR_WIN
    [MenuItem("AssetsTools/BMFont/BMFontTextureTools", false)]
    private static void MyBMFontTools()
    {
        string path = Path.Combine(Application.dataPath, "../Tools/BMFont/bmfont.exe");
        if (!File.Exists(path))
        {
            Debug.LogWarning($"{path}目錄下不存在[bmfont.exe]");
            if (EditorUtility.DisplayDialog("BMFont工具不存在", $"{path}目錄下不存在[bmfont.exe]", "這就去下載"))
            {
                Application.OpenURL("http://www.angelcode.com/products/bmfont/");
            };
            return;
        }
        path = Path.Combine(Application.dataPath, "../Tools/BMFont/BMFontGenerate.bat");
        if (!File.Exists(path))
        {
            File.WriteAllText(path, "bmfont.exe -c %1  -o %2  ");
        }
        BMFontTools bmFont = GetWindow<BMFontTools>();
        bmFont.Show();
    }
#endif

    private void OnGUI()
    {
        GUILayout.Label("使用說明:");
        GUILayout.Label("1颁褂、點擊NewFont創(chuàng)建新的字體配置故黑。");
        GUILayout.Label("2、修改FontName后混埠,點擊ReName修改字體名字揭北。");
        GUILayout.Label("3吏颖、outWidth和outHeight為輸出圖集寬高半醉,如果輸出多張圖集呆奕,請增大圖集寬高瞧壮。");
        GUILayout.Label("4陈轿、點擊Select選中字體配置麦射,選中后為綠色潜秋。");
        GUILayout.Label("5峻呛、點擊下方Add按鈕,增加字符钩述。");
        GUILayout.Label("6牙勘、點擊SelectImg選擇圖片方面,要求路徑是全英文路徑恭金。");
        GUILayout.Label("7横腿、[可選]修改字符的X軸間隔。");
        GUILayout.Label("8搀别、點擊下方SaveAndExportFont輸出字體文件尾抑。");
        GUILayout.Label("9榜苫、如果生成失敗垂睬,請檢查圖片格式是否為PNG-24驹饺,即位深度為32缴渊。");
        

        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        if (GUILayout.Button("Refresh", EditorStyles.toolbarButton))
        {
            InitConfigInfo();
        }

        if (GUILayout.Button("NewFont", EditorStyles.toolbarButton))
        {
            string fileName = $"{DateTime.Now:yyyyMMddhhmmss}.bmfc";
            File.WriteAllText(Path.Combine(configDirPath, fileName), configFlag, Encoding.UTF8);
            InitConfigInfo();
            currentSelect = ToFind(bmfInfoList, x => x.filePath.Contains(fileName)).filePath;
            _fontInfoList = AnalysisConfig(currentSelect);
        }

        if (GUILayout.Button("Delete", EditorStyles.toolbarButton))
        {
            if (File.Exists(currentSelect))
            {
                File.Delete(currentSelect);
            }
            _fontInfoList = new List<FontInfo>();
            InitConfigInfo();
            return;
        }

        GUILayout.EndHorizontal();

        scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
        EditorGUILayout.BeginVertical();
        EditorGUILayout.BeginHorizontal();
        GUILayout.Label("Font Save Path:", GUILayout.MaxWidth(100));
        fontDirPath = GUILayout.TextField(fontDirPath);
        if (GUILayout.Button("SelectFold"))
        {
            fontDirPath = EditorUtility.OpenFolderPanel("SelectFontOutPutDir", "", "");
        }
        EditorGUILayout.EndHorizontal();
        SetBMFontConfigs();
        SetConfigInfo();

        EditorGUILayout.EndVertical();
        EditorGUILayout.EndScrollView();
    }

    private void SetBMFontConfigs()
    {
        for (int i = 0; i < bmfInfoList.Count; i++)
        {
            if (bmfInfoList[i].filePath.Equals(currentSelect))
            {
                GUI.color = Color.green;
            }

            string filePath = bmfInfoList[i].filePath;

            GUILayout.BeginHorizontal();
            GUI.enabled = false;
            EditorGUILayout.TextField(filePath, GUILayout.MaxWidth(500));
            GUI.enabled = true;
            EditorGUILayout.LabelField("FontName:", GUILayout.MaxWidth(80));
            bmfInfoList[i].fileName = EditorGUILayout.TextField(bmfInfoList[i].fileName, GUILayout.MaxWidth(100));
            EditorGUILayout.LabelField("outWidth:", GUILayout.MaxWidth(70));
            bmfInfoList[i].outWidth =
                int.Parse(EditorGUILayout.TextField(bmfInfoList[i].outWidth.ToString(), GUILayout.MaxWidth(50)));
            EditorGUILayout.LabelField("outHeight:", GUILayout.MaxWidth(70));
            bmfInfoList[i].outHeight =
                int.Parse(EditorGUILayout.TextField(bmfInfoList[i].outHeight.ToString(), GUILayout.MaxWidth(50)));
            if (GUILayout.Button("ReName"))
            {
                Regex regex = new Regex(@"/|\\|<|>|\*|\?");
                if (!string.IsNullOrEmpty(bmfInfoList[i].fileName) && !regex.IsMatch(bmfInfoList[i].fileName))
                {
                    string fileNameTemp = filePath.Replace(Path.GetFileNameWithoutExtension(filePath),
                        bmfInfoList[i].fileName);
                    if (File.Exists(fileNameTemp))
                    {
                        Debug.LogError("文件沖突,命名失敗");
                    }
                    else
                    {
                        File.Move(filePath, fileNameTemp);
                    }

                    InitConfigInfo();
                }
                else
                {
                    Debug.LogError("文件名非法或為空,命名失敗");
                }
            }

            if (GUILayout.Button("Select"))
            {
                currentSelect = filePath;
                _fontInfoList = AnalysisConfig(currentSelect);
            }
            GUILayout.EndHorizontal();
            if (filePath.Equals(currentSelect))
            {
                GUI.color = Color.white;
            }
        }
    }

    private void SetConfigInfo()
    {
        if (!string.IsNullOrEmpty(currentSelect))
        {
            for (int i = 0; i < _fontInfoList.Count; i++)
            {
                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button("Select Img", GUILayout.MaxWidth(100)))
                {
                    string pathTemp = EditorUtility.OpenFilePanelWithFilters("選擇圖片", "", new[] { "Image", "png" });
                    string fileName = Path.GetFileName(pathTemp).Replace(".png", "");
                    if (!string.IsNullOrEmpty(pathTemp))
                    {
                        FontInfo fontInfo = new FontInfo();
                        fontInfo.charInfo = _fontInfoList[i].charInfo;
                        if (fontInfo.charInfo == '\0')
                        {
                            if (int.TryParse(fileName, out int rs))
                            {
                                fontInfo.charInfo = rs.ToString()[0];
                            }
                            else
                            {
                                fontInfo.charInfo = fileName[fileName.Length - 1];
                            }
                        }
                        fontInfo.ImgPath = FormatPath(pathTemp);
                        _fontInfoList[i] = fontInfo;
                        int result = 0;
                        if (int.TryParse(fileName, out result) || int.TryParse(fileName[fileName.Length - 1].ToString(), out result))
                        {
                            int i2 = result;
                            int index = i + 1;
                            while (i2 < 10)
                            {
                                i2++;
                                string path2 = pathTemp.Replace(result + ".png", i2 + ".png");
                                if (!string.IsNullOrEmpty(path2) && File.Exists(path2))
                                {
                                    FontInfo fontInfo2 = new FontInfo();
                                    fontInfo2.charInfo = i2.ToString()[0];
                                    fontInfo2.ImgPath = FormatPath(path2);

                                    while (true)
                                    {
                                        if (_fontInfoList.Count <= index)
                                        {
                                            _fontInfoList.Add(fontInfo2);
                                            index++;
                                            break;
                                        }
                                        if (string.IsNullOrEmpty(_fontInfoList[index].ImgPath))
                                        {
                                            _fontInfoList[index] = fontInfo2;
                                            index++;
                                            break;
                                        }
                                        else
                                        {
                                            index++;
                                        }
                                    }
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }

                EditorGUILayout.LabelField("Char:", GUILayout.MaxWidth(55));

                if (!string.IsNullOrEmpty(_fontInfoList[i].charInfo.ToString()))
                {
                    FontInfo info = new FontInfo();
                    string temp =
                        EditorGUILayout.TextField(_fontInfoList[i].charInfo.ToString(), GUILayout.MaxWidth(30));
                    if (temp.Length == 1 && Regex.IsMatch(temp, "[\x20-\x7e]"))
                    {
                        info.charInfo = temp[0];
                        info.ImgPath = _fontInfoList[i].ImgPath;
                        _fontInfoList[i] = info;
                    }
                }

                EditorGUILayout.LabelField("ImgPath:", GUILayout.MaxWidth(55));
                GUI.enabled = false;
                EditorGUILayout.TextField(_fontInfoList[i].ImgPath);
                GUI.enabled = true;
                if (GUILayout.Button("Delete"))
                {
                    _fontInfoList.RemoveAt(i);
                    i--;
                }
                EditorGUILayout.EndHorizontal();
            }
            EditorGUILayout.LabelField($"當前字符數(shù)量:{_fontInfoList.Count}");
            GUILayout.Space(10);
            EditorGUILayout.BeginHorizontal();
            int.TryParse(EditorGUILayout.TextField("額外的文字間隔", xAdvanceOffset.ToString()), out xAdvanceOffset);
            EditorGUILayout.EndHorizontal();
            if (GUILayout.Button("Add"))
            {
                _fontInfoList.Add(new FontInfo());
            }

            GUI.enabled = !IsNullOrEmpty(_fontInfoList);
            GUILayout.Space(50);
            if (GUILayout.Button("Save And Export Font"))
            {
                SaveFontAndExport();
            }

            GUI.enabled = true;
        }
    }

    private void SaveFontAndExport()
    {
        BmfInfo bmfInfo = ToFind(bmfInfoList, x => x.filePath.Equals(currentSelect));
        string baseFontInfo = File.ReadAllText(configDirPath + "/BaseConfig.bmf");
        baseFontInfo = baseFontInfo
            .Replace("outWidth=", $"outWidth={bmfInfo.outWidth}#")
            .Replace("outHeight=", $"outHeight={bmfInfo.outHeight}#");
        baseFontInfo += "\n";
        for (int i = 0; i < _fontInfoList.Count; i++)
        {
            if (string.IsNullOrEmpty(_fontInfoList[i].ImgPath) ||
                string.IsNullOrEmpty(_fontInfoList[i].charInfo.ToString()))
            {
                continue;
            }

            string info = $"icon=\"{_fontInfoList[i].ImgPath}\",{(int)_fontInfoList[i].charInfo},0,0,0\n";
            baseFontInfo += info;
        }

        File.WriteAllText(currentSelect, baseFontInfo);

        ExportFontInfo();

        AssetDatabase.Refresh();
    }

    private void ExportFontInfo()
    {
        string fileName = Path.GetFileNameWithoutExtension(currentSelect);
        string targetDir = Path.Combine(fontDirPath, fileName);
        if (!Directory.Exists(targetDir))
        {
            Directory.CreateDirectory(targetDir);
        }

        Process process = new Process();
        string batPath = Path.Combine(configDirPath, "BMFontGenerate.bat");
        process.StartInfo.FileName = batPath;
        process.StartInfo.WorkingDirectory = configDirPath;
        process.StartInfo.Arguments =
            string.Format("{0} {1}", currentSelect, Path.Combine(fontDirPath, targetDir + "/" + fileName));
        process.Start();
        process.WaitForExit();
        AssetDatabase.Refresh();
        GenFontInfo(targetDir, fileName);
    }

    private string GetAssetPath(string path)
    {
        string pathTemp = path.Replace("\\", "/");
        pathTemp = pathTemp.Replace(Application.dataPath, "Assets");
        return pathTemp;
    }

    private void GenFontInfo(string fontDirPath, string fileName)
    {
        string matPath = Path.Combine(fontDirPath, fileName + "_0.mat");
        Material mat = AssetDatabase.LoadAssetAtPath<Material>(GetAssetPath(matPath));
        if (mat == null)
        {
            mat = new Material(Shader.Find("UI/Default Font"));
            AssetDatabase.CreateAsset(mat, GetAssetPath(matPath));
        }

        string texturePath = Path.Combine(fontDirPath, fileName + "_0.png");
        Texture _fontTexture = AssetDatabase.LoadAssetAtPath<Texture>(GetAssetPath(texturePath));
        mat = AssetDatabase.LoadAssetAtPath<Material>(GetAssetPath(matPath));
        mat.SetTexture("_MainTex", _fontTexture);

        string fontPath = Path.Combine(fontDirPath, fileName + ".fontsettings");
        Font font = AssetDatabase.LoadAssetAtPath<Font>(GetAssetPath(fontPath));
        if (font == null)
        {
            font = new Font();
            AssetDatabase.CreateAsset(font, GetAssetPath(fontPath));
        }

        string fontConfigPath = Path.Combine(fontDirPath, fileName + ".fnt");
        List<CharacterInfo> chars = GetFontInfo(fontConfigPath, _fontTexture);
        font.material = mat;
        SerializeFont(font, chars, 1);
        //File.Delete(fontConfigPath);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }

    private List<CharacterInfo> GetFontInfo(string fontConfig, Texture texture)
    {
        XmlDocument xml = new XmlDocument();
        xml.Load(fontConfig);

        XmlNode info = xml.GetElementsByTagName("info")[0];
        XmlNodeList chars = xml.GetElementsByTagName("chars")[0].ChildNodes;

        CharacterInfo[] charInfos = new CharacterInfo[chars.Count];

        for (int cnt = 0; cnt < chars.Count; cnt++)
        {
            XmlNode node = chars[cnt];
            CharacterInfo charInfo = new CharacterInfo();
            charInfo.index = ToInt(node, "id");
            charInfo.advance = (int)ToFloat(node, "xadvance") + xAdvanceOffset;
            Rect r = GetUV(node, (Texture2D)texture);
            //這里注意下UV坐標系和從BMFont里得到的信息的坐標系是不一樣的哦牲剃,前者左下角為(0,0)凿傅,
            //右上角為(1,1)聪舒。而后者則是左上角上角為(0,0)箱残,右下角為(圖寬,圖高)

            charInfo.uvBottomLeft = new Vector2(r.xMin, r.yMin);                        //字符uv左下角坐標
            charInfo.uvBottomRight = new Vector2(r.xMax, r.yMin);                       //字符uv右下角坐標
            charInfo.uvTopLeft = new Vector2(r.xMin, r.yMax);                           //字符uv左上角坐標
            charInfo.uvTopRight = new Vector2(r.xMax, r.yMax);                          //字符uv右上角坐標

            //Debug.LogError($"charInfo.advance = {charInfo.advance}");                 // uv1.x    字符的橫軸占用空間
            //Debug.LogError($"r.xMin = {r.xMin}");                                     // uv1.x    uv起始點x坐標
            //Debug.LogError($"r.yMin = {r.yMin}");                                     // uv1.y    uv起始點y坐標
            //Debug.LogError($"r.xMax = {r.xMax}");                                     // uv2.x    uv終止點y坐標
            //Debug.LogError($"r.yMax = {r.yMax}");                                     // uv2.y    uv終止點y坐標

            //Debug.Log("charInfo.minY = " + charInfo.minY);                            // 同上uv
            //Debug.Log("charInfo.maxY = " + charInfo.maxY);
            //Debug.Log("charInfo.minX = " + charInfo.minX);
            //Debug.Log("charInfo.maxX = " + charInfo.maxX);
            //Debug.Log("------------------------");


            r = GetVert(node);
            charInfo.minX = (int)r.xMin;
            charInfo.maxX = (int)r.xMax;

            //不居中
            //charInfo.minY = (int)r.yMax; 
            //charInfo.maxY = (int)r.yMin;
            //居中顯示
            charInfo.minY = (int)((r.yMax - r.yMin) / 2);
            charInfo.maxY = -(int)((r.yMax - r.yMin) / 2);

            charInfos[cnt] = charInfo;
        }
        return new List<CharacterInfo>(charInfos);
    }

    private static void SetLineHeight(SerializedObject font, float height)
    {
        font.FindProperty("m_LineSpacing").floatValue = height;
    }

    private static SerializedObject SerializeFont(Font font, List<CharacterInfo> chars, float lineHeight)
    {
        SerializedObject serializedFont = new SerializedObject(font);
        SetLineHeight(serializedFont, lineHeight);
        SerializeFontCharInfos(serializedFont, chars);
        serializedFont.ApplyModifiedProperties();
        return serializedFont;
    }

    private static void SerializeFontCharInfos(SerializedObject font, List<CharacterInfo> chars)
    {
        SerializedProperty charRects = font.FindProperty("m_CharacterRects");
        charRects.arraySize = chars.Count;
        for (int i = 0; i < chars.Count; ++i)
        {
            CharacterInfo info = chars[i];
            SerializedProperty prop = charRects.GetArrayElementAtIndex(i);
            SerializeCharInfo(prop, info);
        }
    }

    private static void SerializeCharInfo(SerializedProperty prop, CharacterInfo charInfo)
    {
        prop.FindPropertyRelative("index").intValue = charInfo.index;
        prop.FindPropertyRelative("uv").rectValue = charInfo.uv;
        prop.FindPropertyRelative("vert").rectValue = charInfo.vert;
        prop.FindPropertyRelative("advance").floatValue = charInfo.advance;
        prop.FindPropertyRelative("flipped").boolValue = false;
    }

    private List<FontInfo> AnalysisConfig(string configPath)
    {
        List<FontInfo> infoList = new List<FontInfo>();
        string[] fileInfo = File.ReadAllLines(configPath);
        bool isGetInfoFlag = false;
        for (int i = 0; i < fileInfo.Length; i++)
        {
            if (fileInfo[i].Contains(configFlag) || isGetInfoFlag)
            {
                if (!isGetInfoFlag)
                {
                    i++;
                    isGetInfoFlag = true;
                }

                if (i < fileInfo.Length && !string.IsNullOrEmpty(fileInfo[i]))
                {
                    infoList.Add(GetFontInfoByStr(fileInfo[i]));
                }
            }
        }
        return infoList;
    }

    private FontInfo GetFontInfoByStr(string str)
    {
        string[] strTemp = str.Split(',');
        FontInfo fontInfo = new FontInfo();
        string strPathTemp = string.Empty;
        for (int i = 0; i < strTemp.Length; i++)
        {
            if (IsOddDoubleQuota(strTemp[i]))
            {
                strPathTemp += strTemp[i] + ",";
                if (!IsOddDoubleQuota(strPathTemp))
                {
                    strPathTemp = strPathTemp.Substring(0, strPathTemp.Length - 1);
                    break;
                }
            }
            else
            {
                strPathTemp = strTemp[i];
                break;
            }
        }

        fontInfo.ImgPath = strPathTemp.Replace("icon=\"", string.Empty).Replace("\"", string.Empty);
        fontInfo.charInfo = (char)int.Parse(strTemp[strTemp.Length - 4]);
        return fontInfo;
    }

    private bool IsOddDoubleQuota(string str)
    {
        return GetDoubleQuotaCount(str) % 2 == 1;
    }

    private int GetDoubleQuotaCount(string str)
    {
        string[] strArray = str.Split('"');
        int doubleQuotaCount = strArray.Length - 1;
        doubleQuotaCount = doubleQuotaCount < 0 ? 0 : doubleQuotaCount;
        return doubleQuotaCount;
    }

    private string FormatPath(string path)
    {
        path = path.Replace("\\", "/");
        return path;
    }

    private Rect GetUV(XmlNode node, Texture2D textureFile)
    {
        Rect uv = new Rect
        {
            x = ToFloat(node, "x") / textureFile.width,
            y = ToFloat(node, "y") / textureFile.height,
            width = ToFloat(node, "width") / textureFile.width,
            height = ToFloat(node, "height") / textureFile.height
        };
        uv.y = 1f - uv.y - uv.height;
        return uv;
    }

    private Rect GetVert(XmlNode node)
    {
        Rect uv = new Rect
        {
            x = ToFloat(node, "xoffset"),
            y = ToFloat(node, "yoffset"),
            width = ToFloat(node, "width"),
            height = ToFloat(node, "height")
        };
        uv.y = -uv.y;
        uv.height = -uv.height;
        return uv;
    }


    private int ToInt(XmlNode node, string name)
    {
        return Convert.ToInt32(node.Attributes.GetNamedItem(name).InnerText);
    }

    private float ToFloat(XmlNode node, string name)
    {
        return (float)ToInt(node, name);
    }


    static bool IsNullOrEmpty<T>(List<T> lst)
    {
        if (lst == null)
        {
            return true;
        }

        if (lst.Count == 0)
        {
            return true;
        }

        return false;
    }

    static T ToFind<T>(List<T> obj, Predicate<T> predicate)
    {
        for (int i = 0; i < obj.Count; i++)
        {
            if (predicate(obj[i]))
            {
                return obj[i];
            }
        }

        return default;
    }
}

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末举哟,一起剝皮案震驚了整個濱河市妨猩,隨后出現(xiàn)的幾起案子秽褒,更是在濱河造成了極大的恐慌销斟,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泼橘,居然都是意外死亡,警方通過查閱死者的電腦和手機炬灭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門重归,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鼻吮,“玉大人,你說我怎么就攤上這事较鼓”吠” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵笨腥,是天一觀的道長。 經(jīng)常有香客問我勇垛,道長脖母,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任闲孤,我火速辦了婚禮谆级,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肥照。我一直安慰自己们颜,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般彻桃。 火紅的嫁衣襯著肌膚如雪肆饶。 梳的紋絲不亂的頭發(fā)上板惑,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天裆馒,我揣著相機與錄音梗搅,去河邊找鬼钦铺。 笑死烫映,一個胖子當著我的面吹牛识补,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了祠挫?” 一聲冷哼從身側(cè)響起慌植,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎填大,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榨汤,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡蜜宪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年澳窑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片供常。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖手销,靈堂內(nèi)的尸體忽然破棺而出写隶,到底是詐尸還是另有隱情耙册,我是刑警寧澤弃揽,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站则北,受9級特大地震影響矿微,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尚揣,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一涌矢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惑艇,春花似錦蒿辙、人聲如沸拇泛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俺叭。三九已至恭取,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間熄守,已是汗流浹背蜈垮。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工耗跛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人攒发。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓调塌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惠猿。 傳聞我的和親對象是個殘疾皇子羔砾,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

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