Java GUI 实例:贪吃蛇游戏

Java GUI 基础

一、实现效果

  1. 空格暂停;
  2. 上、下、左、右控制方向;
  3. 长度随着食物的吃下,而增加;
  4. 定时刷新;
  5. 累计长度和分数等。

二、实现思路:

  1. 定义所有功能的数据
  2. 绘制功能需要的图形
  3. 添加监听功能需要的事件(帧率事件)键盘、鼠标

三、实现代码

  • Data(数据类):初始化需要的图片;
  • StartGame(游戏主启动类):主要实现窗口的加载,和添加 GamePanel 面板到窗口;
  • GamePanel(游戏的面板):最重要的实现部分,包括:
    • 定义需要的数据
    • 绘制图像
    • 实现事件监听
  1. 静态界面绘制
  • StartGame.java
package com.xxx.gui.snake;

import javax.swing.*;

/**
 * 游戏主启动类
 */
public class StartGame {
    public StartGame() {
        JFrame frame = new JFrame("贪吃蛇");
        // 长宽尺寸,根据内容计算得出
        frame.setBounds(10, 10, 915, 730);
        // 窗口大小不可变,避免游戏变形
        frame.setResizable(false);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        // 将游戏面板添加到窗口中
        frame.add(new GamePanel());
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new StartGame();
    }
}
  • Date.java
package com.xxx.gui.snake;

import javax.swing.*;
import java.net.URL;

/**
 * 数据类
 */
public class Data {
    // 相对路径 header.png
    // 绝对路径 / 相当于当前的项目
    // 顶部图片
    public static URL headerURL = Data.class.getResource("statics/header.png");
    // 图像转为图标
    public static ImageIcon header = new ImageIcon(headerURL);
    // 上下左右
    public static URL upURL = Data.class.getResource("statics/up.png");
    public static URL downURL = Data.class.getResource("statics/down.png");
    public static URL leftURL = Data.class.getResource("statics/left.png");
    public static URL rightURL = Data.class.getResource("statics/right.png");
    public static ImageIcon up = new ImageIcon(upURL);
    public static ImageIcon down = new ImageIcon(downURL);
    public static ImageIcon left = new ImageIcon(leftURL);
    public static ImageIcon right = new ImageIcon(rightURL);
    // 身体
    public static URL bodyURL = Data.class.getResource("statics/body.png");
    public static ImageIcon body = new ImageIcon(bodyURL);
    // 食物
    public static URL foodURL = Data.class.getResource("statics/food.png");
    public static ImageIcon food = new ImageIcon(foodURL);
}
  • GamePanel.java
package com.xxx.gui.snake;

import javax.swing.*;
import java.awt.*;

/**
 * 游戏的面板
 */
public class GamePanel extends JPanel {
    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);
    }
}
  1. 绘制静态小蛇
  • GamePanel.java
package com.xxx.gui.snake;

import javax.swing.*;
import java.awt.*;

/**
 * 游戏的面板
 */
public class GamePanel extends JPanel {
    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600];  // 蛇的 X 坐标,25*25
    int[] snakeY = new int[500];  // 蛇的 Y 坐标,25*25
    String fx;  // 初始方向:向右
    // 游戏当前状态:开始,停止
    boolean isStart = false;    // 默认停止

    // 构造器
    public GamePanel() {
        // 调用初始化
        init();
    }

    // 初始化方法
    public void init() {
        // 初始蛇有三节,包括头
        length = 3;
        // 初始化开始的蛇,给蛇定位
        // 头部位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 第一节身体
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 第二节身体
        snakeX[2] = 50;
        snakeY[2] = 100;
        fx = "R";
    }

    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);

        // 把小蛇画上去
        // 蛇头初始化,向右,需要通过方向来判断
        if (fx.equals("R")) {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("L")) {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("U")) {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("D")) {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        // 根据身体的长度,绘制蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

        //游戏状态
        if (isStart == false) {
            g.setColor(Color.white);
            g.setFont(new Font("黑体", Font.BOLD, 40));   // 设置字体
            g.drawString("按下空格开始游戏", 300, 300);    // 初始文字
        }
    }
}
  1. 小蛇开始移动
  • GamePanel.java
package com.xxx.gui.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 * 游戏的面板
 * ActionListener(接口):实现定时器
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {
    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600];  // 蛇的 X 坐标,25*25
    int[] snakeY = new int[500];  // 蛇的 Y 坐标,25*25
    String fx;  // 初始方向:向右
    // 游戏当前状态:开始,停止
    boolean isStart = false;    // 默认停止

    // 定时器:100(ms) 毫秒刷新一次(1000ms=1秒)
    Timer timer = new Timer(100, this);

    // 构造器
    public GamePanel() {
        // 调用初始化
        init();
        // 获取事件
        this.setFocusable(true);    // 焦点事件
        this.addKeyListener(this); // 键盘监听事件
        timer.start();  // 游戏一开始,定时器启动
    }

    // 初始化方法
    public void init() {
        // 初始蛇有三节,包括头
        length = 3;
        // 初始化开始的蛇,给蛇定位
        // 头部位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 第一节身体
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 第二节身体
        snakeX[2] = 50;
        snakeY[2] = 100;
        fx = "R";
    }

    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);

        // 把小蛇画上去
        // 蛇头初始化,向右,需要通过方向来判断
        if (fx.equals("R")) {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("L")) {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("U")) {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("D")) {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        // 根据身体的长度,绘制蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

        //游戏状态
        if (isStart == false) {
            g.setColor(Color.white);
            g.setFont(new Font("黑体", Font.BOLD, 40));   // 设置字体
            g.drawString("按下空格开始游戏", 300, 300);    // 初始文字
        }
    }

    // 键盘监听事件
    @Override
    public void keyPressed(KeyEvent e) {
        // 获得键盘按键
        int keyCode = e.getKeyCode();
        // 如果按下空格键
        if (keyCode == KeyEvent.VK_SPACE) {
            isStart = !isStart;   // 取反
            repaint();  // 重新绘制
        }
        // 小蛇移动(通过方向键判断)
        if (keyCode == KeyEvent.VK_UP) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        } else if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        }
    }

    // 事件监听:定时执行时的操作(刷新)
    @Override
    public void actionPerformed(ActionEvent e) {
        // 如果是开始状态,让小蛇移动
        if (isStart) {
            // 移动:
            for (int i = length - 1; i > 0; i--) {
                // 身体后一节移到前一节的位置(snakeX[1]=snakeX[0])
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
            // 通过方向键控制,头部移动
            if (fx.equals("R")) {
                // 头部 X 坐标,右移一格(25)
                snakeX[0] = snakeX[0] + 25;
                // 判断边界:超出后,回到最左边
                if (snakeX[0] > 850) snakeX[0] = 25;
            } else if (fx.equals("L")) {
                snakeX[0] = snakeX[0] - 25;
                // 判断边界:超出后,回到最右边
                if (snakeX[0] < 25) snakeX[0] = 850;
            } else if (fx.equals("U")) {
                snakeY[0] = snakeY[0] - 25;
                // 判断边界:超出后,回到最左边
                if (snakeY[0] < 75) snakeY[0] = 650;
            } else if (fx.equals("D")) {
                snakeY[0] = snakeY[0] + 25;
                if (snakeY[0] > 650) snakeY[0] = 75;
            }
            repaint();  // 重画页面
        }
        timer.start();  // 定时器开启
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
}
  1. 小蛇开始吃食物
  • GamePanel.java
package com.xxx.gui.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

/**
 * 游戏的面板
 * ActionListener(接口):实现定时器
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {
    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600];  // 蛇的 X 坐标,25*25
    int[] snakeY = new int[500];  // 蛇的 Y 坐标,25*25
    String fx;  // 初始方向:向右

    // 食物坐标
    int foodX;
    int foodY;
    Random random = new Random();   // 获取随机数

    // 游戏当前状态:开始,停止
    boolean isStart = false;    // 默认停止

    // 定时器:100(ms) 毫秒刷新一次(1000ms=1秒)
    Timer timer = new Timer(100, this);

    // 构造器
    public GamePanel() {
        // 调用初始化
        init();
        // 获取事件
        this.setFocusable(true);    // 焦点事件
        this.addKeyListener(this); // 键盘监听事件
        timer.start();  // 游戏一开始,定时器启动
    }

    // 初始化方法
    public void init() {
        // 初始蛇有三节,包括头
        length = 3;
        // 初始化开始的蛇,给蛇定位
        // 头部位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 第一节身体
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 第二节身体
        snakeX[2] = 50;
        snakeY[2] = 100;
        fx = "R";
        // 把食物随机分布在界面上
        foodX = 25 + 25 * random.nextInt(34); // 34:850/25
        foodY = 75 + 25 * random.nextInt(34); // 24:600/25

    }

    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);

        // 把小蛇画上去
        // 蛇头初始化,向右,需要通过方向来判断
        if (fx.equals("R")) {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("L")) {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("U")) {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("D")) {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        // 根据身体的长度,绘制蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

        // 画食物
        Data.food.paintIcon(this, g, foodX, foodY);

        //游戏状态
        if (isStart == false) {
            g.setColor(Color.white);
            g.setFont(new Font("黑体", Font.BOLD, 40));   // 设置字体
            g.drawString("按下空格开始游戏", 300, 300);    // 初始文字
        }
    }

    // 键盘监听事件
    @Override
    public void keyPressed(KeyEvent e) {
        // 获得键盘按键
        int keyCode = e.getKeyCode();
        // 如果按下空格键
        if (keyCode == KeyEvent.VK_SPACE) {
            isStart = !isStart;   // 取反
            repaint();  // 重新绘制
        }
        // 小蛇移动(通过方向键判断)
        if (keyCode == KeyEvent.VK_UP) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        } else if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        }
    }

    // 事件监听:定时执行时的操作(刷新)
    @Override
    public void actionPerformed(ActionEvent e) {
        // 如果是开始状态,让小蛇移动
        if (isStart) {
            // 吃食物
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                // 身体长度+1
                length++;
                // 再次随机食物
                // 把食物随机分布在界面上
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(34);
            }
            // 移动:
            for (int i = length - 1; i > 0; i--) {
                // 身体后一节移到前一节的位置(snakeX[1]=snakeX[0])
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
            // 通过方向键控制,头部移动
            if (fx.equals("R")) {
                // 头部 X 坐标,右移一格(25)
                snakeX[0] = snakeX[0] + 25;
                // 判断边界:超出后,回到最左边
                if (snakeX[0] > 850) snakeX[0] = 25;
            } else if (fx.equals("L")) {
                snakeX[0] = snakeX[0] - 25;
                // 判断边界:超出后,回到最右边
                if (snakeX[0] < 25) snakeX[0] = 850;
            } else if (fx.equals("U")) {
                snakeY[0] = snakeY[0] - 25;
                // 判断边界:超出后,回到最左边
                if (snakeY[0] < 75) snakeY[0] = 650;
            } else if (fx.equals("D")) {
                snakeY[0] = snakeY[0] + 25;
                if (snakeY[0] > 650) snakeY[0] = 75;
            }
            repaint();  // 重画页面
        }
        timer.start();  // 定时器开启
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
}
  1. 失败判定,积分系统
  • GamePanel.java
package com.xxx.gui.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

/**
 * 游戏的面板
 * ActionListener(接口):实现定时器
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {
    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600];  // 蛇的 X 坐标,25*25
    int[] snakeY = new int[500];  // 蛇的 Y 坐标,25*25
    String fx;  // 初始方向:向右
    // 食物坐标
    int foodX;
    int foodY;
    Random random = new Random();   // 获取随机数
    // 成绩
    int score;
    // 游戏当前状态:开始,停止
    boolean isStart = false;    // 默认停止
    // 游戏失败状态
    boolean isFail = false;
    // 定时器:100(ms) 毫秒刷新一次(1000ms=1秒)
    Timer timer = new Timer(100, this);

    // 构造器
    public GamePanel() {
        // 调用初始化
        init();
        // 获取事件
        this.setFocusable(true);    // 焦点事件
        this.addKeyListener(this); // 键盘监听事件
        timer.start();  // 游戏一开始,定时器启动
    }

    // 初始化方法
    public void init() {
        // 初始蛇有三节,包括头
        length = 3;
        // 初始化开始的蛇,给蛇定位
        // 头部位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 第一节身体
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 第二节身体
        snakeX[2] = 50;
        snakeY[2] = 100;
        fx = "R";
        // 把食物随机分布在界面上
        foodX = 25 + 25 * random.nextInt(34); // 34:850/25
        foodY = 75 + 25 * random.nextInt(24); // 24:600/25

        score = 0;
    }

    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);
        // 画积分
        g.setColor(Color.WHITE);
        g.setFont(new Font("黑体", Font.BOLD, 16));
        g.drawString("长度 " + length, 750, 32);
        g.drawString("分数 " + score, 750, 53);
        // 画食物
        Data.food.paintIcon(this, g, foodX, foodY);
        // 把小蛇画上去
        // 蛇头初始化,向右,需要通过方向来判断
        if (fx.equals("R")) {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("L")) {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("U")) {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("D")) {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        }
        // 根据身体的长度,绘制蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }
        // 游戏状态
        if (isStart == false) {
            g.setColor(Color.white);
            g.setFont(new Font("黑体", Font.BOLD, 40));   // 设置字体
            g.drawString("按下空格开始游戏", 300, 300);    // 初始文字
        }
        //  失败状态
        if (isFail) {
            g.setColor(Color.RED);
            g.setFont(new Font("黑体", Font.BOLD, 40));   // 设置字体
            g.drawString("失败,按下空格重新开始", 300, 300);    // 初始文字
        }
    }

    // 键盘监听事件
    @Override
    public void keyPressed(KeyEvent e) {
        // 获得键盘按键
        int keyCode = e.getKeyCode();
        // 如果按下空格键
        if (keyCode == KeyEvent.VK_SPACE) {
            if (isFail) {
                // 重新开始
                isFail = false;
                init();
            } else {
                isStart = !isStart;   // 取反
            }
            repaint();  // 重新绘制
        }
        // 小蛇移动(通过方向键判断)
        if (keyCode == KeyEvent.VK_UP) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        } else if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        }
    }

    // 事件监听:定时执行时的操作(刷新)
    @Override
    public void actionPerformed(ActionEvent e) {
        // 如果是开始状态,且没有结束,让小蛇移动
        if (isStart && isFail == false) {
            // 吃食物
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                // 身体长度+1
                length++;
                // 分数+10
                score += 10;
                // 再次随机食物
                // 把食物随机分布在界面上
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(24);
            }
            // 移动:
            for (int i = length - 1; i > 0; i--) {
                // 身体后一节移到前一节的位置(snakeX[1]=snakeX[0])
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
            // 通过方向键控制,头部移动
            if (fx.equals("R")) {
                // 头部 X 坐标,右移一格(25)
                snakeX[0] = snakeX[0] + 25;
                // 判断边界:超出后,回到最左边
                if (snakeX[0] > 850) snakeX[0] = 25;
            } else if (fx.equals("L")) {
                snakeX[0] = snakeX[0] - 25;
                // 判断边界:超出后,回到最右边
                if (snakeX[0] < 25) snakeX[0] = 850;
            } else if (fx.equals("U")) {
                snakeY[0] = snakeY[0] - 25;
                // 判断边界:超出后,回到最左边
                if (snakeY[0] < 75) snakeY[0] = 650;
            } else if (fx.equals("D")) {
                snakeY[0] = snakeY[0] + 25;
                if (snakeY[0] > 650) snakeY[0] = 75;
            }
            // 失败判断:撞到自己
            // 遍历身体的每一个坐标
            for (int i = 1; i < length; i++) {
                // 头部坐标与身体坐标重合
                if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
                    // 游戏失败
                    isFail = true;
                }
            }
            repaint();  // 重画页面
        }
        timer.start();  // 定时器开启
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
}

Java 版贪吃蛇,图片素材:

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

推荐阅读更多精彩内容