Unity3D Demo项目开发记录
前言
经过一段时间的学习与实际开发,unity3D也勉强算是强行入门了,正所谓好记性不如烂笔头,更何况本人并非专业从事unity3D开发,会一点C#但也并不熟悉,为了避免后期遗忘,因此特意整理了一个Demo项目,特此记录
本项目是一个简单的Unity学习项目,封装了一下简单、通用功能组件,适用于数据可视化展示
项目特色
1、封装了简单Camera镜头操作、镜头巡航脚步
2、封装单击、双击事件同时绑定脚本(unity3D 游戏物体同时绑定单击、双击事件)
3、封装永远面向屏幕、跟随镜头旋转缩放,缩放大小不变的Billboard公告牌脚本(unity3D 自定义公告牌 )
4、利用LineRenderer,封装自定义流动线路脚本
5、封装自定义弹窗,带遮阴层,可拖动
6、项目用到BestHTTP插件,网传最好用的http插件
7、项目用到XCharts插件,丰富图表展示足够满足需求
8、项目用到DoTween插件,在代码中可轻松实现各种动画效果
项目结构
整体是这样:大目录下进行分组,对应的资源分组存放
场景结构
约定,除了背景、摄镜头、灯光外,所有的3D对象全都放在ObjectRoot下面,所有的UI全都放在UIRoot下面,场景主脚本Main.cs挂在ObjectRoot下面,封装的自定义弹窗脚本Dialog.cs挂在UIRoot下面
运行预览
效果先睹为快,具体介绍在下方,按功能点进行详情介绍
功能详解
背景图
需要单独创建一个摄像头,并只看背景图片,这样才能跟主摄像头相互不影响
背景图片是从网上找的素材,简单的PS了一下,图片风格,黑中带蓝、蓝中带紫、紫色中透着白光,四周偏暗,中间偏亮,也就网传“五彩斑斓的黑”能够与之一拼,更能凸显3D主体对象
镜头操作
包括镜头缩放、鼠标左键进行上下左右旋转,同时可定点巡航、以及指定路线巡航
定点巡航,给定一个坐标点,镜头将会围绕目标点巡航360度
定线巡航,给定多个坐标点,镜头就会依次推进到指定位置,当然了,路线的选择决定了巡航的最终效果,像我这里的坐标点就选得不行,3D对象都跑出镜头外了…
单双击事件绑定
unity3D 游戏物体同时绑定单击、双击事件,具体实现看之前的博客:unity3D 游戏物体同时绑定单击、双击事件
自定义公告牌
永远面向屏幕、跟随镜头旋转缩放,缩放大小不变的Billboard公告牌,具体实现看之前的博客:unity3D 自定义公告牌
流动线路
利用LineRenderer进行画线,材质球设置好流动的光点背景图,封装好脚本,在update中修改材质球的mainTextureOffset值,以一定的速度进行增加或减小,从而达到光点流动的效果,同时利用贝塞尔曲线(参考博客:https://www.cnblogs.com/msxh/p/6270468.html)实现一定的弧度弯曲效果
自定义弹窗
自定义弹窗,带遮阴层,弹出弹窗时鼠标无法操作对象(3D、按钮等),同时左键长按标题栏可拖动弹窗,封装了四个简单弹窗:alert警告框,affirm确认框,scrollBox滚动框,msg提示框
可输出普通文本,也可以操作追加3D对象(如下面的模拟监控功能)
2020-07-28更新
我们之前是定义public的预制体变量,在编辑器进行拖拉赋值,预制体是放在Prefabs文件夹
为了简化这步操作,我决定改成动态加载预制体,首先将预制体放到Resources文件夹下面,然后再代码中进行动态读取
public void Start() { loadPrefabs(); } /// <summary> /// 动态加载预制体 /// </summary> private void loadPrefabs() { scrollBoxPrefab = (GameObject)Resources.Load("Dialog/ScrollBox"); affirmPrefab = (GameObject)Resources.Load("Dialog/Affirm"); alertPrefab = (GameObject)Resources.Load("Dialog/Alert"); msgPrefab = (GameObject)Resources.Load("Dialog/Msg"); }
XCharts图表
一款基于`UGUI`的功能强大、易用、参数可配置的数据可视化图表插件。支持折线图、柱状图、饼图、雷达图、散点图、热力图等常见图表。
XCharts主页:https://github.com/monitor1394/unity-ugui-XCharts
插件自带一个demo场景,各种图表都有例子
拿过来改一改就能用
BestHTTP请求
BestHTTP在网上一搜,好多都说是最好用、最强大的HTTP插件,有各种强大的骚操作,具体的自行百度了解,因为我们现在用不上,简单的http get、post请求,以及json转C#对象就够我们用了
插件同样自带demo场景,里面有各种例子
http请求,发送get、post(PS:如果是打包成WebGL,由于浏览器的同源策略,会存在跨域问题,这一点需要注意),比如我们在程序一运行就发起get请求获取配置文件信息,并在UI面板中设置
Panel面板
一个带背景图的简单基础预制体,可作为其他UI面板的基础
DoTween动画
强大的动画插件,更多介绍查看官网:http://dotween.demigiant.com/documentation.php
项目中常用的就UI面板的进场、离场动画、以及3D对象的动画,比如我们这里的标题、按钮组、左右UI面板都有一个进场动画
模拟监控
使用自定义alert弹窗弹出,目前是直接播放mp4格式视频,做这个组件,主要是为后续接入视频监控做储备
注:unity自带的视频播放组件存在一些问题,有时候会导致程序直接崩掉,我就经常碰到,比如在代码中修改视频url,经常卡死程序自动退出…
场景切换
2020-08-21更新:新增场景切换过渡动画效果,像舞台开幕、闭幕一样
WebGL打包
Edit -> Project Serrings -> Quality 进行WebGL打包参数设置
File -> Build Settings,添加需要打包的场景,选择WebGL,Build,选择文件夹打包,接下来就是等待了,时间看电脑配置,配置越好打包速度越快
后记
开发中,有些功能我们没必要重复造轮子,网上可以找到很多插件,各式各样的功能都有,比如这个网站:http://www.6m5m.com/index.php
不过作者钱包五行缺钱,平时学习时只能下载0金币0积分的资源….,但如果是碰到项目能用的上,又是比较必要的插件,可以让经理或者公司去充值购买
Unity 3D Demo项目暂时记录到这,后续再进行补充
注意:unity中使用默认的字体,打包成WebGL后字体会丢失,导致文字缺失
补充更新
2020-07-31更新
1、新增“3D物体转2D屏幕坐标”脚本;
2、重写原生按钮脚本,扩展鼠标悬浮等事件;
2020-09-01更新
如何读取、写入txt文件? 注意:打成WebGL后,Application.streamingAssetsPath获取到的是http链接,File读取会报错
//新建test.txt空白文件 string path = Application.streamingAssetsPath + "/test.txt"; //写入 File.WriteAllText (path, "{\"name\":\"huanzi\"}", Encoding.UTF8); //读取 File.ReadAllText(path); //{"name":"huanzi"}
如何序列化、反序列化List、Dictionary集合对象?参考下面的博客,亲测可用
Unity中JsonUtility对List和Dictionary的序列化:https://blog.csdn.net/truck_truck/article/details/78292390
unity调用js
打成WebGL后,我们有时想操作js应该如何做?这里记录一下实现过程
Plugins目录下面创建JsLib新目录,新增HuanZiJsLib.jslib文件,内容如下:
在C#中调用
//引入、指定js方法 [DllImport("__Internal")] private static extern void Test(string text); //引入、指定js方法 [DllImport("__Internal")] private static extern void GoToView(string url); //调用 Test("C#调用JS"); GoToView("http://xxxxxxx");
打成WebGL后才有效果,其他运行方式无效
2020-11-27更新
1、封装定点焦聚,开发中我们经常会用到定点焦聚功能,传入一个坐标点,把镜头焦聚过去,同时设置镜头目标点
在CameraMove.cs中封装定点焦聚方法
/// <summary> /// 封装镜头聚焦,传入一个坐标,镜头聚焦过去 /// </summary> /// <param name="point">聚焦坐标</param> /// <param name="duration">焦聚动画时长</param> /// <param name="distance">镜头推荐距离</param> /// <param name="callback">聚焦结束回调</param> public void CamerafocusByPoint(Vector3 point,float duration,float distance,Action callback) { canMove = false; canRotation = false; canZoom = false; distance = Vector3.Distance(this.transform.position, point) - distance; //获取摄像头移动的目标坐标 moveTargetPoint = GetBetweenPointByDist(transform.position, point, distance); //把摄像朝向目标点 this.transform.DOLookAt(point, duration).OnComplete(() => { currentAngleForX = this.transform.eulerAngles.x; if (callback != null) { callback.Invoke(); } }); //重设旋转轴心点 rotaAxis = new Vector3(point.x, 0, point.z); float dist = Vector3.Distance(this.transform.position, moveTargetPoint); Vector3 move = Vector3.Lerp(this.transform.position, moveTargetPoint, Time.deltaTime * 5); this.transform.position = dist > 0.01f ? move : moveTargetPoint; if (Vector3.Distance(this.transform.position, moveTargetPoint) == 0) { moveTargetPoint = Vector3.zero; canMove = true; canRotation = true; canZoom = true; } }
调用
//按钮9,定点焦聚 buttonGroup.Find("Button9").GetComponent<Button>().onClick.AddListener(() => { Camera.main.GetComponent<CameraMove>().CamerafocusByPoint(GameObject.Find("Rack").transform.position,2F,5F,null); });
效果
2、开发中,我们可能会碰到这种需求:使用鼠标操作的UI对象(例如长滚动列表等),仅操作UI对象,不操作3D世界,这时应该怎么做呢?
首先,我们要获取到鼠标移动到UI上时,射线穿过的所有UI对象,如何判断哪个UI需要仅操作UI对象不操作3D世界呢?可在对应的UI对象绑定上一个自定义脚本,判断射线穿过的所有UI对象中,有绑定自定义脚本的UI时,停用鼠标对3D世界的操作即可;
自定义脚本
/// <summary> /// 绑定该脚本在UI对象,鼠标悬浮在当前对象时停用鼠标操作镜头 /// </summary> public class CameraEnableByMouseHovering : MonoBehaviour { }
获取鼠标射线穿过的UI对象
public class IsPointerOverUI { //射线上所有UI对象 public List<RaycastResult> results; /// <summary> /// 判断鼠标是否放在UI上,作用等同于:EventSystem.current.IsPointerOverGameObject() /// </summary> /// <returns></returns> public bool IsPointerOverUIObject() { PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current); eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y); results = new List<RaycastResult>(); EventSystem.current.RaycastAll(eventDataCurrentPosition, results); return results.Count > 0; } }
在CameraMove中进行判断
/// <summary> /// 是否停用鼠标操作镜头 /// </summary> private bool IsEnable() { //鼠标是否放在UI上 IsPointerOverUI ip = new IsPointerOverUI (); if (ip.IsPointerOverUIObject()) { for (var i = 0; i < ip.results.Count; i++) { //该UI对象是否有绑定我们在CameraEnableByMouseHovering脚本 CameraEnableByMouseHovering cameraEnableByMouseHovering = ip.results[i].gameObject.GetComponent<CameraEnableByMouseHovering>(); if (cameraEnableByMouseHovering != null) { return true; } } } return false; }
当鼠标进行,滚轮缩放、左键移动镜头等操作时,调用IsEnable进行判断
/// <summary> /// 鼠标左键双击移动摄像头到事件坐标 /// </summary> private void Move() { if (IsEnable()) { return; } //省略其他代码 }
效果
2020-11-30更新
unity自带在JSON库太弱了,复杂在情况下不能满足我们在要求,这时候就要换一个JSON库
window -> Asset Store,搜索JSON .NET For Unity,下载、安装
序列化
反序列化
代码开源
代码已经开源、托管到我的GitHub、码云: