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

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

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

重影解决办法:

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

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

总控制脚本

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

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

javascript 复制代码
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脚本中的函数)

javascript 复制代码
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)
    }
}
相关推荐
细节控菜鸡42 分钟前
【2025最新】ArcGIS for JS 实现随着时间变化而变化的热力图
开发语言·javascript·arcgis
2501_916008891 小时前
Web 前端开发常用工具推荐与团队实践分享
android·前端·ios·小程序·uni-app·iphone·webview
SkylerHu1 小时前
前端代码规范:husky+ lint-staged+pre-commit
前端·代码规范
菜鸟una2 小时前
【微信小程序 + 消息订阅 + 授权】 微信小程序实现消息订阅流程介绍,代码示例(仅前端)
前端·vue.js·微信小程序·小程序·typescript·taro·1024程序员节
Yeats_Liao2 小时前
Go Web 编程快速入门 05 - 表单处理:urlencoded 与 multipart
前端·golang·iphone
飞翔的佩奇2 小时前
【完整源码+数据集+部署教程】【运动的&足球】足球场地区域图像分割系统源码&数据集全套:改进yolo11-RFAConv
前端·python·yolo·计算机视觉·数据集·yolo11·足球场地区域图像分割系统
拉不动的猪2 小时前
h5后台切换检测利用visibilitychange的缺点分析
前端·javascript·面试
桃子不吃李子2 小时前
nextTick的使用
前端·javascript·vue.js
萌新小码农‍2 小时前
SpringBoot+alibaba的easyexcel实现前端使用excel表格批量插入
前端·spring boot·excel
冰暮流星3 小时前
css3新增背景图片样式
前端·css·css3