123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- import { _decorator, Component, Node, Button, Animation, Sprite, SpriteFrame, resources, Label, Layout, instantiate, tween, Vec3, UIOpacity } from 'cc';
- import { TodayListItem } from './DataManager';
- const { ccclass, property } = _decorator;
- /**
- * 名单管理器,控制今日名单UI的显示和隐藏,显示今日允许进入的人员名单
- */
- @ccclass('RosterManager')
- export class RosterManager extends Component {
- @property({
- type: Node,
- tooltip: '名单UI面板'
- })
- rosterPanel: Node = null;
-
- @property({
- type: Button,
- tooltip: '关闭按钮'
- })
- closeButton: Button = null;
-
- @property({
- type: Node,
- tooltip: '人员头像容器'
- })
- avatarContainer: Node = null;
-
- @property({
- tooltip: '动画时间(秒)',
- range: [0.1, 2.0, 0.1]
- })
- animDuration: number = 0.5;
-
- @property({
- tooltip: '是否使用自定义动画',
- type: Boolean
- })
- useCustomAnimation: boolean = true;
-
- // 用于动画控制的组件
- private rosterOpacity: UIOpacity = null;
- // 头像项预制体
- @property({
- type: Node,
- tooltip: '头像项预制体'
- })
- avatarItemPrefab: Node = null;
-
- // 当前显示的名单数据
- private currentRosterItems: TodayListItem[] = [];
-
- start() {
- // 初始化隐藏名单面板
- if (this.rosterPanel) {
- this.rosterPanel.active = false;
-
- // 确保面板有UIOpacity组件
- this.rosterOpacity = this.rosterPanel.getComponent(UIOpacity);
- if (!this.rosterOpacity) {
- this.rosterOpacity = this.rosterPanel.addComponent(UIOpacity);
- }
- }
-
- // 注册关闭按钮点击事件
- this.setupCloseButton();
- }
-
- /**
- * 设置关闭按钮事件
- */
- private setupCloseButton() {
- if (this.closeButton) {
- // 移除可能已存在的事件监听
- this.closeButton.node.off('click');
-
- // 使用按钮的点击事件监听方式
- this.closeButton.node.on('click', () => {
- console.log('名单关闭按钮被点击');
- this.hideRosterPanel();
- }, this);
-
- console.log('名单关闭按钮事件已注册');
- } else {
- console.error('名单关闭按钮未设置');
- }
- }
-
- /**
- * 显示名单面板
- */
- public showRosterPanel() {
- if (this.rosterPanel) {
- this.rosterPanel.active = true;
-
- // 确保面板在最前面
- this.rosterPanel.setSiblingIndex(999);
-
- // 判断使用哪种动画方式
- if (this.useCustomAnimation) {
- // 使用自定义的口袋动画效果
- this.playPocketOutAnimation();
- }
- } else {
- console.error('名单面板未设置');
- }
- }
-
- /**
- * 播放从口袋掏出的动画
- */
- private playPocketOutAnimation() {
- // 设置初始状态 - 名单从左侧口袋掏出
- this.rosterPanel.setScale(new Vec3(0.4, 0.2, 1)); // 扁平状态
- this.rosterPanel.setPosition(new Vec3(-150, -250, 0)); // 从左下角(口袋位置)开始
- this.rosterPanel.setRotationFromEuler(new Vec3(0, 0, 25)); // 初始倾斜角度,向左倾斜
-
- if (this.rosterOpacity) {
- this.rosterOpacity.opacity = 50; // 半透明开始
- }
-
- // 创建动画 - 掏出口袋的感觉
- tween(this.rosterPanel)
- // 先抽出动作,有种抽纸张的感觉
- .to(this.animDuration * 0.5, {
- scale: new Vec3(0.7, 0.8, 1),
- position: new Vec3(-100, -100, 0),
- eulerAngles: new Vec3(0, 0, 10) // 减少倾斜
- }, {
- easing: 'cubicOut'
- })
- // 然后放到正确位置
- .to(this.animDuration * 0.4, {
- scale: new Vec3(1, 1, 1),
- position: new Vec3(0, 0, 0),
- eulerAngles: new Vec3(0, 0, 0)
- }, {
- easing: 'backOut'
- })
- .start();
-
- // 透明度动画
- if (this.rosterOpacity) {
- tween(this.rosterOpacity)
- .to(this.animDuration * 0.7, { opacity: 255 })
- .start();
- }
- }
-
- /**
- * 隐藏名单面板
- */
- public hideRosterPanel() {
- console.log('hideRosterPanel被调用');
-
- if (this.rosterPanel) {
- // 判断使用哪种动画方式
- if (this.useCustomAnimation) {
- // 使用自定义的口袋动画效果
- this.playPocketInAnimation(() => {
- this.rosterPanel.active = false;
- console.log('名单面板已隐藏(动画后)');
- });
- } else {
- this.rosterPanel.active = false;
- console.log('名单面板已隐藏(立即)');
- }
- }
- }
-
- /**
- * 播放放回口袋的动画
- */
- private playPocketInAnimation(callback?: Function) {
- // 创建放回口袋的动画
- tween(this.rosterPanel)
- // 先抬起并旋转准备放回
- .to(this.animDuration * 0.3, {
- position: new Vec3(-50, -50, 0),
- eulerAngles: new Vec3(0, 0, 15),
- scale: new Vec3(0.9, 0.9, 1)
- }, {
- easing: 'sineIn'
- })
- // 然后塞入口袋
- .to(this.animDuration * 0.4, {
- position: new Vec3(-150, -250, 0),
- eulerAngles: new Vec3(0, 0, 25),
- scale: new Vec3(0.4, 0.2, 1) // 扁平化,像塞入口袋
- }, {
- easing: 'quadIn'
- })
- .call(() => {
- if (callback) callback();
- })
- .start();
-
- // 透明度动画
- if (this.rosterOpacity) {
- tween(this.rosterOpacity)
- .to(this.animDuration * 0.6, { opacity: 0 })
- .start();
- }
- }
-
- /**
- * 设置并显示今日名单
- * @param todayListItems 由GameFlowManager提供的今日名单数据,必须符合TodayListItem接口
- */
- public setTodayList(todayListItems: TodayListItem[]): void {
- console.log("设置今日名单数据");
-
- if (!todayListItems || todayListItems.length === 0) {
- console.warn('今日名单数据为空');
- return;
- }
-
- // 保存当前名单数据
- this.currentRosterItems = todayListItems;
-
- // 显示名单数据
- this.displayCurrentRoster();
- }
-
- /**
- * 显示当前保存的名单数据
- */
- private displayCurrentRoster(): void {
- if (!this.currentRosterItems || this.currentRosterItems.length === 0) {
- console.warn('当前没有名单数据可显示');
- return;
- }
-
- this.displayListItems(this.currentRosterItems);
- }
-
- /**
- * 显示名单项目列表
- * @param listItems 名单项目列表,必须包含name和avatarPath属性
- */
- private displayListItems(listItems: TodayListItem[]): void {
- console.log("开始显示名单项目列表...");
-
- if (!this.avatarContainer) {
- console.error('无法显示名单:avatarContainer未设置');
- return;
- }
-
- if (!listItems || listItems.length === 0) {
- console.error('无法显示名单:名单数据为空');
- return;
- }
-
- // 检查预制体
- if (!this.avatarItemPrefab) {
- console.error('头像项预制体未设置');
- return;
- }
-
- console.log(`准备显示${listItems.length}个名单项目`);
-
- // 清空现有内容
- this.avatarContainer.removeAllChildren();
-
- // 遍历所有人员数据,创建头像项
- listItems.forEach(item => {
- // 创建新的头像项
- const avatarItem = instantiate(this.avatarItemPrefab);
- this.avatarContainer.addChild(avatarItem);
-
- // 设置姓名
- const nameLabel = avatarItem.getComponentInChildren(Label);
- if (nameLabel && item.name) {
- nameLabel.string = item.name;
- }
-
- // 加载头像
- if (item.avatarPath) {
- // 移除可能存在的.png后缀
- const pathWithoutExtension = item.avatarPath.replace(/\.png$/, '');
-
- // 确保路径以/spriteFrame结尾
- const path = pathWithoutExtension.endsWith('/spriteFrame')
- ? pathWithoutExtension
- : `${pathWithoutExtension}/spriteFrame`;
-
- resources.load(path, SpriteFrame, (err, spriteFrame) => {
- if (err) {
- console.error(`加载头像失败: ${path}`, err);
- return;
- }
-
- // 查找头像显示组件
- const sprite = avatarItem.getComponentInChildren(Sprite);
- if (sprite) {
- sprite.spriteFrame = spriteFrame;
- }
- });
- }
- });
-
- // 更新布局
- const layout = this.avatarContainer.getComponent(Layout);
- if (layout) {
- this.scheduleOnce(() => {
- layout.updateLayout();
- }, 0.1);
- }
- }
-
- onDestroy() {
- // 移除按钮事件监听
- if (this.closeButton) {
- this.closeButton.node.off('click');
- }
- }
- }
|