现在的前端框架如 Vue 和 React 都推崇组合式API,用起来非常爽,我最近接触了 cocos 后对这种非组合式的写法不禁有些抵触,便借鉴了 Vue 的思路简单实现了类似的效果,开发组各位大佬对这种写法怎么看?
下面是我改造后的一步两步代码
hooks.js
提供组合式的能力
let curUpdateArr = null;
function myClass(config) {
const { setup } = config;
const newConfig = Object.assign({}, config)
const updateArr = [];
newConfig.update = () => {
updateArr.forEach(item => item())
}
newConfig.onLoad = function () {
curUpdateArr = updateArr
let data = setup(this)
curUpdateArr = null
for (let key in data) {
if (typeof data[key] === 'function') {
this[key] = data[key]
} else {
Object.defineProperty(this, key, {
get() {
return data[key].value
},
set(value) {
data[key].value = value
}
})
}
}
}
return cc.Class(newConfig)
}
function useState(value) {
return { value }
}
function update(func) {
curUpdateArr.push(func)
}
module.exports = { myClass, useState, update };
useTimer.js
封装了一个计时器
const { useState, update } = require("hooks")
function useTimer(initVal = 0) {
let timer = useState(initVal)
update(dt => {
timer.value += dt;
})
return {
timer
}
}
module.exports = useTimer
Game.js
用组合式API重写了代码,和 Vue 很像
const { myClass, useState, update } = require("hooks")
const useTimer = require("useTimer")
module.exports = myClass({
extends: cc.Component,
properties: {
// 地面节点,用于确定星星生成的高度
ground: {
default: null,
type: cc.Node
},
// Player 节点,用于获取主角弹跳的高度,和控制主角行动开关
player: {
default: null,
type: cc.Node
},
// 这个属性引用了星星预制资源
starPrefab: {
default: null,
type: cc.Prefab
},
// 星星产生后消失时间的随机范围
maxStarDuration: 0,
minStarDuration: 0,
// score label 的引用
scoreDisplay: {
default: null,
type: cc.Label
}
},
setup(props) {
// 初始化计时器
let { timer } = useTimer()
let starDuration = useState(0);
function gameOver() {
// 停止 Player 节点的跳跃动作
props.player.stopAllActions();
// 重新加载场景 game
cc.director.loadScene('game');
}
update(() => {
// 每帧更新计时器,超过限度还没有生成新的星星
// 就会调用游戏失败逻辑
if (timer.value > starDuration.value) {
gameOver();
}
})
// 获取地平面的 y 轴坐标
const groundY = props.ground.y + props.ground.height / 2
function getNewStarPosition() {
let randX = 0;
// 根据地平面位置和主角跳跃高度,随机得到一个星星的 y 坐标
let randY = groundY + Math.random() * props.player.getComponent('Player').jumpHeight + 50;
// 根据屏幕宽度,随机得到一个星星 x 坐标
let maxX = props.node.width / 2;
randX = (Math.random() - 0.5) * 2 * maxX;
// 返回星星坐标
return cc.v2(randX, randY);
}
function spawnNewStar() {
// 使用给定的模板在场景中生成一个新节点
let newStar = cc.instantiate(props.starPrefab);
// 将新增的节点添加到 Canvas 节点下面
props.node.addChild(newStar);
// 为星星设置一个随机位置
newStar.setPosition(getNewStarPosition());
// 在星星脚本组件上保存 Game 对象的引用
newStar.getComponent('Star').game = props;
// 重置计时器,根据消失时间范围随机取一个值
starDuration.value = props.minStarDuration + Math.random() * (props.maxStarDuration - props.minStarDuration);
timer.value = 0;
}
// 生成一个新的星星
spawnNewStar();
let score = useState(0);
function gainScore() {
score.value += 1;
// 更新 scoreDisplay Label 的文字
props.scoreDisplay.string = 'Score: ' + score.value;
}
return {
timer,
starDuration,
spawnNewStar,
gainScore
}
},
})