第三人稱射擊游戲核心代碼

本文是學習官方教程SURVIVAL SHOOTER TUTORIAL的筆記;
主要目的是用于記錄一些關鍵的代碼和步驟;
如果感興趣,推薦還是觀看官方的教程:教程鏈接

預覽

1. 控制角色移動

創(chuàng)建地板用于射線捕捉,把Layer設定為Floor

新建一個PlayerMovement腳本,綁定在角色上,并且添加以上屬性

public float speed = 6f;
Vector3 movement;
Animator anim;
Rigidbody playerRigidbody;
public int floorMask;
float camerRayLength = 100f;

void Awake (){
    floorMask = LayerMask.GetMask ("Floor");  //綁定LayerMask
    anim = GetComponent<Animator> ();
    playerRigidbody = GetComponent<Rigidbody> ();
}

射線檢測是否和地面碰撞,并且讓角色旋轉

void Turing() {
    //捕捉主攝像機和鼠標交集的射線
    Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
    RaycastHit floorHit;
    //射線, out 射線點, 長度, 層級,以上幾個關鍵屬性如果產生交集就讓角色旋轉
    if (Physics.Raycast (camRay, out floorHit, camerRayLength, floorMask)) {
        Vector3 playerToMouse = floorHit.point - transform.position;
        playerToMouse.y = 0f;
        Quaternion newRotation = Quaternion.LookRotation (playerToMouse);
        playerRigidbody.MoveRotation (newRotation);
    }
}

捕捉輸入的h和v,移動角色

void Move(float h, float v) {
    movement.Set (h, 0, v);
    movement = movement.normalized * speed * Time.deltaTime;
    playerRigidbody.MovePosition (transform.position + movement);
}

如果角色移動就播放移動動畫

//如果輸入的上下左右不等于0,則讓動畫狀態(tài)機的IsWalking屬性變?yōu)閠rue,具體的動畫切換在角色綁定的動畫狀態(tài)機中
void Animating (float h, float v){
    bool walking = (h != 0f || v != 0f);
    anim.SetBool ("IsWalking", walking);
}

FiexdUpdate輸入Input,并且統(tǒng)一調用以上幾個方法

void FixedUpdate(){
    //具體的單詞參考Edit->ProjectSetting->Input面板
    float h = Input.GetAxisRaw ("Horizontal");  
    float v = Input.GetAxisRaw ("Vertical");
    Move (h, v); //移動角色
    Animating (h, v); //判斷是否播放移動動畫
    Turing (); //旋轉角色
}

ok 運行游戲試試看,此時玩家可以使用鍵盤移動了,并且會面朝鼠標指向的方向.

2. 控制攝像機跟隨玩家

新建一個CameraLookAt腳本,代碼如下

public class CameraLookAtPlayer : MonoBehaviour {
    public Transform target;    //用于編輯器中綁定玩家
    public float smoothing = 5f;    //用于計算順滑度
    Vector3 offset;
    void Start() {
        //首先初始化的時候保存相機和玩家的相對位置
        offset = transform.position - target.position;
    }
        
//這里不要使用FixedUpdate, 移動端屏幕會有視覺卡頓
    void Update () {
        //計算出相機跟隨的位置
        Vector3 targetCamPos = target.position + offset;
        //設置相機的位置,這里用到了Vector3.Lerp,是一個差值計算,使得移動更柔和.但是會略微消耗計算量
        //由于主攝像機只有1個,所以可以忽略這個計算量的消耗
        transform.position = Vector3.Lerp (transform.position, targetCamPos, smoothing * Time.deltaTime);
    }
}

在項目中設定好攝像機和玩家的距離

然后在把CamerLookAt腳本綁定在主攝像機上,并且把玩家設定到target屬性中


相機的設置

ok,運行一下游戲,此時攝像機會跟隨玩家移動,并且有一個緩慢加速的過程

3.創(chuàng)建一個敵人,自動尋路跟隨玩家

首先添加Navigation烘焙

  1. 首先把場景中需要烘焙的物件設置為static, 一些障礙物體要添加碰撞體
  2. 點擊Window->Navigation面板,設置烘焙屬性并且烘焙

創(chuàng)建一個怪物并且基礎設置

  1. 設置Rigidbody
  2. 添加一個膠囊碰撞體,CapsuleCollider組件,用于尋找玩家做碰撞
  3. 添加一個球碰撞體,SphereCollider組件,用于以后攻擊玩家做碰撞
  4. 添加一個尋路組件,NavMeshAgent,用于配合Navigation尋路
  5. 給怪物設置一個動畫控制機AminatorController,用于切換動畫.

給添加怪物尋路代碼

public class ZombunnyMovement_ym : MonoBehaviour {
    Transform player;
    NavMeshAgent nav;
    void Awake(){
        //因為怪物是生成器生成的,所以player需要在生成的時候遍歷一下場景,找到玩家
        player = GameObject.FindGameObjectWithTag ("Player").transform;
        //讀出導航組件
        nav = GetComponent<NavMeshAgent> ();
    }

    void Update() {
        //導航到目的地:玩家的坐標,這里注意,一定是transform.position,而不是transform
        nav.SetDestination (player.position);
    }
}

ok,此時運行游戲,怪物會一直朝著玩家移動

4.設置血條UI

首先設置UI如下圖效果

  1. 在場景中創(chuàng)建一個Canvas
  2. 里面創(chuàng)建一個空物體用來存放HealthUI
  3. 創(chuàng)建一個Slider,刪除手柄,只要進度條,用來顯示血量


    Hierarchy中的層級

    如圖,左下角有一個紅心,和一個Slider,Slider刪掉了手柄


    聲明一下,這種UI不是我做的......

5.給玩家添加生命控制的腳本,和給敵人添加攻擊的腳本

首先是添加控制玩家生命的代碼

新建一個PlayerHealth腳本

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class PlayerHealth_ym : MonoBehaviour {

    public int startingHealth = 100;
    public int currentHealth;   //當前血量
    public Slider healthSlider; //用來存放Slider,收到傷害修改它的值
    public Image damageImage;   //之前設置的收到傷害覆蓋全屏紅色閃爍的圖片
    public AudioClip deathClip; //用來存放死亡的聲音
    public float flashSpeed = 5f;
    public Color flashColor = new Color (1f, 0f, 0f, 0.1f);

    Animator anim;
    AudioSource playAudio;
    PlayerMovement_ym playMovement; //用來死亡的時候取消移動組件,防止玩家移動
    bool isDead;
    bool damaged;

    void Awake () {
        anim = GetComponent<Animator> ();
        playAudio = GetComponent<AudioSource> ();
        playMovement = GetComponent<PlayerMovement_ym> ();
        currentHealth = startingHealth;
    }

    // Update is called once per frame
    void Update () {
        //如果受到傷害,就改變圖片顏色,否則,Lerp過渡顏色到空顏色
        if(damaged) {
            damageImage.color = flashColor;
        } else {
            damageImage.color = Color.Lerp (damageImage.color, Color.clear, flashSpeed * Time.deltaTime);
        }
        damaged = false;
    }

    //public方法,用于怪物攻擊玩家時調用
    public void TakeDamage(int amount) {
        damaged = true;
        currentHealth -= amount;
        healthSlider.value = currentHealth;
        playAudio.Play ();
        if(currentHealth <= 0 && !isDead) {
            Death ();
        }
    }

    //死亡時播放死亡聲音,設置死亡動畫,并且關閉玩家移動方法
    void Death(){
        isDead = true;
        anim.SetTrigger ("Die");
        playAudio.clip = deathClip;
        playAudio.Play ();
        playMovement.enabled = false;
    }
}

接著給腳本的public屬性綁定物件,如圖:

接下來是給怪物添加攻擊腳本

新建一個Empty_Attack腳本:

using UnityEngine;
using System.Collections;

public class Empty_Attack_ym : MonoBehaviour {


    public float timeBetweenAttacks = 0.5f; //攻擊間隔
    public int attackDamage = 10;   //攻擊力
    public bool playerInRange;  //玩家是否在攻擊范圍內

    Animator anim;
    GameObject player;
    PlayerHealth_ym playerHealth;

    float timer;

    void Awake(){
        player = GameObject.FindGameObjectWithTag ("Player");
        playerHealth = player.GetComponent<PlayerHealth_ym> ();
        anim = GetComponent<Animator> ();
    }

    //當碰撞體進入
    void OnTriggerEnter (Collider other){
        if(other.gameObject == player) {
            playerInRange = true;
        }
    }

    //當碰撞體離開
    void OnTriggerExit (Collider other) {
        if(other.gameObject == player) {
            playerInRange = false;
        }
    }

    void Update(){
        timer += Time.deltaTime;
        //當攻擊正在范圍內,并且timer的值大于攻擊間隔,進行攻擊
        if(timer >= timeBetweenAttacks && playerInRange) {
            Attack ();
        }
        //當玩家當前生命小于0, 怪物的動畫狀態(tài)機切換為玩家已死的狀態(tài)(此時播放開心的動畫);
        if(playerHealth.currentHealth <= 0) {
            anim.SetTrigger ("PlayerDie");
        }
    }

    void Attack(){
        timer = 0f;
        if(playerHealth.currentHealth > 0) {
            //調用玩家生命控制器的被攻擊的方法,傳入攻擊傷害值
            playerHealth.TakeDamage (attackDamage);
        }
    }
}

ok,運行一下,玩家會被怪物打死...

6. 給玩家添加攻擊動作,和怪物生命腳本

首先配置資源

  1. 把玩家射擊的粒子預設組件拷貝到玩家的槍上
  2. 給玩家的槍添加一個Line Renderer組件
    • 設置Parameters的StartWidth和EndWidth為0.07
    • 設置Materials->Element0的材質為LineRenderMaterial
  3. 給玩家的槍添加一個Light組件,并且設置好亮度角度, 初始取消它,等待攻擊的時候再臨時激活
  4. 把怪物受到傷害的粒子組件拷貝到怪物身上
  5. 把怪物的Layer設置為Shootable,一會射線檢測的LayerMask用得到

開始添加玩家攻擊腳本PlayerShooting

代碼都有注釋, 就不需要額外說明了

using UnityEngine;
using System.Collections;

public class PlayerShooting_ym : MonoBehaviour {
    
    public int damagePerShot = 20;  //傷害
    public float timeBetweenBullets = 0.15f;    //攻擊間隔
    public float range = 100f;  //射線最大距離

    float timer;    //用于攻擊間隔計時
    Ray shootRay;   //子彈的射線
    RaycastHit shootHit;
    int shootableMask;  //用于存放LayerMask
    ParticleSystem gunParticles;
    LineRenderer gunLine;   //射線的渲染
    AudioSource gunAudio;
    Light gunLight;         //槍上的燈光
    float effectsDisplayTime = 0.2f;    //粒子時長比例

    void Awake() {
        shootableMask = LayerMask.GetMask ("Shootable");
        gunParticles = GetComponent<ParticleSystem> ();
        gunLine = GetComponent<LineRenderer> ();
        gunAudio = GetComponent<AudioSource> ();
        gunLight = GetComponent<Light> ();
    }

    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        timer += Time.deltaTime;
        //當點擊Fire1鍵時,并且攻擊間隔達到預設時,可以攻擊
        if(Input.GetButton("Fire1") && timer >= timeBetweenBullets) {
            Shoot ();
        }
        //當計時器大于槍的粒子時間時,取消射擊的樣式, 這里射擊樣式的時間是攻擊間隔 * 粒子時長比例
        if(timer >= timeBetweenBullets * effectsDisplayTime) {
            DisableEffects ();
        }
    }

    // Shoot方法是關鍵
    void Shoot() {
        timer = 0f;

        //激活聲音,燈光
        gunAudio.Play ();
        gunLight.enabled = true;

        //停止之前的射擊粒子,重新播放
        gunParticles.Stop ();
        gunParticles.Play ();

        //打開LineRenderer組件, 設置0點為gameobject默認位置
        gunLine.enabled = true;
        gunLine.SetPosition (0, transform.position);

        //設置射線的位置
        shootRay.origin = transform.position;
        //設置射線的方向,正前方
        shootRay.direction = transform.forward;

        //用于捕捉射線,然后根據(jù)射線的射擊點進行做一些事情
        //物理.射線捕捉(射線, 得到的點shootHit, 最大距離, 進行計算的Layer) 
        if(Physics.Raycast(shootRay, out shootHit, range, shootableMask)) {
            //得到怪物的生命組件
            ZombunnyHealth_ym enemyHealth = shootHit.collider.GetComponent<ZombunnyHealth_ym> ();
            //如果怪物的生命組件不為空, 調用這個組件的TakeDamage方法進行扣血
            if(enemyHealth != null) {
                enemyHealth.TakeDamage (damagePerShot, shootHit.point);
            }
            //設置LineRenderer的第二個位置,為shootHit.point, 兩點確定一條直線
            gunLine.SetPosition (1, shootHit.point);
        }
        else {
            //如果沒有捕捉到射擊點
            //設置LineRenderer的第二個位置為, 射線初始位置 + 射線方向(0, 0, 1) * 最大距離
            gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);
        }
    } 

    void DisableEffects(){
        //取消LineRenderer組件和燈光組件
        gunLine.enabled = false;
        gunLight.enabled = false;
    } 

}

給怪物添加生命組件ZombunnyHealth

    public int startingHealth = 100;    //初始生命
    public int currentHealth;   //當前生命
    public float sinkSpeed = 2.5f;  //下沉速度
    public int scoreValue = 10; //此怪物殺死計多少分
    public AudioClip deathClip; //保存死亡時播放的聲音

    Animator anim;
    AudioSource enemyAudio;
    ParticleSystem hitParticles;
    CapsuleCollider capsuleCollider;    //capsulecollider是用來做移動碰撞捕捉
    bool isDead;
    bool isSinking;

    void Awake(){
        anim = GetComponent<Animator> ();
        enemyAudio = GetComponent<AudioSource> ();
        hitParticles = GetComponentInChildren<ParticleSystem> ();
        capsuleCollider = GetComponent<CapsuleCollider> ();
        currentHealth = startingHealth;
    }

    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        //如果可以下沉, 設定下沉的動畫
        //transform.Translate是位置變化,必須放在循環(huán)中,才能達到像動畫一樣的效果
        if(isSinking){
            //位置.變化((0, -1, 0) * 下沉速度)
            transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime);
        }
    }

    public void TakeDamage(int amount, Vector3 hitPoint) {
        if(isDead) {
            return;
        }
        //播放挨打的聲音
        enemyAudio.Play ();

        //設置粒子的位置等于攻擊點的位置
        hitParticles.transform.position = hitPoint;
        //播放粒子
        hitParticles.Play ();

        //扣血
        currentHealth -= amount;
        if(currentHealth <= 0) {
            //調用死亡方法
            Death ();
        }
    }

    void Death() {
        isDead = true;
        //把碰撞體設置為isTrigger
        capsuleCollider.isTrigger = true;
        //設置動畫控制器的觸發(fā)"Dead"
        anim.SetTrigger ("Dead");
        //切換聲音片為死亡聲音
        enemyAudio.clip = deathClip;
        //播放聲音
        enemyAudio.Play ();
    }

    //這個是公開方法, 實在模型動畫里的event調用的,當動畫播放到某一個時間時,會調用這個函數(shù)
    public void StartSinking(){
        //取消尋路組件
        GetComponent<NavMeshAgent> ().enabled = false;
        //取消剛體組件
        GetComponent<Rigidbody> ().isKinematic = true;
        //設置可以下沉, 一會在Updata里面就會調用下沉動畫
        isSinking = true;

        //添加修改靜態(tài)變量,然后更新UI的分數(shù)
        SourceValue_ym.score += scoreValue;

        //兩秒之后銷毀本物體
        Destroy (gameObject, 2f);
    }
}

給場景添加計分Text

  1. 在場景中間添加一個Text,如圖:


  2. 給Text添加一個SourceValue組件,用于改變Text的文字
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class SourceValue_ym : MonoBehaviour {

    public static int score;
    Text text;
        
    void Awake () {
        text = GetComponent<Text> ();
        score = 0;
    }

    // Update is called once per frame
    void Update () {
        //之前在ZombunnyHealth組件中已經添加了怪物死亡修改score的方法
        text.text = "Score: " + score;
    }
}

ok,現(xiàn)在運行一下游戲, 玩家可以射擊攻擊怪物了,并且怪物會被玩家打死, 而且有計分

一具尸體...

6. 創(chuàng)建怪物生成器

首先創(chuàng)建兩個新怪物,引用之前的邏輯

第一個怪物ZomBear:

  1. 把ZomBear的模型拖進Scene中
  2. 把Zombunny的所有組件拷貝到ZomBear上: 拷貝所有組件的方法擴展鏈接
  3. 因為兩個動畫狀態(tài)機和Avatar(化生)都是一樣的,所以動畫狀態(tài)機可以使用同一個

第二個怪物Hellephant:

  1. 把Hellephant的模型拖進Scene中
  2. 把Zombunny的所有組件拷貝到Hellephant上
  3. 因為兩個動畫的Avatar不一樣, 所以播放的動畫不能重用, 但是動畫邏輯可以重用
  • 創(chuàng)建一個Animator Override Controller(動畫覆蓋控制器)
  • 動畫控制器選ZombunnyAC, Original中的動畫片段選用Hellephant自己的動畫
  • 這樣就覆蓋動畫片段, 并且使用原有的動畫邏輯了


    Animator Override Controller 例子

修改一下兩個新怪物的屬性,需要修改的內容有:

  • 移動速度
  • 生命值
  • 攻擊力, 攻擊間隔
  • 收到傷害聲音, 死亡聲音

接下來創(chuàng)建怪物生成器的代碼

創(chuàng)建EnemyManager組件, 一會用來生成怪物

using UnityEngine;
using System.Collections;

public class EnemyManager_ym : MonoBehaviour {

    public PlayerHealth_ym playerHealth;    //玩家的血量
    public GameObject enemy;    //要創(chuàng)建的怪物
    public float spawnTime = 3f;    //創(chuàng)建間隔時間
    public Transform[] spawnPoints; //創(chuàng)建位置

    // Use this for initialization
    void Start () {
        //循環(huán)調用("方法名", 初始等待時間, 循環(huán)間隔時間)
        InvokeRepeating ("Spawn", spawnTime * 0.7f, spawnTime);
    }

    void Spawn() {
        if(playerHealth.currentHealth <= 0) {
            return;
        }
        //隨機得到數(shù)組范圍內的一個整數(shù)
        int spawnPointIndex = Random.Range (0, spawnPoints.Length);
        //實例化一個物件(物件, 位置, 旋轉角度);
        Instantiate (enemy, spawnPoints [spawnPointIndex].position, spawnPoints [spawnPointIndex].rotation);
    }
}

  1. 在場景中創(chuàng)建一個空物體, 把坐標還原成(0, 0, 0), 取名為EnemyManager
  2. 在EnemyManager中創(chuàng)建三個空物體, 并且拖動到場景不同的位置中, 用于設定3種怪物的生成位置
  3. 在EnemyManager中綁定三個EnemyManager組件, 用于創(chuàng)建三種不同的怪物, 如圖:
這是官方的做法, 有優(yōu)有劣吧
ok, 可以出不同怪物了

ok, 現(xiàn)在運行一下游戲, 怪物從不同地方出來了, 可以好好的干一仗了

7. 最后,完善失敗場景

  1. 首先添加UI, GameOver Text提示,和灰藍色Mask, 如最后效果圖
  2. 放好位置之后, 把Text和Mask的顏色Alpha都設置為0, 平時讓玩家看不到.
  3. 然后給UI做動畫, 等待死亡就觸發(fā)它
  • 選中整個HealthCanvas, 打開Window->Animation窗口, 新建一個動畫
  • 此時系統(tǒng)會自動創(chuàng)建一個AnimatorController, 然后會要求取名新建一個AnimationClip
  • 添加Text和Mask的顏色動畫, 縮放動畫
  1. 給系統(tǒng)生成的HealthCanvas動畫控制器做邏輯
  • 做一個空動畫, 然后鏈接到之前那個UI的AnimationClip上
  • 添加一個Trigger("GameOver") 觸發(fā)動畫
  • 這類動畫記得把Exit With Time去掉勾選, 這樣一觸發(fā)就會立刻切換動畫狀態(tài)

最后添加GameOverManager組件給HealthCanvas:

using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;

public class GameOverManager_ym : MonoBehaviour {
    public PlayerHealth_ym playerHealth;
    public float restartDelay = 3f; //重新開始的等待時間

    Animator anim;
    float restartTimer; ////重新開始的計時

    void Start () {
        anim = GetComponent<Animator> ();
    }

    void Update () {
        if(playerHealth.currentHealth <=0) {
            //觸發(fā)失敗動畫
            anim.SetTrigger ("GameOver");

            //計時
            restartTimer += Time.deltaTime;
            if (restartTimer >= restartDelay) {
                //新的場景切換, 這個demo切換原來的場景就是重新開始游戲
                SceneManager.LoadScene (0);
            }
        }
    }
}
死亡效果

ok, 玩家被怪物打死之后會有提示, 并且在3秒之后就會重新開始游戲啦

至此, 整個教程已經結束

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市竞穷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌御蒲,老刑警劉巖掘剪,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異画拾,居然都是意外死亡啥繁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門青抛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旗闽,“玉大人,你說我怎么就攤上這事蜜另∈适遥” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵举瑰,是天一觀的道長捣辆。 經常有香客問我,道長嘶居,這世上最難降的妖魔是什么罪帖? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮邮屁,結果婚禮上整袁,老公的妹妹穿的比我還像新娘。我一直安慰自己佑吝,他們只是感情好坐昙,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芋忿,像睡著了一般炸客。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上戈钢,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天痹仙,我揣著相機與錄音,去河邊找鬼殉了。 笑死开仰,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播众弓,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼恩溅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谓娃?” 一聲冷哼從身側響起脚乡,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滨达,沒想到半個月后奶稠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡捡遍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年窒典,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稽莉。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡瀑志,死狀恐怖,靈堂內的尸體忽然破棺而出污秆,到底是詐尸還是另有隱情劈猪,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布良拼,位于F島的核電站战得,受9級特大地震影響,放射性物質發(fā)生泄漏庸推。R本人自食惡果不足惜常侦,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贬媒。 院中可真熱鬧聋亡,春花似錦、人聲如沸际乘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脖含。三九已至罪塔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間养葵,已是汗流浹背征堪。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留关拒,地道東北人佃蚜。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓咳榜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親爽锥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容