cocos小游戏实战-02-事件中心

事件中心(发布订阅模式)

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

https://web03-1252477692.cos.ap-guangzhou.myqcloud.com/blog/images/QQ%E6%88%AA%E5%9B%BE20220707201817.png

程序化编辑动画剪辑

文档地址:

程序化编辑动画剪辑

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()
  }

人物移动

https://web03-1252477692.cos.ap-guangzhou.myqcloud.com/blog/images/QQ%E6%88%AA%E5%9B%BE20220708103518.png

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加上对应的事件类型枚举

https://web03-1252477692.cos.ap-guangzhou.myqcloud.com/blog/images/QQ%E6%88%AA%E5%9B%BE20220709131204.png

本节源码地址:

https://gitee.com/yuan30/cramped-room-of-death/tree/day2/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值