头号玩家——【掘金大冒险:Super-Hawking】教你一小时做超级玛丽

495 阅读3分钟

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

游戏在线体验地址:biao-code.github.io/magic-game/

游戏地址:github.com/biao-code/m…

团队成员:hoow, 正经程序员

游戏介绍

前几天看到的这个活动,就跟朋友一起报了名,由于最近快要考试,复习加上每天还要更文完成4月更文挑战,很难抽出时间来做这款游戏。今天放假一天,在逛掘金的时候,才发现活动明天就结束了,专门腾出一下午的时间来做这款游戏

这款游戏主要基于kaboomjs,它提供了大量的API和示例让我们挑选,节省了大量的时间来写代码,我只需要改人物的贴图和设计游戏的关卡,就可以玩这个游戏

这是一款网页版的闯关小游戏,你可以通过按键盘右下方的左右键和空格键来操作人物的移动。小人贴图我换成了掘金挖矿里的人物,小人没有生命值,但只要碰到陷阱和怪物就会死,无限条命,可以尽情的玩,但要注意的是:人物死后,不是在当前关重生,而是直接从第一关重生,我有好几次都快到终点了,因为碰到了陷阱,只能从头再来,就很烦。

游戏总共只有6关,数量虽然不多,但每一关的难度都不简单,如果有人能一条命就通关,那我只能称你为“最强王者”。每一关都有5个金币,想要百分百通过不只是要到终点,还需要将这些金币都拿到。

游戏

chrome-capture-2022-3-23 (1).gif

具体实现

游戏一共设计了6个关卡,在字符数组中绘制了地图,设计代码如下:

const LEVELS = [
	[
		"                              ",
		"                 ====        $",
		"                             $",
		"           $$            =   $",
		"  %      ====            =   $",
		"                         =   $",
		"                         =    ",
		"   @ > ^^      =>>>>>>>> =   @",
		"===============================",
	   ],
	   [
		   "                                $           ",
		"                                =    $     @",
		"                          =     =    $      ",
		"                   =            =    $      ",
		"            =                   =    $      ",
		"     =                          = > > > > >=",
		"  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
		"============================================",
	   ],
	   [
		"     $    $    $    $     $",
		"                           ",
		"                           ",
		"                           ",
		"                           ",
		"                           ",
		"                           ",
		"^^   >^^^^>^^^^>^^^^>^^^^^@",
		"===========================",
	   ],
	   [
		"      ==      ==      ==        $@ ",
		"  ===     ===     ===     ===      ",
		"      $       $       $       $    ",
		"      =       =       =       =    ",
		"      =       =       =       =    ",
		"      =       =       =       =    ",
		"      =       =       =       =    ",
		"=>   >=>     >=>     >=>     >=    ",
		"===================================",
	   ],
	   [
		"         $^^                       ",
		"         ===                      @",
		"              ^                  $ ",
		"              =                 $  ",
		"    =%=       =                $   ",
		"              =  $                 ",
		"              =                    ",
		"   ^^         =^^>^^^^=^^^  >    $=",
		"===================================",
	   ],
	   [
		"      =            $      ^^^ =^     ",
		"      =            =          =     ",
		"    $ =                       =@s   ",
		"      =                ^      =     ",
		"   $  =                ^      =     ",
		"      =                ^      = ^  ^",
		"      =                ^      =  $  ",
		"= > ^^=^^^^^^^^>^      ^  >^>>=>^>^^",
		"=================      ==============",
	   ],
]

上面每一个符号都代码一个地图对象,在下面定义了每个对象对应的内容。

// 定义每个符号在水平图中的含义
const levelConf = {
	// 网格大小
	width: 64,
	height: 64,
	// 将每个对象定义为组件列表
	"=": () => [
		sprite("grass"),
		area(),
		solid(),
		origin("bot"),
	],
	"$": () => [
		sprite("coin"),
		area(),
		pos(0, -9),
		origin("bot"),
		"coin",
	],
	"%": () => [
		sprite("prize"),
		area(),
		solid(),
		origin("bot"),
		"prize",
	],
	"^": () => [
		sprite("spike"),
		area(),
		solid(),
		origin("bot"),
		"danger",
	],
	"#": () => [
		sprite("apple"),
		area(),
		origin("bot"),
		body(),
		"apple",
	],
	">": () => [
		sprite("ghosty"),
		area(),
		origin("bot"),
		body(),
		patrol(),
		"enemy",
	],
	"@": () => [
		sprite("portal"),
		area({ scale: 0.5, }),
		origin("bot"),
		pos(0, -12),
		"portal",
	],
}

游戏主要交互动作方法,包含按键绑定,玩家对象的主要操作方法。

scene("game", ({ levelId, coins } = { levelId: 0, coins: 0 }) => {

	gravity(3200)

	// 向场景添加关卡
	const level = addLevel(LEVELS[levelId ?? 0], levelConf)

	// 定义玩家对象

	const player = add([
		sprite("bean"),
		pos(0, 0),
		area(),
		scale(1),
		// 使重心下降并可跳跃
		body(),
		// 我们在上面定义的自定义组件
		big(),
		origin("bot"),
	])

	// action() 每帧运行一次
	player.onUpdate(() => {
		// 中央摄影机图层
		camPos(player.pos)
		// 制止坠落死亡
		if (player.pos.y >= FALL_DEATH) {
			go("lose")
		}
	})

	// 如果玩家与任何带有“危险”标签的obj碰撞,则失败
	player.onCollide("danger", () => {
		go("lose")
		play("hit")
	})

	player.onCollide("portal", () => {
		play("portal")
		if (levelId + 1 < LEVELS.length) {
			go("game", {
				levelId: levelId + 1,
				coins: coins,
			})
		} else {
			go("win")
		}
	})

	player.onGround((l) => {
		if (l.is("enemy")) {
			player.jump(JUMP_FORCE * 1.5)
			destroy(l)
			addKaboom(player.pos)
			play("powerup")
		}
	})

	player.onCollide("enemy", (e, col) => {
		// 如果不是从上面,那就去死吧
		if (!col.isBottom()) {
			go("lose")
			play("hit")
		}
	})

	let hasApple = false

	player.onHeadbutt((obj) => {
		if (obj.is("prize") && !hasApple) {
			const apple = level.spawn("#", obj.gridPos.sub(0, 1))
			apple.jump()
			hasApple = true
			play("blip")
		}
	})

	// 玩家在与“苹果”obj的碰撞中变大
	player.onCollide("apple", (a) => {
		destroy(a)
		// 正如我们在big()组件中定义的
		player.biggify(3)
		hasApple = false
		play("powerup")
	})

	let coinPitch = 0

	onUpdate(() => {
		if (coinPitch > 0) {
			coinPitch = Math.max(0, coinPitch - dt() * 100)
		}
	})

	player.onCollide("coin", (c) => {
		destroy(c)
		play("coin", {
			detune: coinPitch,
		})
		coinPitch += 100
		coins += 1
		coinsLabel.text = coins
	})

	const coinsLabel = add([
		text(coins),
		pos(24, 24),
		fixed(),
	])

	// 空格键跳跃
	onKeyPress("space", () => {
		// 这两个函数由body()组件提供
		if (player.isGrounded()) {
			player.jump(JUMP_FORCE)
		}
	})
        // 下面都是按键绑定
	onKeyDown("left", () => {
		player.move(-MOVE_SPEED, 0)
	})

	onKeyDown("right", () => {
		player.move(MOVE_SPEED, 0)
	})

	onKeyPress("down", () => {
		player.weight = 3
	})

	onKeyRelease("down", () => {
		player.weight = 1
	})

	onKeyPress("f", () => {
		fullscreen(!fullscreen())
	})

	onKeyPress("w", () => {
		if (player.isGrounded()) {
			player.jump(JUMP_FORCE)
		}
	})
	onKeyDown("a", () => {
		player.move(-MOVE_SPEED, 0)
	})
	onKeyDown("d", () => {
		player.move(MOVE_SPEED, 0)
	})

})