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)
    }
}
相关推荐
学嵌入式的小杨同学20 分钟前
从零打造 Linux 终端 MP3 播放器!用 C 语言实现音乐自由
linux·c语言·开发语言·前端·vscode·ci/cd·vim
weixin_425543731 小时前
TRAE CN3.3.25 构建的Electron简易DEMO应用
前端·typescript·electron·vite·nestjs
Mr Xu_2 小时前
【Vue3 + ECharts 实战】正确使用 showLoading、resize 与 dispose 避免内存泄漏
前端·信息可视化·vue·echarts
0思必得02 小时前
[Web自动化] Selenium设置相关执行文件路径
前端·爬虫·python·selenium·自动化
雯0609~2 小时前
hiprint:实现项目部署与打印1-官网提供普通html版本
前端·html
yuezhilangniao2 小时前
AI智能体全栈开发工程化规范 备忘 ~ fastAPI+Next.js
javascript·人工智能·fastapi
不绝1913 小时前
UGUI——进阶篇
前端
Exquisite.3 小时前
企业高性能web服务器(4)
运维·服务器·前端·网络·mysql
铅笔侠_小龙虾3 小时前
Flutter Demo
开发语言·javascript·flutter
2501_944525544 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 账户详情页面
android·java·开发语言·前端·javascript·flutter