VR开发实战HTC Vive项目之八大恒星(射线的管理与使用)

一、框架视图

二、主要代码

VRTK_OutlineObjectCopyHighlighter

// Outline Object Copy|Highlighters|40030
namespace VRTK.Highlighters
{
    using UnityEngine;
    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Creates a mesh copy and applies an outline shader which is toggled on and off when highlighting the object.
    /// </summary>
    /// <remarks>
    ///   > A valid mesh must be found or provided for the clone mesh to be created.
    ///
    /// **Script Usage:**
    ///  * Place the `VRTK_OutlineObjectCopyHighlighter` script on either:
    ///    * The GameObject of the Interactable Object to highlight.
    ///    * Any other scene GameObject and then link that GameObject to the Interactable Objects `Object Highlighter` parameter to denote use of the highlighter.
    ///  * Ensure the `Active` parameter is checked.
    /// </remarks>
    /// <example>
    /// `VRTK/Examples/005_Controller_BasicObjectGrabbing` demonstrates the outline highlighting on the green sphere when the controller touches it.
    ///
    /// `VRTK/Examples/035_Controller_OpacityAndHighlighting` demonstrates the outline highlighting if the left controller collides with the green box.
    /// </example>
    [AddComponentMenu("VRTK/Scripts/Interactions/Highlighters/VRTK_OutlineObjectCopyHighlighter")]
    public class VRTK_OutlineObjectCopyHighlighter : VRTK_BaseHighlighter
    {
        [Tooltip("The thickness of the outline effect")]
        public float thickness = 1f;
        [Tooltip("The GameObjects to use as the model to outline. If one isn't provided then the first GameObject with a valid Renderer in the current GameObject hierarchy will be used.")]
        public GameObject[] customOutlineModels;
        [Tooltip("A path to a GameObject to find at runtime, if the GameObject doesn't exist at edit time.")]
        public string[] customOutlineModelPaths;
        [Tooltip("If the mesh has multiple sub-meshes to highlight then this should be checked, otherwise only the first mesh will be highlighted.")]
        public bool enableSubmeshHighlight = false;

        protected Material stencilOutline;
        protected Renderer[] highlightModels;
        protected string[] copyComponents = new string[] { "UnityEngine.MeshFilter", "UnityEngine.MeshRenderer" };

        /// <summary>
        /// The Initialise method sets up the highlighter for use.
        /// </summary>
        /// <param name="color">Not used.</param>
        /// <param name="affectObject">An optional GameObject to specify which object to apply the highlighting to.</param>
        /// <param name="options">A dictionary array containing the highlighter options:\r     * `&lt;'thickness', float&gt;` - Same as `thickness` inspector parameter.\r     * `&lt;'customOutlineModels', GameObject[]&gt;` - Same as `customOutlineModels` inspector parameter.\r     * `&lt;'customOutlineModelPaths', string[]&gt;` - Same as `customOutlineModelPaths` inspector parameter.</param>
        public override void Initialise(Color? color = null, GameObject affectObject = null, Dictionary<string, object> options = null)
        {
            objectToAffect = (affectObject != null ? affectObject : gameObject);
            usesClonedObject = true;

            if (stencilOutline == null)
            {
                stencilOutline = Instantiate((Material)Resources.Load("OutlineBasic"));
            }
            SetOptions(options);
            ResetHighlighter();
        }

        /// <summary>
        /// The ResetHighlighter method creates the additional model to use as the outline highlighted object.
        /// </summary>
        public override void ResetHighlighter()
        {
            DeleteExistingHighlightModels();
            //First try and use the paths if they have been set
            ResetHighlighterWithCustomModelPaths();
            //If the custom models have been set then use these to override any set paths.
            ResetHighlighterWithCustomModels();
            //if no highlights set then try falling back
            ResetHighlightersWithCurrentGameObject();
        }

        /// <summary>
        /// The Highlight method initiates the outline object to be enabled and display the outline colour.
        /// </summary>
        /// <param name="color">The colour to outline with.</param>
        /// <param name="duration">Not used.</param>
        public override void Highlight(Color? color, float duration = 0f)
        {
            if (highlightModels != null && highlightModels.Length > 0 && stencilOutline != null)
            {
                stencilOutline.SetFloat("_Thickness", thickness);
                stencilOutline.SetColor("_OutlineColor", (Color)color);

                for (int i = 0; i < highlightModels.Length; i++)
                {
                    if (highlightModels[i] != null)
                    {
                        highlightModels[i].gameObject.SetActive(true);
                        highlightModels[i].material = stencilOutline;
                    }
                }
            }
        }

        /// <summary>
        /// The Unhighlight method hides the outline object and removes the outline colour.
        /// </summary>
        /// <param name="color">Not used.</param>
        /// <param name="duration">Not used.</param>
        public override void Unhighlight(Color? color = null, float duration = 0f)
        {
            if (objectToAffect == null)
            {
                return;
            }

            if (highlightModels != null)
            {
                for (int i = 0; i < highlightModels.Length; i++)
                {
                    if (highlightModels[i] != null)
                    {
                        highlightModels[i].gameObject.SetActive(false);
                    }
                }
            }
        }

        protected virtual void OnEnable()
        {
            if (customOutlineModels == null)
            {
                customOutlineModels = new GameObject[0];
            }

            if (customOutlineModelPaths == null)
            {
                customOutlineModelPaths = new string[0];
            }
        }

        protected virtual void OnDestroy()
        {
            if (highlightModels != null)
            {
                for (int i = 0; i < highlightModels.Length; i++)
                {
                    if (highlightModels[i] != null)
                    {
                        Destroy(highlightModels[i]);
                    }
                }
            }
            Destroy(stencilOutline);
        }

        protected virtual void ResetHighlighterWithCustomModels()
        {
            if (customOutlineModels != null && customOutlineModels.Length > 0)
            {
                highlightModels = new Renderer[customOutlineModels.Length];
                for (int i = 0; i < customOutlineModels.Length; i++)
                {
                    highlightModels[i] = CreateHighlightModel(customOutlineModels[i], "");
                }
            }
        }

        protected virtual void ResetHighlighterWithCustomModelPaths()
        {
            if (customOutlineModelPaths != null && customOutlineModelPaths.Length > 0)
            {
                highlightModels = new Renderer[customOutlineModelPaths.Length];
                for (int i = 0; i < customOutlineModelPaths.Length; i++)
                {
                    highlightModels[i] = CreateHighlightModel(null, customOutlineModelPaths[i]);
                }
            }
        }

        protected virtual void ResetHighlightersWithCurrentGameObject()
        {
            if (highlightModels == null || highlightModels.Length == 0)
            {
                highlightModels = new Renderer[1];
                highlightModels[0] = CreateHighlightModel(null, "");
            }
        }

        protected virtual void SetOptions(Dictionary<string, object> options = null)
        {
            float tmpThickness = GetOption<float>(options, "thickness");
            if (tmpThickness > 0f)
            {
                thickness = tmpThickness;
            }

            GameObject[] tmpCustomModels = GetOption<GameObject[]>(options, "customOutlineModels");
            if (tmpCustomModels != null)
            {
                customOutlineModels = tmpCustomModels;
            }

            string[] tmpCustomModelPaths = GetOption<string[]>(options, "customOutlineModelPaths");
            if (tmpCustomModelPaths != null)
            {
                customOutlineModelPaths = tmpCustomModelPaths;
            }
        }

        protected virtual void DeleteExistingHighlightModels()
        {
            VRTK_PlayerObject[] existingHighlighterObjects = objectToAffect.GetComponentsInChildren<VRTK_PlayerObject>(true);
            for (int i = 0; i < existingHighlighterObjects.Length; i++)
            {
                if (existingHighlighterObjects[i].objectType == VRTK_PlayerObject.ObjectTypes.Highlighter)
                {
                    Destroy(existingHighlighterObjects[i].gameObject);
                }
            }
            highlightModels = new Renderer[0];
        }

        protected virtual Renderer CreateHighlightModel(GameObject givenOutlineModel, string givenOutlineModelPath)
        {
            if (givenOutlineModel != null)
            {
                givenOutlineModel = (givenOutlineModel.GetComponent<Renderer>() ? givenOutlineModel : givenOutlineModel.GetComponentInChildren<Renderer>().gameObject);
            }
            else if (givenOutlineModelPath != "")
            {
                Transform getChildModel = objectToAffect.transform.Find(givenOutlineModelPath);
                givenOutlineModel = (getChildModel ? getChildModel.gameObject : null);
            }

            GameObject copyModel = givenOutlineModel;
            if (copyModel == null)
            {
                Renderer copyModelRenderer = objectToAffect.GetComponentInChildren<Renderer>();
                copyModel = (copyModelRenderer != null ? copyModelRenderer.gameObject : null);
            }

            if (copyModel == null)
            { 
                VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_OutlineObjectCopyHighlighter", "Renderer", "the same or child", " to add the highlighter to"));
                return null;
            }

            GameObject highlightModel = new GameObject(objectToAffect.name + "_HighlightModel");
            highlightModel.transform.SetParent(copyModel.transform.parent, false);
            highlightModel.transform.localPosition = copyModel.transform.localPosition;
            highlightModel.transform.localRotation = copyModel.transform.localRotation;
            highlightModel.transform.localScale = copyModel.transform.localScale;
            highlightModel.transform.SetParent(objectToAffect.transform);

            Component[] copyModelComponents = copyModel.GetComponents<Component>();
            for (int i = 0; i < copyModelComponents.Length; i++)
            {
                Component copyModelComponent = copyModelComponents[i];
                if (Array.IndexOf(copyComponents, copyModelComponent.GetType().ToString()) >= 0)
                {
                    VRTK_SharedMethods.CloneComponent(copyModelComponent, highlightModel);
                }
            }

            MeshFilter copyMesh = copyModel.GetComponent<MeshFilter>();
            MeshFilter highlightMesh = highlightModel.GetComponent<MeshFilter>();
            Renderer returnHighlightModel = highlightModel.GetComponent<Renderer>();
            if (highlightMesh != null)
            {
                if (enableSubmeshHighlight)
                {
                    HashSet<CombineInstance> combine = new HashSet<CombineInstance>();
                    for (int i = 0; i < copyMesh.mesh.subMeshCount; i++)
                    {
                        CombineInstance ci = new CombineInstance();
                        ci.mesh = copyMesh.mesh;
                        ci.subMeshIndex = i;
                        ci.transform = copyMesh.transform.localToWorldMatrix;
                        combine.Add(ci);
                    }

                    highlightMesh.mesh = new Mesh();
                    highlightMesh.mesh.CombineMeshes(combine.ToArray(), true, false);
                }
                else
                {
                    highlightMesh.mesh = copyMesh.mesh;
                }
                returnHighlightModel.material = stencilOutline;
                returnHighlightModel.shadowCastingMode = copyModel.transform.GetComponent<Renderer>().shadowCastingMode;
            }
            highlightModel.SetActive(false);

            VRTK_PlayerObject.SetPlayerObject(highlightModel, VRTK_PlayerObject.ObjectTypes.Highlighter);

            return returnHighlightModel;
        }
    }
}

UITipsCanvas

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

/// <summary>
/// 指向目标位置
/// </summary>
public class UITipsCanvas : MonoBehaviour {

    public  GameObject player;
    private Vector3 targetPos; 

    void Start () {

        // player = GameObject.FindGameObjectWithTag(Tags.Player);
     
        targetPos = new Vector3(0,transform.position.y,0);
    }
    
    
    void Update () {

        targetPos.x = player.transform.position.x;
        targetPos.z = player.transform.position.z;
        transform.LookAt(targetPos);
        
       
    }
}

PointerManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRTK;
using VRTK.Highlighters;

/// <summary>
/// 指针管理
/// </summary>
public class PointerManager : MonoBehaviour {

    public Color EnterColor, SetColor;

    private VRTK_Pointer pointer;

    private int musicID;

    private  GameObject go;
    private void Awake()
    {
        pointer = GetComponent<VRTK_Pointer>();
        pointer.DestinationMarkerEnter += Pointer_DestinationMarkerEnter;
        pointer.DestinationMarkerExit += Pointer_DestinationMarkerExit;
        pointer.DestinationMarkerSet += Pointer_DestinationMarkerSet;
    }

    private void OnDestroy()
    {
        pointer.DestinationMarkerEnter -= Pointer_DestinationMarkerEnter;
        pointer.DestinationMarkerExit -= Pointer_DestinationMarkerExit;
        pointer.DestinationMarkerSet -= Pointer_DestinationMarkerSet;
    }


    private void Pointer_DestinationMarkerSet(object sender, DestinationMarkerEventArgs e)
    {
        HighLight(e.target,SetColor);
        switch (e.target.name)
        {
            case "Mercury":
                musicID = 1;
                break;
            case "Venus":
                musicID = 2;
                break;
            case "Earth":
                musicID = 3;
                break;
            case "Mars":
                musicID = 4;
                break;
            case "Jupiter":
                musicID = 5;
                break;
            case "Saturn":
                musicID = 6;
                break;
            case "Uranus":
                musicID = 7;
                break;
            case "Neptune":
                musicID = 8;
                break;
            case "Sun":
                musicID = 9;
                break;
            default:
                break;
        }
        AudioManager.Instance.PlaySound(musicID);
    }

    private void Pointer_DestinationMarkerExit(object sender, DestinationMarkerEventArgs e)
    {
        //GameObject go = e.target.transform.GetChild(0).gameObject;
        try
        {
            go = e.target.transform.Find("UITipsCanvas").gameObject;
            go.SetActive(false);
        }
        catch (System.Exception)
        {
            Debug.Log("不存在");
        } 
        HighLight(e.target,Color.clear);
      
    }

    private void Pointer_DestinationMarkerEnter(object sender, DestinationMarkerEventArgs e)
    {
        // GameObject go = e.target.transform.GetChild(0).gameObject;
        try
        {
            go = e.target.transform.Find("UITipsCanvas").gameObject;
            go.SetActive(true);
        }
        catch (System.Exception)
        {
            Debug.Log("不存在");
        }
       
        HighLight(e.target,EnterColor);

    }

    /// <summary>
    /// 高亮
    /// </summary>
    /// <param name="target"></param>
    /// <param name="color"></param>
    private void HighLight(Transform target,Color color) {

        VRTK_BaseHighlighter highlighter = (target!=null?target.GetComponent<VRTK_BaseHighlighter>():null);
        if (highlighter!=null)
        {
            highlighter.Initialise();
            if (color!=Color.clear)
            {
                highlighter.Highlight(color);
            }
            else
            {
                highlighter.Unhighlight();
            }

        }

    }

}

AudioManager

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using System;

public class AudioManager : MonoBehaviour
{
    private static AudioManager instance;

    public static AudioManager Instance
    {
        get
        {
            return instance;
        }
    }

    private Dictionary<int, string> audioPathDict;      // 存放音频文件路径

    private AudioSource musicAudioSource;

    private List<AudioSource> unusedSoundAudioSourceList;   // 存放可以使用的音频组件

    private List<AudioSource> usedSoundAudioSourceList;     // 存放正在使用的音频组件

    private Dictionary<int, AudioClip> audioClipDict;       // 缓存音频文件

    private float musicVolume = 1;

    private float soundVolume = 1;

    private string musicVolumePrefs = "MusicVolume";

    private string soundVolumePrefs = "SoundVolume";

    private int poolCount = 3;         // 对象池数量

    void Awake()
    {
        DontDestroyOnLoad(this.gameObject);
        instance = this;

        audioPathDict = new Dictionary<int, string>()       // 这里设置音频文件路径。需要修改。 TODO
        {

            { 1, "Audios/水星"},
            { 2, "Audios/金星"},
            { 3,"Audios/地球"},
            { 4,"Audios/火星"},
            { 5,"Audios/木星"},
            { 6,"Audios/土星"},
            { 7,"Audios/天王星"},
            { 8,"Audios/海王星"},
        };

        musicAudioSource = gameObject.AddComponent<AudioSource>();
        musicAudioSource.spatialBlend = 1;//设置3D音效
        musicAudioSource.maxDistance = 150; //设置3d距离
        unusedSoundAudioSourceList = new List<AudioSource>();
        usedSoundAudioSourceList = new List<AudioSource>();
        audioClipDict = new Dictionary<int, AudioClip>();
    }

    void Start()
    {
        // 从本地缓存读取声音音量
        if (PlayerPrefs.HasKey(musicVolumePrefs))
        {
            musicVolume = PlayerPrefs.GetFloat(musicVolumePrefs);
        }
        if (PlayerPrefs.HasKey(soundVolumePrefs))
        {
            musicVolume = PlayerPrefs.GetFloat(soundVolumePrefs);
        }
    }

    /// <summary>
    /// 播放背景音乐
    /// </summary>
    /// <param name="id"></param>
    /// <param name="loop"></param>
    public void PlayMusic(int id, bool loop = true)
    {
        // 通过Tween将声音淡入淡出
        DOTween.To(() => musicAudioSource.volume, value => musicAudioSource.volume = value, 0, 0.5f).OnComplete(() =>
        {
            musicAudioSource.clip = GetAudioClip(id);
            musicAudioSource.clip.LoadAudioData();
            musicAudioSource.loop = loop;
            musicAudioSource.volume = musicVolume;
            musicAudioSource.Play();
            DOTween.To(() => musicAudioSource.volume, value => musicAudioSource.volume = value, musicVolume, 0.5f);
        });
    }

    /// <summary>
    /// 播放音效
    /// </summary>
    /// <param name="id"></param>
    public void PlaySound(int id, Action action = null)
    {
        
        if (unusedSoundAudioSourceList.Count != 0)
        {
            AudioSource audioSource = UnusedToUsed();
            audioSource.clip = GetAudioClip(id);
            audioSource.clip.LoadAudioData();
            audioSource.Play();

            StartCoroutine(WaitPlayEnd(audioSource, action));
        }
        else
        {
            AddAudioSource();

            AudioSource audioSource = UnusedToUsed();
            audioSource.clip = GetAudioClip(id);
            audioSource.clip.LoadAudioData();
            audioSource.volume = soundVolume;
            audioSource.loop = false;
            audioSource.Play();

            StartCoroutine(WaitPlayEnd(audioSource, action));
        }
    }

    /// <summary>
    /// 播放3d音效
    /// </summary>
    /// <param name="id"></param>
    /// <param name="position"></param>
    public void Play3dSound(int id, Vector3 position)
    {
        AudioClip ac = GetAudioClip(id);
        AudioSource.PlayClipAtPoint(ac, position);
    }

    /// <summary>
    /// 当播放音效结束后,将其移至未使用集合
    /// </summary>
    /// <param name="audioSource"></param>
    /// <returns></returns>
    IEnumerator WaitPlayEnd(AudioSource audioSource, Action action)
    {
        yield return new WaitUntil(() => { return !audioSource.isPlaying; });
        UsedToUnused(audioSource);
        if (action != null)
        {
            action();
        }
    }

    /// <summary>
    /// 获取音频文件,获取后会缓存一份
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    private AudioClip GetAudioClip(int id)
    {
        if (!audioClipDict.ContainsKey(id))
        {
            if (!audioPathDict.ContainsKey(id))
                return null;
            AudioClip ac = Resources.Load(audioPathDict[id]) as AudioClip;
            audioClipDict.Add(id, ac);
        }
        return audioClipDict[id];
    }

    /// <summary>
    /// 添加音频组件
    /// </summary>
    /// <returns></returns>
    private AudioSource AddAudioSource()
    {
        if (unusedSoundAudioSourceList.Count != 0)
        {
            return UnusedToUsed();
        }
        else
        { 

            AudioSource audioSource ;
            if (gameObject.AddComponent<AudioSource>()==null)
            {
                audioSource = gameObject.AddComponent<AudioSource>();
            }
            else
            {
                audioSource = gameObject.GetComponent<AudioSource>();
            }
            unusedSoundAudioSourceList.Add(audioSource);
            return audioSource;
        }
    }

    /// <summary>
    /// 将未使用的音频组件移至已使用集合里
    /// </summary>
    /// <returns></returns>
    private AudioSource UnusedToUsed()
    {
        AudioSource audioSource = unusedSoundAudioSourceList[0];
        unusedSoundAudioSourceList.RemoveAt(0);
        usedSoundAudioSourceList.Add(audioSource);
        return audioSource;
    }

    /// <summary>
    /// 将使用完的音频组件移至未使用集合里
    /// </summary>
    /// <param name="audioSource"></param>
    private void UsedToUnused(AudioSource audioSource)
    {
        if (usedSoundAudioSourceList.Contains(audioSource))
        {
            usedSoundAudioSourceList.Remove(audioSource);
        }
        if (unusedSoundAudioSourceList.Count >= poolCount)
        {
            Destroy(audioSource);
        }
        else if (audioSource != null && !unusedSoundAudioSourceList.Contains(audioSource))
        {
            unusedSoundAudioSourceList.Add(audioSource);
        }
    }

    /// <summary>
    /// 修改背景音乐音量
    /// </summary>
    /// <param name="volume"></param>
    public void ChangeMusicVolume(float volume)
    {
        musicVolume = volume;
        musicAudioSource.volume = volume;

        PlayerPrefs.SetFloat(musicVolumePrefs, volume);
    }

    /// <summary>
    /// 修改音效音量
    /// </summary>
    /// <param name="volume"></param>
    public void ChangeSoundVolume(float volume)
    {
        soundVolume = volume;
        for (int i = 0; i < unusedSoundAudioSourceList.Count; i++)
        {
            unusedSoundAudioSourceList[i].volume = volume;
        }
        for (int i = 0; i < usedSoundAudioSourceList.Count; i++)
        {
            usedSoundAudioSourceList[i].volume = volume;
        }

        PlayerPrefs.SetFloat(soundVolumePrefs, volume);
    }

    /// <summary>
    /// 是否3d音效
    /// </summary>
    /// <param name="is3d"></param>
    public void Is3DSounds(bool is3d)
    {
        if (is3d)
            musicAudioSource.spatialBlend = 1;//设置3D音效  
        else
            musicAudioSource.spatialBlend = 0;//设置3D音效  

    }

    /// <summary>
    /// 设置3d音效距离
    /// </summary>
    /// <param name="distance"></param>
    public void Set3DDistance(float distance)
    {

        musicAudioSource.maxDistance = distance; //设置3d音效距离
    }

    /// <summary>
    /// 设置3D位置信息
    /// </summary>
    /// <param name="pos"></param>
    public void SetAudioPos(Vector3 pos)
    {
        transform.position = pos;
    }

    /// <summary>
    /// 播放速度
    /// </summary>
    /// <param name="isFast"></param>
    /// <param name="speed"></param>
    public void PlaySpeed(bool isFast, float speed)
    {
        if (isFast)
        {
            musicAudioSource.pitch = speed;

        }
        else
        {
            musicAudioSource.pitch = 1;
        }

    }

    /// <summary>
    /// 停止播放
    /// </summary>
    public void StopSound() {

        musicAudioSource.Stop();
    }
}

三、主要效果图

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容