任务:
我正在将现有的HTML5项目(基于Phaser的科学软件)集成到Angular中,以更好地构造不断扩展的UI。
该软件位于其自己的组件中,并且正在运行。该软件的所有功能都通过 controller 类公开。我在Phaser项目状态下有一个控制器类的实例。
想象一个新的组件(例如,顶部菜单),我想说:
<div (click)="this.controller.makeAction()"></div>
,其中“ this.controller”是一个实例,保存在顶部菜单组件中。
相关研究:
我在很多地方都读到,在不相关的组件之间进行通信的推荐方法是通过服务并使用rxjs BehaviorSubject。好的...问题来了:
问题:
创建服务时,必须设置BehaviorSubject的实例。问题是我不知道什么时候游戏才能准备就绪,以便进入状态,从而为服务提供控制器。所以.. BehaviorSubject保持为空,并且出现错误。
我真的想避免将Angular代码放在Phaser项目中,因为应该将它们尽可能地分离(当前没有任何耦合)。
问题:
我的方法正确吗?控制器将被分配给任何UI元素,从而被分配给任何组件。如何解决此问题?
相关代码:
1。服务
// Omitting imports and decorators
export class UserActionControllerService {
private _userActionController = new BehaviorSubject<UserActionController>(null);
userActionController = this._userActionController.asObservable();
constructor() { }
setUAC(userActionController: UserActionController){
this._userActionController.next(userActionController);
}
}
2。在顶部菜单中使用服务
//Omitting imports and decorator
export class TopMenuComponent implements OnInit {
userActionController: UserActionController;
constructor(private uac: UserActionControllerService) { }
ngOnInit() {
this.uac.userActionController.subscribe((value) => {
this.userActionController = value;
});
}
和html ...
<p>{{this.userActionController | async | json}}</p>
3。在服务中设置控制器的值
//Ommitting imports and decorator
export class GteCoreComponent implements OnInit {
game: Phaser.Game;
constructor(private userActionControllerService: UserActionControllerService) {}
ngOnInit() {
this.game = new GTE(width, height);
this.userActionControllerService.setUAC(this.game.state.states.MainScene.userActionController);
}
}
最后一行会产生错误,因为尚未创建游戏。我尝试使用setTimeout()
,但仍无济于事。
谢谢您的帮助!
编辑:
我设法使其与setTimeout一起使用,这似乎是一种hack。还有其他建议吗?
EDIT2:
根据请求,这是GTE类:
export class GTE extends Phaser.Game {
game: Phaser.Game;
constructor(width?: number, height?: number) {
super(width, height, Phaser.CANVAS, 'phaser-div', null, false, true);
this.game = this;
this.game.state.add('Boot', Boot, false);
this.game.state.add('MainScene', MainScene, false);
this.game.state.start('Boot');
}
}
答案 0 :(得分:0)
正如您所指出的,您已经使用setTimeout
解决了问题,这表明时间问题是导致问题的原因。
setTimeout
做两件事:
首先,它将回调的处理延迟指定的毫秒数。这可能会出现问题,好像用户的浏览器慢一些/忙一些,可能要花费超过指定时间才能使您等待的事物准备就绪。最好是从事件中触发此类活动,而不是等待一段时间-如果您等待足够长的时间以确保它准备就绪,那么对于大多数用户而言,等待时间将比必要时间长得多。
setTimeout
要做的第二件事是将回调的执行推送到下一个JS执行帧。在某些情况下可能需要这样做,因为它允许其他排队的工作在继续之前完成。即使您将超时设置为0ms,这也将起作用-并且如果这可以解决问题,那么在这种情况下,setTimeout
是可以安全使用的。
尽管如此,即使您处在第二种情况下,取消某个事件也会更加简洁-某人或您未来的自我出现并在以后取消操作的可能性较小!
看看Phaser.Game documentation,有一个isBooted标志,因此,如果没有暴露事件,则可以在GTE类中创建一个事件:
export class GTE extends Phaser.Game {
game: Phaser.Game;
private readyCallback: () => null;
constructor(width?: number, height?: number, ready: () => null) {
super(width, height, Phaser.CANVAS, 'phaser-div', null, false, true);
this.game = this;
this.game.state.add('Boot', Boot, false);
this.game.state.add('MainScene', MainScene, false);
this.game.state.start('Boot');
this.readyCallback = ready;
setTimeout(() => this.checkReady(), 100);
}
checkReady() {
if (this.game.isBooted) this.readyCallback();
else setTimeout(() => this.checkReady(), 100);
}
}