易遗忘的知识点笔记
TransformPoint和InverseTransformPoint
TransformPoint 是变换自身坐标到世界坐标 ,InverseTransformPoint 是变换世界坐标到自身坐标
比如说物体A的坐标内有一个3,3,3的点 你想知道这个点在世界坐标的位置 就应该用TransformPoint
反之在世界坐标下有一个点 你想知道这个点如果是在物体A的坐标下是一个什么位置 就应该用InverseTransformPoint
其实就是在编辑器里把物体拽到根目录下的位置和物体在某物体内的位置之间的一个转换
Vector3 local_pos = transform.InverseTransformPoint(new Vector3(15, 15, 0));//变换世界坐标到自身坐标
Debug.Log(local_pos);//输出的局部坐标的值和挂载脚本的transform中的position、scale有关。输出的局部坐标的值local_pos =(世界坐标的值-transform.position)/scale
如世界坐标的值是(15,15,0),transform的position是(1,1,0),transform的scale是(0.5,0.5,1),则输出的局部坐标的值为(15-1)/0.5=28,即此时世界坐标(15,15,0)转局部坐标为(28,28,0);
Mathf
Mathf.RoundToInt 四舍五入到整数返回指定的值四舍五入到最近的整数。如果数字末尾是.5,因此它是在两个整数中间,不管是偶数或是奇数,将返回偶数。
如:
Debug.Log(Mathf.RoundToInt(-10.7));// -11
Debug.Log(Mathf.RoundToInt(-10.5));// -10
Debug.Log(Mathf.RoundToInt(11.5));// 12
Debug.Log(Mathf.RoundToInt(10.2));// 10
Debug.Log(Mathf.RoundToInt(10.7));// 11
Random.insideUnitSphere返回半径为1的球体内的一个随机点。(只读)
Mathf .RoundToInt返回f四舍五入到最接近的整数。
如果数字以.5结尾,那么它位于两个整数之间的中间,其中一个为偶数,另一个为奇数,则返回偶数。
Mathf.NextPowerOfTwo返回下一个2的幂值。
Debug.Log(Mathf.NextPowerOfTwo(7));// prints 8
Debug.Log(Mathf.NextPowerOfTwo(139));// prints 256
Mathf .ClosestPowerOfTwo返回两个值的最接近的幂
Debug.Log(Mathf.ClosestPowerOfTwo(7)); //打印8
Debug.Log(Mathf.ClosestPowerOfTwo(19));//打印16
Tooltip
[Tooltip("彩笔颜色")]
public Color Pen_Color = new Color(255f, 255f, 255f, 0f);
[Tooltip("钢笔宽度")]
public int Pen_Width = 20;
在Inspector面板上,将鼠标放在Pen_Color上会显示“彩笔颜色”这几个字。
同理,将鼠标放在Pen_Width上会显示“钢笔宽度”这几个字。
Texture
texture.GetPixels(x, y, texture.width, texture.height);//获取以x,y 为起始点,大小为width,height的一个区块,返回的是一个数组,数组内颜色的点顺序为从左至右,从下至上。
texture.GetPixels32();//获取(指定mipmap level级别)的整张贴图的像素颜色(使用Color32格式),返回的是Color32[]类型的数组。
Physics2D.OverlapPoint 重叠点
相关参数介绍
point: 一个世界空间中的点。
layerMask:只在某些层过滤检测碰撞器。
minDepth:只包括Z坐标(深度)大于这个值的对象。
maxDepth:只包括Z坐标(深度)小于这个值的对象。
作用:
检测一个碰撞器是否与世界空间中的一个点重叠。
还要注意这个函数会分配内存给返回的Collider2D对象。在你需要频繁做这个检测的时候,可以使用OverlapPointNonAlloc这个函数去避免这些开销。
Vector3 position = Camera.main.ScreenToWorldPoint (Input.mousePosition);
var object = Physics2D.OverlapPoint (new Vector2 (position .x, position .y),
Vector3.normalized和Vector3.Normalized区别
Vector3.normalized
当前向量是不改变,返回一个新的规范化的向量,只读,返回值是vector3类型的,所以定义一个新的vector3类型的变量来接收它。
Vector3.Normalize
改变当前向量,当前向量长度是变为1,无返回值。
Vector3 pos = new Vector3(3, 4, 5);
Vector3 pos1 = pos.normalized;
Debug.Log(pos1);
Vector3 p = new Vector3(3, 4, 5);
p.Normalize();
Debug.Log(p);
两者输出的值都是(0.4,0.6,0.7),输出的值都是一样的。
主角旋转
float v = Input.GetAxis("Vertical");
float h = Input.GetAxis("Horizontal");
if(Mathf.Abs(v)>0.1f|| Mathf.Abs(h) > 0.1f)
{
Vector3 dir = new Vector3(h, 0, v);
Quaternion targetDir = Quaternion.LookRotation(dir, Vector3.up);
transform.rotation = Quaternion.Lerp(transform.rotation, targetDir, rotateSpeed);
}
操作2D网格
我们打图集的时候,采取紧密的打图集的方式的话,图集的某个sprite中可能包含有附近的sprite的一些残缺的图片内容在里面,这时我们可以自己写个Image将其余内容过滤掉
不过在Unity2018或者之后的版本中,Unity的Image里面就内置有了这个功能,Image下有个Use Sprite Mesh,打勾就可以了。
零碎的知识点
Sprite不能强转为Object
判断一个物体是否为空,用System.Object.ReferenceEquals(Obj,null)效率比较高
Animation.Sample()如果想让Animation在编辑器状态下预览,也可以用这个接口
当你想要直接获得动画的运行结果,而不是等帧数执行到这,这时候就得调用Sample
Texture2D.GetRawTextureData()获取贴图的原始数据,运行时使用需要开启贴图的Read/Write,如果出于节省内存的考虑,可以在编辑模式下提前提取贴图的原始数据,运行期在合并。
KeyValuePair 和 Dictionary 的关系
1、KeyValuePair
a、KeyValuePair 是一个结构体(struct);
b、KeyValuePair 只包含一个Key、Value的键值对。
2、Dictionary
a、Dictionary 可以简单的看作是KeyValuePair 的集合;
b、Dictionary 可以包含多个Key、Value的键值对。
AssetDatabase-FindAssets此类用于查找资源,filter参数包括名称,标签或者是类型
Name
通过资源的名字来过滤(不包括扩展名),通过空格隔开的字段当成一个独立的名字来搜索。比如“test asset”,这个资源的名字就会被隔开。注意的是,名字可以用来定义资源。此外,过滤的字段还可以指定为子节。比如例子中的 test asset 就可以用test来匹配。
Labels
资源可以添加标签,特定标签的资源可以用在每个标签前加关键字"l:"来查找,这就表明了此字符串是通过标签来查找的。
Types
查找明确定义了类型的资源,可以用在资源类型前添加关键字 “t:"的方法。如果过滤字符串中包含多个类型,那么符合其中一个类型的资源都会返回。这里的类型既可以是内置的类型如"Texture2d"或者用建立的脚本类型。 创建的类是指在项目里创建的ScriptObject类。如果想查找所有的资源,就用 Object.
第二个参数用于限制查找的路径和这些路径的子文件。这就不在所有资源中查找快多了。
AssetPostprocessor的用法
AssetPostProcessor是一个编辑器类,一个资源导入的一个管理器,在资源导入之前和之后可以根据导入的资源做一些设置和一些数据的修改,比如网格,纹理的压缩,模型添加组件等。简单使用当资源导入之前和之后都会发送通知,可以根据不同的资源类型,在导入之前和之后做不同的处理。
using System.Collections;
using UnityEditor;
public class MyEditor : AssetPostprocessor {
//模型导入之前调用
public void OnPreprocessModel()
{
Debug.Log ("OnPreprocessModel="+this.assetPath);
}
//模型导入之前调用
public void OnPostprocessModel(GameObject go)
{
Debug.Log ("OnPostprocessModel="+go.name);
}
//纹理导入之前调用,针对入到的纹理进行设置
public void OnPreprocessTexture()
{
Debug.Log ("OnPreProcessTexture="+this.assetPath);
TextureImporter impor = this.assetImporter as TextureImporter;
impor.textureFormat = TextureImporterFormat.ARGB32;
impor.maxTextureSize = 512;
impor.textureType = TextureImporterType.Advanced;
impor.mipmapEnabled = false;
}
public void OnPostprocessTexture(Texture2D tex)
{
Debug.Log ("OnPostProcessTexture="+this.assetPath);
}
public void OnPostprocessAudio(AudioClip clip)
{
}
public void OnPreprocessAudio()
{
AudioImporter audio = this.assetImporter as AudioImporter;
audio.format = AudioImporterFormat.Compressed;
}
//所有的资源的导入,删除,移动,都会调用此方法,注意,这个方法是static的
public static void OnPostprocessAllAssets(string[]importedAsset,string[] deletedAssets,string[] movedAssets,string[]movedFromAssetPaths)
{
Debug.Log ("OnPostprocessAllAssets");
foreach (string str in importedAsset) {
Debug.Log("importedAsset = "+str);
}
foreach (string str in deletedAssets) {
Debug.Log("deletedAssets = "+str);
}
foreach (string str in movedAssets) {
Debug.Log("movedAssets = "+str);
}
foreach (string str in movedFromAssetPaths) {
Debug.Log("movedFromAssetPaths = "+str);
}
}
}
点击任意区域继续…
在游戏中,可能我们需要点击任意区域关闭显示或者继续其他的操作之类的,这时我们会新建一个Image,将UI填满整个屏幕,然后再添加点击事件,确实是这么做,但这么做有个什么弊端呢?就会会造成过多的overdraw,而一个Image是由Rect区域来控制触发响应事件的,由mesh来显示形状的,而产生overdraw就是由于mesh产生的,所以我们清除掉mesh就不会产生overdraw了。
新建一个在Canvas中的空物体,再添加一个自己新建的脚本挂载上去,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RaycastTest : Image
{
protected override void OnPopulateMesh(VertexHelper toFill)
{
toFill.Clear();
}
}
这样就避免了产生过多的overdraw,而且还能响应点击事件。
射线检测的层级
//一条从主相机到屏幕点击点的射线
Ray ray = Camera.Main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
//发射一条射线返回相关信息(如需设定检测所需层级,距离一定要加!!!)
//CanRay为定义的LayerMask层,如无法手动添加选择,可代码
// int CanRay=1 << 10 ;
/*
1 << 10 打开第10的层。
~(1 << 10) 打开除了第10之外的层。
~(1 << 0) 打开所有的层。
(1 << 10) | (1 << 8) 打开第10和第8的层。
*/
if (Physics.Raycast(ray, out hit, 1000, CanRay))
{
if (hit.transform.gameObject.name == “renwu”)
{
print(“移动射线检测打到了人物”);
}
//当父子物体都有碰撞器时,如需检测子物体,需将父物体层级关闭,或只打开子物体层级
//但如需获取子物体信息时,需用hit.collider.name(名字),如用hit.transform.name时,返回的是父物体名字
}
UI点击(只点击UI)
///
/// 判断是否点击的是UI,有效应对安卓没有反应的情况,true为UI
///
private bool IsPointerOverUIObject()
{
PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
List results = new List();
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
return results.Count > 0;
}
在屏幕上实时显示帧率
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShowFrameRate : MonoBehaviour
{
//上一次更新帧率的时间
private float lastUpdateShowTime = 0f;
//更新帧率的时间间隔
private float updateShowDeltaTime = 0.1f;
//updateShowDeltaTime秒内执行的帧数
private int frameCount = 0;
private float fPS = 0;
private GUIStyle fontStyle;
void Awake()
{
//Application.targetFrameRate = 100;
fontStyle = new GUIStyle();
fontStyle.normal.background = null; //设置背景填充
fontStyle.normal.textColor = Color.red; //设置字体颜色
fontStyle.fontSize = 40; //字体大小
}
void Start()
{
lastUpdateShowTime = Time.realtimeSinceStartup;
}
void Update()
{
frameCount++;
if (Time.realtimeSinceStartup - lastUpdateShowTime >= updateShowDeltaTime)
{
fPS = frameCount / (Time.realtimeSinceStartup - lastUpdateShowTime);
frameCount = 0;
lastUpdateShowTime = Time.realtimeSinceStartup;
}
}
void OnGUI()
{
GUI.Label(new Rect(Screen.width / 4, 0, Screen.width / 5, Screen.width / 10), "FPS: " + fPS,fontStyle);
}
}
人物幻影效果:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerPhantom : MonoSingleton<PlayerPhantom>
{
public SkinnedMeshRenderer player;
public MeshFilter myMesh;
public MeshRenderer myRenderer;
public Animation myAnim;
// Start is called before the first frame update
public void StartPhantom()
{
StartCoroutine(Phantom());
}
IEnumerator Phantom()
{
yield return new WaitForSeconds(0.3f);
myRenderer.enabled = true;
Mesh temp = new Mesh();
player.BakeMesh(temp);
myMesh.mesh = temp;
myAnim.Play("PlayerShadow");
yield return new WaitForSeconds(0.5f);
myRenderer.enabled = false;
}
}
在程序中显示当前系统时间,不论系统时间格式设置成什么,界面显示的时间都必须是“yyyy/MM/dd HH:mm:ss"格式的。
this.label2.Text = System.DateTime.Now.ToString(“yyyy/MM/dd HH:mm:ss”,System.Globalization.DateTimeFormatInfo.InvariantInfo);
批处理命令 压缩文件 并复制到某个目录
WinRAR a -r -ep1 %ANDROID_Project%.rar %ANDROID_Project%
echo Copy Patch:%ANDROID_Project%.rar %FILE_PATH%
copy /y %ANDROID_Project%.rar %FILE_PATH%
ContentSizeFitter嵌套使用时需要强制刷新
foreach (ContentSizeFitter child in LevelUpNeedOne.GetComponentsInChildren<ContentSizeFitter>(true))
{
LayoutRebuilder.ForceRebuildLayoutImmediate(child.GetComponent<RectTransform>());
}//强制刷新,否则嵌套ContentSizeFitter 不会及时刷新