雨涵的Unity工作笔记

Unity是 实时3D互动内容创作和运营平台 。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。Unity 平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。简单来说,Unity3D不单单可以做游戏,还可以通过相关接口与API来进行设备的交互【例如虚拟仿真】

使用的编辑器为:Visual Studio 2019

Unity编辑器版本为:2020.2.6f1(其实这个没必要一样,自己感觉用着舒服就行)

部分内容参考自网络,在文章的末尾会给大家做罗列文章内容的出处

文章内容纯笔者自己的看法和撰写,不代表任何立场

前言

大家好,我是雨涵,也可以叫我大山(大山这个名字比较亲切一些,不管叫哪一个都是不错的),这篇文章内容可能会超级的长,但是记录的东西绝对是我从学生转变到工作就业之后的工作笔记,本来寻思着写一些最基础的内容,但是后来转念一想,与其长篇幅的去赘述动一动鼠标键盘就能找到的东西,岂不是浪费宝贵的学习时间,毕竟学习的时间就少,大部分时间都用在了思考功能如何实现以及打游戏以及摸鱼看剧上。

正文内容

UI功能篇

这一篇主要说明的是一些常用的UI功能,也会附带着相关的代码,具体怎么用,都会有注释的,毕竟注释可不能少的呦!!!

UI滚动效果

先看一张动态的图,看一下具体是什么效果

这里放的是图片哦,没有是因为不还没来的及准备图片嘛

这里用到的组件主要就是Scroll View 组件,并在Scroll View 的content身上放上Content Size Fitter以及Grid Layut Group这两个组件(在这里建议哈:尽量少用Grid Layout Group,多了会影响一定性能),里面的参数就自定义吧,需要多大的尺寸自行调整即可。

基础滚动列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public enum PageScrollType {
Horizontal,
Vertical
}
public class PageScrollView : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{

#region 字段
protected ScrollRect rect;
protected int pageCount;
private RectTransform content;
protected float[] pages;
public float moveTime = 0.3f;
private float timer = 0;
private float startMovePos;
protected int currentPage = 0;
private bool isDraging = false;
private bool isMoving = false;
// 是不是开启自动滚动
public bool IsAutoScroll;
public float AutoScrollTime = 2;
private float AutoScrollTimer = 0;
public PageScrollType pageScrollType = PageScrollType.Horizontal;
public float vertical;
#endregion

#region 事件
public Action<int> OnPageChange;
#endregion

#region Unity回调
// Start is called before the first frame update
protected virtual void Start()
{
Init();
}
// Update is called once per frame
protected virtual void Update()
{
ListenerMove();
ListenerAutoScroll();
vertical = rect.verticalNormalizedPosition;
}
public void OnEndDrag(PointerEventData eventData)
{
// 滚动到离得最近的一页
this.ScrollToPage(CaculateMinDistancePage());
isDraging = false;
AutoScrollTimer = 0;
}
public void OnBeginDrag(PointerEventData eventData)
{
isDraging = true;
}
#endregion

#region 方法
public void Init() {
rect = transform.GetComponent<ScrollRect>();
if (rect == null)
{
throw new System.Exception(" 未查询到ScrollRect! ");
}
content = transform.Find("Viewport/Content").GetComponent<RectTransform>();
pageCount = content.childCount;
if (pageCount == 1)
{
throw new System.Exception("只有一页,不用进行分页滚动!");
}
pages = new float[pageCount];
for (int i = 0; i < pages.Length; i++)
{
switch (pageScrollType)
{
case PageScrollType.Horizontal:
pages[i] = i * (1.0f / (float)(pageCount - 1));
break;
case PageScrollType.Vertical:
pages[i] = 1 - i * (1.0f / (float)(pageCount - 1));
break;
}
}
}
// 监听移动
public void ListenerMove()
{
if (isMoving)
{
timer += Time.deltaTime * (1 / moveTime);
switch (pageScrollType)
{
case PageScrollType.Horizontal:
rect.horizontalNormalizedPosition = Mathf.Lerp(startMovePos, pages[currentPage], timer);
break;
case PageScrollType.Vertical:
rect.verticalNormalizedPosition = Mathf.Lerp(startMovePos, pages[currentPage], timer);
break;
}
if (timer >= 1)
{
isMoving = false;
}
}
}
// 监听自动滚动
public void ListenerAutoScroll()
{

if (isDraging) { return; }
if (IsAutoScroll)
{
AutoScrollTimer += Time.deltaTime;
if (AutoScrollTimer >= AutoScrollTime)
{
AutoScrollTimer = 0;
// 滚动到下一页
currentPage++;
currentPage %= pageCount;
ScrollToPage(currentPage);
}
}
}

public void ScrollToPage(int page)
{
isMoving = true;
this.currentPage = page;
timer = 0;
switch (pageScrollType)
{
case PageScrollType.Horizontal:
startMovePos = rect.horizontalNormalizedPosition;
break;
case PageScrollType.Vertical:
startMovePos = rect.verticalNormalizedPosition;
break;
}
if (OnPageChange != null)
OnPageChange(this.currentPage);
}
// 计算离得最近的一页
public int CaculateMinDistancePage() {
int minPage = 0;
// 计算出离得最近的一页
for (int i = 1; i < pages.Length; i++)
{
switch (pageScrollType)
{
case PageScrollType.Horizontal:

if (Mathf.Abs(pages[i] - rect.horizontalNormalizedPosition) < Mathf.Abs(pages[minPage] - rect.horizontalNormalizedPosition))
{
minPage = i;
}
break;
case PageScrollType.Vertical:

if (Mathf.Abs(pages[i] - rect.verticalNormalizedPosition) < Mathf.Abs(pages[minPage] - rect.verticalNormalizedPosition))
{
minPage = i;
}
break;
}
}
return minPage;
}
#endregion
}

Scale 缩放滚动列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScalePageScrollView : PageScrollView
{
#region 字段
// 所有页的Object
protected GameObject[] items;
public float currentScale = 1f;
public float otherScale = 0.6f;
public int lastPage;
public int nextPage;
#endregion

#region Unity回调
protected override void Start()
{
base.Start();
items = new GameObject[pageCount];
// 初始化所有的GameObject
for (int i = 0; i < pageCount; i++)
{
items[i] = transform.Find("Viewport/Content").GetChild(i).gameObject;
}

}
protected override void Update()
{
base.Update();
ListenerScale();
}
#endregion
// 监听scale
public void ListenerScale() {
// 找到上一页 和 下一页

for (int i = 0; i < pages.Length; i++)
{
if ( pages[i] <= rect.horizontalNormalizedPosition )
{
lastPage = i;
}
}

for (int i = 0; i < pages.Length; i++)
{
if (pages[i] > rect.horizontalNormalizedPosition)
{
nextPage = i;
break;
}
}

if ( nextPage == lastPage )
{
return;
}

float percent = (rect.horizontalNormalizedPosition - pages[lastPage]) / ( pages[nextPage] - pages[lastPage] );
items[lastPage].transform.localScale = Vector3.Lerp(Vector3.one * currentScale, Vector3.one * otherScale, percent);
items[nextPage].transform.localScale = Vector3.Lerp(Vector3.one * currentScale, Vector3.one * otherScale, 1 - percent);

for (int i = 0; i < items.Length; i++)
{
if (i != lastPage && i != nextPage)
{
items[i].transform.localScale = Vector3.one * otherScale;
}

}
}
}

旋转滚动列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotationScaleScrollView : ScalePageScrollView
{
public float rotation;
protected override void Update()
{
base.Update();
ListenerItemRotation();
}
public void ListenerItemRotation() {

if (nextPage == lastPage)
return;
float percent = (rect.horizontalNormalizedPosition - pages[lastPage]) / (pages[nextPage] - pages[lastPage]);

if (nextPage > currentPage)
{
items[lastPage].transform.localRotation = Quaternion.Euler(-Vector3.Lerp(Vector3.zero, new Vector3(0, rotation, 0), percent));
items[nextPage].transform.localRotation = Quaternion.Euler(Vector3.Lerp(Vector3.zero, new Vector3(0, rotation, 0), 1 - percent));
}
else {
items[lastPage].transform.localRotation = Quaternion.Euler(-Vector3.Lerp(Vector3.zero, new Vector3(0, rotation, 0), percent));
items[nextPage].transform.localRotation = Quaternion.Euler(Vector3.Lerp(Vector3.zero, new Vector3(0, rotation, 0), 1 - percent));
}

for (int i = 0; i < items.Length; i++)
{
//if (i != lastPage && i != nextPage)
//{
// items[i].transform.localScale = Vector3.one * otherScale;
//}

if ( i != lastPage && i != nextPage )
{
if (i < currentPage)
{
items[i].transform.localRotation = Quaternion.Euler(new Vector3(0, -rotation, 0));
}
else if (i == currentPage)
{
//items[i].transform.localRotation = Quaternion.Euler(new Vector3(0, 0, 0));
}
else if (i > currentPage)
{
items[i].transform.localRotation = Quaternion.Euler(new Vector3(0, rotation, 0));
}
}
}
}
}

不规则UI按钮

本来开开心心的琢磨如何实现功能,后来发现这UI也不是规则的呀,后来整了半天,总算是琢磨出一套方案

挠头表情 - Bing images
  • 创建一个Camera,重命名为UICamera,设置Culling Mask为UI,设置相机的投射模式(Projection)为正交模式(Orthographic),注意主摄像机的Culling Mask去掉UI层。
    Canvas使用Screen Space - Camera模式,并赋值UICamera。【注意,UICamera不需要Audio Listener,直接去掉。
不规则Btn UICamera.png
  • Button的子节点创建一个空节点(这里重命名为IrregularUIBtnMgr),挂上IrregularUIBtnMgr脚本(脚本代码见文章最下面),会自动挂上Polygon Collider 2D组件,将坐标归零。

  • 选中IrregularUIBtnMgr节点,点击Polygon Collider 2D组件中的Edit Collider旁边的按钮,即可直接编辑多边形碰撞形状。

  • 最后要调节WidthHeight,确保包住整个不规则碰撞区域。

  • 最后是代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(PolygonCollider2D))]
public class IrregularUIBtnMgr : Image
{
private PolygonCollider2D _polygon = null;
private PolygonCollider2D polygon
{
get
{
if (_polygon == null) _polygon = GetComponent<PolygonCollider2D>();
return _polygon;
}
}
//设置只响应点击,不进行渲染
protected IrregularUIBtnMgr()
{
useLegacyMeshGeneration = true;
}

protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
}

public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
{
return polygon.OverlapPoint(eventCamera.ScreenToWorldPoint(screenPoint));
}

#if UNITY_EDITOR
protected override void Reset()
{
//重置不规则区域
base.Reset();
transform.position = Vector3.zero;
float w = (rectTransform.sizeDelta.x * 0.5f) + 0.1f;
float h = (rectTransform.sizeDelta.y * 0.5f) + 0.1f;
polygon.points = new Vector2[]
{
new Vector2(-w,-h),
new Vector2(w,-h),
new Vector2(w,h),
new Vector2(-w,h)
};
}
#endif
}

#if UNITY_EDITOR
[CustomEditor(typeof(IrregularUIBtnMgr), true)]
public class UIPolygonInspector : Editor
{
public override void OnInspectorGUI()
{
//什么都不写用于隐藏面板的显示
}
}
#endif

自定义UI按钮框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class PolygonUIMesh : Graphic
{
private void OnGUI()
{
// 实时检测更新绘制 OnPopulateMesh 中 transform.child 位置
SetAllDirty();
}
/// <summary>
/// 根据 transform.child 位置 绘制 Mesh
/// </summary>
/// <param name="vh"></param>
protected override void OnPopulateMesh(VertexHelper vh)
{
if (transform.childCount <= 2) return;
Color32 color32 = color;
vh.Clear();
// 几何图形的顶点,本例中根据子节点坐标确定顶点
foreach (Transform child in transform)
vh.AddVert(child.localPosition, color32, new Vector2(0f, 0f));
// 几何图形中的三角形
for (int i = 0; i < (transform.childCount - 2); i++)
vh.AddTriangle(i + 1, i + 2, 0);
}
/// <summary>
/// 点的辅助绘制
/// </summary>
private void OnDrawGizmos()
{
if (transform.childCount == 0) return;
for (int i = 0; i < transform.childCount; i++)
Gizmos.DrawSphere(transform.GetChild(i).position, 2.55f);
}
}

特效篇

移动端篇

最近想着准备做一个手游玩,但是想了半天如果自己做登录,服务器运营的成本又太高,突然灵机一动,可以接入SDK,后来看了QQ的登录,发现有点麻烦,后来选择了TapTap登录,接入也比较简单

具体的参考文档(还是比较详细,按照相关操作一般能实现基本内容,但是剩下的肯定是需要自己去实现的):TapTap 开发者文档

这里就阐述一些比较常用的功能如何实现

首先肯定是需要先到TapTap开发者,申请个人开发者,毕竟得需要Client IDClient Token,到时候需要使用到

最后我是雨涵,多记点笔记