Commit bef3ef18 authored by 邱旭's avatar 邱旭

10.FlppyBird-创建障碍

parent 76d6aefa
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<title>10.FlppyBird-创建障碍</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
<script src="../lib/flppyBirdLib.js"></script>
</head>
<body>
</body>
<script>
/**
* 屏幕宽高
* @type {{width: number, height: number}}
*/
const winSize = {
width: document.body.clientWidth,
height: document.body.clientHeight,
}
</script>
<script>
/**
* Bird
*/
class Bird extends Sprite {
speed; // bird的速度 每次移动多少距离
gravity; // 重力加速度
constructor(gravity = 0.2, speed = 0) {
super("../images/bird/bird_01.png");
this.speed = speed; // 保存speed
this.gravity = gravity; // 保存gravity
}
ready() {
super.ready();
// 放到合适的位置
const { width, height } = this.size;
this.top = (winSize.height - height) / 2 - 100;
this.left = (winSize.width - width) / 2;
}
update() {
super.update();
// v = v0 + a * t²
this.speed += this.gravity; // 速度 = 速度 + 加速度 * 时间²
this.top += this.speed; // 更新位置
}
}
/**
* 滚动器
*/
class ScrollMgr extends GameObject {
speed; // 滚动速度
bg1; // bg1
bg2; // bg2
constructor(bg1, bg2, speed = 5) {
super();
this.bg1 = bg1;
this.bg2 = bg2;
this.speed = speed;
this.addChild(this.bg1);
this.addChild(this.bg2);
}
ready() {
super.ready();
this.bg1.top = winSize.height - this.bg1.size.height; // 放在底部
this.bg2.top = winSize.height - this.bg2.size.height; // 放在底部
}
update() {
super.update();
// 获取一些参数
let bg1Left = this.bg1.left;
const bg1Width = this.bg1.size.width;
// 计算位置
bg1Left -= this.speed; // 计算位置
this.bg1.left = bg1Left; // 设置bg1的位置
this.bg2.left = bg1Left + this.bg1.size.width; // bg2跟在bg1后面
// 如果超出屏幕则交换bg1和bg2,为了做到循环滚动
if (bg1Left <= -bg1Width) {
const temp = this.bg1;
this.bg1 = this.bg2;
this.bg2 = temp;
}
}
}
</script>
<script>
/**
* Pie
*/
class Pie extends GameObject {
up; // 钢管上部分
down; // 钢管下部分
constructor() {
super();
this.up = this.addChild(new Sprite("../images/bird/pie.png"));
this.down = this.addChild(new Sprite("../images/bird/pie.png"));
}
/**
* 注意此函数非真正的ready函数,在被addChild的时候都会执行一次
*/
ready() {
super.ready();
// 随机中间的距离
const dis = Math.random() * 80 + 100;
this.down.top = this.up.size.height + dis;
}
}
/**
* PieMgr
*/
class PieMgr extends GameObject {
pieArr = []; // 所有的Pie
speed; // Pie移动的速度
delay; // 添加Pie的延时
constructor(speed, delay) {
super();
this.speed = speed;
this.delay = delay;
}
ready() {
super.ready();
// 创建计时器来固定事件创建Pie
setInterval(() => this.createPie(), this.delay);
}
/**
* 创建Pie
*/
createPie() {
const pie = this.addChild(new Pie());
this.pieArr.push(pie); // 加入列表统一管理
pie.top = Math.random() * -150; // 高度随机
pie.left = winSize.width; // 从屏幕左边出现
}
update() {
super.update();
// 所有的Pie同时向左移动
const { speed, pieArr } = this;
pieArr.forEach((pie) => {
pie.left -= speed;
});
}
}
</script>
<script>
/**
* FlppyBird
*/
class FlppyBird extends GameStage {
bird;
async reloadRes() {
const path = "../images/bird/";
const promises = [
"bird_01.png", "bird_02.png", "bird_03.png", "pie.png",
"land.png", "background.png", "start_button.png"
].map((v) => {
return loadImgAsync(`${path}${v}`);
});
return Promise.all(promises);
}
async ready() {
// 创建鸟
const bird = this.bird = new Bird();
// 创建背景
const bg1 = new Sprite("../images/bird/background.png");
const bg2 = new Sprite("../images/bird/background.png");
const bgMgr = new ScrollMgr(bg1, bg2, 2);
// 创建地面
const land1 = new Sprite("../images/bird/land.png");
const land2 = new Sprite("../images/bird/land.png");
const landMgr = new ScrollMgr(land1, land2, 4);
const pieMgr = new PieMgr(4, 1000); // 创建PieMgr
this.addChild(bgMgr);
this.addChild(pieMgr); // 加在背景和地面的中间
this.addChild(landMgr);
this.addChild(bird);
// 将背景放在地面的上面,因为默认top是0,子节点在内部定位在底部,所以只需要把背景定位在负的land的高度就可以了
bgMgr.top = -land1.size.height;
// 使用mousedown监听鼠标按下,并获得鼠标点击的位置
document.addEventListener('mousedown', this.mouseDown);
}
mouseDown = () => {
this.bird.speed = -8;
}
destroy() {
super.destroy();
document.removeEventListener('mousedown', this.mouseDown);
}
}
// 创建游戏实例
new FlppyBird();
</script>
</html>
# FlppyBird - 创建障碍
引入概念:`动态生成障碍`
本节将在上节的内容上,实现动态添加障碍
## 1.实现`Pie`类
- 实现`Pie`类,继承`GameObject`
- `Pie`包含一个上的一个下的
- 上下两个均为`Sprite`,且中间距离随机
```javascript
/**
* Pie
*/
class Pie extends GameObject {
up; // 钢管上部分
down; // 钢管下部分
constructor() {
super();
this.up = this.addChild(new Sprite("../images/bird/pie_up.png"));
this.down = this.addChild(new Sprite("../images/bird/pie_down.png"));
}
/**
* 注意此函数非真正的ready函数,在被addChild的时候都会执行一次
*/
ready() {
super.ready();
// 随机中间的距离
const dis = Math.random() * 80 + 100;
this.down.top = this.up.size.height + dis;
}
}
```
尝试在`FlppyBird``ready`函数中创建,得到如下效果
![10_1.png](../images/10_1.png)
## 2.实现动态创建`Pie`且高度随机,从屏幕左边出现向左移动
- 创建`PieMgr`类管理`Pie`的创建和移动
- 实现创建`Pie`的方法,让`Pie`从屏幕左边出现且高度随机,并统一管理
- 创建定时器,固定事件创建`Pie`
-`update`函数中让所有`Pie`同时向左移动
```javascript
/**
* PieMgr
*/
class PieMgr extends GameObject {
pieArr = []; // 所有的Pie
speed; // Pie移动的速度
delay; // 添加Pie的延时
constructor(speed, delay) {
super();
this.speed = speed;
this.delay = delay;
}
ready() {
super.ready();
// 创建计时器来固定事件创建Pie
setInterval(() => this.createPie(), this.delay);
}
/**
* 创建Pie
*/
createPie() {
const pie = this.addChild(new Pie());
this.pieArr.push(pie); // 加入列表统一管理
pie.top = Math.random() * -150; // 高度随机
pie.left = winSize.width; // 从屏幕左边出现
}
update() {
super.update();
// 所有的Pie同时向左移动
const { speed, pieArr } = this;
pieArr.forEach((pie) => {
pie.left -= speed;
});
}
}
```
## 3.创建`PieMgr`实例并添加到节点树上
- 创建`PieMgr`实例
- 添加到节点树上,且层级在地面和背景的中间
```javascript
class FlppyBird extends GameStage {
/* ... */
async ready() {
// 创建背景
const bg1 = new Sprite("../images/bird/background.png");
const bg2 = new Sprite("../images/bird/background.png");
const bgMgr = new ScrollMgr(bg1, bg2, 2);
// 创建地面
const land1 = new Sprite("../images/bird/land.png");
const land2 = new Sprite("../images/bird/land.png");
const landMgr = new ScrollMgr(land1, land2, 4);
const pieMgr = new PieMgr(4, 1000); // 创建PieMgr
this.addChild(bgMgr);
this.addChild(pieMgr); // 加在背景和地面的中间
this.addChild(landMgr);
/* ... */
}
/* ... */
}
```
运行案例发现,每秒创建一个`Pie`,且从屏幕右边出现,不断相左移动
![10_2.gif](../images/10_2.gif)
...@@ -41,12 +41,6 @@ class GameObject { ...@@ -41,12 +41,6 @@ class GameObject {
this.dom.style.position = "absolute"; this.dom.style.position = "absolute";
} }
/**
* 生命周期 start 所有构造函数完成,执行此函数
*/
async start() {
}
/** /**
* 生命周期 start 加入显示列表执行此函数 * 生命周期 start 加入显示列表执行此函数
*/ */
...@@ -70,11 +64,8 @@ class GameObject { ...@@ -70,11 +64,8 @@ class GameObject {
child.parent = this; child.parent = this;
// 容错:防止子类重写的start不是async函数 // 容错:防止子类重写的start不是async函数
// TODO dom无法在节点不在渲染树的上的时候拿到clientWidth等属性,故将start和ready放在这里 // TODO dom无法在节点不在渲染树的上的时候拿到clientWidth等属性,故将ready放在这里
(async () => { child.ready();
await child.start();
child.ready();
})();
return child; return child;
} }
...@@ -156,7 +147,7 @@ class GameStage extends GameObject { ...@@ -156,7 +147,7 @@ class GameStage extends GameObject {
async _gameStart() { async _gameStart() {
await this.reloadRes(); await this.reloadRes();
this.start(); this.ready();
} }
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment