使用Flutter和SpriteWidget開發(fā)第一個(gè)跨平臺(tái)Android/IOS手機(jī)游戲 – Beginner Tutorial

基于Flutter和Flame游戲開發(fā)引擎學(xué)習(xí)資料觉既,Create a Mobile Game with Flutter and Flame – Beginner Tutorial崩泡,對于更加適用于復(fù)雜一點(diǎn)手機(jī)游戲的Flutter 開發(fā)的2D游戲引擎SpriteWidget也制作了boxGame游戲例子嘀倒,希望通過起始能夠?qū)τ赟priteWidget有比較好的了解和掌握,如果能夠幫助到你殿遂,別忘了給我點(diǎn)贊哦冗懦!

如果你有什么問題,可以發(fā)郵件給我溅呢,或者在Github上留言,方便的話猿挚,我會(huì)盡力幫助你咐旧。

你在了解的過程,也可以參考下面的文章:

1. Create a Mobile Game with Flutter and Flame – Beginner Tutorial

2.飛行射擊游戲spritewidget/spaceblast

3.Flutter高性能復(fù)雜游戲2D開發(fā)游戲引擎spritewidget

前提條件:

1. Android Studio - Flutter開發(fā)工具绩蜻,當(dāng)然你也可以使用其他的铣墨,但本例使用AS開發(fā)。

2.Flutter SDK/Framework - AS開發(fā)插件办绝,你需要具備開發(fā)Flutter的AS開發(fā)環(huán)境伊约,如果你還沒有掌握Flutter的開發(fā)基礎(chǔ),請先嘗試Flutter開發(fā)學(xué)習(xí)孕蝉。

你可以在Github找到這個(gè)練習(xí)的完整代碼屡律。

開始擼代碼吧:

Step 1: 創(chuàng)建一個(gè)Flutter Application(略,希望你是了解Flutter的)

Step 2: 添加spritewidget插件以及清理Application

打開./pubspec.yamland降淮,增加下面的內(nèi)容在thecupertino_icons行下面并且在derdependencies分類之下(注意縮進(jìn)).

spritewidget:

然后記得執(zhí)行flutter packages get超埋,或者點(diǎn)擊AS界面上的Packages Get來添加插件。

下一步是清理代碼佳鳖,F(xiàn)lutter Project新創(chuàng)建的是一個(gè)example霍殴,打開./lib/main.dart,清空代碼只保留void main() {}系吩,并且確保使用material library來運(yùn)行runApp() .

2.1 空程序

然后刪除./test目錄繁成,不然會(huì)出現(xiàn)錯(cuò)誤,這里我們用不上test方法淑玫。

Step 3:? 添加主程序

打開./lib/main.dart,添加一下代碼面睛,和Flutter創(chuàng)建一個(gè)新的界面主程序一樣:

import 'package:flutter/material.dart';

import 'package:spritewidget/spritewidget.dart';

main () async {

? runApp(MyApp());

}

class MyAppextends StatelessWidget {

? @override

? Widget build(BuildContext context) {

??? return MaterialApp(

????? title:"spriteWidget Game Eniger",

????? theme: ThemeData(

????? primarySwatch: Colors.blue,

????? ),

????? home: BoxGameScene(),

??? );

? }

}

class BoxGameSceneextends StatefulWidget {

? @override

? State createState() => BoxGameSceneState();

}

class BoxGameSceneStateextends State {

? void initState(){

??? super.initState();

? }

? @override

? Widget build(BuildContext context) {

??? return null;

? }

}

這樣就創(chuàng)建了一個(gè)Flutter的主程序絮蒿。

Step 4: 添加游戲根節(jié)點(diǎn)RootNode:

新建一個(gè)dart文件命名為box_game.dart,創(chuàng)建class繼承NodeWithSize叁鉴,import游戲包:

import 'package:spritewidget/spritewidget.dart';

class BoxGame extends NodeWithSize {

? BoxGame() :super(new Size(320.0,320.0)){

? }

}

說明:創(chuàng)建一個(gè)BoxGame集成NodeWithSize作為RootNode土涝,這樣就可以實(shí)現(xiàn)spritewidget游戲引擎的游戲loop。一個(gè)基本的游戲loop在NodeWithSize以及繼承的Node中可以通過update()和paint()方法來實(shí)現(xiàn)幌墓。

update()方法實(shí)現(xiàn)游戲node的移動(dòng)或者更新(比如timer)但壮。

paint()方法實(shí)現(xiàn)游戲node的繪制冀泻。

這里有一個(gè)非常重要的概念,spritewidget產(chǎn)生了自己的cooridinate system蜡饵,使用Node自身坐標(biāo)系進(jìn)行對象位置的處理弹渔,因此:

BoxGame() :super(new Size(320.0,320.0)){}

就是給BoxGame這個(gè)Node初始化一個(gè)320*320尺寸的作為Node的自身坐標(biāo)系,這樣就可以按照這個(gè)坐標(biāo)系進(jìn)行新的子Node(child)的添加溯祸,每個(gè)Node肢专,無論parent/child,都可以有自己自身的坐標(biāo)系焦辅,以及起始位置相對于parent的坐標(biāo)位置博杖。

這個(gè)會(huì)在文后詳細(xì)的把自己研究的坐標(biāo)系相關(guān)信息進(jìn)行描述。

Step 4: 添加RootNode到主程序

在main.dart中引入box_game.dart筷登,然后在主程序app的state中初始化一個(gè)NodeWithSize剃根,然后返回spritewidget():

import 'package:boxgamespritewidget/box_game.dart';

NodeWithSize _game;

_game =new BoxGame();

return SpriteWidget(_game);

這是main.dart就像下面的代碼:

import 'package:flutter/material.dart';

import 'package:spritewidget/spritewidget.dart';

import 'package:boxgamespritewidget/box_game.dart';

main () async {

? runApp(MyApp());

}

class MyAppextends StatelessWidget {

? @override

? Widget build(BuildContext context) {

??? return MaterialApp(

????? title:"spriteWidget Game Eniger",

????? theme: ThemeData(

????? primarySwatch: Colors.blue,

????? ),

????? home: BoxGameScene(),

??? );

? }

}

class BoxGameSceneextends StatefulWidget {

? @override

? State createState() => BoxGameSceneState();

}

class BoxGameSceneStateextends State {

? NodeWithSize _game;

? void initState(){

??? super.initState();

??? _game =new BoxGame();

? }

? @override

? Widget build(BuildContext context) {

??? return SpriteWidget(_game);

? }

}

這時(shí)候你的app已經(jīng)是個(gè)游戲app了。你可以運(yùn)行一下看看前方。

Step 5: 繪制游戲主界面

添加游戲主節(jié)點(diǎn)

spritewidget添加對象非常簡單狈醉,只需要在parentNode中addChild(childNode)就可以了,當(dāng)然镣丑,需要你首先定義childNode舔糖。

我們在RootNode中添加一個(gè)游戲主界面_gameScreen,打開box_game.dart, 在BoxGame初始化時(shí)添加childNode莺匠。

Node_gameScreen;

BoxGame() :super(new Size(320.0,320.0)){

? _gameScreen =new Node();

? addChild(_gameScreen);

}

在rootNode中添加一個(gè)_gameScreen金吗,是為了更好的在各Node進(jìn)行交互時(shí)能夠更有層次,方便不同parentNode和childNode的不同更新和繪制趣竣。

添加虛擬游戲操縱桿

spritewidget有一個(gè)炫酷的Node稱之為VirtualJoystick摇庙,可以通過這個(gè)組件來控制游戲?qū)ο蟮妮斎耄热鐐€(gè)方向在按住屏幕是的移動(dòng)遥缕。

打開box_game.dart卫袒,相同于添加_gameScreen的方法,但是把_gameScreen作為父節(jié)點(diǎn)单匣,將VirtualJoystick添加到_gameScreen中夕凝,

VirtualJoystick_joystick;

BoxGame() :super(new Size(320.0,320.0)){

? _gameScreen =new Node();

? addChild(_gameScreen);

? _joystick =new VirtualJoystick();

? _gameScreen.addChild(_joystick);

}

這時(shí)候你可以運(yùn)行程序看看,你會(huì)發(fā)現(xiàn)VirtualJoystick并不存在户秤,怎么回事码秉?

原來,我們定義的RootNode使用了一個(gè)size(320, 320)的區(qū)域作為自己的坐標(biāo)系統(tǒng)鸡号,那么對于不同尺寸的手機(jī)屏幕转砖,并不是320x320的,這樣就會(huì)根據(jù)rootNode的定義區(qū)域?qū)⑵聊贿M(jìn)行適配鲸伴,這時(shí)候VirtualJoystick缺省會(huì)被添加到屏幕的最下方府蔗,因此晋控,我們需要初始化320x320

上的_gameScreen,設(shè)置它為最下端的屏幕Node姓赤,需要重新設(shè)置_gameScreen.position,

打開box_game.dart赡译,在class BoxGame 中override方法spriteBoxPerformedLayout(),

@override

void spriteBoxPerformedLayout() {

? _gameScreen.position =new Offset(0.0,spriteBox.visibleArea.height);

}

讓_gameScreen.position設(shè)置為基于ParentNode的高度方向坐標(biāo)為y設(shè)置為spriteBox.visibleArea.height模捂, 也就是320捶朵,向上平移320的高度。spriteBox.visibleArea之的是屏幕可見部分在320x320父節(jié)點(diǎn)中顯示的部分狂男,具體值可以參考后續(xù)說明综看。

這時(shí)再運(yùn)行程序,一個(gè)很不錯(cuò)的游戲操縱桿在界面上顯示了岖食。

繪制并添加一個(gè)Box

新建一個(gè)BoxNode類集成Node红碑,然后在繪制一個(gè)正方形,打開box_gam.dart泡垃,在BoxGame類下面創(chuàng)建一個(gè)BoxNode類析珊,也可以新生成一個(gè)dart文件來創(chuàng)建BoxNode,然后在box_gam.dart中引用蔑穴。

class BoxNode extends Node {

? BoxNode() {
??? position = new Offset(0, 0);
? }

? @override
? void paint(Canvas canvas) {
??? ???
? }
}

和BoxGame一樣忠寻,BoxNode除了繼承Node類以外,同樣可以初始化相對于parentNode的position = new Offset(0,0); 通過paint()和update()進(jìn)行繪制和更新存和。

說明:NodeWithSize實(shí)際上繼承Node奕剃,但是增加了size和pivot點(diǎn),可以通過更好的尺寸和支點(diǎn)來對Node進(jìn)行更新和繪制捐腿。適合作為ParentNode或者RootNode纵朋,各個(gè)游戲?qū)ο罂梢允褂肗ode創(chuàng)建。

在BoxNode中使用paint()進(jìn)行繪制一個(gè)正方形茄袖,

@override
void paint(Canvas canvas) {

? boxRect = Rect.fromLTWH(
????? spriteBox.visibleArea.height / 2 - 15,
????? - spriteBox.visibleArea.height / 2 - 75,
????? 30,
????? 30,
? );

? Paint boxPaint = Paint();
? boxPaint.color = Color(0xff00ff00);

? canvas.drawRect(boxRect, boxPaint);
}

說明:基本上來講操软,如果child的中點(diǎn)就是parent的size的中點(diǎn)減去偏移(前提是child的position初始化為new Offset(0, 0)。繪制對象使用canvas進(jìn)行宪祥,對象和Paint()就可以實(shí)時(shí)繪制對象聂薪。

然后將BoxNode實(shí)例化,并添加到BoxGame的_gameScreen游戲主節(jié)點(diǎn)中蝗羊,在BoxGame類的BoxGame() :super(new Size(320.0,320.0)){}初始化的_gameScreen下面添加胆建,

BoxNode_box;

BoxGame() : super(new Size(320.0, 320.0)){

? _gameScreen = new Node();

? addChild(_gameScreen);

? _joystick = new VirtualJoystick();

? _gameScreen.addChild(_joystick);

? _box = new BoxNode();

? _gameScreen.addChild(_box);

}

OK,這時(shí)候運(yùn)行程序肘交,一個(gè)綠色的box以及一個(gè)虛擬游戲操縱桿就會(huì)出現(xiàn)在游戲主界面。

Step 6: 處理虛擬游戲操作

我們要通過虛擬游戲操縱桿來控制box的移動(dòng)扑馁,需要在主界面update()中來通過VirtualJoystick來根據(jù)VirtualJoystick.value來改變box.position.

給BoxNode添加根據(jù)VirtualJoystick.value來更新自身position的方法涯呻,在class BoxNode中添加凉驻,

void applyThrust(Offset joystickValue) {

? Offset oldPos = position;

? Offset target = new Offset(joystickValue.dx * 160.0, joystickValue.dy * 220.0);

? double filterFactor = 0.2;

? position = new Offset(

? ? ? GameMath.filter(oldPos.dx, target.dx, filterFactor),

? ? ? GameMath.filter(oldPos.dy, target.dy, filterFactor));

}

說明:可以看到首先獲取BoxNode的當(dāng)前position作為舊的oldPos, 然后根據(jù)VirtualJoystick.value計(jì)算VirtualJoystick滑動(dòng)屏幕的移動(dòng)亮并根據(jù)屏幕尺寸進(jìn)行放大,這里使用x放大160复罐,y放大220涝登,基本上是根據(jù)測試結(jié)果,會(huì)讓VirtualJoystick操作box時(shí)看著比較流暢效诅。

GameMath.filter方法可以在oldPos和target之間按照一個(gè)0-1之間的filterFactor插入多個(gè)移動(dòng)位置胀滚,而不是直接將Box對象從oldPos移動(dòng)到target,這樣在更新的時(shí)候就會(huì)產(chǎn)生Box連續(xù)移動(dòng)的效果乱投,移動(dòng)效果會(huì)比較流暢咽笼。

然后在BoxGame中,override主游戲RootNode的update()方法戚炫,將box的position的改變進(jìn)行更新剑刑,這樣就可以是的Box在屏幕上根據(jù)VirtualJoystick的操作進(jìn)行移動(dòng),

void update(double dt) {

? _box.applyThrust(_joystick.value);

}

這樣整個(gè)box_game.dart就像下面的代碼双肤,

import 'dart:ui';

import 'package:flutter/gestures.dart';

import 'package:spritewidget/spritewidget.dart';

class BoxGame extends NodeWithSize {

? // Game screen nodes

? Node _gameScreen;

? VirtualJoystick _joystick;

? BoxNode _box;

? double _scroll = 0.0;

? BoxGame() : super(new Size(320.0, 320.0)){

? ? _gameScreen = new Node();

? ? addChild(_gameScreen);

? ? _joystick = new VirtualJoystick();

? ? _gameScreen.addChild(_joystick);

? ? _box = new BoxNode();

? ? _gameScreen.addChild(_box);

? }

? @override

? void spriteBoxPerformedLayout() {

? ? _gameScreen.position = new Offset(0.0, spriteBox.visibleArea.height);

? }

? void update(double dt) {

? ? _box.applyThrust(_joystick.value);

? }

}

class BoxNode extends Node {

? bool hasWon = false;

? Rect boxRect;

? BoxNode() {

? ? position = new Offset(0, 0);

? }

? @override

? void paint(Canvas canvas) {

? ? boxRect = Rect.fromLTWH(

? ? ? ? spriteBox.visibleArea.height / 2 - 15,

? ? ? ? - spriteBox.visibleArea.height / 2 - 75,

? ? ? ? 30,

? ? ? ? 30,

? ? );

? ? Paint boxPaint = Paint();

? ? boxPaint.color = Color(0xff00ff00);

? ? canvas.drawRect(boxRect, boxPaint);

? }

? void applyThrust(Offset joystickValue) {

? ? Offset oldPos = position;

? ? Offset target = new Offset(joystickValue.dx * 160.0, joystickValue.dy * 220.0);

? ? double filterFactor = 0.2;

? ? position = new Offset(

? ? ? ? GameMath.filter(oldPos.dx, target.dx, filterFactor),

? ? ? ? GameMath.filter(oldPos.dy, target.dy, filterFactor));

? }

}

好了施掏,運(yùn)行一下程序,你可以通過VirtualJoystick來控制Box的移動(dòng)了茅糜,是不是很酷七芭?

Step 7: 添加點(diǎn)擊事件控制box變色展示win狀態(tài)

我們希望游戲?qū)ο罂梢员稽c(diǎn)擊操作,spritewidget使用handleEvent(SpriteBoxEvent event){}來處理輸入交互蔑赘,SpriteBoxEvent包含多種點(diǎn)擊屏幕處理事件狸驳,我們這里需要使用PointerDownEvent事件,當(dāng)Box被點(diǎn)中時(shí)進(jìn)行變色米死。

如果需要實(shí)現(xiàn)點(diǎn)擊事件锌历,需要對于點(diǎn)擊檢測Node進(jìn)行設(shè)置,允許點(diǎn)擊檢測對象可以進(jìn)行交互峦筒,但是不允許多點(diǎn)碰觸究西,我們希望通過對BoxGame作為RootNode進(jìn)行對象檢測,如果發(fā)現(xiàn)在RootNode中點(diǎn)擊到的區(qū)域是box的區(qū)域物喷,則表明box被點(diǎn)中了卤材,然后處理box被點(diǎn)擊方法,首先在BoxGame初始化的時(shí)候BoxGame() :super(new Size(320.0,320.0)){}設(shè)置BoxGame定義為峦失,

userInteractionEnabled =true;

handleMultiplePointers =false;

然后在BoxGame類中添加override方法扇丛,

@override

bool handleEvent(SpriteBoxEvent event) {

? if (event.type == PointerDownEvent) {

? ? Offset newPoint = convertPointToNodeSpace(event.boxPosition);

? ? if(newPoint.dx > _box.boxRect.left &&

? ? ? ? newPoint.dx < _box.boxRect.left + 30 &&

? ? ? ? newPoint.dy > _box.boxRect.top + spriteBox.visibleArea.height &&

? ? ? ? newPoint.dy < _box.boxRect.top + spriteBox.visibleArea.height + 30){

? ? ??? //do box actions.

? ? }else{

? ? ? return false;

? ? }

? }

? return true;

}

說明:當(dāng)點(diǎn)擊事件發(fā)生時(shí),判斷是否為屏幕被點(diǎn)中尉辑,這是由于點(diǎn)中的位置為屏幕缺省坐標(biāo)點(diǎn)帆精,如果需要和RootNode的childNode的范圍進(jìn)行比較,需要首先使用convertPointToNodeSpace()方法將缺省坐標(biāo)點(diǎn)轉(zhuǎn)換為RootNode坐標(biāo)系統(tǒng),然后再和box的范圍進(jìn)行比較卓练,由于_gameScreen作為主Node隘蝎,初始化為new Offset(0.0, spriteBox.visibleArea.height),所以在對點(diǎn)擊的位置判斷是襟企,需要把_box的位置也添加相同的偏移進(jìn)行對比嘱么。

添加點(diǎn)擊位置判斷是box范圍時(shí),處理box的方法顽悼,在BoxNode類中定義曼振,

bool hasWon =false;

定義一個(gè)布爾參數(shù)來分辨點(diǎn)擊每次的狀態(tài),然后在paint()函數(shù)中蔚龙,更改

boxPaint.color = Color(0xff00ff00);

布爾參數(shù)判讀來使用不同顏色畫筆冰评,

if (hasWon) {

? boxPaint.color = Color(0xff00ff00);

} else {

? boxPaint.color = Color(0xffffffff);

}

如果布爾參數(shù)為true,畫筆為綠色府蛇,否則為白色集索,然后定義一個(gè)點(diǎn)擊調(diào)用的方法,來根據(jù)點(diǎn)擊改變布爾參數(shù)的值汇跨,

void onTapDown() {

? hasWon = !hasWon;

}

box被點(diǎn)中一次布爾參數(shù)為true务荆,再點(diǎn)擊變?yōu)閒alse,這樣不同的點(diǎn)擊布爾參數(shù)就會(huì)有不同的狀態(tài)穷遂,這樣就會(huì)在paint()方法中使用不同顏色的畫筆來繪制box函匕。

然后在BoxGame的bool handleEvent(SpriteBoxEvent event) {} 方法中判斷點(diǎn)中box時(shí)調(diào)用onTapDown()方法,替換

//do box actions.

_box.onTapDown();

這樣就完成了點(diǎn)擊輸入方式交互蚪黑,整體box_game.dart的代碼如下盅惜,

import 'dart:ui';

import 'package:flutter/gestures.dart';
import 'package:spritewidget/spritewidget.dart';

class BoxGame extends NodeWithSize {

? Node _gameScreen;
? VirtualJoystick _joystick;

? BoxNode _box;

? double _scroll = 0.0;

? BoxGame() : super(new Size(320.0, 320.0)){

??? userInteractionEnabled = true;
??? handleMultiplePointers = false;

??? _gameScreen = new Node();
??? addChild(_gameScreen);

??? _joystick = new VirtualJoystick();
??? _gameScreen.addChild(_joystick);

??? _box = new BoxNode();
??? _gameScreen.addChild(_box);

? }

? @override
? bool handleEvent(SpriteBoxEvent event) {

??? if (event.type == PointerDownEvent) {

????? Offset newPoint = convertPointToNodeSpace(event.boxPosition);

????? if(newPoint.dx > _box.boxRect.left &&
????????? newPoint.dx < _box.boxRect.left + 30 &&
????????? newPoint.dy > _box.boxRect.top + spriteBox.visibleArea.height &&
????????? newPoint.dy < _box.boxRect.top + spriteBox.visibleArea.height + 30){
??????? _box.onTapDown();
????? }else{
??????? return false;
????? }
??? }
??? return true;
? }

? @override
? void spriteBoxPerformedLayout() {
??? _gameScreen.position = new Offset(0.0, spriteBox.visibleArea.height);
? }

? void update(double dt) {

??? _box.applyThrust(_joystick.value);

? }
}

class BoxNode extends Node {

? bool hasWon = false;
? Rect boxRect;

? BoxNode() {
??? position = new Offset(0, 0);
? }

? @override
? void paint(Canvas canvas) {

??? boxRect = Rect.fromLTWH(
??????? spriteBox.visibleArea.height / 2 - 15,
??????? - spriteBox.visibleArea.height / 2 - 75,
??????? 30,
??????? 30,
??? );
???
??? Paint boxPaint = Paint();
??? boxPaint.color = Color(0xffffffff);

??? if (hasWon) {
????? boxPaint.color = Color(0xff00ff00);
??? } else {
????? boxPaint.color = Color(0xffffffff);
??? }

??? canvas.drawRect(boxRect, boxPaint);
? }

? void onTapDown() {
??? hasWon = !hasWon;
? }

? void applyThrust(Offset joystickValue) {

??? Offset oldPos = position;
??? Offset target = new Offset(joystickValue.dx * 160.0, joystickValue.dy * 220.0);
??? double filterFactor = 0.2;

??? position = new Offset(
??????? GameMath.filter(oldPos.dx, target.dx, filterFactor),
??????? GameMath.filter(oldPos.dy, target.dy, filterFactor));
? }
}

好了你可以體驗(yàn)一下這個(gè)游戲了。

關(guān)于Node的坐標(biāo)系統(tǒng)和世界坐標(biāo)系統(tǒng)的相關(guān)聯(lián)忌穿,后期添加抒寂。

你可以在Github找到這個(gè)練習(xí)的完整代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掠剑,一起剝皮案震驚了整個(gè)濱河市屈芜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌朴译,老刑警劉巖井佑,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異眠寿,居然都是意外死亡躬翁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門盯拱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盒发,“玉大人例嘱,你說我怎么就攤上這事∧ⅲ” “怎么了蝶防?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長明吩。 經(jīng)常有香客問我,道長殷费,這世上最難降的妖魔是什么印荔? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮详羡,結(jié)果婚禮上仍律,老公的妹妹穿的比我還像新娘。我一直安慰自己实柠,他們只是感情好水泉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窒盐,像睡著了一般草则。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蟹漓,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天炕横,我揣著相機(jī)與錄音,去河邊找鬼葡粒。 笑死份殿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嗽交。 我是一名探鬼主播卿嘲,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼夫壁!你這毒婦竟也來了拾枣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤掌唾,失蹤者是張志新(化名)和其女友劉穎放前,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糯彬,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凭语,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撩扒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片似扔。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吨些,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炒辉,到底是詐尸還是另有隱情豪墅,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布黔寇,位于F島的核電站偶器,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缝裤。R本人自食惡果不足惜屏轰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望憋飞。 院中可真熱鬧霎苗,春花似錦、人聲如沸榛做。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽检眯。三九已至厘擂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間轰传,已是汗流浹背驴党。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留获茬,地道東北人港庄。 一個(gè)月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像恕曲,于是被迫代替她去往敵國和親鹏氧。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349