From fa4dbec3a6b14619063abfcefe1b855a3bf8e955 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Mon, 31 Aug 2026 20:09:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E5=93=8D=E5=BA=94=E5=BC=8F?= =?UTF-8?q?=E8=A7=86=E5=9B=BE=E7=BB=84=E4=BB=B6=20PlayerInfoView=20+=20pre?= =?UTF-8?q?fab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit views/player-info-view.ts: - 继承 cc.Component,持有 StoreBinder + session 引用 - bindTo(session) 订阅 PlayerStore.state,自动刷新 nickname/bean/avatar - onDestroy 退订 - 替代旧 setHallRoomCard/setBean 等手工回调 prefab: 3 子节点(NicknameLabel/BeanLabel/AvatarSprite) + PlayerInfoView 脚本, 脚本组件 UUID = 76ec1wXb3tPT7yT3odu/daO (MCP add_component 真实挂载). 验证日志已确认: - script hasScript: true (Cocos 识别) - 3 个节点引用写入 actualValue uuid 均非空 Co-Authored-By: Claude Opus 5 (1M context) --- .../assets/framework/ui/prefabs/views.meta | 9 + .../ui/prefabs/views/PlayerInfoView.prefab | 524 ++++++++++++++++++ .../framework/ui/views/player-info-view.ts | 60 ++ 3 files changed, 593 insertions(+) create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/views.meta create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/views/PlayerInfoView.prefab create mode 100644 cocoscreator_projects/YouleNexus/assets/framework/ui/views/player-info-view.ts diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/views.meta b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/views.meta new file mode 100644 index 0000000..96095d9 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/views.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "23cd62ce-d6ca-4e9a-bfc6-0e01817ec839", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/views/PlayerInfoView.prefab b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/views/PlayerInfoView.prefab new file mode 100644 index 0000000..b403255 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/prefabs/views/PlayerInfoView.prefab @@ -0,0 +1,524 @@ +[ + { + "__type__": "cc.Prefab", + "_name": "PlayerInfoView", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "data": { + "__id__": 1 + }, + "optimizationPolicy": 0, + "persistent": false + }, + { + "__type__": "cc.Node", + "_name": "PlayerInfoView", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [ + { + "__id__": 2 + }, + { + "__id__": 3 + }, + { + "__id__": 4 + } + ], + "_active": true, + "_components": [ + { + "__id__": 20 + }, + { + "__id__": 22 + } + ], + "_prefab": { + "__id__": 24 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 100, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "NicknameLabel", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 5 + }, + { + "__id__": 7 + } + ], + "_prefab": { + "__id__": 9 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 50, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "BeanLabel", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 10 + }, + { + "__id__": 12 + } + ], + "_prefab": { + "__id__": 14 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "AvatarSprite", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 15 + }, + { + "__id__": 17 + } + ], + "_prefab": { + "__id__": 19 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": -200, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": { + "__id__": 6 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 197.83203125, + "height": 50.4 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "ImgweF6zNBroUJU3twdzFF" + }, + { + "__type__": "cc.Label", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": { + "__id__": 8 + }, + "_string": "(nickname)", + "_horizontalAlign": 1, + "_verticalAlign": 1, + "_actualFontSize": 40, + "_fontSize": 40, + "_fontFamily": "Arial", + "_lineHeight": 40, + "_overflow": 0, + "_enableWrapText": true, + "_font": null, + "_isSystemFontUsed": true, + "_spacingX": 0, + "_isItalic": false, + "_isBold": false, + "_isUnderline": false, + "_underlineHeight": 2, + "_cacheMode": 0, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "OeS0TOr07FF4sJCedO1GjM" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "6dD5dzGQtD2KKmcYVOCSHE", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 3 + }, + "_enabled": true, + "__prefab": { + "__id__": 11 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 115.625, + "height": 50.4 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "AsGm4Pl7VuYM73ZkqbTW/V" + }, + { + "__type__": "cc.Label", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 3 + }, + "_enabled": true, + "__prefab": { + "__id__": 13 + }, + "_string": "(bean)", + "_horizontalAlign": 1, + "_verticalAlign": 1, + "_actualFontSize": 40, + "_fontSize": 40, + "_fontFamily": "Arial", + "_lineHeight": 40, + "_overflow": 0, + "_enableWrapText": true, + "_font": null, + "_isSystemFontUsed": true, + "_spacingX": 0, + "_isItalic": false, + "_isBold": false, + "_isUnderline": false, + "_underlineHeight": 2, + "_cacheMode": 0, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "iPmQSgoSDPg/d3VUwqKHNb" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "e3E/tTHLlHe5g1dnkykTgU", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 4 + }, + "_enabled": true, + "__prefab": { + "__id__": 16 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 100, + "height": 100 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "FlqhcA+fEPhWyVAYeBDH7M" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 4 + }, + "_enabled": true, + "__prefab": { + "__id__": 18 + }, + "_spriteFrame": { + "__uuid__": "57520716-48c8-4a19-8acf-41c9f8777fb0@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_customMaterial": null, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_type": 1, + "_fillType": 0, + "_sizeMode": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_useGrayscale": false, + "_atlas": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "Wx7bX1R+5+TKlO29/02mYn" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "dcOu3u+aRG7o2OgFH0A9Es", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 21 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 500, + "height": 200 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "qv5d/NrAvarCxNFrSx0ZjC" + }, + { + "__type__": "PlayerInfoView", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 23 + }, + "nicknameLabel": { + "uuid": "6dD5dzGQtD2KKmcYVOCSHE" + }, + "beanLabel": { + "uuid": "e3E/tTHLlHe5g1dnkykTgU" + }, + "avatarSprite": { + "uuid": "dcOu3u+aRG7o2OgFH0A9Es" + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "j+hiHQ/N7ow/yALOUdKhii" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "4bmv14yadKDbO4NOJjy3DL", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + } +] \ No newline at end of file diff --git a/cocoscreator_projects/YouleNexus/assets/framework/ui/views/player-info-view.ts b/cocoscreator_projects/YouleNexus/assets/framework/ui/views/player-info-view.ts new file mode 100644 index 0000000..c1479d2 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/ui/views/player-info-view.ts @@ -0,0 +1,60 @@ +import { _decorator, Component, Label, Sprite } from 'cc'; +import { createStoreBinder, type StoreBinder } from '../store-binder.ts'; +import { PlatformSession } from '../../platform/session.ts'; + +const { ccclass, property } = _decorator; + +/** + * PlayerInfoView:响应式视图组件基类示范。 + * + * 业务侧使用: + * 1. 在 prefab 上挂 PlayerInfoView + * 2. Inspector 拖入 nicknameLabel / beanLabel / avatarSprite 三个节点 + * 3. 场景初始化时调用 PlayerInfoView.bindTo(session) + * 4. PlayerStore 一旦变化,视图自动刷新(替代旧 setHallRoomCard 等手工回调) + * + * 节点树要求(prefab 内): + * PlayerInfoView (this) + * ├── NicknameLabel (cc.Label) + * ├── BeanLabel (cc.Label) + * └── AvatarSprite (cc.Sprite) + */ +@ccclass('PlayerInfoView') +export class PlayerInfoView extends Component { + @property(Label) + nicknameLabel: Label = null!; + + @property(Label) + beanLabel: Label = null!; + + @property(Sprite) + avatarSprite: Sprite = null!; + + private binder: StoreBinder = createStoreBinder(); + private session: PlatformSession | null = null; + + /** + * 由场景初始化代码调用一次,把 PlayerInfoView 绑定到 PlatformSession。 + * 之后 PlayerStore 变化 → 视图自动刷新。 + */ + bindTo(session: PlatformSession): void { + this.unbindIfNeeded(); + this.session = session; + this.binder.bind(session.player, 'state', (p) => this.applyPlayer(p)); + } + + onDestroy(): void { + this.unbindIfNeeded(); + } + + private unbindIfNeeded(): void { + this.binder.unbindAll(); + this.session = null; + } + + private applyPlayer(p: { nickname: string; bean: number; avatar: string }): void { + if (this.nicknameLabel) this.nicknameLabel.string = p.nickname ?? ''; + if (this.beanLabel) this.beanLabel.string = String(p.bean ?? 0); + // avatar 图片加载(异步)留给业务侧扩展: this.avatarLoader.load(p.avatar).then(spriteFrame => { ... }) + } +} \ No newline at end of file