Raycast射线实现3D世界交互,如何实现立体界面UI

Raycast射线实现3D世界交互,如何实现立体界面UI

说明

本文为B站视频的配套文章,如果您觉得不错,请一定关注三联一波!

简介

大家好,我是Nowpaper

在一些3D游戏中,游戏界面是和3D世界存在互动关系,它们不再是在屏幕上2D显示,而是以3D的形式出现在游戏世界当中,这种帅气的UI如何在CocosCreator中实现呢?

在3.0以前的版本中,实现是非常费劲的,而在Cocos Creator 3.0以后的版本,我们可以借助3D系统,完成3D效果的UI,在本文章当中,我将使用Creator实现常见的3D界面

资产准备

既然是3DUI,就得需要一些3DUI的素材,这个部分最好请专业的美术设计师制作一些,这篇帖子中的3D文件使用Blender来制作而来,包含了独立的材质和网格模型

为了更好的展示,需要一个基本的三维场景,在这里我将使用CocosStore中,两个免费素材,当然你可以使用自己的素材制作场景,当下载好了之后,解压资源包,这里我推荐直接用文件浏览器,打开项目Assets,拷贝对应的文件到里面,打开CocosCreator会执行编译扫描

hello-3d-world | Cocos Store

Cocos 宇航员形象 | Cocos Store

创建项目

新建一个项目,我使用的是3.3.0的版本,打开HelloWolrd的Main场景

在这里为了更好的处理逻辑,我将几个按钮都使用独立的节点完成创建,就是自行添加MeshRender,新建一个Node节点,添加MeshRenderer组件,然后将导入的FBX文件中的网格拖动到其中,在材质栏中添加一个材质,拖动FBX文件中的材质球放到其中,完成后添加碰撞盒,并设置好碰撞区域

image1094×1474 145 KB

现在摆放一下它们,让它呈现立体感,并且加入Cocos的宇航员小人,为了更加好看,再调整一下布局,直到你感觉舒适了为止,我们的期望是,当点击Start按钮,镜头将会从骑士切换到太空人,点击back按钮,则会返回到骑士的镜头上

image1505×852 385 KB

处理逻辑

下面实现一下点击触发逻辑,这里我们将需要两个脚本,一个用来处理按钮点击后的逻辑,一个用来检查用户的点击

新建第一个脚本,用来处理点击后的逻辑,这个我们就完成的简单一点,直接移动摄像机到指定的目标点,参数我们采用外部设置的方式,直接在Creator编辑器中填写,摄像机为了控制方便,我也将它引用进来,处理代码相对比较简单,使用Tween完成缓动动画

import { _decorator, Camera, Vec3, v3, tween, Component } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('MoveCameraOnButtonClick')

export class MoveCameraOnButtonClick extends Component {

@property(Camera)

camera:Camera = null;

@property

target:Vec3 = v3();

onClick(){

tween(this.camera.node).to(1,{position:this.target},{easing:"sineOut"}).start();

}

}

现在建立第二脚本,这是用来处理点击判定的,所以稍微有一些复杂,它的原理是通过点击屏幕的时候,摄像机位置产生的射线,经过屏幕的点击坐标,然后看能射中那个3D物体,如果这个3D物体上,有按钮的脚本,就认定成功了,所以还要用到Canvas对象,来处理屏幕点击事件,在Start方法里,监听它的鼠标移动和点击抬起来的事件,实现按钮检查,通过screenPointToRay方法,获得一个射线,这里的代码就是通过物理射线的方式,取得到底是哪个3D物体被命中,如果命中,就对色值进行处理,为了简单,直接将色值做了预设指定,来方便调整,而且还得记录一下上一次的按钮是哪个,以便于修正处理,当鼠标移动的时候,如果没有检查到button,那么还得环境颜色设置成为默认值,如果检查到了,将选中的button材质的环境色设置成为命中色,我们需要对没有命中时候,取消到当前的按钮,而在MouseUp事件中,处理OnClick事件调用,执行里面的逻辑

具体代码如下:

import { _decorator, Component, Node, Camera, Canvas, EventMouse, geometry, Color, PhysicsSystem, MeshRenderer } from 'cc';

import { MoveCameraOnButtonClick } from './MoveCameraOnButtonClick';

const { ccclass, property } = _decorator;

@ccclass('UIButtonCheck')

export class UIButtonCheck extends Component {

@property(Camera)

camera: Camera = null;

@property(Canvas)

canvas: Canvas = null;

private _cur_button:Node = null;

private _color1:Color = Color.BLACK;

private _color2:Color = new Color(5,29,127);

start() {

this.canvas.node.on(Node.EventType.MOUSE_MOVE, this.checkButton, this);

this.canvas.node.on(Node.EventType.MOUSE_UP, this.onMouseUp, this);

}

private checkButton(event: EventMouse) {

const outRay = new geometry.Ray();

this.camera.screenPointToRay(event.getLocationX(),event.getLocationY(),outRay);

if(PhysicsSystem.instance.raycast(outRay)){

const button = PhysicsSystem.instance.raycastResults.find(

(v)=>{return v.collider.getComponent(MoveCameraOnButtonClick)}

);

if(button){

if(button.collider.node == this._cur_button) return;

if(this._cur_button){

this.setButtonColor(this._cur_button,this._color1);

}

this._cur_button = button.collider.node;

this.setButtonColor(this._cur_button,this._color2);

}else if(this._cur_button){

this.setButtonColor(this._cur_button,this._color1);

this._cur_button = null;

}

}else if(this._cur_button){

this.setButtonColor(this._cur_button,this._color1);

this._cur_button = null;

}

}

private onMouseUp(event: EventMouse) {

if(this._cur_button){

this._cur_button.getComponent(MoveCameraOnButtonClick).onClick();

}

}

private setButtonColor(node:Node,color:Color){

node.getComponent(MeshRenderer).getMaterial(0).setProperty("emissive",color);

}

}

现在返回到Creator编辑器,为所有的按钮文字都添加一个第一个脚本,设置好对应的摄像机引用,和摄像机移动目标点,这里你需要移动摄像机来记录目标点,挨个设置一下

image1074×378 35.4 KB

将第二个脚本,UIButtonCheck加入到场景,并且建立Canvas,以用来引用到按钮检查器当中,完成引用之后保存运行

image1088×653 48 KB

看看效果,当鼠标移入到文字的时候,就能看到颜色变化,点击后,摄像机会跟着移动到目标

跟随式3D UI

第一种3DUI已经完成,更多的扩展,可以参考我的代码,但是我们会发现,交互按钮长驻在场景中,摄像机移动走了之后,就会失去按钮的可见性,那么有没有办法,让按钮元素跟随摄像机移动呢?这样就可以作整体UI,符合那种需要和3D场景脱离的3D UI,答案当然是可以的,

做法很简单,现在复制一下Main这个场景,我们在新的场景中实现,将文本按钮作一下调整,然后直接将它挂到摄像机的子节点下,然后它们就会跟随摄像机移动了

image1621×874 505 KB

其实是非常简单的实现,在某一些游戏里,比如手持武器、跟随UI,都是这种实现方式来完成的

和2D交互

那么更进一步 ,如何和2D UI联动呢,这个其实非常简单,为了演示这个过程,咱们需要准备一个2D界面,我在这里使用的是,官方商城中的免费2D UI资源,商城里面基础素材还是足够丰富的

image1339×918 181 KB

简单搭建一个弹窗,为了产生UI互动,先写一个UI处理,比如GameUI,实现以下简单的功能,两个方法来处理,隐蔽和显示指定Node

import { _decorator, Component, Node } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('GameUI')

export class GameUI extends Component {

@property(Node)

panel:Node = null;

onClickShowPanel(){

this.panel.active = true;

}

onClickHidePanel(){

this.panel.active = false;

}

}

返回到Creator中,为Canvas添加GameUI的组件,并且将弹窗节点作为目标,操作完后将其隐蔽,当然了在那之前,我们需要给Close按钮,添加Cocos的Button组件,让它能够调用GameUI上的隐藏弹窗方法

image967×884 66.7 KB

我们将3DButton给封装一下,用来触发指定的游戏逻辑,额,类名是不支持数字开头,叫DButton也没什么关系,我们继续

import { _decorator, Component, Node, EventHandler } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('DButton')

export class DButton extends Component {

@property([EventHandler])

clickEvents:EventHandler[] = [];

onClick(){

EventHandler.emitEvents(this.clickEvents);

}

}

这里将参考Cocos的Button组件的事件方式,写了一个onClick来触发它所引用到的事件,保存返回到Creator,给Option添加DButton的组件,并且指定好点击显示弹窗的事件,这个操作基本上和2D的button是一样的

image946×479 32.1 KB

但是它怎么能够被3DUI检查器触发处理呢,这时候就得需要一些面向对象的改造,首先,让移动摄像机的按钮脚本里面的类,继承自DButton,由于都有onClick方法,所以在处理onClick方法的时候,子类里的就会覆盖父类里的,

import { _decorator, Component, Node, Camera, Vec3, v3, tween } from 'cc';

import { DButton } from './3DButton';

const { ccclass, property } = _decorator;

@ccclass('MoveCameraOnButtonClick')

export class MoveCameraOnButtonClick extends DButton {

@property(Camera)

camera:Camera = null;

@property

target:Vec3 = v3();

onClick(){

tween(this.camera.node).to(1,{position:this.target},{easing:"sineOut"}).start();

}

}

改造一下UIButtonCheck.ts 脚本,

image1906×410 74.3 KB

image1656×271 40.8 KB

采用寻找和处理DButton类名,而不是之前的移动摄像机的脚本类名,这样的话,就通过面向对象的方法实现了点击调用,而且不用太大的代码重构,现在保存到项目中,运行一下看看效果,当点击Option的时候,就会弹出弹窗,点击关闭按钮,就会关闭它,看起来还是不错的

代码地址

在这个基础上,你可以做出很多的有趣的处理,实现更多的3DUI或者场景交互

https://github.com/Nowpaper/CC3DUI-Demo-For-Raycast

其他

本人喜欢研究各种有趣的玩法,以下是往期制作,可以移步研究

用RenderTexture实现Sprite版小地图和炫酷的传送门

好玩的编队代码,魔性队伍排列惊喜不断完全停不下来

手撸三个有关Bundle详细教程,大厅+子游戏模式从入门到进阶

Cocos3D《病毒传播模拟器》游戏版本1 开发日志和总结

案例开发 四图猜词 Part1~4 全集教程

相关典藏

佳能好还是尼康相机好深入分析优劣对比,佳能和尼康哪个质量更可靠
准噶尔灭族
我和黑大佬的365天知乎

准噶尔灭族

📅 08-25 👁️‍🗨️ 3082
科学记数法计算器
365bet.com亚洲版

科学记数法计算器

📅 11-14 👁️‍🗨️ 3719