> 技术文档 > cocos使用脚本创建带昵称跟随的玩家,并解决昵称重影问题

cocos使用脚本创建带昵称跟随的玩家,并解决昵称重影问题

玩家昵称跟随的实现过程在上一篇文章有讲,但是如何实现多玩家多昵称跟随?我在开发过程中使用了一种走捷径的思维,就是将玩家和昵称放在一个空节点下面,然后存储为一个预制体,然后再通过脚本动态创建预制体实例,结果就是:昵称有重影,每多创建一个玩家昵称就会多一层重影......后来研究发现,原来是因为昵称放在玩家节点下面后,就会导致每一个玩家节点下都有一个canvas节点和label节点+Camera节点。

如果每个玩家的 Label 都在自己的 Canvas 下,并且这些 Canvas 都被主摄像机渲染,所有 Label 会被叠加渲染,造成昵称“重影”效果。

重影解决办法:

场景中只保留一个 Canvas(一般是全局 UI),所有玩家的昵称 Label 都作为这个 Canvas 的子节点。玩家预制体中不要再带 Canvas 节点,只保留 Label。

然后将玩家Body和name单独存储为预制体,再通过脚本来动态生成玩家对象和昵称对象,将玩家对象和昵称对象单独放在各自的节点下面,比如玩家放在Person这个节点下,昵称放在Canvas这个节点下。

总控制脚本

再Game上绑定游戏总控制脚本,总控制脚本中绑定玩家预制体和昵称预制体,还有总摄象机

然后在脚本中动态创建玩家和昵称,然后放在各自的节点下:

import { _decorator, Camera, Component, EventKeyboard, Input, input, instantiate, KeyCode, Label, Prefab,} from \'cc\'import { follow } from \'./follow\'const { ccclass, property } = _decorator@ccclass(\'game\')export class game extends Component { // 玩家预制体 @property(Prefab) public playerPrefab: Prefab = null // 玩家昵称预制体 @property(Prefab) public namePrefab: Prefab = null @property({ type: Camera }) mainCamera: Camera = null // 3D摄像机 // 玩家序号 private playerIndex = 0 start() { // 监听按键输入 input.on(Input.EventType.KEY_UP, this.onKeyUp, this) } onKeyUp(event: EventKeyboard) { switch (event.keyCode) { case KeyCode.KEY_P: console.log(\'P key pressed\') this.createPlayer() break } } // 创建玩家 createPlayer() { // 创建玩家 const playerNode = instantiate(this.playerPrefab) // 创建玩家昵称 const nameNode = instantiate(this.namePrefab) // 获取follow组件 const followComp = nameNode.getComponent(follow) // 设置玩家节点 followComp.setPlayerNode(playerNode) // 设置3D摄像机 followComp.setMainCamera(this.mainCamera) // 设置玩家昵称 this.playerIndex++ const nameLabel = nameNode.getComponent(Label) nameLabel.string = `1024小神${this.playerIndex}` // 将玩家昵称添加到canvas节点 const canvasNode = this.node.getChildByName(\'Canvas\') nameNode.setParent(canvasNode) // 将玩家添加到Person节点 const personNode = this.node.getChildByName(\'Person\') playerNode.setParent(personNode) // 设置玩家位置 playerNode.setPosition(0, 1, 0) } update(deltaTime: number) {}}

昵称跟随脚本

在昵称跟随脚本中,添加动态配置玩家和摄像机的函数,然后在总控制脚本中就可以获取到这个组件来设置(ts脚本也是组件,可以通过getComponent来获取到实例,并调用ts脚本中的函数)

import { _decorator, Camera, Component, Node, UITransform, Vec3,} from \'cc\'const { ccclass, property } = _decorator@ccclass(\'follow\')export class follow extends Component { // @property({ type: Node }) playerNode: Node = null // @property({ type: Camera }) mainCamera: Camera = null // 3D摄像机 // @property({ type: Node }) canvasNode: Node = null // Canvas节点 // 头顶偏移 private _offset: Vec3 = new Vec3(0, 2, 0) // 动态设置玩家节点 public setPlayerNode(playerNode: Node) { if (this.playerNode) return this.playerNode = playerNode } // 动态设置3D摄像机 public setMainCamera(mainCamera: Camera) { if (this.mainCamera) return this.mainCamera = mainCamera } start() { this.canvasNode = this.node.parent } // update update(dt: number) { if (!this.playerNode || !this.mainCamera || !this.canvasNode) return // 1. 计算头顶世界坐标 const headWorldPos = this.playerNode .getWorldPosition() .add(this._offset) // 2. 世界坐标转屏幕坐标 const screenPos = new Vec3() this.mainCamera.worldToScreen(headWorldPos, screenPos) // console.log(\'screenPos\', screenPos) // 3. 屏幕坐标转UI坐标 const canvasUITrans = this.canvasNode.getComponent(UITransform) const widgetPos = canvasUITrans.convertToNodeSpaceAR( new Vec3(screenPos.x, screenPos.y, 0) ) // console.log(\'widgetPos\', widgetPos) // 4. 设置UI节点位置 this.node.setPosition(widgetPos) }}

小家电礼品网