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重新包装,修改了一个小缺陷,据说这个游戏最初是由乔布斯和他的一个朋友设计


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

查看所有标签

猜你喜欢:

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

Python for Everyone

Python for Everyone

Cay S. Horstmann、Rance D. Necaise / John Wiley & Sons / 2013-4-26 / GBP 181.99

Cay Horstmann's" Python for Everyone "provides readers with step-by-step guidance, a feature that is immensely helpful for building confidence and providing an outline for the task at hand. "Problem S......一起来看看 《Python for Everyone》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具