前排提要:
Unity的Matrix4x4类:https://www.cxyzjd.com/article/yizhe0731/108410116
特别说下该类的TRS方法:
public static Matrix4x4 TRS(Vector3 pos, Quaternion q, Vector3 s);
它可以根据参数生成一个矩阵,使用该矩阵可以将物体缩放s倍,旋转指定e度(e为参数q对应的欧拉角),平移pos个单位(因为这里并没有更改物体的transform,而是直接将物体的每个顶点乘上矩阵,所以无论物体有无父物体都是一样的,使用坐标系见补充1)
补充:
1.这里旋转和平移变换的坐标系取决于该矩阵所乘的那个坐标是哪个坐标系
2.三个变换的顺序为先缩放,再旋转,最后平移(TRS),这很重要,因为矩阵乘法不可交换(当然并不是所有,旋转和缩放矩阵是可交换的,而位移和缩放、旋转矩阵则不能交换)
实际使用中需要注意的地方:
初始有位移 none
初始有旋转 位移和旋转
初始有缩放 位移
第一列是具体的情况,第二列是影响到的属性,这些属性的值之所以异常都是因为变换顺序的问题,这里以第三种情况为例解释:
即当初始缩放不为1时,实际的位移要乘上缩放,这里可以看作先进行了缩放变换,然后进行了位移变换,又因为缩放矩阵和位移矩阵不能交换,所以会出现这样的现象
Unity中Lerp与SmoothDamp函数使用误区浅析:https://www.sohu.com/a/211459755_667928
Input类的常用方法:016-Unity中Input的设置及相关代码_TQT的博客-优快云博客
Koreographer的一些方法:015-Koreographer插件学习_TQT的博客-优快云博客
DoTween:014-DoTween动画插件学习_TQT的博客-优快云博客
几种Find方法:012-Unity中的几种Find方法_TQT的博客-优快云博客
Easy Touch方法:EasyTouch5学习笔记_TQT的博客-优快云博客
FairyGUI:https://blog.youkuaiyun.com/qq_38234381/article/category/9373291
存档的一些API及对流的理解:018-存档功能的实现和对于流的理解_TQT的博客-优快云博客
Unity和Android:Unity与Android联合开发模式_TQT的博客-优快云博客
C#笔记:C#学习笔记_TQT的博客-优快云博客
射线检测:006-Unity实战开发古迹探险日志之射线检测_TQT的博客-优快云博客
Unity中Lerp与SmoothDamp函数使用误区浅析:Unity中Lerp与SmoothDamp函数使用误区浅析 - 简书
资源管理器相关代码:Unity中资源管理器的创建_TQT的博客-优快云博客
UniRx:UniRx学习笔记_TQT的博客-优快云博客_unirx
Shader:Shader笔记_TQT的博客-优快云博客
isActiveAndEnabled:https://www.nuomiphp.com/eplan/101304.html
Material与ShareMaterial的区别:Unity3D中Material与ShareMaterial引用的区别 - 简书
Unity中获取物体大小:Unity中获取物体尺寸_TQT的博客-优快云博客
Unity碰撞条件:刚体碰撞条件测试_TQT的博客-优快云博客
相机:
Camera.main表示调用一开始的那个相机
Camera.main.ScreenToWorldPoint:举例说明的话就是Camera.main.ScreenToWorldPoint(Input.mousePosition),表示将鼠标的位置转化为世界坐标,即将屏幕坐标转化为世界坐标,z值保持与摄像机的相
同。屏幕坐标原点在左下,世界坐标原点在中间,注意只有当摄像机为正交时才能转换
mainCamera.backgroundColor:游戏窗口下的背景颜色
mainCamera.orthographicSize:控制相机的视野大小,一般视野越小,物体越大,反之则越小
PointerEventData .pressEventCamera
与最后一个OnPointerPress事件关联的摄像头(onPointerPress就是鼠标按下触发的函数)
PointerEventData .enterEventCamera
与最后一个OnPointerEnter事件关联的摄像头(onPointerEnter就是鼠标进入触发的函数)
这个主要用于多相机混合的时候判断当前按钮的事件是由哪个相机触发的
详情:关于按钮点击事件的PointerEventData属性详解_qq_41056203的博客-优快云博客_pointereventdata
Time.timeScale:
1:“timeScale不会影响Update和LateUpdate的执行速度”
2:“FixedUpdate是根据时间来的,所以timeScale只会影响FixedUpdate的速度”
Time.timeScale影响的是Unity的游戏时间缩放比例。Unity里面所有跟时间有关系的东西都是根据timeScale来演算的。
我们如果想暂停游戏的话,Time.timeScale = 0 可以暂停游戏,Time.timeScale = 1 恢复正常,但这是作用于整个游戏的设置,不单单是当前场景,记得在需要的时候重置回Time.timeScale = 1。当然也可以使
用Time.timeScale来做游戏的1倍、2倍整体加速。
我们不能仅仅去记这句话,游戏暂停的都是所有和帧率无关的事情。需要注意的是Update和LateUpdate并没有停,依然在渲染,只是你的代码中依赖于Time.deltaTime的逻辑将会被停掉了,因为
Time.deltaTime = 0了。所以游戏看起来是被冻结了。
以上出自:Unity关于Time.timeScale详解_天生爱赞美的博客-优快云博客_time.timescale
Time.deltaTime:可以理解为每秒多少帧,可以消除在帧率不同的设备上引起的误差
Time.fixedDeltaTime:固定的一个常数,建议直接用Time.deltaTime代替
Time.deltaTime:Unity增量时间Time.deltaTime详解_Chinar优快云的博客-优快云博客
如果设置 Time.timeScale = 0; FixedUpdate 将会停止执行,Update 和LaterUpdate不受影响,依托于 Time.deltaTime 的代码将受影响,因为这个时候 Time.deltaTime 为0了,Time.fixedDeltaTime 不受影响
辨析:
Awake函数:是在脚本对象第一次被SetActive(true)之后执行的,注意第一次SetActive(true)时其它组件可能并没有依附到对象上
Start:对象第一帧时执行,同样是当物体被SetActive(true)之后执行
Awake要比Start早。所有脚本的Awake之间的先后顺序是不可控的,所以我们要注意脚本对象实例化的先后顺序。也就是说,我们先实例化内容,后续才可使用
enable是控制GameObject上的组件激活/关闭,实际上是禁用的Start和Update等一类方法
SetActive是控制GameObject对象禁用或使用(禁用时物体会消失)
Input.GetAxisRaw 与 Input.GetAxis 区别:Unity3D Input.GetAxisRaw 与 Input.GetAxis 区别 - 简书
关于几种Update方法的区别与联系:Unity中Update,FixedUpdate,LateUpdate的区别 - 南山砍柴 - 博客园
LateUpdate的详细解释:Unity3D中Update和Lateupdate的区别 - 马语者 - 博客园
Color和Color32的区别主要在于前者的值是0-1,后者的值是0-255
Invoke和协程的区别:使用上除前者不能带参数外相同
场景:
LoadScene:加载场景。使用:UnityEngine.SceneManagement.SceneManager.LoadScene(1);1是要加载场景序号
异步加载:SceneManager.LoadSceneAsync(1)
返回值为一个AsyncOperation类型的值,通过该值可以获得并修改异步加载的一些信息
如op.allowSceneActivation,表示加载完是否立即加载场景,op.progress,加载进度
同步加载:SceneManager.LoadScene(1)
前者是先将要加载页面加载完之后再导入(即加载时我们可以进行其它工作),后者是一边导入一边加载(只能进行当前的加载工作),常常会出现卡顿的情况
SceneManager.GetActiveScene().buildIndex:返回一个整数,即当前场景的编号
GameObject.activeInHierarchy:返回Bool值,判断GameObject是否显示在场景中
DontDestroyOnLoad:一个gameObject参数,表示加载场景时不要摧毁掉该gameObject
注意:换场景不销毁物体脚本的执行情况,Start和Awake不执行,即不销毁物体不受场景变换的影响
旋转变换:
transform.Rotate:调整对象的旋转,如:
transform.Rotate(Vector3.up * speed, Space.Self);
Vector3.up是以y轴为旋转中心,方法每执行一次旋转的角度便增加speed度,第二个参数是以自身的轴为主还是以全局坐标系的轴为主;也可以
transform.Rotate(new Vector3(0, 0, speed));
以z轴为旋转中心,方法每执行一次旋转的角度便增加speed度,此时没有第二个参数默认为自身坐标系
该方法可以解决大部分3D或者2D游戏中玩家角色旋转的问题,下面是一个样例:
float h = Input.GetAxis("Horizontal");
if (h < -0.1 || h > 0.1)
this.transform.Rotate(0, h * rotateSpeed, 0);
LookRotation()计算让Z轴(局部坐标系)对齐forward(第一个参数),让Y轴(局部坐标系)对齐upward(第二个参数) 所需要的旋转四元数。下面举例说明,如Quaternion.LookRotation(new Vector3(x, 0,
0)),会发现当x小于0,对象的Z轴(局部坐标系)会指向全局坐标系下X轴的负方向,x大于0则指向X轴正方向。另外就是我们在使用这个方法时常常用pos的差来做参数,这个差表示一个向量,对象的Z轴将与
这个向量的方向对齐
补充:1.A-B,则向量指向A 2.若有两个参数,则以第一个参数为准(Z轴和Y轴夹角90度,防止冲突)
Quaternion.Euler(new Vector3(0, 90, 0)),将欧拉角转换为四元数(这个父物体有无影响其实是取决于我们是把值赋给localRotation还是rotation)
LookAt()后面一般加一个位置,这个位置是世界坐标系下的,其作用是使该方法所作用的物体的局部坐标的z轴指向该位置,注意改变的是旋转(使z轴指向指定坐标点),而不是位置,但被指向的物体旋转不会
影响所作用物体的旋转,只有位置改变才能影响所作用物体的旋转
Quaternion.identity四元数(0,0,0,1),即不旋转,转换为欧拉角相当于(0,0,0)
transform.rotation.eulerAngles四元数转欧拉角,注意由于transform.rotation的特殊性不能用transform.rotation.eulerAngles = Vector3.one;这样的语句对transform.rotation赋值
Quaternion.AngleAxis(angle, Vector3.forward),第一个参数是要旋转的角度,第二个参数是旋转所绕的轴
MoveRotation刚体的非静态方法,一个四元数参数表示目标旋转,将刚体旋转到指定旋转
_rigidbody.MoveRotation(Quaternion.Euler(new Vector3(0, 60, 0)));
transform.localRotation = Quaternion.Euler(new Vector3(0, 60, 0));
//效果相同
Vector3.RotateTowards(),旋转一个向量,第一个参数为需要旋转的向量,第二个参数为需要旋转向量的目标向量,第三个参数为一次旋转的弧度增量(只是参考值,即实际的增量可能大于它也可能小于它,但无论大于还是小于都相差不大),第四个参数为一次旋转的最大数值增量(若两个向量长度相同,则该值可以填0,长度不同,current向量会以该值为参考值,逐渐增大其大小,直到与target相同),返回旋转后的向量
SetFromToRotation:四元数对象的非静态方法,接受两个向量,返回这两个向量之间的角度给当前四元数,该角度转化为欧拉角在某一轴上的取值范围是0-360之间的,不可能为负值
transform.RotateAround,有三个参数,点(也可以不指定),轴,速度(执行一次转多少度),即可以指定一个点和过该点的轴,使当前物体按照指定的速度旋转
数学相关:
Mathf方法:Unity Mathf/Math数学运算函数说明全集(Chinar总结)_Chinar优快云的博客-优快云博客_unity向下取整
Vector3.Distance(A.position, B.position):计算A和B之间的距离
normalized:对向量进行单位化,使用方法:向量名.normalized
Mathf.Clamp:将某个属性的值限制在指定范围内。如Mathf.Clamp(posX, 0, 20),如果posX超过20,返回20,小于0,返回0,在这之间,返回posX
Lerp类函数:
Vector3.Lerp:对某个对象的位置进行平滑的移动。用法:Vector3.Lerp(A.position, B.position, num),第一个是起始位置,第二个是目标位置,num是变化速度,越大越快,一般写time.deltaTime表示每秒1个单位
Color.Lerp:渐变颜色用,如:
Color.Lerp(mainCamera.backgroundColor, Color.red, speed * Time.deltaTime)
放在Update中,第一个参数初始颜色,第二个参数目标颜色,第三个参数变化的速度(每秒speed个单位),值越大,变化越快
Mathf.Lerp:数值渐变用,如:
Mathf.Lerp(mainCamera.orthographicSize, 4, speed * Time.deltaTime)
//详细计算方法:开始值+(结束值-开始值)*第三个参数
放在Update中,第一个参数初始数值,第二个参数目标数值,第三个变化速度
可见使用Lerp类函数,变化的速度都是先快后慢的
Lerp函数第三个参数乘以Time.deltaTime的原因:
使不同帧率情况下初始值到结尾值的时间相同,如48FPS和24FPS,在不乘的情况下前者一秒干完的事情后者需要2s,乘上的话前者虽然次数多但每次完成的进度变小,后者虽然次数少,但每次完成的进度
多,最终达到相同时间完成的效果
Random.Range:后面一般两个参数,表示生成随机数的范围,返回一个int,注意前闭后开
transform.forward:返回一个向量,表示当前物体的前方
两个起始点为原点的向量相减,会得到由第二个向量的点指向第一个向量的点的向量
Vector3.Project(navmeshagent.desiredVelocity, transform.forward);求第一个向量在第二个向量上的投影,包括大小和方向
Mathf.Deg2Rad度数转弧度的常量 Rad2Deg弧度转度数
Vector3.Cross(transform.forward, navmeshagent.desiredVelocity);左手定则,拇指与第一个向量重合,食指与第二个重合,中指所指即为答案向量,即两个向量的向量积
Vector3.Angle(targetDir, nowDir);两个向量的夹角
Mathf.SmoothDamp:与lerp相似,但更侧重于缓动(lerp侧重于插值),参数:current:运动开始的位置,target:运动结束的位置,currentVelocity物体当前的速度,要加ref关键字,使用时只需要传一个全局
变量进入就可以了,函数每次调用时会自动赋值,smoothTime:整个缓动的运动时间,maxSpeed:运动过程中的最大速度(限制),deltaTime:函数每次调用或者值每次更新的间隔时间,默认是
Time.deltaTime
Mathf.Round()四舍五入到整数
Mathf.Atan2(x, y)//表示x/y的反正切
Mathf.Approximately()有两个浮点型参数,比较第一个浮点数和第二个浮点数是否相同,返回一个bool值(不推荐用,最好自己写)
保留小数点后的指定位数:
Math.Round(f, x),f表示待舍入小数,x表示舍入到小数点几位
string.Format("{0:F}", f),F(f)表示后面的小数是一个float类型,D(d)代表double,默认是F2,即保留两位小数
数据:
PlayerPrefs.SetString:用于保存和读取数据用
//保存数据
PlayerPrefs.SetString("Name",mName);
PlayerPrefs.SetInt("Age",mAge);
PlayerPrefs.SetFloat("Grade",mGrade)
//读取数据
mName=PlayerPrefs.GetString("Name","DefaultValue");
mAge=PlayerPrefs.GetInt("Age",0);
mGrade=PlayerPrefs.GetFloat("Grade",0F);
第一个参数是键,第二个参数是值
这里我们可以得出:
1、unity3D中的数据持久化是以键值对的形式存储的,可以看做一个字典。
2、unity3D中的值通过键名来读取的,当值不存在时,返回默认值。
选自:对于PlayerPrefs学习以及存储的研究_果vinegar的博客-优快云博客_playerprefs
Resources.Load:通过Resources模块,调用它的load函数:可以直接load并返回某个类型的Object,前提是要把这个资源放在Resource命名的文件夹下,可以加泛型,如:
public static ManagerVars GetManagerVars()
{
return Resources.Load<ManagerVars>("ManagerVarsContainer");
}
PlayerPrefs.DeleteAll()将所有存储的数据删除
Resources文件夹和StreamingAssets文件夹有很多相似之处(例如都用来加载文件夹中的资源,但加载方式不同),下面是两个比较重要的地方:
①json文件存放于StreamingAssets文件夹下不会压缩,若存在于Resources文件夹,压缩会导致加载json文件内容时出现问题(区别)。
②StreamingAssets文件夹手游端只可读,PC端可读可写,Resources文件夹相同。
详情可参照:Unity 对Application.streamingAssetsPath和Application.persistentDataPath路径的理解_wnagleiming的博客-优快云博客
动画:
animator.SetFloat("Speed", 0, dampTime, Time.deltaTime);,在设置状态机参数时可以加阻尼时间,使动作更加自然,Time.deltaTime固定要加
animator.IsInTransition(0)判断相应状态机的0层是否正在切换状态
animator.GetCurrentAnimatorStateInfo(0).IsTag("Attack")判断状态机第0层标签为Attack的动画是否正在播放
OnAnimatorMove方法,使用这个方法会覆盖根运动,调用时机为在相应状态机和动画已经被求值之后,但在OnAnimatorIK之前,若状态机的渲染模式为Normal,则每一帧执行(Update),若为
AnimatePhysics,则按照每隔固定时间执行(FixedUpdate)
animator.Play,有三个参数,第一个是想要播放动画的状态名,第二个是该状态在状态机的层数,第三个是从动画的哪个部分播放,为0时可以达到重新播放的效果
DoTween动画默认变换方式:开头和结尾速度比较慢,中间快的动画
DoTween中可以通过SetUpdate(true)来实现当前动画不受timeScale影响
DoTween可以通过SetId为动画设置Id,从而对该动画进行暂停,杀死,播放等操作
animator.runtimeAnimatorController = playerAnimator;设置animator运行时的状态机,仅限于编辑器状态下
AnimatorStateInfo.normalizedTime说明:
正常情况下返回当前播放的进度,下面博客讲异常情况下https://blog.youkuaiyun.com/yaoyutian/article/details/79507287
AnimatorStateInfo.lenght表示当前状态的时长
GetCurrentAnimatorStateInfo和GetCurrentAnimatorClipInfo区别:即State和Clip的区别,一个State可以包含多个Clip
位置变换:
transform.Translate:表示物体移动,如
transform.Translate(new Vector3 (1,1,1), Space.World);
第一个参数表示每执行一次方法,物体的的位置都会在x、y和z轴的正方向上移动1个单位,这里的x,y,z轴表示的局部坐标系的轴。第二参数是选择是以全球坐标系为主还是自身坐标系为主,也可以不写,此时默认为自身坐标系
说一下这个函数在使用时的一个坑:
使用标准:
为第一个参数尽量写Vector3.up等世界坐标系下的向量,而不要写transform.up等自身坐标系下的向量,第二个参数默认即可
使用误区:
若第一个参数用transform.up等自身坐标系下的向量,第二个参数保持默认的话也就是自身坐标系的话会出现混乱,解决方案可以用标准写法,也可以把第二个参数改为世界坐标系,具体的原因可能与该方法的实现有关
Vector3.MoveTowards:用于移动目标,如:
transform.position = Vector3.MoveTowards(transform.position, startPosition.position, speed * Time.deltaTime)
第一个参数是起始位置,第二个参数是目标位置,第三个参数是控制移动速度的(放在Update方法里),前面说过
MovePosition:刚体组件的方法,一个参数表示目标位置,可以将当前物体移动到目标物体,且受到物理效果的影响
与translate区别:老袁的u3d学习笔记之一:Translate方法和MovePosition方法的区别_老袁_新浪博客
Unity移动物体相关方法:https://www.cnblogs.com/qiaogaojian/p/6158924.html
UI:
mPassword.Select();//用代码实现选择一个InputField
mLoginBtn.onClick.Invoke();//用代码调用一个按钮被点击后的方法
EventSystem.current.IsPointerOverGameObject():若当前点击了UI界面,则返回True,否则返回False
在移动端检测可以用上面方法的重载,如下
int fingerId = Input.GetTouch(0).fingerId;
if (EventSystem.current.IsPointerOverGameObject(fingerId)) return;
在某些情况下,重载方法会引发其它的问题,这时可以自己写一个类似的方法,如下
private bool IsPointerOverGameObject(Vector2 mousePosition)
{
PointerEventData eventData = new PointerEventData(EventSystem.current);
//创建一个点击事件,在当前EventSystem下
eventData.position = mousePosition;//设置点击事件的位置
List<RaycastResult> raycastResults = new List<RaycastResult>();//这个是用来存射线碰撞到的物体的
EventSystem.current.RaycastAll(eventData, raycastResults);//有当前的EventSystem向点击位置发射射线,将碰撞到的物体存到上面的列表中
return raycastResults.Count > 0;
}
Image组件的图片其实还是Sprite
UI组件注册的函数无论是值在代码中改变还是玩家手动改变都会执行
UI物体的自适应需要自己做,非UI物体的缩放Unity已经处理好,位置默认情况下以高度为主(即无论游戏窗口如何改变竖直方向的物体总能看到),如更改为以宽度为主可用:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
public float initOrthoSize;
public float initWidth;
public float initHeight;
float factWidth;
float factHeight;
void Start()
{
factWidth = Screen.width;
factHeight = Screen.height;
//公式:实际视口 = 初始视口(最佳视口)*初始宽高比(最佳宽高比)/实际宽高比(实际宽高比)
GetComponent<Camera>().orthographicSize = initOrthoSize * (initWidth / initHeight) / (factWidth / factHeight);
}
}
parent.GetComponent<RectTransform>().sizeDelta = new Vector2(width, height);
//可以通过这种方式设置UI的长宽
对于 UGUI 元素来说,RectTransform.anchoredPosition (Vector2) 是Pivot相对于anchor来设置的位置,RectTransform.position (Vector3) 是三维坐标(in world space),是相对于世界原点的
RectTransform组件的anchoredPosition属性返回屏幕坐标,以pivot为准;position是世界坐标,以pivot为准
panelRectTrans.offsetMin = Vector2.zero; //RectTrans矩形左下角与锚点左下角的距离
panelRectTrans.offsetMax = Vector2.zero; //RectTrans矩形右上角与锚点右上角的距离
panelRectTrans.anchoredPosition3D = Vector3.zero; //RectTrans矩形轴心相对锚点的位置
panelRectTrans.anchorMin = Vector2.zero; //锚点位置,在代码中或者Debug模式下修改同时也会修改RectTrans的大小
panelRectTrans.anchorMax = Vector2.one; //但在Normal模式下修改只会改变锚点的位置
//panelRectTrans.sizeDelta RectTrans相对于锚点之间距离的大小
RectTransform和transform在世界画布的模式下位置坐标是一致的
UI图片和普通图片大小上的区别:UI图片和普通图片都是通过将原像素转化为unity单位,之后按照unity单位进行显示的,但转换方式略有差别(UI图片在得到最终unity单位时会多除一个Reference Pixels Per
Unit,如果Image设置为Slice,还会乘以一个Pixels Per Unit Multiplier),详见:https://blog.youkuaiyun.com/gz_huangzl/article/details/52484611和https://zhuanlan.zhihu.com/p/353169511
移动端:
安卓端真机调试(利用命令行和adb):在adb所在目录下输入以下命令:adb logcat -s Unity可以之查看Unity的Debug日志
基础触摸命令:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Test : MonoBehaviour
{
public Text info;
void Update()
{
if (Input.touchCount > 0)
{
//每当有手指触摸屏幕时Unity会创建一个Touch对象
info.text = string.Empty;
Touch myTouch = Input.touches[0];
//创建的Touch对象保存在touches数组中
//也可以使用Input.GetTouch(index)获得Touch对象
info.text += "fingerId:" + myTouch.fingerId + "\n";
//每一个手指的id
info.text += "deltaPostion:" + myTouch.deltaPosition + "\n";
//当前帧与上一帧的位置差
info.text += "deltaTime:" + myTouch.deltaTime + "\n";
//当前帧与上一帧的时间差
info.text += "tapCount:" + myTouch.tapCount + "\n";
//当一个手指抬起时所对应的Touch对象并不立即销毁,若在短时间内双击,则tapCount为2
info.text += "phase:" + myTouch.phase + "\n";
//触摸的阶段,开始,移动,静止,取消(由于某些原因,常常是系统原因,取消了对某个手指的追踪)
//myTouch.position表示Touch对象的位置
//myTouch.rawPosition表示的是Touch对象原始的位置
}
}
}
Input.backButtonLeavesApp = true;//将退出按键返回给Android
//Debug.Log(Input.GetAxis("Mouse X"));//鼠标移动在X方向上的位置差
//Debug.Log(Input.GetAxis("Mouse Y"));//鼠标移动在Y方向上的位置差
//Input.GetMouseButtonDown(0);
//上面的三个移动PC端通用,但前面两个要比移动端的deltaPostion要很多,UGUI的API基本都通用
Unity和Android方面
基本:
AndroidJavaClass jc = new AndroidJavaClass("android.util.Log"); //双引号中为具体到类的路径
jc.CallStatic<int>("e", "UnityAndroidTest", "ErrorTest"); //调用类中的静态方法,尖括号中的是返回值,没有返回值可以不写,然后参数第一个是方法名,后面是该方法的参数
AndroidJavaObject jo = new AndroidJavaObject("cn.tqt.test.Test") //双引号中为具体到类的路径,可以新建一个该类的对象
jo.Call<int>("add", 1, 2) //调用该实例的方法
关于fingerId:Unity 3D FingerID_hcking18的专栏-优快云博客
关于密钥:
在将游戏打包成apk发布到手机上时我们应该在Unity中设置好该apk的密钥(如果不设置的话会使用默认的debug密钥),密钥的作用:https://wenku.baidu.com/view/716168d028ea81c758f578ee.html,简单来
说就是标记一个特定的apk,(举个例子)后续如果需要更新时新的apk必须和旧的apk具有相同的密钥才能覆盖安装,否则将不能安装
屏幕显示:
Screen.SetResolution,设置分辨率,有三个参数,第一个是长,第二个是宽,第三个是是否全屏,如Screen.SetResolution(960, 600, false)
Screen.width和Screen.height分别表示当前游戏窗口的宽度和高度,以像素为单位
物理相关:
collider.bounds可以查看碰撞体的一些范围参数,注意bounds是一个结构体类型的变量,collider.bounds.center,碰撞体的中心,collider.bounds.extents,以x分量为例,即中心点到左侧(或右侧)边的距离,
collider.bounds.size计算方法为extents乘以2,collider.bounds.min,以x分量为例,即中心点的坐标减去extents的x分量,max加上,collider.bounds.Expand(2),表示将碰撞体的每条边以其中点为轴心长度增加2
collider是一个碰撞器对象,区别于collision(碰撞对象,存储一次碰撞中的信息)
检测碰撞方向:
if (collision.GetContact(0).normal.x == -1)//相对collision从右边撞过来
{
}
if (collision.GetContact(0).normal.x == 1)//相对collision从左边撞过来
{
}
if (collision.GetContact(0).normal.y == 1)//相对collision从下边撞过来
{
}
if (collision.GetContact(0).normal.y == -1)//相对collision从上边撞过来
{
}
//注意上面的值主要是参考Unity面板显示的值,运行时值有可能不正确,所以最好在使用之前自己先输出实验一下
//GetContact(0)是得到第一个接触点,normal是得到该接触点法线,即切线的垂线,该值为一个Vector2的向量,表示以切点为原点的末端点的坐标
//不再推荐用这种,最好是用下面这种
if (Mathf.Abs(collision.GetContact(0).normal.x+1) < errorCons)//从左边撞过来,判断它们的差是否在误差允许范围之内
{
}
collision.gameObject.name.Contains("Health"),碰撞检测时判断物体的名字中是否含指定的字母
Physics2D.IgnoreLayerCollision,取消指定两个层的碰撞,前两个参数为指定层(直接用层号表示),第三个参数为启用或者禁用碰撞
Physics2D.GetIgnoreLayerCollision,获得两个层之间是否存在碰撞,两个参数为指定层
另外还有可以取消和获得两个特定碰撞器之间碰撞的方法,用法同上面层级的相似
IsTouchingLayers:碰撞器的非静态方法,参数为有一个LayerMask,表示要检测的层
OverlapCircle:Physics2D的静态方法,用来检测指定层级内是否有物体与指定圆形区域发生碰撞,常用三个参数,圆心,半径,层级,返回值可以是碰撞的物体,也可以是bool类型(注意圆形区域是虚拟的)
Rigidbody的AddForce方法,参数为一个Vector3,即力的大小和方向,世界坐标
AddRelativeForce,自身坐标
rgd.bodyType = RigidbodyType2D.Dynamic改变刚体类型
Trigger碰撞器只能通过OnTrigger类方法发挥作用,其它情况下不发挥作用,如在OnCollision类方法中无法检测到Trigger碰撞器
碰撞等物理方法的执行间隔和FixedUpdate相同
父对象的碰撞器和子对象的碰撞器是独立的,并不是子对象的碰撞器融合到父对象的碰撞器上
用GetComponent得到一个挂有多个碰撞器的游戏物体上碰撞器组件时总是得到第一个,要用GetComponents才可以
以后尽量不要两个物体用相同的状态机,可以复制一个,改下名,因为用相同的状态机虽然可以,但给人的感觉不是很安全
Trigger碰撞事件参数是指造成当前这次触发事件的碰撞器,非Trigger碰撞事件参数指当前物体与碰撞物体的碰撞信息(若有多个碰撞物体,则每个物体都会执行一遍,碰撞信息也会有多个,分别对应每个物
体),其中在Exit事件中碰撞信息的碰撞点将为空,因为当前碰撞的物体已经离开了,所有自然不会有碰撞点
碰撞的Exit方法并不是一定要物体走出碰撞范围,只要没了碰撞就会执行(如禁用碰撞体,碰撞屏蔽等)
音乐:
AudioSource.PlayClipAtPoint:一般来说有两个参数,第一个是AudioClip类型,第二个是位置,也有第三个参数,音量
一些细节:
static void PlayClipAtPoint(AudioClip clip,Vector3 position, float volume = 1.0F);
使用AudioSource.PlayClipAtPoint播放声音,会自动生成一个名为"One shot audio"的物体,并自动添加了AudioSource和相应的audioclip,同时播放多个声音时会生成多个同名的物体,各声音的播放互不影响,
但缺点是只能设置音量,位置,不能设置loop(当然也不是不能实现的,只是比较麻烦),还有一些特殊的声音效果也不易实现,只能播放一次,播放完成后,One shot audio自动销毁
因此使用该静态方法不需要担心物体音乐还没播放完物体就被销毁的情况
PlayOneShot:与上面的方法相似,有一个AudioClip和一个float类型表示响度的变量,不是静态方法,只播放一次
上面两个方法的音量都可以大于1
再补充一点:
PlayOneShot只是在AudioSource没有音效脚本正在播放的情况下可以播放
Pause和Stop的大体区别可以理解前者是暂停,后者是终止
具体(Pause,UnPause,play,stop)
Pause后可以通过Play或UnPause继续播放 (IsPlaying: false - true)
IsPlaying为true时调用Play重新播放,UnPause无影响
Stop后只可以通过Play播放,UnPause无效
特性:
[SerializeField]:可以使私有变量或者自定义类的公有变量成为可序列化的(前提是该自定义类必须是可序列化的),它是Unity特有的标记
[System.Serializable]:对类、结构、枚举使用,表示该类型是可序列化的
注意:
1.Unity中只有可以序列化的对象才能够显示在Inspector面板中(私有变量和自定义类型的公有私有变量都是不可序列化的)
2.可序列化不代表公有,对私有变量进行可序列化操作只是让其在Inspector面板显示,代码之间的访问权限仍旧不变
3.Untiy中可以在面板显示类型(可序列化的)的对象Unity会自动对其进行初始化,否则不做处理
[HideInInspector]:一般在公有类型成员前面用,用于隐藏该成员,使之在Unity编辑器中的窗口消失
[ExecuteInEditMode]:unity开发 --------- ExecuteInEditMode_u012085988的专栏-优快云博客
[RequireComponent(typeof(Movement))]:加在MonoBehaviour类前,如果该脚本所在游戏物体缺少typeof中的组件时会自动给它加上
常用:
Invoke:控制方法调用时机,如Invoke("Fly", 0.1f);在0.1秒后执行Fly方法,注意第二个参数不能是double,不能调用静态函数
CanelInvoke();取消InvokeRepeating的调用
InvokeRepeating("Move", 0, deltaTime - 0.15f)第一个参数调用的方法名,第二个参数第一次多久后调用,第三个参数第一次之后每隔多久调用一次
Instantiate:实例化prefab对象,一般参数有三个,第一个是prefab的名称,第二个是在场景中的位置,第三个是关于旋转的
用法:Instantiate(boom, transform.position, Quaternion.identity);第三个参数一般都是这样用,意味着不旋转,Quaternion详情:Unity - Scripting API: Quaternion
当物体选择按照其它物体的角度初始化时物体的自身坐标系会与选择物体的自身坐标系重合
对拥有子级对象的可以使用GetChild(num),num为儿子的排名,如num=0,即为第一个儿子
gameObject.AddComponent<组件类型>(),返回值为组件引用
SetParent(foodHolder.transform, false);第二个参数表示是否保留世界坐标下的变换,true表示成为子物体前后物体的世界坐标始终不变(默认),false表示局部坐标不变,说详细点就是物体原本的变换和成为子物体后的变换相同(加载UI时常用)
GetKeyDown的检测时间比较短
代码为按钮添加点击事件:
GetComponent<Button>().onClick.AddListener(() => { EventCenter.Broadcast<string>(EventType.ShowText, "Hello World!"); });
//主要是AddListener()方法,可以给按钮的点击事件订阅一个方法,方法用lambda作为参数填写
GameObject是所有存在于Unity场景内的物体的基类
MonoBehaviour是所有挂载在GameObject上的组件的基类
它们都是Object的子类,之间并没有继承关系
Unity中的组件是引用类型
Unity生命周期函数
说明一下:
1.下面的方法都是在满足按照上图所示的时机执行,如:OnApplicationPause和OnDisable都是在满足相应的情况下(暂停和禁用),在一帧的相关方法都执行完毕之后才会执行,因此下面只讲了触发条件
2.注意图中两个编辑器下方法(OnDrrawGizmos和OnReset)的执行时机和其它方法没有可比性,但它们彼此可以比较
OnMouseUpAsButton,触发条件为鼠标在当前脚本所挂物体点击且在当前脚本所挂物体松开时触发
在游戏运行后动态的添加脚本,该脚本的Awake和Start函数依旧执行,即这种情况并不能影响脚本生命周期的执行(注意这里是添加,并不是禁用又启用,后者的情况若在禁用前生命周期函数已经执行,则禁用又启用,已经执行过的生命周期函数并不会再次执行)
将一个物体禁用相当于禁用其上的所有组件
注意禁用后只是不执行该脚本的生命周期函数
OnEnable:在游戏启动(在Start之前)或脚本启用时执行一次,如果脚本在启动前就是禁用的,那么在游戏启动时该方法不会执行
OnDisable:在游戏结束(在OnDestroy之前,OnDestroy除了在调用销毁语句时执行,也在游戏结束时执行)或者脚本禁用时执行一次,如果脚本在启动前就是禁用的,那么在游戏结束时该方法不会执行
OnReset:编辑器环境下执行,游戏运行时不会执行。编辑器环境下,当把一个脚本添加到某个游戏物体上或者点击了某个脚本的Reset按钮之后会执行一次
Destroy方法:删除一个游戏物体或组件等。如果Object为组件,则会将其从gameobject中删除并销毁;如为GameObject则将销毁它的全部组件及其所有子物体。可以指定多长时间后销毁。注意调用该方法后游戏物体或组件并不会立刻销毁,一般当前帧执行,下一帧就销毁了。物体销毁后其引用为null
DestroyImmediate和Destroy区别:【Unity】Destroy和DestroyImmediate的区别_Zok93-优快云博客
FixedUpdate:
如果物理时间间隔设置为0.02,则说明FixedUpdate每秒执行50次,但每次执行的时间间隔不同
new Vector3(x, y),最后z不写的话默认置为0,而不是用原来的值
Unity脚本的public变量,会优先以编辑器中设置的值为准而不是脚本中初始化的值,当我们使用HideInspactor隐藏后,该变量仍旧在面板存在,只是不显示,所以上述特性仍旧适用(一种方法是先将公有变量
显示,在编辑器界面设完值后再将其隐藏)
GetComponentInParent会从自己开始检查,同理GetComponentInChildren也相同
其它:
Setposition:设置线中顶点位置。使用:right.SetPosition(0, newPos.position);right.SetPosition(1, transform.position);其中right为一个LineRenderer的变量,这样可以可以在newPos.position和transform.position之间产生一条线段
Debug.DrawRay(down.transform.position, Vector2.down * 1f, Color.red);将射线检测的射线显示出来,第一个参数是射线开始的位置,第二个参数是方向和大小,第三个是颜色
SendMessage:用于对象之间的通信,常常为一个参数,为需要在其它脚本中调用的方法名,该方法可以是私有也可以是公有
若有第二个参数,则为目标函数的参数
关于双人游戏双方玩家的操作:可以参考siki学院Tanks教程
Physics2D.Linecast:一般两个position参数,即从第一个参数的位置向第二个参数的位置发射射线,返回一个RaycastHit2D值,可以通过查看该值的transform属性是否为空判断是否碰撞到物体,也可以通过调用collider属性来得到被碰撞的物体
ColorUtility.TryParseHtmlString:通过十六进制编码获取颜色,使用:ColorUtility.TryParseHtmlString("#CCEEFFFF", out tempColor); tempColor需要提前在外界声明,返回一个Color类型
Unity中的脚本一般继承自MonoBehaviour,所以可被当作组件添加显示到面板,当然还继承了其它一些内容,这里不再详述。若删去MonoBehaviour,则该脚本不仅不能当作组件添加到物体,而且若在其它脚本(继承自MonoBehaviour)中有该脚本类的对象,则该对象将不被面板显示,如何显示下面有介绍
不继承MonoBehaviour的一个例子,如下:
这里的每一个Element实际是一个类的对象,该类:
实际上,在unity里,自定义数据类型无法显示在inspector面板里,需要对定义数据类型的类或者结构体使用[System.Serializable]
参考:Unity3d学习日记:使用[System.Serializable]-腾讯游戏学堂
MatchTarget:
if (ani.GetCurrentAnimatorStateInfo(0).IsName("Slide") && ani.IsInTransition(0) == false)//判断当前状态是否处于第0层的Slide状态以及当前层中是否处于转换状态
{
ani.MatchTarget(matchTarget, Quaternion.identity, AvatarTarget.Root, new MatchTargetWeightMask(new Vector3(1, 1, 1), 0), 0.30f, 0.63f);
//参数分别为:需要匹配的位置,需要匹配的角度(前两个相似理解),需要匹配的部位,匹配的程度,0-1之间的数,若为1为完全匹配,0则是不去匹配,动画差值开始计算的时间,动画差值结束计算的时间
}
Ik动画:
void OnAnimatorIK(int layerIndex)//这个函数会同Update一样一直检测
{
if (layerIndex == 1) //判断当前层是否是第1层
{
ani.SetIKPosition(AvatarIKGoal.LeftHand, leftHand.position);//将对应骨骼设置到设定好的位置
ani.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1);//调整匹配的权重
//也可以调整旋转
ani.SetIKPosition(AvatarIKGoal.RightHand, rightHand.position);
ani.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
}
}
导航网格:
private NavMeshAgent navmeshagent;
navmeshagent.desiredVelocity //导航网格代理根据计算期望的速度,包括大小和方向
navmeshagent.nextPosition = transform.position;//控制导航网格代理的位置,常用来保证网格和相应物体位置不互相脱离
NavMeshPath path = new NavMeshPath();
if (navmeshagent.CalculatePath(other.transform.position, path))//
计算是否有到指定位置的路径,如果有保存在path中,不包括起点和终点
path.corners[i]数组存储路径上的每一个点
navmeshagent.updatePosition = false;//导航网格代理不再控制物体位置
navmeshagent.updateRotation = false;//导航网格代理不再控制物体旋转
navmeshagent.isStopped//导航网格代理停止运作
navmeshagent.remainingDistance//距离目的地还剩下的距离
navMeshAgent.stoppingDistance;//距离目的地多少距离停止
navMeshAgent.SetDestination();//设置导航网格代理的目的地
other.isTrigger发生碰撞检测的物体上的碰撞组件是否为触发检测
使用Unity标准资源包时获得相应组件上绑定的轴的值 (前提是Unity已内置好了标准资源包)
using UnityStandardAssets.CrossPlatformInput;
Debug.log(CrossPlatformInputManager.GetAxis("Horizontal"));
查看应用当前运行的平台:
if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
{
}
使用代码创建资源配置文件:
public class GameObjectPoolList : ScriptableObject
{//一般进行资源配置需要脚本挂载在游戏物体上,而继承该类后可以直接对脚本进行资源配置(该类可以序列化)
public List<GameObjectPool> poolList;
}
public class PoolManagerEditor
{
[MenuItem("Manager/Create GameObjectPoolConfig")]
static void CreateGameObjectPoolList()
{
GameObjectPoolList poolList = ScriptableObject.CreateInstance<GameObjectPoolList();
//创建继承自ScriptablieObject类的类的对象需要用上面的方法创建
string path = PoolManager.PoolConfigPath;//提供资源配置文件位于的地址
AssetDatabase.CreateAsset(poolList, path);//创建
AssetDatabase.SaveAssets();//保存
}
}
//详情:https://blog.youkuaiyun.com/candycat1992/article/details/52181814
Unity父物体缩放改变时,并不能按照正常的方式计算子物体的世界坐标,应将缩放考虑进去
EventSystem最好不要删,可以多个Canvas公用一个,否则一些UI事件可能不能正常进行(如按钮点击事件),除此之外,它也可以处理非UI事件
关于事件的介绍:Unity3D中uGUI事件系统简述及使用方法总结 - Tiny&zzh - 博客园
需要注意的PointerUp事件必须先有PointerDown事件触发才能触发,而且PointerUp的触发没有区域限制,即哪怕鼠标松开时其位置不在挂载PointerUp脚本的物体上也会触发
TimeScale:
Time.timeScale影响的是Unity中的游戏时间缩放比例,Unity中里面所有跟时间有关系的东西都是根据timeScale来演算的
将TimeScale设为0暂停的都是所有和帧率无关的事情,这些主要是指所有的物理事件和依赖时间的函数、刚体力和速度等,而且FixedUpdate将不会执行,但Update和LateUpdate还会
有错误欢迎指出