phaser3 战疫小游戏关卡实现

1,746 阅读5分钟

我正在参加掘金社区游戏创意投稿大赛团队赛,详情请看:游戏创意投稿大赛

前言

2022 年,我加入了“猿创营”,我在大帅带领下,参加了掘金游戏创意大赛,感觉收益颇多,这也是我首次在掘金写文章,希望以后多多参加这样的活动,收获更多的知识。

演示地址:game.runjs.cool

代码仓库:github.com/maqi1520/ph…

队友文章:《 使用 phaser3 从零实现一个战疫小游戏》

技术选型

我之前也没有开发过 H5 游戏,我和我的群友在群里讨论,大家选择的技术栈多大多都是保底实现 HTML+JS+CSS,而我们队伍想通过本次开发,希望在以后的工作中也能够快速开发出一款小游戏;所以我们选择比较新的技术架构。

  • 游戏引擎:Phaser,一个开源的 JavaScript 2D 游戏开发框架;
  • 项目脚手架: Vite: ,可快速启动 web 开发服务器,可以快速热更新,
  • 开发语言:Typescript: 使用 ts 可以有非常强大类型提示功能,可以减少我们查 api 文档的次数

寓意

细心的掘友们,应该不难发现,这个是一个很简单英雄消灭怪物,寻找食物的游戏。结合当下疫情的严峻形式,我们融入了一些元素到这款游戏中,由于很久没有出门玩耍了,我们选择了绿色的草坪,这样的游戏场景。然后我们给英雄也戴上了口罩,希望疫情早日结束吧!

PS:寓意消灭新冠病毒,争取生命。

游戏介绍

进入游戏后,方向键控制英雄行走,空格键攻击怪物。当英雄走到怪物一定距离后怪物能追踪英雄,英雄靠近怪物后可以攻击怪物,当英雄不小心让怪物近身后英雄会掉血,直到血掉完了,游戏也就完了。英雄看见食物可以拾取,拾取到一个食物得 10 份,第一关 30 分,进入下一关,第二个 100 分。

游戏关卡

我来开发负责这个关卡的功能。下面来说说我加入的关卡逻辑:

根据获得积分来定位关卡,当获得 30 分后就进入下一关,获得 100 分又进入下一关等以此类推。

因此我们先定义一个数据结构:

[
  {
    name: "Level-1",
    score: 30,
  },
  {
    name: "Level-2",
    score: 100,
  },
]

Level-1Level-2正好对应 Tiled JSON 文件用来作为游戏地图名称。

下面是加载游戏地图的主要代码

第一步:

function preload ()
{
  this.load.tilemapTiledJSON('Level-1', 'maps/Level1.json');
}

先在 preload 加载游戏资源

第二步

function create ()
{
  var map = this.make.tilemap({ key: 'Level-1' });
}

将资源地图作为当前的地图。

我们从 loading 场景中加载第一关的游戏地图

create(): void {
  this.scene.start("game-scene", {
    name: "Level-1",
  });
  this.scene.start("ui-scene", {
    name: "Level-1",
  });
}

然后在初始化地图的 initMap 方法可以接收一个参数 name

private initMap(name: string): void {
  this.map = this.make.tilemap({
    key: name,
    tileWidth: 16,
    tileHeight: 16,
  });
  this.tileset = this.map.addTilesetImage("Grass", "Grass");
  this.groundLayer = this.map.createLayer("Ground", this.tileset, 0, 0);
  this.wallsLayer = this.map.createLayer("Walls", this.tileset, 0, 0);

  this.wallsLayer.setCollisionByProperty({ collides: true });

  this.physics.world.setBounds(
    0,
    0,
    this.wallsLayer.width,
    this.wallsLayer.height
  );
  //this.showDebugWalls();
}

在不增加游戏元素的时候,以上代码就可以做到同一个 Game 类来实现多张地图的逻辑。

UI 显示游戏关卡

同样,在 UI 场景我们需要显示当前是第几关

create(props: any): void {
    this.levelName = props.name;
    this.score = new Score(this, 20, 20, 0);

    new Text(this, 20, 100, `关卡:${this.levelName}`);

    this.initListeners();
  }

我们可以在 create 方法中 实例化一个文本,坐标是 (20,100), 并且将当前关卡名称保存在当前类中,方便在当前类中获取关卡名称。

接下来就是监听英雄获得食物的逻辑

private initListeners(): void {
    this.game.events.on(EVENTS_NAME.chestLoot, this.chestLootHandler, this);
    this.game.events.once(EVENTS_NAME.gameEnd, this.gameEndHandler, this);
  }

下面代码是监听英雄获得食物后要处理的事情,获得一个食物加 10 分。

this.chestLootHandler = () => {
      this.score.changeValue(ScoreOperations.INCREASE, 10);

      const currentIndex = LEVELS.findIndex(
        (item) => item.name === this.levelName
      );

      if (LEVELS[currentIndex].score === this.score.getValue()) {
        const nextLevel = LEVELS[currentIndex + 1];
        if (nextLevel) {
          this.game.events.off(EVENTS_NAME.chestLoot, this.chestLootHandler);
          this.game.events.off(EVENTS_NAME.gameEnd, this.gameEndHandler);
          this.scene.get("game-scene").scene.restart({ name: nextLevel.name });
          this.scene.restart({ name: nextLevel.name });
        } else {
          if (this.score.getValue() === gameConfig.winScore) {
            this.game.events.emit(EVENTS_NAME.gameEnd, "win");
          }
        }
      }
    };

先找到当前关卡的数据,然后判断当前的得分是否跟关卡的预设得分是否相同,如果相同,说明关卡要进入下一关,如果没有下一关,当前游戏就结束了,如果有下一关,我们就重新加载 game 场景和 UI 场景。 有一点需要注意的是,我们重新加载当前场景,需要将绑定的全局监听事件取消绑定,如果不取消绑定就会触发 2 次。 以上就是关卡的主要内容。

小结

当然我们可以加入更复杂的逻辑,由于时间关系,就先开发到这里了,以后有时间再继续开发吧,如果有小伙伴有好的想法,也可以加入进来一起开发。接下来来总结下开发小游戏的主要点吧

  • 一:JavaScript 基础还是要巩固下,扎实的基础才能更快地融入开发
  • 二: 地图设计第一次尝试可能会比较慢,熟练之后就很快了,只有有好的想法,就可以设计出更复杂的关卡
  • 三:对应 phaser3 的框架的熟悉程度可以一步一步来,边开发边学习,并不是要 api 都熟悉了才可以进入开发。

最后

我在小游戏项目开发中获得了很多宝贵的知识,如精灵图,精灵表,地图,动画、碰撞检查、事件通知等。这些相加就可以创造出一个小的游戏,也懂的了靠几段代码就可以创造出一个小游戏。这也是我第一次开发项目。

最后祝大家早日抗疫成功!!!

感谢小马在这个小游戏里面投入很多精力,也为我打开了一扇大门。

感谢@大帅老猿帮忙设计的口罩精灵图, 大帅还创建了“猿创营”,群里有很多开发大佬可以互相帮忙答疑和交流技术,同时大帅还会分享做外包,搞副业等,感兴趣的小伙伴可以留言“入群”。