需求
让UI始终跟随3D场景中的物件。(如角色的头顶的名称、血条等)
实现
使用camera的covertToUINode方法,获得UI应被设置到的位置,再setPostion就行。
public updatePos()
{
this._cam.convertToUINode(this._refNode.position, this._uiNode.parent, this._pos);
this._uiNode.setPosition(this._pos);
}
下面的代码提供了功能:根据物件的位置设置对应UI的位置,让UI始终跟随对应物件。可添加和删除对应关系。
在编辑模式下可先设置好若干组对应关系。
WorldPosToUiCtr.ts
import { _decorator, Component, Node, Camera, Vec2, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('WorldPosToUiElement')
class WorldPosToUiElement
{
@property({ type: Node, visible: true, displayName: '3d世界中的参考点' })
private _refNode: Node;
public get world():Node
{
return this._refNode;
}
@property({ type: Node, visible: true, displayName: '要设置位置的UI节点' })
private _uiNode: Node;
public get ui():Node
{
return this._uiNode;
}
private _cam:Camera;
private _pos:Vec3;
public set cam(v:Camera)
{
if (!this._cam)
{
this._cam = v;
}
}
public constructor (worldRef:Node, ui:Node, c:Camera)
{
this._refNode = worldRef;
this._uiNode = ui;
this.cam = c;
this._pos = new Vec3(0,0,0);
}
public destroy()
{
this._refNode = null;
this._uiNode = null;
this.cam = null;
}
public updatePos()
{
if (this._refNode.active)
{
this._cam.convertToUINode(this._refNode.worldPosition, this._uiNode.parent, this._pos);
this._uiNode.setPosition(this._pos);
}
// 根据世界物件的active设置对于UI的active
if (this._uiNode.active != this._refNode.activeInHierarchy)
{
this._uiNode.active = this._refNode.activeInHierarchy;
}
}
}
@ccclass('WorldPosToUiCtr')
export class WorldPosToUiCtr extends Component
{
@property({ type: Camera, visible: true, displayName: '相机' })
private _cam: Camera;
@property({ type: [WorldPosToUiElement], visible: true, displayName: '各元素' })
private _elements: Array = new Array();
start()
{
this.init();
}
onDestroy()
{
this.myDestroy();
}
update(deltaTime: number)
{
if(this._elements)
{
for(const e of this._elements)
{
e.updatePos();
}
}
}
private init(): void
{
if(this._elements)
{
for(const e of this._elements)
{
e.cam = this._cam;
}
}
}
private myDestroy(): void
{
if (this._elements)
{
for(const e of this._elements)
{
e.destroy();
}
this._elements = null;
}
}
public addElement(world:Node, ui:Node):void
{
if (!this._elements)
{
this._elements = new Array();
}
if (!this.containElement(world, ui))
{
this._elements.push(new WorldPosToUiElement(world, ui, this._cam));
// console.info('添加元素,添加后数量:'+this._elements.length);
}
}
public removeElement(world:Node, ui:Node):void
{
if (this._elements)
{
let e:WorldPosToUiElement = this.getElement(world, ui);
if (e)
{
this._elements.splice(this._elements.indexOf(e), 1);
// console.info('移除元素,移除后数量:'+this._elements.length);
}
}
}
private containElement(world:Node, ui:Node):boolean
{
let ret:boolean = false;
if (this._elements)
{
for(const e of this._elements)
{
if (e.world == world && e.ui == ui)
{
ret = true;
break;
}
}
}
return ret;
}
private getElement(world:Node, ui:Node):WorldPosToUiElement
{
let ret:WorldPosToUiElement = null;
if (this._elements)
{
for(const e of this._elements)
{
if (e.world == world && e.ui == ui)
{
ret = e;
break;
}
}
}
return ret;
}
}
用于测试的一些代码
物体随机移动
RandomMoveCtr.ts
import { _decorator, Component, Node, CCFloat, Vec3, randomRange } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('RandomMoveCtr')
export class RandomMoveCtr extends Component
{
@property({ type: CCFloat, visible: true, displayName: 'x范围' })
private _xRange:number = 5;
@property({ type: CCFloat, visible: true, displayName: 'y范围' })
private _yRange:number = 8;
private _center:Vec3;
private _targetPos:Vec3;
@property({ type: CCFloat, visible: true, displayName: '移动速度' })
private _moveSpeed:number = 10;
start()
{
this.init();
}
onDestroy()
{
this.myDestroy();
}
update(deltaTime: number)
{
if(this._targetPos.equals(this.node.position, 0.1))
{
this.createTarget();
}
else
{
const dir = (new Vec3(this._targetPos.x, this._targetPos.y, this._targetPos.z)).subtract(this.node.position).normalize();
const offset = dir.multiplyScalar(this._moveSpeed * deltaTime);
this.node.setPosition(this.node.position.add(offset));
}
}
private init(): void
{
this._center = new Vec3(this.node.position.x, this.node.position.y, this.node.position.z);
this.createTarget();
}
private myDestroy(): void
{
}
private createTarget():void
{
this._targetPos = new Vec3(this._center.x + randomRange(this._xRange*-1, this._xRange), this._center.y + randomRange(this._yRange*-1, this._yRange), this._center.z);
}
}
运行时通过按键添加和移除 物件-UI对应关系
ElementTest.ts
import { _decorator, Component, Node, EventKeyboard, input, Input, KeyCode, instantiate } from 'cc';
import { WorldPosToUiCtr } from '../WorldPosToUiCtr';
const { ccclass, property } = _decorator;
@ccclass('ElementTest')
export class ElementTest extends Component
{
@property({ type: Node, visible: true, displayName: '3d世界中的参考点' })
private _refNode: Node;
@property({ type: Node, visible: true, displayName: '要设置位置的UI节点' })
private _uiNode: Node;
@property({ type: WorldPosToUiCtr, visible: true, displayName: 'Ctr' })
private _ctr: WorldPosToUiCtr;
start()
{
this.init();
}
onDestroy()
{
this.myDestroy();
}
update(deltaTime: number)
{
}
private init(): void
{
input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
}
private myDestroy(): void
{
input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
}
private onKeyDown(event: EventKeyboard): void
{
if (event.keyCode == KeyCode.KEY_A)
{
// 添加
let world = instantiate(this._refNode);
world.setParent(this._refNode.parent);
let ui = instantiate(this._uiNode);
ui.setParent(this._uiNode.parent);
this._ctr.addElement(world, ui);
// this._ctr.addElement(this._refNode, this._uiNode);
}
else if (event.keyCode == KeyCode.KEY_D)
{
// 移除
this._ctr.removeElement(this._refNode, this._uiNode);
}
}
}
额外知识1:ts获取当前的月和日
const date:Date = new Date();
const m = date.getMonth()+1;
const d = date.getDate();
额外知识2:CocosCreator中,为按钮添加点击事件
// 添加关注
this._btn.node.on(Button.EventType.CLICK, this.onClickBtn, this);
// 移除关注
this._btn.node.off(Button.EventType.CLICK, this.onClickBtn, this);
// 点击按钮后,做什么
private onClickBtn():void
{
this.close();
}