专栏首页Creator星球游戏开发社区链表的应用—贪吃蛇游戏(附项目)

链表的应用—贪吃蛇游戏(附项目)

前言

今天我们介绍的项目,是各位大多数人都有玩过,就算没玩过也不可能没听过的一款经典游戏,那就是 贪 吃 蛇 。

制作一个贪吃蛇游戏也有许多不同的方法,这篇文章将向大家展示的是如何使用 链表 的原理来制作一个贪吃蛇。

效果展示

吃吃吃

正文

整体思路

  1. 首先还是需要简单说下链表(LinkedList)的定义:链表实际上是一种链式数据结构链表中的每一个数据节点都保存了其下一个节点的指针(也就是引用)
  2. 蛇就是由一节一节的身体构成的(当然头也是身体的一部分),我们控制头部的移动头部会带着第一节身体移动,然后每一节身体移动的时候都会带着下一节身体一起移动(禁止套娃)。

代码实现

提示:这里只展示最主要部分的代码,完整项目文章底部rar获取

  1. 首先我们先实现一个链表节点的组件 Body 。该组件需要保存下一个节点的引用上一个位置,以及设置下一节点移动的函数:
const { ccclass, property } = cc._decorator;

@ccclass
export default class Body extends cc.Component {

    @property({ type: Body, tooltip: '下一节身体' })
    protected nextBody: Body = null;

    protected lastPos: cc.Vec2 = null; // 上一个位置

    /**
     * 设置下一节身体
     * @param body 节点
     */
    public setNextBody(body: Body) {
        this.nextBody = body;
        body.node.setPosition(this.lastPos);
    }

    /**
     * 移动该节点
     * 同时让下一个节点(如果有的话)移动到该节点之前的位置
     * @param nextPos 目标位置
     */
    public move(nextPos: cc.Vec2) {
        // 保存当前位置
        this.lastPos = this.node.getPosition();
        // 自身移动到新的位置
        this.node.setPosition(nextPos);
        // 让下一个节点移动到之前的位置
        if (this.nextBody) this.nextBody.move(this.lastPos);
    }

}
  1. 实现蛇头部的组件 Head ,该组件继承于 Body ,所以 Head 拥有 Body 的所有属性以及函数。同时我们需要给 Head 新增一些头部特有的功能,比如移动和吃:
import Body from "./Body";
import PoolManager from "../manager/PoolManager";
import { GameEvent } from "../../eazax-ccc/core/GameEvent";

const { ccclass, property } = cc._decorator;

export enum Direction {
    Up = 1, // 上
    Down, // 下
    Left, // 左
    Right // 右
}

@ccclass
export default class Head extends Body {

    private timer: number = 0; // 计时器

    private moveInterval: number = 0.2; // 移动间隔

    private moveDistance: number = 20; // 移动距离

    private bodyList: Body[] = []; // 身体节点集合

    private curDirection: Direction = Direction.Up; // 当前移动的方向

    private nextDirection: Direction = Direction.Up; // 下次移动的方向

    private isAlive: boolean = false; // 是否活着

    private static instance: Head = null; // 实例

    /**
     * 获取头部位置
     */
    public static get pos() { return this.instance.node.getPosition(); }

    protected onLoad() {
        Head.instance = this;

        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);

        GameEvent.on('gameinit', this.onGameInit, this);
        GameEvent.on('gamestart', this.onGameStart, this);
    }

    protected onDestroy() {
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);

        GameEvent.off('gameinit', this.onGameInit, this);
        GameEvent.off('gamestart', this.onGameStart, this);
    }

    protected update(dt: number) {
        if (!this.isAlive) return;
        // 计时
        this.timer += dt;
        if (this.timer >= this.moveInterval) {
            this.timer -= this.moveInterval;
            // 移动到下一个位置
            switch (this.nextDirection) {
                case Direction.Up:
                    this.curDirection = Direction.Up;
                    this.move(cc.v2(this.node.x, this.node.y + this.moveDistance));
                    break;
                case Direction.Down:
                    this.curDirection = Direction.Down;
                    this.move(cc.v2(this.node.x, this.node.y - this.moveDistance));
                    break;
                case Direction.Left:
                    this.curDirection = Direction.Left;
                    this.move(cc.v2(this.node.x - this.moveDistance, this.node.y));
                    break;
                case Direction.Right:
                    this.curDirection = Direction.Right;
                    this.move(cc.v2(this.node.x + this.moveDistance, this.node.y));
                    break;
            }
        }
    }

    /**
     * 初始化
     */
    private onGameInit() {
        this.isAlive = false;
        this.node.setPosition(0, 0);
        this.nextDirection = Direction.Up;
        for (let i = 0; i < this.bodyList.length; i++) {
            PoolManager.put(this.bodyList[i].node);
        }
        this.bodyList = [];
        this.nextBody = null;
    }

    /**
     * 动起来
     */
    private onGameStart() {
        this.onGameInit();
        this.isAlive = true;
    }

    /**
     * 吃吃吃,长长长
     */
    private eat(food: cc.Node) {
        cc.log('eat');
        // 食物吃掉
        PoolManager.put(food);
        // 获取一节身体
        let node = PoolManager.get('body');
        node.setParent(this.node.parent);
        // 设置身体位置并保存身体的引用
        let body = node.getComponent(Body);
        if (this.bodyList.length > 0) this.bodyList[this.bodyList.length - 1].setNextBody(body);
        else this.setNextBody(body);
        this.bodyList.push(body);
        // 发射事件
        GameEvent.emit('snakeeat');
    }

    /**
     * GG
     */
    private die() {
        cc.log('GG');
        this.isAlive = false;
        GameEvent.emit('snakedie');
    }

    /**
     * 键盘回调
     * @param event 按键事件
     */
    private onKeyDown(event: any) {
        switch (event.keyCode) {
            case cc.macro.KEY.up:
                if (this.curDirection !== Direction.Down) this.nextDirection = Direction.Up;
                break;
            case cc.macro.KEY.down:
                if (this.curDirection !== Direction.Up) this.nextDirection = Direction.Down;
                break;
            case cc.macro.KEY.left:
                if (this.curDirection !== Direction.Right) this.nextDirection = Direction.Left;
                break;
            case cc.macro.KEY.right:
                if (this.curDirection !== Direction.Left) this.nextDirection = Direction.Right;
                break;
        }
    }

    /**
     * 碰撞回调
     * @param other
     * @param self
     */
    protected onCollisionEnter(other: cc.BoxCollider, self: cc.BoxCollider) {
        switch (other.node.name) {
            case 'food':
                this.eat(other.node);
                break;
            case 'body':
            case 'border':
                this.die();
                break;
        }
    }

}

本文分享自微信公众号 - Creator星球游戏开发社区(creator-star)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Creator3D新教程,你能射中靶心么?

    长按屏幕,拖动瞄准,放手发射。风向、重力和距离影响最终结果!越靠近中心得分越高!最高分10分!

    张晓衡
  • 狂热「小工」的9款Creator游戏源码及图文教程,等你来拿!

    他在 Cocos 论坛上公开了自己9款小游戏作品,完成度相当之高,是不可多得的Creator学习资源,下面是论坛链接地址:https://forum.cocos...

    张晓衡
  • KUOKUO的趣味教程 | 小怪物的视野(2)

    本篇承接上一集故事《KUOKUO的趣味教程 | 进击的小怪诞生(1)》,看小怪是如何自我进化的!

    张晓衡
  • [Cocos Creator] 链表的应用之贪吃蛇(附项目)

    今天我们介绍的项目,是各位大多数人都有玩过,就算没玩过也不可能没听过的一款经典游戏,那就是 贪 吃 蛇 。

    陈皮皮
  • Javascript中关键参数this浅析

    Sb_Coco
  • JavaScript中的 this的理解

    在JavaScript编程中 , this的关键字总是让初学者感到迷惑 , this到底是什么呢?

    越陌度阡
  • C++之this指针

    有个问题是刚开始学习C++的人都想知道的,那就是C++的类对象的大小是多少?可能的猜测是它所有数据成员的大小加上所有函数指针的大小,这样就是类的大小。我们来测试...

    zy010101
  • Mybatis 源码分析(一)之 Mybatis 的Executor的初始化

    Mybatis系列: Mybatis 基础介绍与逆向工程的构建 :https://www.jianshu.com/p/1c18db4d7a38 Mybati...

    zoro
  • 那些年下过的大雨

    想了解一下用纯CSS和JS怎么实现一段下雨的动画,于是去CodePen上面搜了一下,发现了很多很有意思的东西。有空可以常去上面逛逛,在对技术产生敬畏的同时也能学...

    糊糊糊糊糊了
  • 深入浅出JavaScript之this

    JavaScript中的this比较灵活,根据在不同环境下,或者同一个函数在不同方式调用下,this都有可能是不同的。但是有一个总的原则,那就是this指的是,...

    哲洛不闹

扫码关注云+社区

领取腾讯云代金券