寫給VR手游開發(fā)小白的教程:(二)UnityVR插件CardboardSDKForUnity解析(一)

現(xiàn)在我們已經(jīng)有了開發(fā)環(huán)境立磁,還沒安裝環(huán)境的小伙伴可以看上一篇:
(一)Unity3D進(jìn)行Android開發(fā)的環(huán)境搭建(虛擬機(jī)調(diào)試)

今天主要介紹的是谷歌為自己的Cardboard平臺(tái)提供的開發(fā)工具Cardboard SDK插件解析涌攻。Cardboard是谷歌公司在14年發(fā)布的一款極具創(chuàng)意的產(chǎn)品,由手機(jī)內(nèi)部的傳感器支持掏导,它僅需硬紙板和兩片透鏡就能打造移動(dòng)平臺(tái)上的VR體驗(yàn)。Cardboard這里不多做介紹,網(wǎng)上可以買到原裝正版卿城,價(jià)格在50以內(nèi)很便宜,有條件的人可以去體驗(yàn)一下铅搓。

在正式開始之前瑟押,先說明一下本節(jié)需要對(duì)Unity3D引擎有一些基礎(chǔ)才能較流暢的看完,若是中間有疑問可以評(píng)論也可私信星掰,疑問較多也可以再補(bǔ)充一章專門介紹基礎(chǔ)的東西多望。

上一章忘記說了,如果是VR應(yīng)用的話氢烘,就不要在虛擬機(jī)里跑了(虛擬機(jī)根本沒有傳感器去定位你的頭部怀偷,也就不存在VR一說了,不過簡(jiǎn)單的小游戲還是可以在虛擬機(jī)上跑的播玖,所以如果是跑大型應(yīng)用的話椎工,還是選擇真機(jī)吧),虛擬機(jī)的存在其實(shí)純粹是為了學(xué)習(xí)示例用的蜀踏。

沒有真機(jī)的同學(xué)维蒙,這個(gè)demo在Unity里也可以跑,具體按住Alt移動(dòng)鼠標(biāo)就相當(dāng)于轉(zhuǎn)動(dòng)頭部果覆,按住Ctrl移動(dòng)鼠標(biāo)相當(dāng)于歪脖子看颅痊。

/***************************************************************分割線*******************************************************************/
首先要為Unity安裝這個(gè)插件,對(duì)于本插件随静,目前網(wǎng)上有些資源是不帶demo的八千,正好我這邊有一個(gè)帶demo的,附上下載地址:
http://download.csdn.net/detail/mao_xiao_feng/9577849
導(dǎo)入以后燎猛,工程目錄下多了以下兩個(gè)文件夾,雙擊打開DemoScene下的場(chǎng)景

Paste_Image.png

場(chǎng)景大概就是以下這個(gè)樣子了照皆,這個(gè)demo是插件中提供的官方示例重绷,一定要把里面所有東西都研究透,才能很好理解SDK當(dāng)中的每一個(gè)腳本的功能膜毁。

Paste_Image.png

直接切入主題昭卓,看到CardboardMain這個(gè)物體已經(jīng)被設(shè)為了Prefab愤钾,那么它一定是獲得VR效果的關(guān)鍵性物體,事實(shí)上所有VR的實(shí)現(xiàn)都在這個(gè)物體極其子物體下候醒,我們把它一層層的剝離開來能颁。
CardboardMain物體上只綁定了這一個(gè)腳本,開始看源碼

Paste_Image.png
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

// The Cardboard object communicates with the head-mounted display in order to:
// - Query the device for viewing parameters
// - Retrieve the latest head tracking data
// - Provide the rendered scene to the device for distortion correction

public class Cardboard : MonoBehaviour {
// The singleton instance of the Cardboard class.
public static Cardboard SDK {
get {
if (sdk == null) {
sdk = UnityEngine.Object.FindObjectOfType<Cardboard>();
}
if (sdk == null) {
Debug.Log("Creating Cardboard object");
var go = new GameObject("Cardboard");
sdk = go.AddComponent<Cardboard>();
go.transform.localPosition = Vector3.zero;
}
return sdk;
}
}
private static Cardboard sdk = null;

public bool DistortionCorrection {
get {
return distortionCorrection;
}
set {
if (value != distortionCorrection && device != null) {
device.SetDistortionCorrectionEnabled(value && NativeDistortionCorrectionSupported);
}
distortionCorrection = value;
}
}
 [SerializeField]
private bool distortionCorrection = true;

public bool VRModeEnabled {
get {
return vrModeEnabled;
}
set {
if (value != vrModeEnabled && device != null) {
device.SetVRModeEnabled(value);
}
vrModeEnabled = value;
}
}
 [SerializeField]
private bool vrModeEnabled = true;

public bool EnableAlignmentMarker {
get {
return enableAlignmentMarker;
}
set {
if (value != enableAlignmentMarker && device != null) {
device.SetAlignmentMarkerEnabled(value && NativeUILayerSupported);
}
enableAlignmentMarker = value;
}
}
 [SerializeField]
private bool enableAlignmentMarker = true;

public bool EnableSettingsButton {
get {
return enableSettingsButton;
}
set {
if (value != enableSettingsButton && device != null) {
device.SetSettingsButtonEnabled(value && NativeUILayerSupported);
}
enableSettingsButton = value;
}
}
 [SerializeField]
private bool enableSettingsButton = true;

public bool TapIsTrigger = true;

public float NeckModelScale {
get {
return neckModelScale;
}
set {
value = Mathf.Clamp01(value);
if (!Mathf.Approximately(value, neckModelScale) && device != null) {
device.SetNeckModelScale(value);
}
neckModelScale = value;
}
}
 [SerializeField]
private float neckModelScale = 0.0f;

public bool AutoDriftCorrection {
get {
return autoDriftCorrection;
}
set {
if (value != autoDriftCorrection && device != null) {
device.SetAutoDriftCorrectionEnabled(value);
}
autoDriftCorrection = value;
}
}
 [SerializeField]
private bool autoDriftCorrection = true;

#if UNITY_IOS
public bool SyncWithCardboardApp {
get {
return syncWithCardboardApp;
}
set {
if (value && value != syncWithCardboardApp) {
Debug.LogWarning("Remember to enable iCloud capability in Xcode, "
+ "and set the 'iCloud Documents' checkbox. "
+ "Not doing this may cause the app to crash if the user tries to sync.");
}
syncWithCardboardApp = value;
}
}
 [SerializeField]
private bool syncWithCardboardApp = false;
#endif

#if UNITY_EDITOR
// Mock settings for in-editor emulation of Cardboard while playing.
public bool autoUntiltHead = true;

// Whether to perform distortion correction in the editor.
public bool simulateDistortionCorrection = true;

// Use unity remote as the input source.
 [HideInInspector]
public bool UseUnityRemoteInput = false;

public CardboardProfile.ScreenSizes ScreenSize {
get {
return screenSize;
}
set {
if (value != screenSize) {
screenSize = value;
device.UpdateScreenData();
}
}
}
 [SerializeField]
private CardboardProfile.ScreenSizes screenSize = CardboardProfile.ScreenSizes.Nexus5;

public CardboardProfile.DeviceTypes DeviceType {
get {
return deviceType;
}
set {
if (value != deviceType) {
deviceType = value;
device.UpdateScreenData();
}
}
}
 [SerializeField]
public CardboardProfile.DeviceTypes deviceType = CardboardProfile.DeviceTypes.CardboardJun2014;
#endif

// The VR device that will be providing input data.
private static BaseVRDevice device;

public bool NativeDistortionCorrectionSupported { get; private set; }

public bool NativeUILayerSupported { get; private set; }

// The texture that Unity renders the scene to. This is sent to the VR device,
// which renders it to screen, correcting for lens distortion.
public RenderTexture StereoScreen {
get {
// Don't need it except for distortion correction.
if (!distortionCorrection || !vrModeEnabled) {
return null;
}
if (stereoScreen == null && NativeDistortionCorrectionSupported) {
StereoScreen = CreateStereoScreen(); // Note: use set{}
}
return stereoScreen;
}
set {
if (value == stereoScreen) {
return;
}
if (!NativeDistortionCorrectionSupported && value != null) {
Debug.LogError("Can't set StereoScreen: native distortion correction is not supported.");
return;
}
if (stereoScreen != null) {
stereoScreen.Release();
}
stereoScreen = value;
if (stereoScreen != null && !stereoScreen.IsCreated()) {
stereoScreen.Create();
}
if (device != null) {
device.SetStereoScreen(stereoScreen);
}
}
}
private static RenderTexture stereoScreen = null;

public bool UseDistortionEffect {
get {
return !NativeDistortionCorrectionSupported && distortionCorrection && vrModeEnabled
&& SystemInfo.supportsRenderTextures;
}
}

// Describes the current device, including phone screen.
public CardboardProfile Profile {
get {
return device.Profile;
}
}

// Distinguish the stereo eyes.
public enum Eye {
Left,
Right,
Center
}

// When asking for project, viewport, etc, whether to assume viewing through
// the lenses.
public enum Distortion {
Distorted, // Viewing through the lenses
Undistorted // No lenses
}

// The transformation of head from origin in the tracking system.
public Pose3D HeadPose {
get {
return device.GetHeadPose();
}
}

// The transformation from head to eye.
public Pose3D EyePose(Eye eye) {
return device.GetEyePose(eye);
}

// The projection matrix for a given eye.
public Matrix4x4 Projection(Eye eye, Distortion distortion = Distortion.Distorted) {
return device.GetProjection(eye, distortion);
}

// The screen-space rectangle each eye should render into.
public Rect Viewport(Eye eye, Distortion distortion = Distortion.Distorted) {
return device.GetViewport(eye, distortion);
}

// The distance range from the viewer in user-space meters where objects
// may be viewed comfortably in stereo. If the center of interest falls
// outside this range, the stereo eye separation should be adjusted to
// keep the onscreen disparity within the limits set by this range.
public Vector2 ComfortableViewingRange {
get {
return defaultComfortableViewingRange;
}
}
private readonly Vector2 defaultComfortableViewingRange = new Vector2(1.0f, 100000.0f);

private void InitDevice() {
if (device != null) {
device.Destroy();
}
device = BaseVRDevice.GetDevice();
device.Init();

List<string> diagnostics = new List<string>();
NativeDistortionCorrectionSupported = device.SupportsNativeDistortionCorrection(diagnostics);
if (diagnostics.Count > 0) {
Debug.LogWarning("Built-in distortion correction disabled. Causes: ["
+ String.Join("; ", diagnostics.ToArray()) + "]");
}
diagnostics.Clear();
NativeUILayerSupported = device.SupportsNativeUILayer(diagnostics);
if (diagnostics.Count > 0) {
Debug.LogWarning("Built-in UI layer disabled. Causes: ["
+ String.Join("; ", diagnostics.ToArray()) + "]");
}

device.SetVRModeEnabled(vrModeEnabled);
device.SetDistortionCorrectionEnabled(distortionCorrection
&& NativeDistortionCorrectionSupported);
device.SetAlignmentMarkerEnabled(enableAlignmentMarker
&& NativeUILayerSupported);
device.SetSettingsButtonEnabled(enableSettingsButton
&& NativeUILayerSupported);
device.SetNeckModelScale(neckModelScale);
device.SetAutoDriftCorrectionEnabled(autoDriftCorrection);

device.UpdateScreenData();
}

// NOTE: Each scene load causes an OnDestroy of the current SDK, followed
// by and Awake of a new one. That should not cause the underlying native
// code to hiccup. Exception: developer may call Application.DontDestroyOnLoad
// on the SDK if they want it to survive across scene loads.
void Awake() {
if (sdk == null) {
sdk = this;
}
if (sdk != this) {
Debug.LogWarning("Cardboard SDK object should be a singleton.");
enabled = false;
return;
}
#if UNITY_IOS
Application.targetFrameRate = 60;
#endif
InitDevice();
AddDummyCamera();
StereoScreen = null;
}

public event Action OnTrigger;

public event Action OnTilt;

public bool Triggered { get; private set; }

public bool Tilted { get; private set; }

private bool updated = false;

private CardboardUILayer uiLayer = null;

public void UpdateState() {
if (!updated) {
device.UpdateState();
if (TapIsTrigger) {
if (Input.GetMouseButtonUp(0)) {
device.triggered = true;
}
if (Input.GetKeyUp(KeyCode.Escape)) {
device.tilted = true;
}
}
updated = true;
}
}

private void DispatchEvents() {
Triggered = device.triggered;
Tilted = device.tilted;
device.triggered = false;
device.tilted = false;

if (Tilted) {
if (OnTilt != null) {
OnTilt();
}
}
if (Triggered) {
if (OnTrigger != null) {
OnTrigger();
}
}
}

private void AddDummyCamera() {
var go = gameObject;
if (go.GetComponent<Camera>()) {
go = new GameObject("CardboardDummy");
go.transform.parent = gameObject.transform;
}
var cam = go.AddComponent<Camera>();
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = Color.black;
cam.cullingMask = 0;
cam.useOcclusionCulling = false;
cam.depth = -100;
}

IEnumerator EndOfFrame() {
while (true) {
yield return new WaitForEndOfFrame();
UpdateState();
device.PostRender(vrModeEnabled);
if (vrModeEnabled && !NativeUILayerSupported) {
if (uiLayer == null) {
uiLayer = new CardboardUILayer();
}
uiLayer.Draw();
}
DispatchEvents();
updated = false;
}
}

// Return a StereoScreen with sensible default values.
public RenderTexture CreateStereoScreen() {
return device.CreateStereoScreen();
}

// Reset the tracker so that the user's current direction becomes forward.
public void Recenter() {
device.Recenter();
}

// Set the screen coordinates of the mouse/touch event.
public void SetTouchCoordinates(int x, int y) {
device.SetTouchCoordinates(x, y);
}

void OnEnable() {
device.OnPause(false);
StartCoroutine("EndOfFrame");
}

void OnDisable() {
StopCoroutine("EndOfFrame");
device.OnPause(true);
}

void OnApplicationPause(bool pause) {
device.OnPause(pause);
}

void OnApplicationFocus(bool focus) {
device.OnFocus(focus);
}

void OnLevelWasLoaded(int level) {
device.Reset();
}

void OnDestroy() {
if (device != null) {
device.Destroy();
}
if (sdk == this) {
sdk = null;
}
}

void OnApplicationQuit() {
device.OnApplicationQuit();
}

//********* OBSOLETE ACCESSORS *********

 [System.Obsolete("Use DistortionCorrection instead.")]
public bool nativeDistortionCorrection {
get { return DistortionCorrection; }
set { DistortionCorrection = value; }
}

 [System.Obsolete("InCardboard is deprecated.")]
public bool InCardboard { get { return true; } }

 [System.Obsolete("Use Triggered instead.")]
public bool CardboardTriggered { get { return Triggered; } }

 [System.Obsolete("Use HeadPose instead.")]
public Matrix4x4 HeadView { get { return HeadPose.Matrix; } }

 [System.Obsolete("Use HeadPose instead.")]
public Quaternion HeadRotation { get { return HeadPose.Orientation; } }

 [System.Obsolete("Use HeadPose instead.")]
public Vector3 HeadPosition { get { return HeadPose.Position; } }

 [System.Obsolete("Use EyePose() instead.")]
public Matrix4x4 EyeView(Eye eye) {
return EyePose(eye).Matrix;
}

 [System.Obsolete("Use EyePose() instead.")]
public Vector3 EyeOffset(Eye eye) {
return EyePose(eye).Position;
}

 [System.Obsolete("Use Projection() instead.")]
public Matrix4x4 UndistortedProjection(Eye eye) {
return Projection(eye, Distortion.Undistorted);
}

 [System.Obsolete("Use Viewport() instead.")]
public Rect EyeRect(Eye eye) {
return Viewport(eye, Distortion.Distorted);
}

 [System.Obsolete("Use ComfortableViewingRange instead.")]
public float MinimumComfortDistance { get { return ComfortableViewingRange.x; } }

 [System.Obsolete("Use ComfortableViewingRange instead.")]
public float MaximumComfortDistance { get { return ComfortableViewingRange.y; } }
}

來自CODE的代碼片Cardboard.cs

上面的源代碼理解起來比較麻煩倒淫,我解析的時(shí)候會(huì)把變量定義等一些順序調(diào)換一下注釋部分
The Cardboard object communicates with the head-mounted display in order to:Query the device for viewing parameters Retrieve the latest head tracking data Provide the rendered scene to the device for distortion correction 翻譯/Cardboard物體與頭盔顯示器進(jìn)行交互以獲得設(shè)備上的一些參數(shù)伙菊,返回最近頭部跟蹤數(shù)據(jù),為被渲染的場(chǎng)景提供失真校正/
定義了一個(gè)Cardboard類型的靜態(tài)公有變量SDK敌土,主要用來獲取游戲中的Cardboard.cs腳本镜硕,可以看到它是只讀的,即SDK提供了一個(gè)共有的訪問接口返干,在任何時(shí)候它都只有一個(gè)實(shí)例兴枯。 // The singleton instance of the Cardboard class. 翻譯/Cardboard類的單件實(shí)例/---關(guān)于單件模式,編程用的還是挺多的矩欠。
BaseVRDevice類型的私有靜態(tài)變量device财剖,BaseVRDevice也是插件當(dāng)中定義的一個(gè)類,注意0┗础躺坟!它與Cardboard類不同,它不是腳本類8媚瞳氓!這個(gè)變量用的比較多 // The VR device that will be providing input data. 翻譯/VR設(shè)備(在這里直接理解為手機(jī)吧!)提供輸入的數(shù)據(jù)/
公有bool類型的變量DistortionCorrection栓袖,默認(rèn)為true匣摘。DistortionCorrection我把它翻譯為失真校正(暫且這樣翻譯吧),讀屬性不說了裹刮,寫屬性加了個(gè)if語句音榜,想說明假如設(shè)備接入了并且支持失真校正功能,才能修改它的值捧弃,不然一直為true赠叼。(這個(gè)變量是控制扭曲的,開或者關(guān)的效果如下违霞,差距應(yīng)該一目了然)


[圖片上傳中嘴办。。买鸽。(5)]
同上還有VRModeEnabled(VR模式的使能涧郊,開了就是分屏),EnableAlignmentMarker(就是下圖黃圈中間那根線眼五,關(guān)了妆艘,線就沒了)彤灶,EnableSettingsButton(就是下圖綠圈內(nèi)的設(shè)置按鈕,同樣也是關(guān)了就沒了)批旺,AutoDriftCorrection(這個(gè)屬性還沒搞懂先放著)幌陕,NeckModelScale(這個(gè)是專業(yè)術(shù)語,應(yīng)該是頸部的微調(diào)吧...猜的汽煮,值在0-1之間搏熄,調(diào)整的話視角會(huì)有微小的變化,但可以忽略不計(jì))
[圖片上傳中逗物。搬卒。。(6)]
后面從#if UNITY_IOS到#endif中間的部分是預(yù)編譯部分翎卓,根據(jù)iOS契邀,unity_editor,Android選擇性的編譯失暴,可以根據(jù)自己的平臺(tái)選擇性的看坯门。我們現(xiàn)在使用unity編輯器在運(yùn)行,所以他編譯的是#if UNITY_EDITOR到#endif中間的這部分逗扒。
RenderTexture類型的公有變量StereoScreen古戴,渲染紋理是一種即時(shí)更新的紋理。//The texture that Unity renders the scene to. This is sent to the VR device,which renders it to screen, correcting for lens distortion. 翻譯/Unity渲染場(chǎng)景產(chǎn)生的紋理矩肩,它被傳輸?shù)絍R設(shè)備上现恼,在屏幕上被繪制產(chǎn)生正確的透鏡彎曲效果/這個(gè)變量的讀屬性當(dāng)vr模式或者distortionCorrection關(guān)閉時(shí),為null黍檩,寫屬性也只有在支持distortionCorrection的時(shí)候可以使用叉袍,總的來說,可以把它看作產(chǎn)生彎曲效果的一個(gè)工具或者一個(gè)中間量刽酱。
bool類型只讀屬性的UseDistortionEffect喳逛,這個(gè)量是用來判斷透鏡效果是否開啟的量,后面會(huì)用到棵里。
CardboardProfile類型的公有變量Profile润文,也是只讀的。 // Describes the current device, including phone screen. 翻譯/描述當(dāng)前設(shè)備殿怜,包括手機(jī)屏幕/
公有枚舉類型Eye典蝌。 // Distinguish the stereo eyes. 翻譯/看立體效果時(shí)用來區(qū)分左右眼/
公有枚舉類型Distortion。這個(gè)量主要控制在某些特殊情況下是否需要透鏡預(yù)覽头谜,在下面會(huì)用到
Pose3D類型的公有量HeadPose赠法。 // The transformation of head from origin in the tracking system. 翻譯/在跟蹤在系統(tǒng)中,頭部距離起始點(diǎn)的信息/由于這里Pose3D也是插件自定義的類乔夯,后續(xù)需要分析Pose3D這個(gè)類才能知道信息是如何傳遞的砖织。同理還有變量EyePose。
返回Matrix4x4類型的方法Projection(Eye eye, Distortion distortion = Distortion.Distorted)返回結(jié)果是device.GetProjection(eye, distortion);從兩個(gè)參數(shù)推斷這個(gè)方法是要根據(jù)眼睛計(jì)算返回一個(gè)投影矩陣末荐,在Unity圣典中闡述過Matrix4x4類型的投影矩陣侧纯,關(guān)于投影矩陣,涉及到計(jì)算機(jī)圖形學(xué)的東西甲脏,我目前也正在學(xué)習(xí)眶熬,給一個(gè)鏈接大家可以學(xué)習(xí)一下。
http://blog.csdn.net/yanwei2016/article/details/7326180
Viewport(Eye eye, Distortion distortion = Distortion.Distorted)方法返回視口矩形块请,應(yīng)該是處理在屏幕上的位置的一個(gè)方法
Vector2類型的變量ComfortableViewingRange娜氏,只讀,控制我們?nèi)タ纯臻g中物體的一個(gè)最舒服的距離范圍墩新,這個(gè)量不需要去配置贸弥,理解就行。
私有方法InitDevice()用于初始化設(shè)備海渊,因?yàn)樯婕暗紹aseVRDevice類绵疲,我們以后再去解析其細(xì)節(jié)。
私有方法AddDummyCamera()用來添加一個(gè)黑色背景臣疑,當(dāng)然背景也可以自己更換盔憨。
一些瑣碎的定義先到這里

/********************************************************分割線********************************************************************/
NOTE: Each scene load causes an OnDestroy of the current SDK, followed by and Awake of a new one. That should not cause the underlying native code to hiccup. Exception: developer may call Application.DontDestroyOnLoad on the SDK if they want it to survive across scene loads.
翻譯/說明:每一個(gè)新場(chǎng)景被加載會(huì)導(dǎo)致現(xiàn)在sdk的destroy,然后產(chǎn)生新的sdk讯沈,這會(huì)發(fā)生一些問題(這句話實(shí)在不會(huì)翻譯了)郁岩,所以開發(fā)者們?cè)谇袚Q場(chǎng)景的時(shí)候如果希望sdk依然存活,可以使用Application.DontDestroyOnLoad/缺狠。這里其實(shí)可以間接的說明SDK是采用的單件模式问慎,因?yàn)槲覀儫o法用new()去創(chuàng)建,而且自始而終最多只有一個(gè)實(shí)例儒老。
比較關(guān)鍵的awake函數(shù)來了

  void Awake() {  
  if (sdk == null) {   
   sdk = this;    }  
  if (sdk != this) { 
     Debug.LogWarning("Cardboard SDK object should be a singleton.");      enabled = false;      return;  
  }
#if UNITY_IOS   
 Application.targetFrameRate = 60;
#endif  
  InitDevice();  
  AddDummyCamera();    
StereoScreen = null;  }

相信上面的語句根據(jù)我之前的解釋大家都能看懂了
這個(gè)類中沒有Update()所以我認(rèn)為更新可能放在BaseVRDevice類型的device里面了蝴乔。
我先碼到這里(太累了),后面接著此篇驮樊,另外會(huì)解析BaseVRDevice這個(gè)類(很重要^闭!)囚衔,還有CardboardProfile這個(gè)類挖腰。
總結(jié):這一章還只是把每個(gè)類的功能和大致框架整理了一下,并沒有涉及到很多細(xì)節(jié)的東西练湿,也沒有涉及到原理猴仑,注意我們現(xiàn)在還在頂端的父物體Cardboard物體上分析,后面當(dāng)涉及到原理的的時(shí)候,就要開始看下層的Head辽俗,和它的子物體幾個(gè)camera了疾渣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市崖飘,隨后出現(xiàn)的幾起案子榴捡,更是在濱河造成了極大的恐慌,老刑警劉巖朱浴,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吊圾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡翰蠢,警方通過查閱死者的電腦和手機(jī)项乒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梁沧,“玉大人檀何,你說我怎么就攤上這事〕媚幔” “怎么了埃碱?”我有些...
    開封第一講書人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)酥泞。 經(jīng)常有香客問我砚殿,道長(zhǎng),這世上最難降的妖魔是什么芝囤? 我笑而不...
    開封第一講書人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任似炎,我火速辦了婚禮,結(jié)果婚禮上悯姊,老公的妹妹穿的比我還像新娘羡藐。我一直安慰自己,他們只是感情好悯许,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開白布仆嗦。 她就那樣靜靜地躺著,像睡著了一般先壕。 火紅的嫁衣襯著肌膚如雪瘩扼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,895評(píng)論 1 314
  • 那天垃僚,我揣著相機(jī)與錄音集绰,去河邊找鬼。 笑死谆棺,一個(gè)胖子當(dāng)著我的面吹牛栽燕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼碍岔,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼浴讯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起付秕,我...
    開封第一講書人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤兰珍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后询吴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亮元,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年猛计,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爆捞。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奉瘤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出煮甥,到底是詐尸還是另有隱情盗温,我是刑警寧澤,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布成肘,位于F島的核電站卖局,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏双霍。R本人自食惡果不足惜砚偶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洒闸。 院中可真熱鬧染坯,春花似錦、人聲如沸丘逸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽深纲。三九已至仲锄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間囤萤,已是汗流浹背昼窗。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涛舍,地道東北人澄惊。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親掸驱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肛搬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361

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