Worktile 技术分享:RxJS实战练习-经典游戏Breakout

栏目: 编程语言 · 发布时间: 5年前

内容简介:1.ticker$ 数据流 interval配合scheduler/animationFrame 作为游戏随时间变化的控制数据流2.key$ 数据流检测keydown/keyup 玩家控制的部分(整个状态中的一个副作用),改变底部船桨的位置

效果图

Worktile 技术分享:RxJS实战练习-经典游戏Breakout

数据流分析

1.ticker$ 数据流 interval配合scheduler/animationFrame 作为游戏随时间变化的控制数据流

ticker$ = interval(this.TICKER_INTERVAL, animationFrame).pipe( map(() => ({ time: Date.now(), deltaTime: null })), scan((previous, current) => ({ time: current.time, deltaTime: (current.time - previous.time) / 1000 })) ); // Observable单播 每次订阅都是启动一个数据流

2.key$ 数据流检测keydown/keyup 玩家控制的部分(整个状态中的一个副作用),改变底部船桨的位置

PADDLE_CONTROLS = { ArrowLeft: -1, ArrowRight: 1 }; key$ = merge( fromEvent(document, 'keydown').pipe( map(event => this.PADDLE_CONTROLS[event['key']] || 0) ), fromEvent(document, 'keyup').pipe(map(event => 0)) ).pipe(distinctUntilChanged()); // 提供船桨移动的方位的数据源

实现逻辑:按下‘<’直到 keyup 输出 -1 / 按下‘>’直到 keyup 输出 1 / keyup 输出 0 3.paddle$ 数据流使用操作符withLatestFrom合并了ticker$和key$ 持续流出船桨的位置

createPaddle$(ticker$: Observable<{ time: number; deltaTime: any }>) { return ticker$.pipe( withLatestFrom(this.key$), // withLatestFrom操作符 作为游戏开始的触发条件,只有这个数据流产生数据才会往下游流动 scan<[{ deltaTime: number; time: number }, number], number>( (position: number, [ticker, direction]) => { const nextPosition = position + direction * ticker.deltaTime * this.PADDLE_SPEED; return Math.max( Math.min( nextPosition, this.breakoutCanvasService.stage.width - config.PADDLE_WIDTH / 2 ), config.PADDLE_WIDTH / 2 ); }, this.breakoutCanvasService.stage.width / 2 ), distinctUntilChanged() ); }

3.createState$ 数据流使用withLatestFrom合并ticker$和paddle$ 最终输出界面需要的全部状态数据

createState$(ticker$, paddle$) {
return ticker$.pipe(
withLatestFrom(paddle$),
scan<
[{ deltaTime: number; time: number }, number], { ball: Ball; bricks: Brick[]; score: number }
>(({ ball, bricks, score }, [ticker, paddle]) => { const remainingBricks = [];
const collisions = { paddle: false, // 球撞船桨 floor: false, // wall: false, // 撞墙 ceiling: false, // 撞顶 brick: false // 球撞砖块 };
ball.position.x = ball.position.x + ball.direction.x * ticker.deltaTime * this.BALL_SPEED; ball.position.y = ball.position.y + ball.direction.y * ticker.deltaTime * this.BALL_SPEED;
bricks.forEach(brick => { if (!this.isCollision(brick, ball)) { remainingBricks.push(brick); } else { collisions.brick = true; score = score + 10; } });
collisions.paddle = this.isHit(paddle, ball);
if (
ball.position.x < config.BALL_RADIUS || ball.position.x >
this.breakoutCanvasService.stage.width - config.BALL_RADIUS
) {
ball.direction.x = -ball.direction.x; collisions.wall = true;
}

collisions.ceiling = ball.position.y < config.BALL_RADIUS;
if (collisions.brick || collisions.paddle || collisions.ceiling) {
if (collisions.paddle) {
ball.direction.y = -Math.abs(ball.direction.y);
} else {
ball.direction.y = -ball.direction.y; }
}

return { ball: ball, bricks: remainingBricks, collisions: collisions, score: score }; }, this.initState()) ); }

  • 用到ticker$流控制球的移动位置
  • 根据当前状态控制下一步的状态,包括计分、球的运动方向、砖块数量

4.game$ 数据流最终的游戏控状态输出流(包括这状态数据、船桨位置数据)

game$ = Observable.create(observer => { this.breakoutCanvasService.drawIntro(); this.restart = new Subject(); const paddle$ = this.createPaddle$(this.ticker$); // 数据源吐出船桨的位置 const state$ = this.createState$(this.ticker$, paddle$); this.ticker$ .pipe( withLatestFrom(paddle$, state$), OperatorMerge(this.restart) ) .subscribe(observer); // 这个this.ticker$ 也可以不使用,直接通过merge合并后面两个数据流 });

merge数据流restart$后 可以通过error方法终止流从而控制游戏结束

状态

两个结果状态:砖块数量、分数

两个影响状态的副作用:时间、游戏者的行为

状态交叉点

球接触砖块 -> 砖块消失

球接触船桨/墙 -> 球自然改变运动方向

整个过程用rxjs实现不需要额外保存中间数据,在管道中实现数据的缓存、状态处理 。

两个字形容 “优秀”

演示地址: http://tiny.pubuzhixing.com/

github: https://github.com/pubuzhixing8/tiny-game

出处:《深入浅出RxJS》十四章实例,使用TS+Angular重新包装,修改了一个小缺陷,据说这个游戏最初是由乔布斯和他的一个朋友设计


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

图论导引

图论导引

[美] Douglas B.West / 机械工业出版社 / 2004-10 / 59.00元

图论在计算科学、社会科学和自然科学等各个领域都有广泛应用。本书是本科生或研究生一学期或两学期的图论课程教材。全书力求保持按证明的难度和算法的复杂性循序渐进的风格,使学生能够深入理解书中的内容。书中包括对证明技巧的讨论、1200多道习题、400多幅插图以及许多例题,而且对所有定理都给出了详细完整的证明。虽然本书包括许多算法和应用,但是重点在于理解图论结构和分析图论问题的技巧。一起来看看 《图论导引》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试