pyera快速教程: 从贪吃蛇开始

1. 游戏设计与数据设计

感谢您选择pyera,如果有任何问题想反馈和讨论可以加群:
py/era 技术吹水群 543014634
完整贪吃蛇代码参考:https://github.com/qsjl11/pyera_snake

贪吃蛇是一款家喻户晓的游戏,因为游戏系统非常简单,因此合适作为各种引擎的练习用,这里我们也用这个作为一个练习的起点。

在正式码代码之前,我们要做好设计工作。好的准备才能保证后期的顺利。
作为教程本游戏的设计比较粗浅,只要大家能够明白意思就好。

游戏设计:

1. 游戏本身采用n*n的正方形矩阵。n是可以自定义的
2. 每个矩阵包含3种状态,分别是苹果、蛇身子和空地。
3. 每一回合,蛇头会向指定方向挪动一格。后面的蛇身会跟随前一蛇身移动。
4. 苹果随机出现在空地上,直到被蛇吃掉。
5. 每吃掉一格苹果,蛇身会加长一格
6. 当蛇头前进方向受到边界或者蛇身阻碍的时候,游戏结束
7. 地图左上角为(0,0),向右为x轴,向下为y轴
8. 游戏可以保存和读取

数据设计:

1. 设置地图大小的变量map_size
2. 地图保存在dict中,dict的键由元胞(x,y)构成,值为整数【0,1】,分别代表空地,苹果
3. 蛇身体由一个list保存,每个元素是一个含有两个元素的list,表示x,y坐标
4. 一个list [x,y] 表示苹果的位置。

流程设计:

1. 开始游戏/读取游戏
2. 游戏循环
* 根据地图矩阵来绘制地图
* 绘制命令,【上下左右】【保存读取】
* 等待输入
* 移动蛇,更新地图信息
* 判断蛇能够吃到苹果,能吃到的话,长度+1 

2. 配置开发环境

工欲善其事必先利其器。开发环境是非常重要的。这里不赘述基本的配置方法,只讨论涉及本游戏的配置。基本配置方法请参考文档【开发环境配置】,选择‘完全配置环境”进行配置。
文档 http://www.jianshu.com/p/79096b775d40

当基本配置完成之后,我们需要先删除一些多余的东西。因为源代码包中含有很多展示性的东西,这些东西对于我们来说是没有必要的。

这里要说明,data文件夹保存配置数据,script保存运行的游戏脚本,因此只有这两个文件中有内容需要删除。Pyera_engine和build.py文件请勿删除。

要保留的内容有由红线标出:

image.png

右键删除时如果弹出窗口,记得勾掉sage delete 和search in comments and strings. 否则可能不让删除那些文件。

image.png

清理过后的窗口如图

image.png

双击mainflow.py, 将里面的代码全部删除后改为并保存:

# -*- coding: UTF-8 -*-
import core.game as game
import script.base_lib as lib

def open_func():
    pass

def main_func():
    pass

此时右键运行pyera_engine/pyera.py尽管没有内容,但是还是可以打开运行窗口的。如图:

image.png

不妨在open_func函数中写下一句:

def open_func():
    game.pl('pyera贪吃蛇游戏加载中')

再次运行后可以看到

image.png

你所写下的内容已经出现在了游戏窗口当中。

至此,配置环境的工作完成了。

3. 简单的数据配置

根据之前的游戏设计,需要有一个数据来保存map_size。本章中,则展现pyera的数据配置功能。
pyera中所有的预先设定的数据都在data文件夹中。
“core_cfg.json”包含系统的基本配置。
“core_event_sort.json"则与事件顺序系统有关,暂时不用管这个功能。

本章节中将建立一个名为“map”的json数据文件,里面保存一个“map_size"的变量。

新建map文件。右键点击data文件夹->new->file

image.png

输入:map.json“,点击确定。

image.png

双击打开map.json文件,并输入一下代码,注意大括号和双引号为英文半角字符:

    {
        "map_size": 10
    }

Ctrl+s 保存后数据配置就算结束了,但是怎么调用呢。这里演示一下。打开mainflow.py, 将open_func函数改为:

    def open_func():
        game.pl('pyera贪吃蛇游戏加载中')
        map_size=game.data['map']['map_size']
        game.pl('地图大小设置为 '+str(map_size))

运行pyera.py,可以看到结果。如果更改map.json文件中的数值的话,游戏内的输出也会改变。

image.png

这里解释一下open_func中的各句意思

a. 第一句单纯的输出。game.pl() 函数输出一句话并自动换行.
b. 第二句为读取数据。game.data是一个dict,第一个键值为文件名,第二键值为保存的内容。
c. 第三句输出map_size, str()函数用来使map_size变为字符串,进而可以与其他字符串做+运算。

4. 开始写代码,“开始游戏”与”读取游戏“

本节将构建开始游戏界面,并利用现成的存储/读取游戏界面。

如前所述,mianflow.py/open_func()是程序的入口。现在我们将利用open_func()中添加一条分割线和两个按钮:“开始游戏”和“读取游戏”

添加分割线

在open_func()添加“game.pline()“即可,可以运行测试一下,会多出一条线。

添加读取游戏按钮

open_func()中接如下代码

game.pcmd('[001]  读取游戏', 1,  lib.load_func, arg=(open_func,))

这里详细说一下pcmd命令的使用方法。pcmd会打印一个字符串。当点击这个字符串或者输入对应数字命令的时候,程序会跳转到对应命令指定的函数,并可以传递参数。这里,

   第一个参数是显示的字符串;
   第二参数是对应的苏子命令,这里是1;
   第三个参数是跳转函数 为 lib.load_func,
   第四个为传递的参数,为open_func。

Savelaod.load_func就是读取列表函数,该函数的参数表示当执行“退出”命令时执行哪个函数。这里,参数open_func的意思是如果不去读档直接退出的话,则执行open_func。
注意:这里open_func后面不加括号!

至此,mainflow文件应当如下:
# -*- coding: UTF-8 -*-
import core.game as game
import script.base_lib as lib
    
    
def open_func():
    game.pl('pyera贪吃蛇游戏加载中')
    map_size=game.data['map']['map_size']
    game.pl('地图大小设置为 '+str(map_size))
    game.pline()
    
    game.pcmd('[001]  读取游戏', 1, lib.load_func, arg=(open_func,))
    
def main_func():
    pass

运行后如图:


image.png

点击读取游戏或者在输入栏输入‘1’可以进入读取界面,退出则返回,大家可以自己尝试一下。

添加新的游戏按钮

尽管还有其他的输入命令的方式,pyera建议使用关联函数的办法绘制按钮。其他方式可以参考详细文档。

添加开始游戏按钮,需要如下几步:

   a. 添加一个新函数,用来做处理开始游戏的各种变量设置。添加函数 def newgame_func()
   b. 在open_func中追加输出命令,将开始游戏关联到 newgame_func()上

game.pcmd('[002] 开始游戏', 2, newgame_func)

相关部分代码文件如下:

def open_func():
    game.pl('pyera贪吃蛇游戏加载中')
    map_size=game.data['map']['map_size']
    game.pl('地图大小设置为 '+str(map_size))
    game.pline()
    
    game.pcmd('[001]  读取游戏', 1,  lib.load_func, arg=(open_func,))
    game.pcmd('[002]  开始游戏', 2, newgame_func)
    
def newgame_func():
    pass
版面修整。

运行后我们会发现,此时两个命令紧紧的挨在一起不好看。最好是分成两行。
因此在两个pcmd命令之间加入一句话 game.pl() 这样可以引入一个换行。完成后如图:


image.png

至此,开始界面就算搭建完成了。代码如下:

# -*- coding: UTF-8 -*-
import core.game as game
import script.base_lib as lib


def open_func():
    game.pl('pyera贪吃蛇游戏加载中')
    map_size=game.data['map']['map_size']
    game.pl('地图大小设置为 '+str(map_size))
    game.pline()

    game.pcmd('[001]  读取游戏', 1, lib.load_func, arg=(open_func,))
    game.pl()
    game.pcmd('[002]  开始游戏', 2, newgame_func)

def newgame_func():
    pass

def main_func():
    pass

def open_menu():
    pass

newgame_func的具体工作在下一节中讲解。

5. newgame_func与相关函数

在点击开始游戏后,会执行newgame_func。这个函数将用来做一些初始化工作, newgame_func执行后会执行main_func。这里先把整段函数写出来。再一一解释

def newgame_func():
    # 初始化地图数据dic,0是空地,1是苹果
    map_size = game.data['map']['map_size']
    game.data['mapdata'] = {}
    for y in range(0, map_size):
        for x in range(0, map_size):
            game.data['mapdata'][(x, y)] = 0

    # 初始化蛇list, 三格长度,靠左下角放置
    snake_list = []
    snake_list.append((0, 2))
    snake_list.append((0, 1))
    snake_list.append((0, 0))
    game.data['snake_list'] = snake_list
    
    main_func()

def main_func():
    # 添加一个苹果
    create_apple()
    #绘制地图
    draw_map()
初始化地图数据

初始化过程的第一步就是获得map_size。然后建立一个空dict来存储地图数据。地图数据以键值方式存储。键是一个(x,y)的元胞,而值是‘0’空地或者‘1’苹果。不过初始化的过程中全部用0.
需要注意的是range函数。Range( 1,3)表示[1,2],而不是[1,2,3],因此这里的x和y的取值范围为0<=x,y<map_size.

初始化蛇list。

首先建立一个空list,然后用append函数来附加位置。分别是(0,2),(0,1),(0,0).这里list[0]元素为蛇头。

进入main_func()

Main_func()是游戏的主体函数,不过这里只完成制作苹果和显示两个步骤,以示效果。

添加苹果

添加苹果是一个单独的函数create_apple(), 函数如下:

def create_apple():
    map_size = game.data['map']['map_size']
    
    # 随机苹果的位置,当位置与蛇重合的时候,重新随机
    import random
    apple_position = (random.randint(0, map_size - 1), random.randint(0, map_size - 1))
    while apple_position in game.data['snake_list']:
    apple_position = (random.randint(0, map_size - 1), random.randint(0, map_size - 1))
    
    # 将mapdata中的对应苹果位置改为1
    game.data['mapdata'][apple_position] = 1

首先依然是获得map_size, 然后使用random模块中的randint()函数获得一个随机整数。这里需要注意的是,因为randint(i,j)的到整数为 i<=value<=j,因此其参数应当为(0,map_size-1)。否则随机的值可能正好为map_size,但实际坐标中最大值为map_size-1,会照成溢出,引起程序错误。

while循环的目的是,当苹果随机的位置恰好落于蛇身体上的时候,则重新随机,直到苹果不产生再蛇身上为止。这样最后apple_position 就是一个有效的位置。

最后将mapdata中对应位置的值改为1,表示该位置是苹果。

绘制地图draw_map(), 其内容如下:
def draw_map():
    map_size = game.data['map']['map_size']
    snake_list=game.data['snake_list']
    mapdata=game.data['mapdata']
    for y in range(0, map_size):
        for x in range(0, map_size):
            pos=(x,y)
            if pos in snake_list:
                game.p('❖', style='special')
                continue
            if mapdata[pos]==1:
                game.p('❁')
                continue
            if mapdata[pos]==0:
                game.p('﹒')
                continue
            game.pl()

为了方便调用,map_size, snake_list 和mapdata都用单独的变量引用。通过双重循环,判断如果是蛇身部分,则绘制'❖', 苹果绘制('❁'), 空地用'﹒'

这里有几点需要注意:

  1. 循环的时候是外循环为y,内循环为x,目的是按照(0,0)->(1,0)->(2,0)->…->(0,1)->(1,1)->…的方式进行循环。因此绘制的时候是一行一行输出的,只能先变x后变y

  2. continue,因为每个位置只用绘制一种元素,因此绘制之后直接用continue跳过后面的循环部分。

  3. 每次内循环完成的时候,都调用game.pl()另起一行,开始下一行打印。

  4. game.p('❖', style='special')中的style是可以自行定义的,具体可以参考详细文档。

目前完整的mainflow.py 如下:

# -*- coding: UTF-8 -*-
import core.game as game
import script.base_lib as lib


def open_func():
    game.pl('pyera贪吃蛇游戏加载中')
    map_size = game.data['map']['map_size']
    game.pl('地图大小设置为 ' + str(map_size))
    game.pline()

    game.pcmd('[001]  读取游戏', 1, lib.load_func, arg=(open_func,))
    game.pl()
    game.pcmd('[002]  开始游戏', 2, newgame_func)


def newgame_func():
    # 初始化地图数据dic,0是空地,1是苹果
    map_size = game.data['map']['map_size']
    game.data['mapdata'] = {}
    for y in range(0, map_size):
        for x in range(0, map_size):
            game.data['mapdata'][(x, y)] = 0

    # 初始化蛇list, 三格长度,靠左下角放置
    snake_list = []
    snake_list.append((0, 2))
    snake_list.append((0, 1))
    snake_list.append((0, 0))
    game.data['snake_list'] = snake_list
    
    main_func()
   

def create_apple():
    map_size = game.data['map']['map_size']

    # 随机苹果的位置,当位置与蛇重合的时候,重新随机
    import random
    apple_position = (random.randint(0, map_size - 1), random.randint(0, map_size - 1))
    while apple_position in game.data['snake_list']:
        apple_position = (random.randint(0, map_size - 1), random.randint(0, map_size - 1))

    # 将mapdata中的对应苹果位置改为1
    game.data['mapdata'][apple_position] = 1


def draw_map():
    map_size = game.data['map']['map_size']
    snake_list=game.data['snake_list']
    mapdata=game.data['mapdata']
    for y in range(0, map_size):
        for x in range(0, map_size):
            pos=(x,y)
            if pos in snake_list:
                game.p('❖', style='special')
                continue
            if mapdata[pos]==1:
                game.p('❁')
                continue
            if mapdata[pos]==0:
                game.p('﹒')
                continue
        game.pl()

def main_func():
     # 添加一个苹果
    create_apple()
    # 绘制地图
    draw_map()

当运行并点击开始游戏的时候,应该有如下结果:


image.png

其中黄色表示蛇,大白点是苹果,小白点是空地。

下一节将讲述main_func()的构建,来实现完整显示与菜单。

6. Main_func的编写 移动与菜单

本节中将完善main_func, 并创建菜单,使蛇可以真正的移动。同时演示保存和读取功能。注意,本章中将使用python中的闭包特性,如果感到难以理解的话,可以多多百度也可加群讨论。

一个关于闭包的参考资料:http://blog.csdn.net/ChangerJJLee/article/details/52598629

本节中修改的函数为main_func,并添加一个函数next_step()和一个全局变量direction。
Next_step()的功能是生成苹果并更新蛇的位置,direction指示蛇前进的方向。

direction = 'xia'

def next_step():
    # 方便调用
    global direction
    snake_list = game.data['snake_list']
    mapdata = game.data['mapdata']

    # 判断是否有苹果,没有就创造一个
    if 1 not in mapdata.values():
        create_apple()

    # 更新蛇身
    for i in range(len(snake_list) - 1, 0, -1):
        snake_list[i] = snake_list[i - 1]
    
    # 更新蛇头位置
    x_head = snake_list[0][0]
    y_head = snake_list[0][1]
    if direction == 'zuo':
        x_head = x_head - 1
    if direction == 'you':
        x_head = x_head + 1
    if direction == 'shang':
        y_head = y_head - 1
    if direction == 'xia':
        y_head = y_head + 1
    snake_list[0] = (x_head, y_head)

    # 返回主函数
    main_func()
  1. 全局变量direction用'shang',‘xia’,‘zuo’,‘you’表示前进方向,初始值为‘xia’。
  2. Next_step()函数中第一步依然是准备变量。
  3. 第二步是判断mapdata中是否右苹果,如果没有就创造一个苹果。这里mapdata.values()是返回dict中所有的值
  4. 第三部更新蛇的身体,这里身体不包含头部(snake_list[0]). 更新的思路很简单蛇的每一块身体都位置都为前一块身体的位置。比如 (2,0)(1,0)(0,0)->(,)(2,0)(1,0)
  5. 第四步是更新蛇头的位置。首先取出蛇头位置,然后根据direction的不同使x/y加减1,最后再赋值给蛇头snake_list[0]
  6. 最后返回main_func()

目前next_step()函数还没有全部完成,剩下的部分将在下一节中完成。

下面将写main_func()有什么变化,并对其中较为关键的语句进行解释:

def main_func():
    # 新界面准备
    game.clr_cmd()
    game.pline()

    # 画图
    draw_map()

    def create_func(direction_name):
        def func():
            global direction
            direction = direction_name
            next_step()
        return func

    # 状态显示
    game.pline('--')
    game.pl('分数:')

    # 绘制命令按钮
    game.pline('--')
    game.pcmd('[1] 左  ', 1, create_func('zuo'))
    game.pcmd('[2] 上  ', 2, create_func('shang'))
    game.pcmd('[3] 右  ', 3, create_func('you'))
    game.pl()
    game.p('        ')
    game.pcmd('[4] 下  ', 4, create_func('xia'))
    game.pl('\n')
    game.pcmd('[5] 存储游戏  ', 5, lib.save_func, arg=main_func)
    game.pcmd('[6] 读取游戏  ', 6, lib.load_func, arg=(main_func, main_func))
  1. game.clr_cmd() 的作用是清除之前所有的命令按钮,以防误操作。通常建立新的界面的时候使用
  2. draw_map() 根据地图数据和蛇身数据绘制地图
  3. Create_func()是一个较难理解点,这个函数的作用是制作另一个函数,即期内部的函数func().func()的内容有一下几点,声明使用全局变量direction,设置direction为dirction_name, 最后调用next_setp(). 当给定不同的direction_name的时候,产生的func()会有所区别。之所以这么写的目的是简化程序。实际上,python提供的各种功能能够有效减少我们的工作量,是我们更加集中于游戏本身的逻辑上。
  4. 状态显示,这里预留分数的记录部分。下一节再扩展。需要注意的是game.pline('--')这个函数。当该函数没有参数的时候会绘制一条默认的粗直线,但是也可以给一个参数,这样会根据所给的字符绘制分割线。需要注意的是这个位置应当是一个双字符快读的内容,即一个全角字符或两个半角字符。
  5. 绘制命令(上下左右):这里create_func()就显出其作用了,create_func 会给出一个新的函数。对于‘左‘按钮,func()会使direction为’zuo‘。以此类推。
  6. Game.pl('\n')的目的是换两行。因为game.pl()函数只会在当前行不为空的时候另起一行。因此只能换行一次。要换两行的话,需要再输出一个换行符, 即game.pl('\n')
  7. 存储游戏按钮。参数依然是点击‘退出’按钮的时候,会返回哪个函数。这里说明一下存储读取的原理。木人状态下,存储和读取实际上就是对game.data的存储和读取。因此任何存放在game.data中的数据都会被存储和读取。
  8. 读取游戏按钮。和之前一样,有所区别的是第一参数为main_func,意思是点退出按钮的时候会退出到main_func

运行一下如下:


image.png

蛇已经可以根据上下左右移动了。

后续工作:尽管可以移动,并且存储和读取功能都已完成。但是目前蛇不会和苹果交互也不会碰到墙壁死亡。下一节中我们将对next_step 更改以实现更多的功能

7. 扩展next_step(), 吃苹果与死亡

本节将扩展next_step的功能,首先我们扩展吃苹果的功能,然后扩展碰撞死亡的功能。

def next_step():
    # 方便调用
    global direction
    snake_list = game.data['snake_list']
    mapdata = game.data['mapdata']

    # 判断是否有苹果,没有就创造一个
    if 1 not in mapdata.values():
        create_apple()

    # 新添加**************************
    # 记录蛇身体的最后一节的位置
    last_snake_position = snake_list[-1]

    # 更新蛇身
    for i in range(len(snake_list) - 1, 0, -1):
        snake_list[i] = snake_list[i - 1]

    # 更新蛇头位置
    x_head = snake_list[0][0]
    y_head = snake_list[0][1]
    if direction == 'zuo':
        x_head = x_head - 1
    if direction == 'you':
        x_head = x_head + 1
    if direction == 'shang':
        y_head = y_head - 1
    if direction == 'xia':
        y_head = y_head + 1
    snake_list[0] = (x_head, y_head)

    # 新添加**************************
    apple_position=(-1,-1)
    # 获得苹果位置
    for k,v in mapdata.items():
        if v==1:
            apple_position=k
            break
    # 判断是否吃到苹果,如果吃到苹果则加一节身体
    if snake_list[0] == apple_position:
        # 苹果消失,所有地格变为0
        for k in mapdata:
            mapdata[k]=0
        #延长蛇身,把动前最后的一个位置添加到蛇身体中
        snake_list.append(last_snake_position)

    # 返回主函数
    main_func()
  1. 新添加代码有两部分。首先是记录蛇身体最后一节的位置。目的是一旦吃苹果需要添加蛇身,则直接添加在这个位置上。因此需要提前记录一下这个位置。
  2. 获得苹果位置。首先定义一个变量apple_position默认值为(-1,-1).因为该位置超出地图显示范围,所以是安全的默认值。然后通过for循环,当值为1的时候,将apple_position等于键k。之所以直接break是因为我们应当只有一个苹果,因此无需多余的循环动作。
  3. 判断是否吃到苹果很简单,就是蛇头的位置是否与苹果位置相等。
  4. 如果相等的话,则先让苹果消失,即把所有地图数据都值为0.
  5. 然后延长折身体,用append函数将last_snake_position再附加到snake_list中去。

此时运行,可以发现当蛇头吃的到苹果的时候,蛇身体延长。


image.png

不过会发现一个问题,当吃完苹果后只有下一回合会才回产生新苹果。因为不影响游戏进行,所以解决方法就不多说了,大家可以自己开动脑筋解决这个问题。

然后是第二部,碰撞死亡检测,所有的检测都发生在最后,因此next_step 函数应当追加以下内容:

    # 身体碰撞检测
    if snake_list[0] in snake_list[1::]:
        game.pl('蛇碰到了它的身体,你失败了', style='special')
        open_func()
        return

    # 超出边界检测
    if snake_list[0][0]<0 or snake_list[0][0]>=map_size or snake_list[0][1]<0 or snake_list[0][1]>=map_size:
        game.pl('蛇碰到了墙壁,你失败了', style='special')
        open_func()
        return

    # 返回主函数
    main_func()
  1. 身体碰撞检测,这里注意是snake_list[1::]表示从snake_list[1]到最后所有的元素。这句话的意思,蛇头是否碰到身体的其他部位。
  2. 超出边界检测,即检测蛇头的x和y是否小于0或者大于等于map_size. 如前几节所说地图的范围是0<=x,y<=map_size-1
  3. 当检测为真后。做了两间事,第一件事是打印失败信息。然后转到open_func()转到新的一句,最后是return退出next_step()函数。如果不加return的话会导致执行玩open_func()函数又会继续执行main_func()函数。

运行后死亡情况如图:


image.png

自此,所有的贪吃蛇开发已经完成了。但是依然有很多可以做的事和可以优化的地方。这里列出几项:
1. 一个苹果被吃掉,立刻产生新的苹果。
2. 蛇头采用不同的图形绘制
3. 添加内部墙壁
4. 即时化
5. 分数系统
总之,这个只是pyera的入门教程,还有很多功能没有介绍,依然列出几项比较有价值的:
1. 事件系统
2. 设定循环主界面
3. 非绑定的输入处理部分,类似于era脚本中的$input部分
4. align排版函数
5. 文字输入功能等。
6. 构建exe运行文件的功能
这些功能可能在以后扩展贪吃蛇游戏的时候慢慢介绍到。大家也可以参考详细文档的部分来了解使用方法。
最后感谢您选择pyera,如果有任何问题想反馈和讨论可以加群:
py/era 技术吹水群 543014634
完整贪吃蛇代码参考:https://github.com/qsjl11/pyera_snake

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

推荐阅读更多精彩内容