—— 系列文章链接
目录
四十二、unity+Loom实现多线程(Thread)和主线程(MainThread)交互
四十五、某物体发射射线打在远处平面,射线与平面的交点,映射到屏幕上(物体旋转发射的射线,转为准心位置)
四十八、判断目标物体是否在视野内 TargetCusorOnBecameVisible
五十一、OnDrawGizmosSelected() 和 OnDrawGizmos() 的区别
四十一、unity 文件流读取图片和www读取图片的比较
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
public class TestReadAndWWW : MonoBehaviour
{
public RawImage img;
public RawImage img1;
string path;
// Start is called before the first frame update
void Start()
{
path = Application.dataPath + "/M1000_001.jpg";
LoadByIO();
StartCoroutine(LoadByWWW());
}
// Update is called once per frame
void Update()
{
}
void LoadByIO()
{
//float time = Time.time;
System.DateTime dtm = System.DateTime.Now;
float time = dtm.Millisecond;
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
fs.Seek(0, SeekOrigin.Begin);
byte[] bytes = new byte[fs.Length];
fs.Read(bytes, 0, (int)fs.Length);
fs.Close();
fs.Dispose();
fs = null;
Texture2D t = new Texture2D(1, 1);
t.LoadImage(bytes);
img.texture = t;
Debug.Log("IO读取图片用时:" + (System.DateTime.Now.Millisecond - time));
//Debug.Log("IO读取图片用时:" + (Time.time - time));
}
IEnumerator LoadByWWW()
{
//float time = Time.time;
System.DateTime dtm = System.DateTime.Now;
float time = dtm.Millisecond;
WWW w = new WWW("file://" + path);
yield return w;
if (string.IsNullOrEmpty(w.error) == false)
{
Debug.Log("error");
}
else
{
img1.texture = w.texture;
}
Debug.Log("www读取图片用时:" + (System.DateTime.Now.Millisecond - time));
}
}
四十二、unity+Loom实现多线程(Thread)和主线程(MainThread)交互
Loom代码不多,只有168行, 然而却具备了子线程运行Action, 子线程与主线程交互的能力!
public static Thread RunAsync(Action a)
public static void QueueOnMainThread(Action action)
public static void QueueOnMainThread(Action action, float time)
首先Loom类继承自MonoBehaviour,第一次使用静态的Loom.Current时,就会判断,是否初始化(其实就是将Loom 挂 载到 一个自己创建的GameObject上),这样,QueueOnMainThread放进来的action就可以在每一帧回调的Update方法进行action调用。
RunAsync用线程池,运行在子线程中。 使用的时候, 当子线程的工作完成后, 可以在后面加一句Loom.QueueOnMainThread()实现线程切换!
最近在做资源更新时,需要显示现在的进度调。我在Unity中开启了一个线程来做下载任务,然后实时刷新下载进度。然后Unity报了一个错误。
get_isActiveAndEnabled can only be called from the main thread.
意思是Unity中的组件只能运行在Unity的主线程中,无法在新开的线程中调用Unity的组件。
用Loom实现多线程与主线程交互
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;
public class Loom :MonoBehaviour
{
public static int maxThreads = 8;
static int numThreads;
private static Loom _current;
//private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
static bool initialized;
public static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying)
return;
initialized = true;
var g = new GameObject("Loom");
_current = g.AddComponent<Loom>();
#if !ARTIST_BUILD
UnityEngine.Object.DontDestroyOnLoad(g);
#endif
}
}
public struct NoDelayedQueueItem
{
public Action<object> action;
public object param;
}
private List<NoDelayedQueueItem> _actions = new List<NoDelayedQueueItem>();
public struct DelayedQueueItem
{
public float time;
public Action<object> action;
public object param;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action<object> taction, object tparam)
{
QueueOnMainThread(taction, tparam, 0f);
}
public static void QueueOnMainThread(Action<object> taction, object tparam, float time)
{
if (time != 0)
{
lock (Current._delayed)
{
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = taction, param = tparam });
}
}
else
{
lock (Current._actions)
{
Current._actions.Add(new NoDelayedQueueItem { action = taction, param = tparam });
}
}
}
public static Thread RunAsync(Action a)
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(100);
}
Interlocked.Increment(ref numThreads);
ThreadPool.QueueUserWorkItem(RunAction, a);
return null;
}
private static void RunAction(object action)
{
try
{
((Action)action)();
}
catch
{
}
finally
{
Interlocked.Decrement(ref numThreads);
}
}
void OnDisable()
{
if (_current == this)
{
_current = null;
}
}
// Use this for initialization
void Start()
{
}
List<NoDelayedQueueItem> _currentActions = new List<NoDelayedQueueItem>();
// Update is called once per frame
void Update()
{
if (_actions.Count > 0)
{
lock (_actions)
{
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
for (int i = 0; i < _currentActions.Count; i++)
{
_currentActions[i].action(_currentActions[i].param);
}
}
if (_delayed.Count > 0)
{
lock (_delayed)
{
_currentDelayed.Clear();
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
for (int i = 0; i < _currentDelayed.Count; i++)
{
_delayed.Remove(_currentDelayed[i]);
}
}
for (int i = 0; i < _currentDelayed.Count; i++)
{
_currentDelayed[i].action(_currentDelayed[i].param);
}
}
}
}
未使用Loom报错方法
public Text mText;
void Start ()
{
Thread thread = new Thread(RefreshText);
thread.Start();
}
void Update ()
{
}
private void RefreshText()
{
mText.text = "Hello Loom!";
}
更改后
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Threading;
public class testLoom : MonoBehaviour
{
public Text mText;
void Start ()
{
// 用Loom的方法调用一个线程
Loom.RunAsync(
() =>
{
Thread thread = new Thread(RefreshText);
thread.Start();
}
);
}
void Update ()
{
}
private void RefreshText()
{
// 用Loom的方法在Unity主线程中调用Text组件
Loom.QueueOnMainThread((param) =>
{
mText.text = "Hello Loom!";
},null);
}
}
熟悉Unity的developer都知道在Unity中的线程不能使用Unity的对象,但可以使用Unity的值类型变量,如Vector3等。这样就使得线程在Unity中显的很鸡肋和蹩脚,因为很多函数很都是UnityEngine类或函数的调用的,对于哪些是可以在多线程使用,风雨冲进行了如下总结:
0. 变量(都能指向相同的内存地址)都是共享的
1. 不是UnityEngine的API能在分线程运行
2. UnityEngine定义的基本结构(int,float,Struct定义的数据类型)可以在分线程计算,如 Vector3(Struct)可以 , 但Texture2d(class,根父类为Object)不可以。
3. UnityEngine定义的基本类型的函数可以在分线程运行,如
int i = 99;
print (i.ToString());
Vector3 x = new Vector3(0,0,9);
x.Normalize();
类的函数不能在分线程运行
obj.name
实际是get_name函数,分线程报错误:get_name can only be called from the main thread.
Texture2D tt = new Texture2D(10,10);
实际会调用UnityEngine里的Internal_Create,分线程报错误:Internal_Create can only be called from the main thread.
其他transform.position,Texture.Apply()等等都不能在分线程里运行。
结论: 分线程可以做 基本类型的计算, 以及非Unity(包括.Net及SDK)的API。
Unity做了这个限制,主要是Unity的函数执行机制是帧序列调用,甚至连Unity的协程Coroutine的执行机制都是确定的,如果可以使用多线程访问UnityEngine的对象和api就得考虑同步问题了,也就是说Unity其实根本没有多线程的机制,协程只是达到一个延时或者是当指定条件满足是才继续执行的机制。
我们的项目目前还有没有比较耗时的计算,所以还没有看到Thread的使用。本来一直没有太考虑着方面的事情,直到在UnityGems.com看到Loom这个类,叹为观止呀。直接贴出人家的介绍(没必要翻译了):
Threads on a Loom
Our class is called Loom. Loom lets you easily run code on another thread and have that other thread run code on the main game thread when it needs to.
There are only two functions to worry about:
- RunAsync(Action) which runs a set of statements on another thread
- QueueOnMainThread(Action, [optional] float time) - which runs a set of statements on the main thread (with an optional delay).
You access Loom using Loom.Current - it deals with creating an invisible game object to interact with the games main thread.
我们只需要关系两个函数:RunAsync(Action)和QueueOnMainThread(Action, [optional] float time) 就可以轻松实现一个函数的两段代码在C#线程和Unity的主线程中交叉运行。原理也很简单:用线程池去运行RunAsync(Action)的函数,在Update中运行QueueOnMainThread(Acition, [optional] float time)传入的函数。
直接贴出源码,供拜读:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;
public class Loom : MonoBehaviour
{
public static int maxThreads = 8;
static int numThreads;
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
static bool initialized;
static void Initialize()
{
if (!initialized)
{
if(!Application.isPlaying)
return;
initialized = true;
var g = new GameObject("Loom");
_current = g.AddComponent<Loom>();
}
}
private List<Action> _actions = new List<Action>();
public struct DelayedQueueItem
{
public float time;
public Action action;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action action)
{
QueueOnMainThread( action, 0f);
}
public static void QueueOnMainThread(Action action, float time)
{
if(time != 0)
{
lock(Current._delayed)
{
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action});
}
}
else
{
lock (Current._actions)
{
Current._actions.Add(action);
}
}
}
public static Thread RunAsync(Action a)
{
Initialize();
while(numThreads >= maxThreads)
{
Thread.Sleep(1);
}
Interlocked.Increment(ref numThreads);
ThreadPool.QueueUserWorkItem(RunAction, a);
return null;
}
private static void RunAction(object action)
{
try
{
((Action)action)();
}
catch
{
}
finally
{
Interlocked.Decrement(ref numThreads);
}
}
void OnDisable()
{
if (_current == this)
{
_current = null;
}
}
// Use this for initialization
void Start()
{
}
List<Action> _currentActions = new List<Action>();
// Update is called once per frame
void Update()
{
lock (_actions)
{
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
foreach(var a in _currentActions)
{
a();
}
lock(_delayed)
{
_currentDelayed.Clear();
_currentDelayed.AddRange(_delayed.Where(d=>d.time <= Time.time));
foreach(var item in _currentDelayed)
_delayed.Remove(item);
}
foreach(var delayed in _currentDelayed)
{
delayed.action();
}
}
}
怎么实现一个函数内使用多线程计算又保持函数体内代码的顺序执行,印象中使用多线程就是要摆脱代码块的顺序执行,但这里是把原本一个函数分拆成为两部分:一部分在C#线程中使用,另一部还是得在Unity的MainThread中使用,怎么解决呢,还得看例子:
//Scale a mesh on a second thread
void ScaleMesh(Mesh mesh, float scale)
{
//Get the vertices of a mesh
var vertices = mesh.vertices;
//Run the action on a new thread
Loom.RunAsync(()=>{
//Loop through the vertices
for(var i = 0; i < vertices.Length; i++)
{
//Scale the vertex
vertices[i] = vertices[i] * scale;
}
//Run some code on the main thread
//to update the mesh
Loom.QueueOnMainThread(()=>{
//Set the vertices
mesh.vertices = vertices;
//Recalculate the bounds
mesh.RecalculateBounds();
});
});
}
这个例子是对Mesh的顶点进行放缩,同时也是一个使用闭包(closure)和lambda表达式的一个很好例子。看完例子,是不是很有把项目中一些耗时的函数给拆分出来,想用这个方法来改进下NGUI的底层机制(看下性能不能改进)。
小结:
本来我以为Loom的实现会比较复杂,当我发现只有100多行的代码是大为惊叹,这也得益于现在语言的改进,至少从语言使用的便利性上还是有很大的进步的。
有了Loom这个工具类,在很多涉及UnityEngine对象的耗时计算还是可以得到一个解决方法的:
如在场景中用A*算法进行大量的数据计算
变形网格中操作大量的顶点
持续的要运行上传数据到服务器
二维码识别等图像处理
Loom简单而又巧妙,佩服Loom的作者。
首先Unity一般是避免使用多线程的,unity提供了一种协程的概念(coroutine) yield,但是这个协程归根到底也不是多线程,它只是起到一个延迟执行的效果。
但是为什么我们需要使用多线程呢?前段时间项目中有一些地方使用协程并不能达到很好的效果,比如在游戏内进行大批量的拷贝和移动文件时,UI显示上会出现卡死现象,我怀疑是主线程被阻塞了。换成多线程后成功解决了问题。
适合使用多线程的地方
- ·解压缩资源
- ·IO操作
- ·网络请求
- ·大量的数据操作
- ·一些资源的加载
使用多线程是请注意
- ·Unity API 无法在Thread中被调用,同时包括所有component
- · 变量都是共享的(都能指向相同的内存地址)
- · UnityEngine 定义的基本结构(int, float, struct 定义的数据类型)可以在分线程计算,如 Vector3(struct)可以, 但 Texture2d(class,根父类为 Object) 不可以
如何使用多线程
多线程使用时必须非常谨慎,而unity自带的Thread,并不是mono而是.net,是两套框架。therad的使用晦涩复杂,同时还需要注意线程安全问题。
所以我选择了一个市面上封装比较好的Loom工具类,代码很少,将会在最后贴出来,非常好用。
//RunAsync 用线程池,运行在子线程。
public static Thread RunAsync(Action a)
//主线程
public static void QueueOnMainThread(Action action)
public static void QueueOnMainThread(Action action, float time)
举个栗子
Loom.RunAsync(delegate {
do something
});
全部代码
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;
public class Loom : MonoBehaviour
{
public static int maxThreads = 10;
public static int numThreads;
private static Loom _current;
private int _count;
public static Loom Current
{
get
{
Initialize();
return _current;
}
}
void Awake()
{
_current = this;
initialized = true;
}
public static bool initialized;
static void Initialize()
{
if (!initialized)
{
if (!Application.isPlaying) {
return;
}
initialized = true;
var g = new GameObject("Loom");
_current = g.AddComponent<Loom>();
}
}
private List<Action> _actions = new List<Action>();
public struct DelayedQueueItem
{
public float time;
public Action action;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action action)
{
QueueOnMainThread(action, 0f);
}
public static void QueueOnMainThread(Action action, float time)
{
if (time != 0)
{
lock (Current._delayed)
{
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
}
}
else
{
lock (Current._actions)
{
Current._actions.Add(action);
}
}
}
public static Thread RunAsync(Action a)
{
Initialize();
while (numThreads >= maxThreads)
{
Thread.Sleep(1);
}
Interlocked.Increment(ref numThreads);
ThreadPool.QueueUserWorkItem(RunAction, a);
return null;
}
private static void RunAction(object action)
{
try
{
((Action)action)();
}
catch
{
}
finally
{
Interlocked.Decrement(ref numThreads);
}
}
void OnDisable()
{
if (_current == this)
{
_current = null;
}
}
// Use this for initialization
void Start() {
}
List<Action> _currentActions = new List<Action>();
// Update is called once per frame
void Update() {
lock (_actions) {
_currentActions.Clear();
_currentActions.AddRange(_actions);
_actions.Clear();
}
foreach (var a in _currentActions) {
a();
}
lock (_delayed) {
_currentDelayed.Clear();
_currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
foreach (var item in _currentDelayed) {
_delayed.Remove(item);
}
}
foreach (var delayed in _currentDelayed) {
delayed.action();
}
}
}
四十三、Unity Android 权限
Android权限
权限是一种限制,用于限制对部分代码或设备上数据的访问。施加限制是为了保护可能被误用以致破坏或损害用户体验的关键数据和代码。每种权限均由一个唯一的标签标识。标签通常指示受限制的操作。
如果应用需要访问受权限保护的功能,则必须在清单中使用 元素声明应用需要该权限。将应用安装到设备上之后,安装程序会通过检查签署应用证书的颁发机构并(在某些情况下)询问用户,确定是否授予请求的权限。如果授予权限,则应用能够使用受保护的功能。否则,其访问这些功能的尝试将会失败,并且不会向用户发送任何通知。
参考: https://developer.android.com/guide/topics/manifest/manifest-intro.html#perms
Unity 权限
Android的权限配置于AndroidManifest.xml中
Unity会自动生成一些权限(Unity生成一个AndroidManifest.xml),然后找到插件(AAR和Android Libraries)的所有Android Manifest。合并到unity生成的xml中。这些都是Unity自动完成的。即使你指定了一份AndroidManifest.xml,Unity仍然会修改或加入一些Unity工程中所需的权限。如要完全修改可以导出Android工程进行修改,生成APK。
Unity根据您的应用程序从脚本调用的设置和Unity API,自动添加必要的权限到清单。例如:
- 网络类添加INTERNET权限(例如在player setting中Internet Access选为require,这时即使你没有网络访问,unity仍会添加Internet权限)
- 使用振动(如Handheld.Vibrate)增加VIBRATE
- 判断网络状态InternetReachability增加ACCESS_NETWORK_STATE
- 位置API(如LocationService)会添加ACCESS_FINE_LOCATION
- WebCamTexture API添加CAMERA权限
- 启用麦克风增加RECORD_AUDIO
- PlayerSetting 中WriteAccess 设置为 Exxternal(SDCard),会添加相应权限
APK在Android6.0设备上运行( Android API大于等于23),则应用程序将使用Android 运行时权限系统。
Android运行时权限系统要求应用程序在运行时授予权限,而不是首次安装时就获取权限。当应用程序运行时,应用程序用户通常可以授予或拒绝每个权限(例如,在拍摄照片之前请求摄像机许可)。这允许应用程序在没有权限的情况下运行有限的功能。
Unity不支持运行时权限系统,所以您的应用程序提示用户允许Android在启动时称为“危险”权限。有关更多信息,请参阅Android关于危险权限的文档。
提示用户允许危险的权限是确保在缺少权限时不会导致崩溃的唯一方法。但是,如果你不想让程序运行开始就弹出这些权限的窗口,可以在AndroidManifest中添加
有关运行时权限系统和处理权限的更多信息,请参阅Android开发人员文档的请求权限部分。
手动修改Unity AndroidManifest的方法
要使用Unity之外创建的Android清单,请将自定义的Android Manifest文件导入到以下位置:Assets / Plugins / Android / AndroidManifest.xml。这将覆盖默认的Unity创建的清单。
在这种情况下,Android Libraries的清单随后会被合并到这份清单中,并且所生成的清单仍然被Unity调整,以确保配置正确。要完全控制清单,包括权限,您需要导出项目并修改Android Studio中的最终清单。
- android.permission.ACCESS_CHECKIN_PROPERTIES 访问登记属性 读取或写入登记check-in数据库属性表的权限
- android.permission.ACCESS_COARSE_LOCATION 获取错略位置 通过WiFi或移动基站的方式获取用户错略的经纬度信息,定位精度大概误差在30~1500米
- android.permission.ACCESS_FINE_LOCATION 获取精确位置 通过GPS芯片接收卫星的定位信息,定位精度达10米以内
- android.permission.ACCESS_LOCATION_EXTRA_COMMANDS 访问定位额外命令 允许程序访问额外的定位提供者指令
- android.permission.ACCESS_MOCK_LOCATION 获取模拟定位信息 获取模拟定位信息,一般用于帮助开发者调试应用
- android.permission.ACCESS_NETWORK_STATE 获取网络状态 获取网络信息状态,如当前的网络连接是否有效
- android.permission.ACCESS_SURFACE_FLINGER 访问Surface Flinger Android平台上底层的图形显示支持,一般用于游戏或照相机预览界面和底层模式的屏幕截图
- android.permission.ACCESS_WIFI_STATE 获取WiFi状态 获取当前WiFi接入的状态以及WLAN热点的信息
- android.permission.ACCOUNT_MANAGER 账户管理 获取账户验证信息,主要为GMail账户信息,只有系统级进程才能访问的权限
- android.permission.AUTHENTICATE_ACCOUNTS 验证账户 允许一个程序通过账户验证方式访问账户管理ACCOUNT_MANAGER相关信息
- android.permission.BATTERY_STATS 电量统计 获取电池电量统计信息
- android.permission.BIND_APPWIDGET 绑定小插件 允许一个程序告诉appWidget服务需要访问小插件的数据库,只有非常少的应用才用到此权限
- android.permission.BIND_DEVICE_ADMIN 绑定设备管理 请求系统管理员接收者receiver,只有系统才能使用
- android.permission.BIND_INPUT_METHOD 绑定输入法 请求InputMethodService服务,只有系统才能使用
- android.permission.BIND_REMOTEVIEWS 绑定RemoteView 必须通过RemoteViewsService服务来请求,只有系统才能用
- android.permission.BIND_WALLPAPER 绑定壁纸 必须通过WallpaperService服务来请求,只有系统才能用
- android.permission.BLUETOOTH 使用蓝牙 允许程序连接配对过的蓝牙设备
- android.permission.BLUETOOTH_ADMIN 蓝牙管理 允许程序进行发现和配对新的蓝牙设备
- android.permission.BRICK 变成砖头 能够禁用手机,非常危险,顾名思义就是让手机变成砖头
- android.permission.BROADCAST_PACKAGE_REMOVED 应用删除时广播 当一个应用在删除时触发一个广播
- android.permission.BROADCAST_SMS 收到短信时广播 当收到短信时触发一个广播
- android.permission.BROADCAST_STICKY 连续广播 允许一个程序收到广播后快速收到下一个广播
- android.permission.BROADCAST_WAP_PUSH WAP PUSH广播 WAP PUSH服务收到后触发一个广播
- android.permission.CALL_PHONE 拨打电话 允许程序从非系统拨号器里输入电话号码
- android.permission.CALL_PRIVILEGED 通话权限 允许程序拨打电话,替换系统的拨号器界面
- android.permission.CAMERA 拍照权限 允许访问摄像头进行拍照
- android.permission.CHANGE_COMPONENT_ENABLED_STATE 改变组件状态 改变组件是否启用状态
- android.permission.CHANGE_CONFIGURATION 改变配置 允许当前应用改变配置,如定位
- android.permission.CHANGE_NETWORK_STATE 改变网络状态 改变网络状态如是否能联网
- android.permission.CHANGE_WIFI_MULTICAST_STATE 改变WiFi多播状态 改变WiFi多播状态
- android.permission.CHANGE_WIFI_STATE 改变WiFi状态 改变WiFi状态
- android.permission.CLEAR_APP_CACHE 清除应用缓存 清除应用缓存
- android.permission.CLEAR_APP_USER_DATA 清除用户数据 清除应用的用户数据
- android.permission.CWJ_GROUP 底层访问权限 允许CWJ账户组访问底层信息
- android.permission.CELL_PHONE_MASTER_EX 手机优化大师扩展权限 手机优化大师扩展权限
- android.permission.CONTROL_LOCATION_UPDATES 控制定位更新 允许获得移动网络定位信息改变
- android.permission.DELETE_CACHE_FILES 删除缓存文件 允许应用删除缓存文件
- android.permission.DELETE_PACKAGES 删除应用 允许程序删除应用
- android.permission.DEVICE_POWER 电源管理 允许访问底层电源管理
- android.permission.DIAGNOSTIC 应用诊断 允许程序到RW到诊断资源
- android.permission.DISABLE_KEYGUARD 禁用键盘锁 允许程序禁用键盘锁
- android.permission.DUMP 转存系统信息 允许程序获取系统dump信息从系统服务
- android.permission.EXPAND_STATUS_BAR 状态栏控制 允许程序扩展或收缩状态栏
- android.permission.FACTORY_TEST 工厂测试模式 允许程序运行工厂测试模式
- android.permission.FLASHLIGHT 使用闪光灯 允许访问闪光灯
- android.permission.FORCE_BACK 强制后退 允许程序强制使用back后退按键,无论Activity是否在顶层
- android.permission.GET_ACCOUNTS 访问账户Gmail列表 访问GMail账户列表
- android.permission.GET_PACKAGE_SIZE 获取应用大小 获取应用的文件大小
- android.permission.GET_TASKS 获取任务信息 允许程序获取当前或最近运行的应用
- android.permission.GLOBAL_SEARCH 允许全局搜索 允许程序使用全局搜索功能
- android.permission.HARDWARE_TEST 硬件测试 访问硬件辅助设备,用于硬件测试
- android.permission.INJECT_EVENTS 注射事件 允许访问本程序的底层事件,获取按键、轨迹球的事件流
- android.permission.INSTALL_LOCATION_PROVIDER 安装定位提供 安装定位提供
- android.permission.INSTALL_PACKAGES 安装应用程序 允许程序安装应用
- android.permission.INTERNAL_SYSTEM_WINDOW 内部系统窗口 允许程序打开内部窗口,不对第三方应用程序开放此权限
- android.permission.INTERNET 访问网络 访问网络连接,可能产生GPRS流量
- android.permission.KILL_BACKGROUND_PROCESSES 结束后台进程 允许程序调用killBackgroundProcesses(String).方法结束后台进程
- android.permission.MANAGE_ACCOUNTS 管理账户 允许程序管理AccountManager中的账户列表
- android.permission.MANAGE_APP_TOKENS 管理程序引用 管理创建、摧毁、Z轴顺序,仅用于系统
- android.permission.MTWEAK_USER 高级权限 允许mTweak用户访问高级系统权限
- android.permission.MTWEAK_FORUM 社区权限 允许使用mTweak社区权限
- android.permission.MASTER_CLEAR 软格式化 允许程序执行软格式化,删除系统配置信息
- android.permission.MODIFY_AUDIO_SETTINGS 修改声音设置 修改声音设置信息
- android.permission.MODIFY_PHONE_STATE 修改电话状态 修改电话状态,如飞行模式,但不包含替换系统拨号器界面
- android.permission.MOUNT_FORMAT_FILESYSTEMS 格式化文件系统 格式化可移动文件系统,比如格式化清空SD卡
- android.permission.MOUNT_UNMOUNT_FILESYSTEMS 挂载文件系统 挂载、反挂载外部文件系统
- android.permission.NFC 允许NFC通讯 允许程序执行NFC近距离通讯操作,用于移动支持
- android.permission.PERSISTENT_ACTIVITY 永久Activity 创建一个永久的Activity,该功能标记为将来将被移除
- android.permission.PROCESS_OUTGOING_CALLS 处理拨出电话 允许程序监视,修改或放弃播出电话
- android.permission.READ_CALENDAR 读取日程提醒 允许程序读取用户的日程信息
- android.permission.READ_CONTACTS 读取联系人 允许应用访问联系人通讯录信息
- android.permission.READ_FRAME_BUFFER 屏幕截图 读取帧缓存用于屏幕截图
- com.android.browser.permission.READ_HISTORY_BOOKMARKS 读取收藏夹和历史记录 读取浏览器收藏夹和历史记录
- android.permission.READ_INPUT_STATE 读取输入状态 读取当前键的输入状态,仅用于系统
- android.permission.READ_LOGS 读取系统日志 读取系统底层日志
- android.permission.READ_PHONE_STATE 读取电话状态 访问电话状态
- android.permission.READ_SMS 读取短信内容 读取短信内容
- android.permission.READ_SYNC_SETTINGS 读取同步设置 读取同步设置,读取Google在线同步设置
- android.permission.READ_SYNC_STATS 读取同步状态 读取同步状态,获得Google在线同步状态
- android.permission.REBOOT 重启设备 允许程序重新启动设备
- android.permission.RECEIVE_BOOT_COMPLETED 开机自动允许 允许程序开机自动运行
- android.permission.RECEIVE_MMS 接收彩信 接收彩信
- android.permission.RECEIVE_SMS 接收短信 接收短信
- android.permission.RECEIVE_WAP_PUSH 接收Wap Push 接收WAP PUSH信息
- android.permission.RECORD_AUDIO 录音 录制声音通过手机或耳机的麦克
- android.permission.REORDER_TASKS 排序系统任务 重新排序系统Z轴运行中的任务
- android.permission.RESTART_PACKAGES 结束系统任务 结束任务通过restartPackage(String)方法,该方式将在外来放弃
- android.permission.SEND_SMS 发送短信 发送短信
- android.permission.SET_ACTIVITY_WATCHER 设置Activity观察其 设置Activity观察器一般用于monkey测试
- com.android.alarm.permission.SET_ALARM 设置闹铃提醒 设置闹铃提醒
- android.permission.SET_ALWAYS_FINISH 设置总是退出 设置程序在后台是否总是退出
- android.permission.SET_ANIMATION_SCALE 设置动画缩放 设置全局动画缩放
- android.permission.SET_DEBUG_APP 设置调试程序 设置调试程序,一般用于开发
- android.permission.SET_ORIENTATION 设置屏幕方向 设置屏幕方向为横屏或标准方式显示,不用于普通应用
- android.permission.SET_PREFERRED_APPLICATIONS 设置应用参数 设置应用的参数,已不再工作具体查看addPackageToPreferred(String)介绍
- android.permission.SET_PROCESS_LIMIT 设置进程限制 允许程序设置最大的进程数量的限制
- android.permission.SET_TIME 设置系统时间 设置系统时间
- android.permission.SET_TIME_ZONE 设置系统时区 设置系统时区
- android.permission.SET_WALLPAPER 设置桌面壁纸 设置桌面壁纸
- android.permission.SET_WALLPAPER_HINTS 设置壁纸建议 设置壁纸建议
- android.permission.SIGNAL_PERSISTENT_PROCESSES 发送永久进程信号 发送一个永久的进程信号
- android.permission.STATUS_BAR 状态栏控制 允许程序打开、关闭、禁用状态栏
- android.permission.SUBSCRIBED_FEEDS_READ 访问订阅内容 访问订阅信息的数据库
- android.permission.SUBSCRIBED_FEEDS_WRITE 写入订阅内容 写入或修改订阅内容的数据库
- android.permission.SYSTEM_ALERT_WINDOW 显示系统窗口 显示系统窗口
- android.permission.UPDATE_DEVICE_STATS 更新设备状态 更新设备状态
- android.permission.USE_CREDENTIALS 使用证书 允许程序请求验证从AccountManager
- android.permission.USE_SIP 使用SIP视频 允许程序使用SIP视频服务
- android.permission.VIBRATE 使用振动 允许振动
- android.permission.WAKE_LOCK 唤醒锁定 允许程序在手机屏幕关闭后后台进程仍然运行
- android.permission.WRITE_APN_SETTINGS 写入GPRS接入点设置 写入网络GPRS接入点设置
- android.permission.WRITE_CALENDAR 写入日程提醒 写入日程,但不可读取
- android.permission.WRITE_CONTACTS 写入联系人 写入联系人,但不可读取
- android.permission.WRITE_EXTERNAL_STORAGE 写入外部存储 允许程序写入外部存储,如SD卡上写文件
- android.permission.WRITE_GSERVICES 写入Google地图数据 允许程序写入Google Map服务数据
- com.android.browser.permission.WRITE_HISTORY_BOOKMARKS 写入收藏夹和历史记录 写入浏览器历史记录或收藏夹,但不可读取
- android.permission.WRITE_SECURE_SETTINGS 读写系统敏感设置 允许程序读写系统安全敏感的设置项
- android.permission.WRITE_SETTINGS 读写系统设置 允许读写系统设置项
- android.permission.WRITE_SMS 编写短信 允许编写短信
四十四、(LitJson)Unity JsonException: Max allowed object depth reached while trying to export from type System.Single
原因:
LitJosn的数据类型支持下面几种
public JsonData(bool boolean);
public JsonData(double number);
public JsonData(int number);
public JsonData(long number);
public JsonData(object obj);
public JsonData(string str);
解决办法:
用于序列化或者反序列化的数据,其类型必须是上面几种
(PS:使用 float 会出错的)
四十五、某物体发射射线打在远处平面,射线与平面的交点,映射到屏幕上(物体旋转发射的射线,转为准心位置)
using UnityEngine;
public class RayCast : MonoBehaviour {
public RectTransform image;
// Use this for initialization
void Start () {
Debug.Log(image.position);
Debug.Log(image.localPosition);
}
// Update is called once per frame
void Update () {
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.forward, out hit, 1000,1<<9))
{
Debug.DrawLine(transform.position, hit.point, Color.green);
//Debug.Log("hit.point " + hit.point);
Vector3 screenPoint = Camera.main.WorldToScreenPoint(hit.point);
//Debug.Log("screenPoint.point " + screenPoint);
// 请注意数据转化,这个位置与你的UIImage的 RenderMode 和 image 锚点也有关
image.localPosition = new Vector3(screenPoint.x - Screen.width/2,screenPoint.y-Screen.height/2,image.localPosition.z);
}
if (Input.GetKeyDown(KeyCode.UpArrow)) {
this.transform.localEulerAngles = this.transform.localEulerAngles + Vector3.right * -10;
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
this.transform.localEulerAngles = this.transform.localEulerAngles + Vector3.right * 10;
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
this.transform.localEulerAngles = this.transform.localEulerAngles + Vector3.up * -10;
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
this.transform.localEulerAngles = this.transform.localEulerAngles + Vector3.up * 10;
}
}
}
四十六、打印代码执行话费时间
Stopwatch sw = new Stopwatch();
UnityEngine.Debug.Log(ArrowBLEDebugHelper.DEBUGMARK + " ++++++++++++++++++ OnBluetoothMessage ");
sw.Start();
sw.Stop();
UnityEngine.Debug.Log(sw.ElapsedMilliseconds);
四十七、关闭Unity Log 打印
Debug.unityLogger.logEnabled = false;
四十八、判断目标物体是否在视野内 TargetCusorOnBecameVisible
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TargetCusorOnBecameVisible : MonoBehaviour
{
// 目标物体
public Transform targetTr;
// 物体在视野内外的事件
public Action OnInVisibleEvent;
public Action OnOutVisibleEvent;
//适配双屏的数值操作
private float height = Screen.height / 2;
private float weight = Screen.width / 4;
// 目标物体是否在指定区域内
private bool isVisible = true;
private bool IsVisible {
get {
return isVisible;
}
set {
isVisible = value;
if (value == true) {
if (OnInVisibleEvent != null)
{
OnInVisibleEvent();
}
Debug.Log(ArrowBLEDebugHelper.DEBUGMARK + " IsVisible is true");
}
else
{
if (OnOutVisibleEvent != null)
{
OnOutVisibleEvent();
}
Debug.Log(ArrowBLEDebugHelper.DEBUGMARK + " IsVisible is false");
}
}
}
void Start() {
// 每秒调用两次
InvokeRepeating("IsInvisible", 0.5f,0.5f);
}
/// <summary>
/// 判断物体是否在某区域内
/// </summary>
/// <returns></returns>
public bool IsInvisible()
{
// 目标物体在视野外
if (targetTr.localPosition.x < -weight || targetTr.localPosition.x > weight
|| targetTr.localPosition.y < -height || targetTr.localPosition.y > height
) {
if (IsVisible == true) {
IsVisible = false;
}
Debug.Log(ArrowBLEDebugHelper.DEBUGMARK + "视野外");
return false;
}
if (IsVisible == false)
{
IsVisible = true;
}
Debug.Log(ArrowBLEDebugHelper.DEBUGMARK + "视野内");
return true;
}
}
四十九、Unity ref 和out、 params的使用
相信很多刚接触C#或者unity的同学,都会看到很多这样那样的函数,带有ref ,out 或者params的参数,今天我就来讲一讲这三者的使用和区别:
ref
直接来看看实现的代码
public void UpdateScore(ref int score) {
score = UnityEngine.Random.Range(80, 100);
}
我使用unity在start里面调用测试
void Start() {
int myscore = 60;
UpdateScore(ref myscore);
print(myscore);
}
看看打印结果:
有C语言或者C++基础的同学可能看出来了,这不就像C语言的指针变量吗?是的,这跟指针变量差不多,其实这是C#的参数引用传递,在方法中对参数所做的任何更改都将反映在该变量中
什么时候使用ref关键字呢
我理解:有时候我们不需要执行完这个方法就可以改变参数的值,或者想要改变的参数值比较多的时候
out
看看代码实现:
public void GetScore(out int score) {
score = UnityEngine.Random.Range(80, 100);
}
调用
void Start() {
int score;
GetScore(out score);
print(score);
}
结果:
其实out跟ref编译原理是一样的,区别就是ref需要先初始化值,而out不需要,总结起来就是ref 有进有出,out只出不进
out什么时候使用呢
我理解:用在没有返回值的函数就能返回值,例如在返回值是void的函数就能返回,还有就是我们经常使用的都是返回一个值的,那么我们如果想在一个函数中返回多个值呢?那么out就起作用了,我们可以使用out返回多个值
params
看看代码实现
public void Getdd(params int[] pp) {
foreach (var p in pp)
{
print(p);
}
}
调用
void Start() {
Getdd(1,2,3);
}
结果:
是不是感觉有些不同,我们以往传递数组的时候,是不是不是这样传递的,params是为动态数组而准备的,我们直接输入数组的元素就行了,如果不加params,我们只能这样调用
public void Getdd( int[] pp) {
foreach (var p in pp)
{
print(p);
}
}
void Start() {
Getdd(new []{1,2,3});
}
五十、Unity3d 控制物体transform移动的几种方法
在Unity开发中我们难免要使用代码控制角色的移动,现将已知的几种方法总结如下:
1)transform.Translate()
function Update() {
//导弹相对于战斗机Fighter以ShootSpeed 的速度向前运动,Vector3.forward在此时表示导弹的正前方
transform.Translate(Vector3.forward * ShootSpeed * Time.deltaTime, Fighter.transform);
}
物体以relativeTo为参照系,沿着translation运动|translation|的距离。如果relativeTo缺省将
以Space.Self为默认值。举个例子:
function Update() { //导弹相对于战斗机Fighter以ShootSpeed 的速度向前运动,Vector3.forward在此时表示导弹的正前方 transform.Translate(Vector3.forward * ShootSpeed * Time.deltaTime, Fighter.transform); }
再举个例子:
在场景中有一个红球和一个蓝球,红球沿着世界坐标系的z轴正方向匀速运动,蓝球沿着红球坐标系的z轴正向以和红球同样的速度匀速运动。
红球运动脚本RedMove.cs:
using UnityEngine;
using System.Collections;
public class RedMove : MonoBehaviour {
public int MoveSpeed = 10;
// Update is called once per frame
void FixedUpdate () {
transform.Translate(Vector3.forward * MoveSpeed * Time.deltaTime, Space.World);
}
}
蓝球运动脚本BlueMove.cs:
using UnityEngine;
using System.Collections;
public class BlueMove : MonoBehaviour {
public GameObject RedBall;
public int MoveSpeed = 10;
// Update is called once per frame
void FixedUpdate () {
transform.Translate(Vector3.forward * MoveSpeed * Time.deltaTime, RedBall.transform);
}
}
1、我们先让红球的坐标系各轴方向与世界坐标系的各轴方向相同,则红球与蓝球在运动过程中是相对静止的:
2、接着我们将红球绕y轴顺时针旋转90度(即使红球的Transform.Rotation.y = 90),此时红球坐标系的z轴正向和世界坐标系的x轴正向重合,此时运动效果如下:
2)指定速度velocity
这种方法只能适用于刚体,因为velocity是刚体特有的属性。代码如下:
void Start () {
gameObject.GetComponent<Rigidbody>().velocity = Vector3.forward * MoveSpeed;
}
3)使用rigbody.MovePosition()
public void MovePosition(Vector3position);
让物体移动到新的位置position。
示例:
void FixedUpdate() {
//让物体向前运动Time.deltaTime距离
rb.MovePosition(transform.position + transform.forward * Time.deltaTime);
}
4)Vector3.MoveTowards()
static function MoveTowards(current: Vector3, target: Vector3, maxDistanceDelta: float): Vector3;
该方法一般以以下形式使用:
using UnityEngine;using System.Collections;
public class YellowMove : MonoBehaviour {
public int MoveSpeed = 10;
Vector3 target;
void Start () {
target = new Vector3(20, transform.position.y, 20);
}
void Update () {
transform.position = Vector3.MoveTowards(transform.position, target, MoveSpeed * Time.deltaTime);
}
}
5)使用lerp()
a、使用Mathf.Lerp()函数
static functionLerp (from
: float,to : float,t : float) : float
调用该函数会返回from与to之间的插值(from + to) * t,t在0~1之间。
使用方法如下:
using UnityEngine;using System.Collections;
public class YellowMove : MonoBehaviour {
public float MoveSpeed = 0.1f;
Vector3 Target = new Vector3(20, 20, 20);
//控制物体向Target移动
void Update () {
gameObject.transform.localPosition = new Vector3(
Mathf.Lerp(transform.position.x, Target.x, MoveSpeed * Time.deltaTime),
Mathf.Lerp(transform.position.y, Target.y, MoveSpeed * Time.deltaTime),
Mathf.Lerp(transform.position.z, Target.z, MoveSpeed * Time.deltaTime));
}
}
b、使用Vector3.Lerp()
public staticVector3
Lerp(Vector3a,Vector3b,
floatt);
其使用方法与Mathf.Lerp()用法相似,不同点是Vector3.Lerp()是对三维向量进行插值,而Mathf.Lerp()是对数字进行插值。
using UnityEngine;
using System.Collections;
public class YellowMove : MonoBehaviour {
public float MoveSpeed = 0.1f;
Vector3 Target = new Vector3(20, 20, 20);
//控制物体向Target移动 void Update () {
gameObject.transform.localPosition = Vector3.Lerp(transform.position, Target, MoveSpeed * Time.deltaTime;
}
}
6)使用SmoothDamp()
a、使用Vector3.SmoothDamp()
static function SmoothDamp (current : Vector3,target : Vector3,ref currentVelocity : Vector3,smoothTime : float,maxSpeed : float
= Mathf.Infinity,deltaTime : float = Time.deltaTime) : Vector3
在smoothTime的时间间隔内从current移动到target,其移动的当前速度为currentVelocity,此方法一般用于摄像机的平滑移动。需要注意的是currentVelocity值一般在开始时指定为零向量,每次调用该方法时该方法会自动给currentVelocity赋值。方便起见以Mathf.SmoothDamp()进行如下测试:
using UnityEngine;
using System.Collections;
public class YellowMove : MonoBehaviour {
public float MoveSpeed = 0f;
float CurrentNum = 0f;
float TargetNum = 20f;
float MoveTime = 10f;
void Update () {
Debug.Log("当前数值:" + CurrentNum + ";当前速度:" + MoveSpeed);
CurrentNum = Mathf.SmoothDamp(CurrentNum, TargetNum, ref MoveSpeed, MoveTime * Time.deltaTime);
}
}
控制台输出:
Vector3.SmoothDamp()用法如下:
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour{
public Transform target;
public float smoothTime = 0.3F;
private Vector3 velocity = Vector3.zero;
void Update() {
//定义一个目标位置在目标变换的上方并且在后面
Vector3 targetPosition = target.TransformPoint(new Vector3(0, 5, -10));
//平滑地移动摄像机朝向目标位置
transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);
}
}
b、使用Mathf.SmoothDamp()
使用方法与Vector3.SmoothDamp()差不多,只是Mathf.SmoothDamp()是对float类型数字操作,而Vector3.SmoothDamp是对三维向量操作。
7)使用CharacterController组件控制角色移动
Unity使用CharacterController(角色控制器)来控制角色骨骼运动,包括移动、跳跃以及各种动作。CharacterController比较复杂,具体详情请参照博客Unity
CharacterController(角色控制器)
8)使用iTween
iTween是Unity3d的一个动画插件,可以让你更加轻松的实现各种动作,iTween实现移动的方式也比较多样,具体的可以参考博客Unity iTween动画库插件
9)使用协程
关于Unity的协程介绍请看博客:Unity协程介绍及使用。
协程和Update方法很类似,不过协程可以在执行切换到下一帧时局部变量任然会保存,但update方法在执行下一帧后局部变量又重新定义了。既然相似,那么我们就可以像在update中每执行一帧改变一次position那样,在协程中改变position然后再执行下一帧来改变物体的位置让物体运动起来。方法如下:
usingUnityEngine;
Using System.Collections;
Public class RedMove: MonoBehaviour {
public Vector3 TargetPosition;
public float MoveSpeed;
Void Start()
{
StartCoroutine(Move(TargetPosition));
}
IEnumerator Move(Vector3 target)
{
while(transform.position != target)
{
transform.position = Vector3.MoveTowards(transform.position, target, MoveSpeed * Time.deltaTime);
Yield return 0;
}
}
}
五十一、OnDrawGizmosSelected() 和 OnDrawGizmos() 的区别
OnDrawGizmosSelected() 在挂在gameObject选中的时候绘制显示
OnDrawGizmos() 一直显示,无论 GameObject 选中与否
注意:Debug.DrwaXXX () 相关函数要在每帧调用的函数中才能看到显示,在Start() Awake() 中 可能看不到显示
void OnDrawGizmosSelected()
{
// Display the explosion radius when selected
Gizmos.color = new Color(1, 1, 0, 0.75F);
Gizmos.DrawSphere(transform.position, explosionRadius);
}
void OnDrawGizmos()
{
// Display the explosion radius when selected
Gizmos.color = new Color(1, 1, 0, 0.75F);
Gizmos.DrawSphere(transform.position, explosionRadius);
}