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)
    }
}
相关推荐
smile boy9 分钟前
个人财务记录应用
前端·javascript·css·css3·html5
hqxstudying14 分钟前
J2EE模式---业务代表模式
java·前端·python·设计模式·java-ee·mvc
90后的晨仔28 分钟前
🚀 Vue 声明式渲染:让 HTML 跟着数据走(超详解)
前端·vue.js
CAD老兵35 分钟前
package.json 中 dependencies 的版本号:它真的是版本号吗?
前端·javascript
汤姆大聪明38 分钟前
Spring Cloud Gateway 服务网关
java·服务器·前端
OEC小胖胖1 小时前
架构篇(一):告别MVC/MVP,为何“组件化”是现代前端的唯一答案?
前端·架构·mvc
alien爱吃蛋挞1 小时前
【JavaEE】Spring Web MVC(上)
前端·spring·java-ee
拾光拾趣录1 小时前
一个 ID 溢出引发的线上资损
前端·javascript