
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
仿 vue angular 做的个小项目, 形似神不似。使用 webapp 那套思想来做游戏开发试一试。
phaser游戏引擎中提供的 DisplayObject 类对象作为标签类的实现, 使用 html 模板的方式封装成组件, 以组件为
基本单位来组织项目结构, 重用代码。 组件提供了 MVVM 框架类似的数据绑定, 结合事件系统和 redux 的状态管理, 可以
深度解耦各个组件之间的依赖, 便于调试,维护和做单元测试。
一个完整的组件定义如下:
@component({
template: `
<container l-bind-visible="GAME_STATE === 'ETC'">
<integer-bitmap-text font="'numbers'" x="creditPosition.x"
y="creditPosition.y" size="size"
anchor-x="0.5" anchor-y="0.5" l-bind-text="creditValue">
</integer-bitmap-text>
</container>
`,
injector: {
GAME_STATE
},
name: 'ScoreBoard'
})
export default class ScoreBoard extends layaAbstract.AbstractComponent {
@data
private betValue = 0;
@prop
private creditPosition;
@prop
private size;
@getter('scoreBoard.creditValue')
private creditValue;
// ...
$$init() {
// ...
}
$$create() {
// ...
}
@watch(['creditValue'])
creditValueHandler() {
// ...
}
}
component 注解标注该类是一个组件类, 要作为一个组件使用,该类还必须继承自 AbstractComponent 类。 注解接受一个 ComponentLike 类型的参数:
interface ComponentLike {
template: string,
injector?: any,
name: string
}
template 属性的是该组件的 HTML 模板。
injector 用于注入 组件实例上下文之外的变量, 用于在模板中直接访问。
name 组件的名称,这个必须和组件类类名相同。
模板就和 MVVM 框架中的模板一样, 只不过没用浏览器提供的那些标签了, 都是 phaser 提供的 DisplayObject 。
<container>标签对应于 phaser.Group 类(稍后会列出完整的可用标签列表),为了忘记 phaser 和替换引擎所
以换了名字。标签中的属性都是 phaser.Group 类对象的属性, 给属性赋值就是给那些类对象赋值。属性可以用字面量
赋值 font="'numbers'" anchor-x="0.5" 注意字符串字面量要加上单引号, 也可以用当前组件的响应式属性赋值
x="creditPosition.x" creditPosition 就是标记为 @getter 的响应式属性, 这个属性的值来自于 redux 保存
的 state 对象。注意虽然属性是响应式的, 但是并没有使用 l-bind 指令, 因此页面并不会动态改变, 只是在第一次
创建这个组件的时候用到 creditPosition.x 的值。而 l-bind-text="creditValue" 这样为标签的 text 属性做
了绑定后,界面就会动态响应 creditValue 值的变化。
响应式属性就是在属性值变化时, 响应式系统可以感知到的属性。这种属性由类的成员变量添加注解来指明, 一共有三中
注解:
指令也是照搬的 vue 和 angular 的概念。指令用来给标签实例添加功能。
指令的形式: l-[名称]-[参数] = "[表达式]", 参数可以没有,表达式可以是任何有效的 JavaScript 表达式, 表达式
中只能访问到组件实例的属性值和基本类型的字面量,只支持一个方法调用 bind 方法。
一共内置了 6 种指令:
<sound-task l-ref="'reelSound' + reelIndex">
<sound key="'scatsto0' + (reelIndex + 1)" l-if="list[0] === scatterKey || list[1] === scatterKey || list[2] === scatterKey"></sound>
<sound key="'spin_end' + (reelIndex + 1)" l-if="list[0] !== scatterKey && list[1] !== scatterKey && list[2] !== scatterKey"></sound>
</sound-task>
然后在组件类的方法中可以访问到该标签实例:
tweenDone(index) {
this.$$refs['reelSound' + index].play();
}
@component({
template: `
<container l-repeat-row="3" l-update="update">
<shadow l-repeat-column="[1,2,3,4,5]">
<tmage key="'symbols'" x="row" y="column + rowIndex * 5" frame="'kuang'" visible="false">
</tmage>
</shadow>
</container>
`,
name: 'WinPanel'
})
还可以自行添加指令, 指令对象的类型定义如下:
export interface Directive {
name: string;
bind: (cpt: AbstractComponent | AbstractSence, target: any, argument: string, value: (context) => any, triggers: Array<string>) => void;
unbind: () => void;
}
bind方法的参数:
cpt 作为指令表达式求值的上下文对象,也就是组件类实例对象, 表达式" foo === 'x' ? : n : m ", 最后会被解析成 function(vm){return vm.foo === 'x' ? vm.n : vm.m}。
target 指令作用的标签对象,可能为 DisplayObject SupportObject 或者是 Component 对象
argument 指令的参数
value: 指令表达式转换后的求值方法,需传入 cpt。
triggers 指令表达式中的所有变量
出于替换游戏引擎和 sdl native 方案。 laya 在 游戏引擎上层做了一层抽象, 也就是标签对应着 laya 类, laya 类使用底层游戏引擎实现功能。每个标签类实现了一个 laya 接口, 这个接口定义了标签类必须要提供的一些方法, 这样, 如果出了更好的游戏引擎, 只需再提供一套实现了接口中相应功能的标签类即可,而无需改动游戏逻辑代码。
举例来说: container 标签类的类定义如下:
class Container extends AbstractDisplayObject<Phaser.Group> implements LayaContainer { //... }
AbstractDisplayObject 泛型类提供了一些通用方法,如果销毁 DisplayObject 实例, 获取底层真实的游戏引擎对象等。 LayaContainer 接口,定义了标签要实现的功能。 可以用 Phaser.Group 实现, 或者其他的游戏引擎对象来实现,或者自己写一个也可以。
LayaContainer 当前只定义了3个抽象方法
interface LayaContainer extends AbstractDisplayObject<any> {
// 添加子元素
add(obj: AbstractDisplayObject<any>): void;
// 移除子元素
remove(obj: AbstractDisplayObject<any>, destory: boolean): boolean;
// 销毁自己
destroy(): void;
}
然后来看 Container 是如何实现的
import {LayaContainer} from '../../abstract/LayaInterface';
import {AbstractDisplayObject} from '../../abstract/AbstractDisplay';
import display from '../../decorators/Display';
import Graphics from './Graphics';
import setUp from '../chain/SetUp';
import userInterface from '../chain/UserInterface';
@display({
require: [],
optional: ['name'],
name: 'Container'
})
export default class Container extends AbstractDisplayObject<Phaser.Group> implements LayaContainer {
/** 定义于 AbstractDisplayObject 中的抽象方法,必须提供此方法的实现,laya 在解析完 xml 模板后, 就是
调用这个方法来构建真实的游戏引擎对象。
this.realObject 也是在 AbstractDisplayObject 中定义, 引用了真实的游戏引擎对象
需要注意的是,参数中的 game 是 LayaGame(Phaser.Game的抽象)。
require 是调用 Phaser.Container 构造函数必须提供的属性值, 也就是 <container> 标签必须存在的属性值。
如果没有,就会报警告。 有哪些属性在 上面的 @display 注解中指明。
optional 是调用 Phaser.Container 构造函数时,可选的参数,同样也是在 @display 中指明。
这里有一点需要注意, 如果在 @display 注解中指明了某个属性, 比如 name, 那么在调用构造函数时才会
传入这个参数和对应的值。 而如果没有在 @display 注解中指明,也就是 optional 为空数组。 那么
<container name="container1"></container> 这里的name不是在构造时传入的, 而是构造函数调用后,
再给对象的 name 存取器属性赋值。 大多数时候无论哪种方式都能正常工作, 但还是建议根据引擎对象的构造函数
明确指明。
*/
buildRealObject(game, require, optional) {
this.realObject = new Phaser.Group(game.realGame, null, optional.name);
}
add(obj: AbstractDisplayObject<any>): void {
this.realObject.add(obj.getRealObject());
}
remove(child, destroy): boolean {
return this.realObject.remove(child, destroy);
}
set Mask(value: Graphics) {
this.realObject.mask = value.getRealObject();
}
set inputEnable(value: boolean) {
this.realObject.ignoreChildInput = !value;
}
}
setUp(Container.prototype, userInterface);
container 对应 Phaser.Group 类, 每个组件必须以 <container> 作为唯一根元素,可以嵌套 <container> 标签。
属性表:
属性名 | 说明 |
---|---|
name | 标识 container,可以在 Phaser.World 中 |
FAQs
build phaser project with template, data bind, and webpack hmr
The npm package laya receives a total of 1 weekly downloads. As such, laya popularity was classified as not popular.
We found that laya demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.