事件中心(发布订阅模式)
Runtime/EventManager.ts
发布和订阅模式,发布事件
import Singleton from '../Base/Singleton'
interface IItem {
ctx: unknown // this
func: Function
}
/**
* 单例模式
* 按钮事件
*/
export default class EventManager extends Singleton {
static get Instance() {
return super.GetInstance<EventManager>()
}
// 使用key->Array<func>的结构存储事件
private eventDic: Map<string, Array<IItem>> = new Map()
// 添加绑定事件
on(eventName: string, func: Function, ctx?: unknown) {
if (this.eventDic.has(eventName)) {
this.eventDic.get(eventName).push({ func, ctx })
} else {
this.eventDic.set(eventName, [{ func, ctx }])
}
}
// 解绑事件
off(eventName: string, func: Function) {
if (this.eventDic.has(eventName)) {
const index = this.eventDic.get(eventName).findIndex(i => i.func === func)
index > -1 && this.eventDic.get(eventName).slice(index, 1)
}
}
// 调用事件
emit(eventName: string, ...params: unknown[]) {
console.log('this.eventDic.has(eventName)', eventName)
if (this.eventDic.has(eventName)) {
this.eventDic.get(eventName).forEach((i: IItem) => {
if (i.ctx) {
i.func.apply(i.ctx, params)
} else {
i.func(...params)
}
})
}
}
// 清除事件
clear() {
this.eventDic.clear()
}
}
export const DataManagerInstance = new EventManager()
切换地图
Scripts/Scene/BattleManager.ts
事件中心的使用-关键代码
onLoad() {
// 绑定事件-关卡切换
EventManager.Instance.on(EVENT_ENUM.NEXT_LEVEL, this.nextLevel, this)
}
// 关卡切换-下一关
nextLevel() {
DataManager.Instance.levelIndex++
this.initLevel()
}
// 关卡清空
clearLevel() {
this.stage.destroyAllChildren()
DataManager.Instance.reset()
}
onDestroy() {
// 解绑事件
EventManager.Instance.off(EVENT_ENUM.NEXT_LEVEL, this.nextLevel)
}
控制台添加动画
新建Sprite—>添加Animation组件→点击动画编辑器→拖入每一帧图片,设置帧率,循环播放
组件勾选PlayOnLoad
程序化编辑动画剪辑
文档地址:
Scripts/Player/PlayerManager.ts
import {
_decorator,
Component,
Node,
Sprite,
UITransform,
Animation,
AnimationClip,
animation,
Vec3,
SpriteFrame,
} from 'cc'
import { TILE_HEIGHT, TILE_WIDTH } from '../Tile/TileManager'
import ResourceManager from '../../Runtime/ResourceManager'
const { ccclass, property } = _decorator
const ANIMATION_SPEED = 1 / 8 // 1秒8帧
@ccclass('PlayerManager')
export class PlayerManager extends Component {
async init() {
const sprite = this.addComponent(Sprite)
sprite.sizeMode = Sprite.SizeMode.CUSTOM
const transform = this.getComponent(UITransform)
transform.setContentSize(TILE_WIDTH * 4, TILE_HEIGHT * 4)
// 加载资源文件夹
const spriteFrames = await ResourceManager.Instance.loadDir('texture/player/idle/top')
// 添加动画
const animationComponent = this.addComponent(Animation)
const animationClip = new AnimationClip()
// 创建一个对象轨道
const track = new animation.ObjectTrack()
// 添加轨道路径为Sprite组件
track.path = new animation.TrackPath().toComponent(Sprite).toProperty('spriteFrame')
const frames: Array<[number, SpriteFrame]> = spriteFrames.map((item, index) => [index * ANIMATION_SPEED, item])
// 设置一条通道channel的关键帧
track.channel.curve.assignSorted(
frames,
/* [
// 为 x 通道的曲线添加关键帧
[0.4, { value: 0.4 }], //[时间,属性]
[0.6, { value: 0.6 }],
[0.8, { value: 0.8 }],
]*/
)
// 最后将轨道添加到动画剪辑以应用
animationClip.addTrack(track)
// 整个动画剪辑的周期 帧数*帧率
animationClip.duration = frames.length * ANIMATION_SPEED
// 循环播放
animationClip.wrapMode = AnimationClip.WrapMode.Loop
// 设置动画,defaultClip,并且播放
animationComponent.defaultClip = animationClip
animationComponent.play()
}
}
Scripts/Scene/BattleManager.ts
start() {
this.generatePlayer()
}
// 创建人物
generatePlayer() {
const player = createUINode()
player.setParent(this.stage)
const playerManager = player.addComponent(PlayerManager)
playerManager.init()
}
人物移动
Enum/index.ts
/**
* 人物移动方向枚举
*/
export enum CONTROLLER_ENUM {
TOP = 'TOP',
LEFT = 'LEFT',
RIGHT = 'RIGHT',
BOTTOM = 'BOTTOM',
TURN_LEFT = 'TURN_LEFT',
TURN_RIGHT = 'TURN_RIGHT',
}
Scripts/Player/PlayerManager.ts
import {
_decorator,
Component,
Node,
Sprite,
UITransform,
Animation,
AnimationClip,
animation,
Vec3,
SpriteFrame,
} from 'cc'
import { TILE_HEIGHT, TILE_WIDTH } from '../Tile/TileManager'
import ResourceManager from '../../Runtime/ResourceManager'
import { CONTROLLER_ENUM, EVENT_ENUM } from '../../Enum'
import EventManager from '../../Runtime/EventManager'
const { ccclass, property } = _decorator
const ANIMATION_SPEED = 1 / 8 // 1秒8帧
@ccclass('PlayerManager')
export class PlayerManager extends Component {
// 坐标
x: number = 0
y: number = 0
// 目标坐标
targetX: number = 0
targetY: number = 0
// 速度
private readonly sped = 1 / 10
init() {
this.render()
EventManager.Instance.on(EVENT_ENUM.PLAY_CTRL, this.move, this)
}
update() {
// 更新人物移动坐标数据
this.updateXY()
// 更新移动位置,由于人物占4个格子居中需要偏移
this.node.setPosition(this.x * TILE_WIDTH - TILE_WIDTH * 1.5, -this.y * TILE_HEIGHT + TILE_HEIGHT * 1.5)
}
// 更新xy,让xy无限趋近于targetX targetY(位移过渡)
updateXY() {
if (this.targetX < this.x) {
this.x -= this.sped
} else if (this.targetX > this.x) {
this.x += this.sped
}
if (this.targetY < this.y) {
this.y -= this.sped
} else if (this.targetY > this.y) {
this.y += this.sped
}
if (Math.abs(this.targetY - this.y) <= 0.1 && Math.abs(this.targetX - this.x) <= 0.1) {
this.x = this.targetX
this.y = this.targetY
}
}
// 人物移动
move(inputDirection: CONTROLLER_ENUM) {
switch (inputDirection) {
case CONTROLLER_ENUM.BOTTOM:
this.targetY += 1
break
case CONTROLLER_ENUM.LEFT:
this.targetX -= 1
break
case CONTROLLER_ENUM.TOP:
this.targetY -= 1
break
case CONTROLLER_ENUM.RIGHT:
this.targetX += 1
break
case CONTROLLER_ENUM.TURN_LEFT:
break
case CONTROLLER_ENUM.TURN_RIGHT:
break
}
}
}
需要给上下左右按钮指定事件并在CustomEventData加上对应的事件类型枚举
本节源码地址: