几年的Unity学习总结

stream:其中类Stream为抽象类。由此有三个派生类。 需要引入命名空间:using System.IO
MemoryStream:对内存进行读取与写入 


BufferedStream:对缓冲器进行读取/写入 


FileStream:对文件执行读取与写入 


TextReader/Writer为抽象类。由此派生类: 


StreamReader/StreamWirter 分别用于对流的读取与写入。


public abstract int Read(byte[] buffer, int offset, int count)


buffer: 字节数组。此方法返回时,该缓冲区包含指定的字符数组,该数组的 offset 和 (offset + count -1) 之间的值由从当前源中读取的字节替换。
offset: buffer 中的从零开始的字节偏移量,从此处开始存储从当前流中读取的数据。
count: 要从当前流中最多读取的字节数。
返回值:读入缓冲区中的总字节数。如果当前可用的字节数没有请求的字节数那么多,则总字节数可能小于请求的字节数,或者如果已到达流的末尾,则为零 (0)。此方法的实现从当前流中读取最多的 count 个字节,并将它们存储在从 offset 开始的 buffer 中。流中的当前位置提升已读取的字节数;




FileStream stream=new FileStream(url,FileMode.Open);
stream.Seek(offset,SeekOrigin.Begin);//本句相当于stream.position=offset;//stream的position默认是0,读一次后position会对应加到读的位置。
//每次读之前最好都先设置一下流的读取位置。SeekOrigin.Begin表示从0开始偏移offset个数开始读,还有SeekOrigin.End和Current表示从结尾和当前位置开始偏移offset个数指定给Position。
int ret =stream.Read(data,offset,count);//这里的offset是data数组的偏移和大小,
stream.Close();//本句必须要加 不要忘记
    //代码意思是从流偏移量为offset的地方开始读,一共读count个字节,存在data数组的offset偏移处,该数组的 offset 和 (offset + count -1) 之间的值由从当前源中读取的字节替换。




Stream.Write(byte[] buffer, int offset, int count)




FileStream stream= new FileStream(url,FileMode.Create);
if(stream!=null){
stream.position=0;
stream.write(data,offset,count);//从stream的0开始写入 一共写count字节。data从offset开始写到stream。
stream.Flush();//如果暂时不close  默认是暂时不写数据的,那么flush是立刻写入数据。
stream.Close();//关闭后 也是立刻写入数据。
}
------------------------------------------------------------------序列化-------------------------------------------------------------------
FileStream stream= new FileStream(url,FileMode.Create);
sdFormater.Write(stream,(int)1);//int
sdFormater.Write(stream,"name");//string
stream.close();
//read:
sdBinaryReader stream=new sdBinaryReader(data,length);//test
int a;string b;
sdFormater.Read(stream,ref a);//a=1
sdFormater.Read(stream,ref b);//b="name"
//只要满足读取和写入的顺序完全一样,数据的序列化和反序列就可以完成,因为每次读取和写入的数据类型所占的字节数是固定的。比如byte1个字节,int占4个字节。
//另外文件的读写分2种,一种是整个数据结构的读写 比如一个大的string 写到byte文件文件里,那么读出来直接转换string就可以获取到数据;另外一种就是上边的 多个数据写到一个文件里边,那么读取也要按照分部读取再来转换(string)类型。直接转换肯定是错误的,读取顺序乱了 也是错误的。


一. 二进制转换成图片
MemoryStream ms = new MemoryStream(bytes);
ms.Position = 0;
Image img = Image.FromStream(ms);
ms.Close();
this.pictureBox1.Image


二. C#中byte[]与string的转换代码


1、System.Text.UnicodeEncoding converter = new System.Text.UnicodeEncoding();
  byte[] inputBytes =converter.GetBytes(inputString);
  string inputString = converter.GetString(inputBytes);


2、string inputString = System.Convert.ToBase64String(inputBytes);
  byte[] inputBytes = System.Convert.FromBase64String(inputString);
FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);


三. C# Stream 和 byte[] 之间的转换


/// 将 Stream 转成 byte[]


public byte[] StreamToBytes(Stream stream)
{
    byte[] bytes = new byte[stream.Length];
    stream.Read(bytes, 0, bytes.Length);
    // 设置当前流的位置为流的开始
    stream.Seek(0, SeekOrigin.Begin);
    return bytes;
}


/// 将 byte[] 转成 Stream


public Stream BytesToStream(byte[] bytes)
{
    Stream stream = new MemoryStream(bytes);
    return stream;
}


四. Stream 和 文件之间的转换


将 Stream 写入文件


public void StreamToFile(Stream stream,string fileName)
{
    // 把 Stream 转换成 byte[]
    byte[] bytes = new byte[stream.Length];
    stream.Read(bytes, 0, bytes.Length);
    // 设置当前流的位置为流的开始
    stream.Seek(0, SeekOrigin.Begin);
    // 把 byte[] 写入文件
    FileStream fs = new FileStream(fileName, FileMode.Create);
    BinaryWriter bw = new BinaryWriter(fs);
    bw.Write(bytes);
    bw.Close();
    fs.Close();
}


五. 从文件读取 Stream


public Stream FileToStream(string fileName)
{            
    // 打开文件
    FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
    // 读取文件的 byte[]
    byte[] bytes = new byte[fileStream.Length];
    fileStream.Read(bytes, 0, bytes.Length);
    fileStream.Close();
    // 把 byte[] 转换成 Stream
    Stream stream = new MemoryStream(bytes);
    return stream;
}
六.FileStream 读文件
byte[] byData = new byte[100];//用stream读到byte数组里边
char[] charData = new char[1000];
FileStream sFile = new FileStream("文件路径",FileMode.Open);
sFile.Seek(55, SeekOrigin.Begin);
sFile.Read(byData, 0, 100); //第一个参数是被传进来的字节数组,用以接受FileStream对象中的数据,第2个参数是字节数组中开始写入数据的位置,它通常是0,表示从数组的 开端文件中向数组写数据,最后一个参数规定从文件读多少字符.


七.StreamReader读取文件:
 
StreamReader objReader = new StreamReader(文件路径);
      string sLine="";
      ArrayList LineList = new ArrayList();    
      while (sLine != null)
      {
        sLine = objReader.ReadLine();
        if (sLine != null&&!sLine.Equals(""))
          LineList.Add(sLine);
      }
            objReader.Close();
            return LineList;


八.StreamWriter写文件:
 
  FileStream fs = new FileStream(文件路径, FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
//开始写入
sw.Write(String);
 //清空缓冲区
sw.Flush();
//关闭流
sw.Close();
fs.Close();
九.读byte[]








File和Directory
File类支持对文件的基本操作,包括创建、拷贝、移动、删除和打开一个文件。Directory类则用于执行常见的各种目录操作,如创建、移动、浏览目录及其子目录。
File类和Directory类都是密封类。不象抽象类Stream,File类和Directory类可以被实例化,但它们不能被其它类继承。
File类和Directory类的基类都是抽象类FileSystemEntry。本文发表于http://bianceng.cn(编程入门)
Stream
File类的静态方法主要是用于创建FileStream类。一个FileStream类的实 例实际上代表一个磁盘文件,它通过Seek()方法进行对文件的随机访问,也同时包含了流的标准输入、标准输出、标准错误等。FileStream默认对 文件的打开方式是同步的,但它同样很好地支持异步操作。
TextReader和TextWriter
TextReader和TextWriter类都是抽象类。和Stream类的字节形式的输入和输出不同,它们用于Unicode字符的输入和输出。
StringReader和StringWriter
StringReader和StringWriter在字符串中读写字符。
StreamReader和StreamWriter
StreamReader和StreamWriter在流中读写字符。
BufferedStream
BufferedStream是为诸如网络流 的其它流添加缓冲的一种流类型。其实,FileStream流自身内部含有缓冲,而MemorySteam流则不需要缓冲。一个BufferStream 类的实例可以由多个其它类型的流复合而成,以达到提高性能的目的。缓冲实际上是内存中的一个字节块,利用缓冲可以避免操作系统频繁地到磁盘上读取数据,从 而减轻了操作系统的负担。
MemoryStream
MemoryStream是一个无缓冲流,它所封装的数据直接放在内存中,因此可以用于快速临时存储、进程间传递信息等。
NetworkSteam
Networksteam表示在互联网络上传递的流。
当使用名字空间System.IO中提供的类时,对存储数据的访问权限必须符合操作系统的安全性要求。

注意:不要使用这些类来编写应用程序对网络文件进行的操作。因为Internet默认的安全政策是不允许对文件直接访问。可以使用IsolatedStroage类来处理网络文件。




--------------------------------------------------------------------------------------------

new2


c#封装下的各个操作系统都可用的读写文件功能。
using System.IO;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
1.FileMode.Create//指定操作系统应创建新文件。 如果文件已存在,它将被覆盖。 这需要 FileIOPermissionAccess.Write 权限。
2.创建文件:
FileStream file=new FileStream("Assets/text.txt", FileMode.Create);/FileMode.Append 是增加新内容,不删旧内容。
byte[]data=Encoding.UTF8.GetByte("aaa");//need using System.Text
byte[]data=new byte[]{0xef,0xbb,0xbf};//之前写这个是csv的utf8格式。
file.Write(data,0,data.Length);//byte,offset,count
file.Close();关闭当前流并释放与之关联的所有资源(如套接字和文件句柄)。


3.文件的读写xml csv 序列化,csv自己解析 比较好 但是大量数据内存大 效率不好。最好的是序列化。序列化有2种方式:1.是保存成prefab模式,直观 好看 参考fbd.cs。2.是保存成byte文件:效率更高。因为unity的prefab序列化过程可能还需要申请临时内存 保存临时数据。不过我觉得差不多吧。
[Serializable]
public class People
{
    public string Name { get; set; }
    public int Age { get; set; }
}//prefab 模式就是在mono里定义public的 People对象,把数据写进去,存成prefab。unity编辑器方便的查看修改序列化内容。
void Write(){//序列化写byte文件
FileStream fs = new FileStream("Asset\test.byte", FileMode.Create);
BinaryFormatter bf = new BinaryFormatter();
List ps = new List();//也可以只new 一个对象
ps.Add(new People() { Name = "gg", Age = 24 });
ps.Add(new People() { Name = "yy", Age = 23 });
bf.Serialize(fs, ps);
fs.Close();
}
void Read(){//读byte文件
FileStream fs = new FileStream("Asset\test.byte", FileMode.Open);
BinaryFormatter bf = new BinaryFormatter();
List ps = bf.Deserialize(fs) as List;
debug.log(ps[0].name)
//http://www.cnblogs.com/ustcwhc/archive/2011/11/01/2232195.html
}


4.文件读写:string[] allline = File.ReadAllLines(path);File.Datele(path),File.Exist(path),StreamReader r=File.OpenText(path)
4.2在java中FileInputStream用来读文件;FileOutputStream用来写文件。
4.3 DirectoryInfo.GetFiles():获取目录中(不包含子目录)的文件,返回类型为FileInfo[],支持通配符查找;
    DirectoryInfo.GetDirectories():获取目录(不包含子目录)的子目录,返回类型为DirectoryInfo[],支持通配符查找;


文章来源于易贤网http://www.ynpxrz.com/n753640c2023.aspx
5.关于内存优化,进入退出战斗关卡把所有资源释放,保证每次进同一关卡的时候内存值是一样的。
如果内存值波动太大,说明有东西没有释放 需要优化。
关于主页内存优化:主页需要加载大量的UIprefab到场景中,玩家每多进入一个功能,加载了一个prefab 内存都会变大。
 1.建议内存大于512的时候把前边加载不用的prefab释放掉。
 2.建议切换场景的时候释放所有资源内存。把引用obj=null;Destroy(obj)再调用Resources.UnloadUnusedAssets();
 3 优化最重要的还是分析unity的profile,当运行在某个地方卡的时候,profile都会显示哪些东西耗cpu最高,然后根据对应的函数地方去修改哪些不对的逻辑内容。


6.关于版本更新,以前的思路是服务器资源只留一个最新版本,容易出问题。最好的方法是 每打一个版本apk CDN上就多保留一套资源目录。
然后以后的服务器任何修改都只在该版本资源文件内,不同版本不会相互牵扯和升级。要升级的话 就强制客户端apk升级,才能用服务器新版本的资源。


7.  object lockd = new object(); void common (){ lock(object ){  }}//锁定内容,防止多线程调用函数里边内容。用在通用模块的通用功能上,防止多处代码同时调用该处代码,有了lock后 会按先后排队执行。


8.关于内存优化写法:
以前:obj.mainTexture=Resources.load(" ") as Texture或实例出来。
以后: Texture tex = obj.mainTexture;
obj.mainTexture = image.texture;
Resources.UnloadAsset(tex);
9.注意脚本中对资源的引用。大部分脚本将在场景转换时随之失效并被回收,但是,在场景之间被保持的脚本不在此列(通常情况是被附 着在DontDestroyOnLoad的GameObject上了)。
而这些脚本很可能含有对其他物体的Component或者资源的引用,这样相关的 资源就都得不到释放,


这绝对是不想要的情况。另外,static的单例(singleton)在场景切换时也不会被摧毁,同样地,如果这种单例含有大量的对资源的引用,也会成为大问题。


因此,尽量减少代码的耦合和对其他脚本的依赖是十分有必要的。如果确实无法避免这种情况,那应当手动地对这些不再使用的引用对象调用Destroy()


或者将其设置为null。这样在Resources.UnloadUnusedAssets()垃圾回收的时候,这些内存将被认为已经无用而被回收。


10 .ref的作用应该是把参数传过去 修改后,代表直接修改的原值。不然传过去的只是临时变量。


11.Stopwatch可以检测程序运行时间,System.Diagnostic.Stopwatch watch=new System.Diagnostic.Stopwatch()
watch.Start()
...
watch.Stop
long velocity=watch.ElapsedTicks;可以用来计算下载时间.测算哪组cdn服务器时间小,就用哪组。


12.Caching.CleanCache(); 可以删除LoadFromCacheOrDownload不能自动删除磁盘的内容,不然需要等磁盘满了才自动删。
新版本的Caching可以设置包的最大caching大小,超过了就自动释放。这个可以解决他们项目组包大的问题了。
Caching.IsVersionCached(url,int)检测该资源是否存在
13.CSV格式的文件比xml效率好,比单纯的text方便易用。但是要注意编码格式,utf8编码前先写(0xEF).(0xBB).(0xBF)这3个byte到byte数组里边。


14内存优化看我的csdn博客
15StringBuilder:如果我们的程序是在单线程下运行,或者是不必考虑到线程同步问题,我们应该优先使用StringBuilder类;
  StringBuffer如果要保证线程安全,自然非StringBuffer莫属了。
  单纯的string进行字符串连接的时候效率极低。
内存冗余最多的同一个功能,当刷新的界面时候,生产了很多的临时list,每刷新一次内存增加一次。这样的东西只能一个个慢慢改了。


16关于内存动态变化:总内存=分配内存+临时内存。当一个新的ui加载后,分配内存会增加,临时内存可能减少 总内存不变,也可能临时内存增加 总内存增加很多。
当加载很多UI后,分配内存需要增加,临时内存慢慢被耗完,那么就会去申请一些临时内存,那么总内存就增加了。当场景内切换,所有分配内存被释放到临时内存,但是总内存几乎不会变化。
所以内存越来越大的原因是东西多后,临时内存被撑大,释放的时候又不能释放临时内存,这是系统管的。不能释放是因为内存被碎片化了,比如一块整的100k内存,可能里边的1k还在用,就释放不了,另外碎片化后,新申请的内存如果大于碎片内存,就无法使用,只能申请新的了。
最好的做法是 在主页ui多的地方 加大优化力度,gc和unload。关闭一个ui就优化一次,或者定时优化,或者是当加载过多的prefab后,把之前加载的ui destroy掉。
少产生临时内存的写法:少用string的字符串连接,少用临时变量,在for循环下的临时变量最好都在for外声明一个,然后用完置为null、
分析内存用profiler的 Deep Profile模式,跑一遍看是哪cpu和内存增加得多,然后可以直接看出是哪些代码造成的。


17 Android遇到bug就用logcat调试。控制台输 adb logcat 然后插入手机就可以调试了。IOS遇到bug就用xcode调试或者pp助手实时同步日志。PC平台就用process explorer或者VS的进程工具Ctrl+Alt+P。
-----log:可以查看bluestack
adb kill-server
adb devices
adb logcat 
adb logcat > d:\log2.txt


18 移动平台的播放视频: Handheld.PlayFullScreenMovie("test.mp4", Color.black, FullScreenMovieControlMode.CancelOnInput);PC平台可以直接MovieTexture 纹理播放。
   对于IOS平台一定需要转换为H264编码的格式才能播放,可以用格式工厂转换成该编码的mp4文件。H.264 Baseline Profile Level 3.0 video, up to 640 x 480 at 30 fps. Note that B frames are not supported in the Baseline profile.


19 对于服务器定义的发送数据函数,一般最好都定义为static类型,随时可以调到。如果只是普通类型的话,数据过来也是能调用到该函数,但是函数内部所有的普通变量都是空的,一旦使用就会崩溃。
所以最好的是static,判断当instance!=null的时候处理一些实例。当然普通的这么判断也可以,但是这个写法不好,容易忘记或者弄错,因为普通类型不需要通过实例去访问变量。static才需要。


20关于通讯,底层socket会把数据都转换为byte流进行发送。零世界之前的服务器是有key优化的,key转换为id发送。


21 对于继承类 要调用基类的函数可用:base.Start();用于需要基类先初始化。


22 关于Resources.load和实例化与内存的关系:
1.加载,单纯的Resources.load后消耗的内存很低,可能只是基础的引用预载、当对象被实例化后才会占用大量内存,当实例化多个对象后和实例化一个相差不大,可能后边实例的对象引用了第一个。
2.卸载,单纯的把所有实例的obj给destroy后,内存不会释放,但是再次实例也不会耗内存,若Object b = Resources.Load("Canvas");这样写,还需要把b=null后再调用Resources.UnloadUnusedAssets()后整个内存才会被释放。
///最好不要给Resources.Load("Canvas");单独定义变量,如果忘记清除变量引用后,内存就释放不了、清除引用是指Resources.Load的引用而不是实例化对象的引用。
所以建议GameObject a = GameObject.Instantiate (Resources.Load("Canvas") as GameObject这样直接写,然后destroy 这个实例对象后,调用unload 就可以释放内存。
23 关于assetbundle的加载和实例化与内存的关系:
1.对于WWW.LoadFromCacheOrDownload方式加载,单纯的load进来也不耗什么内存,实例化后会耗很多内存,destroy实例对象后,不释放内存。调用Resources.UnloadUnusedAssets()或www.assetBundle.Unload (true);才能完全释放Unload (false)后似乎没有释放或者可能释放量极小。
结论:建议使用assetbundle.Unload (false),实例对象销毁后需要调用Resources.UnloadUnusedAssets()
2.对于WWW www = new  WWW()方式加载,单纯的load需要消耗一份内存,实例化后再消耗一份内存。实例destroy后需要调用Resources.UnloadUnusedAssets()或www.assetBundle.Unload (true);才能释放实例对象内存,不能释放load的内存。Unload (false)后似乎没有释放或者可能释放量极小。
结论:一般最好不要使用该方法加载,会消耗双倍内存,可以用Resources.UnloadUnusedAssets()回收实例对象内存,本身load的内存无法手动回收。
3.对于AssetBundle.CreateFromFile():应该和1的情况类似。卸载资源只要引用没有了调用unload是有效的。
24 打bundle:
Application.persistentDataPath::C:/Users/guoyuan02/AppData/LocalLow/DefaultCompany/test
Application.dataPath::C:/Users/Public/Documents/Unity Projects/test/Assets
BuildAssetBundleOptions.CollectDependencies会去查找依赖,
BuildAssetBundleOptions.CompleteAssets会强制包含整个资源,
BuildAssetBundleOptions.DeterministicAssetBundle会确保生成唯一ID,在打包依赖时会有用到,其他选项没什么意义
BuildAssetBundleOptions.DisableWriteTypeTree 禁用写入类型树
BuildAssetBundleOptions.DeterministicAssetBundle 编译资源包使用一个哈希表储存对象ID在资源包中。


25 windows 查看堆栈,用process explorer工具,这个只能看到win层次的堆栈。另外用VS的进程工具Ctrl+Alt+P打开。附到某个进程,然后点暂停就可以看到该时刻的调用堆栈了。


26 splash:作为splash的图片不能用ToNearest来压缩,一定要用原大小。
Center (only scale down) 居中(只是缩小),以正确的像素显示图片,不会缩放图片除非需要。
Scale to fit (letter-boxed)自适应缩放大小(高宽比不变)尝试满屏显示,但不会裁剪,并用黑色填充空白的地方。
Scale to fill (cropped) 自适应填充(裁切不正的)满屏显示,屏幕外的部分将不显示.。
      
27.关于主城性能的优化:
1.在IOS平台有最大句柄的限制,当句柄达到最高的时候部分资源出错,新的资源加载不进来,解决方法:合并bundle资源,减少句柄。
2.主城玩家的名字Bar优化,主页UI过多,当玩家的bar跟随玩家移动的时候,只要x,y轴有变化会触发NGUI的排序功能,过多的bar和过快的移动会导致性能低下、解决方法:对于玩家的血条bar还是放在3d场景中 用3dText 来显示比较好。放UI层太耗了、
3.当出现了在好机子比在差机子上卡或者发热严重 耗电多的问题,一般都是显卡渲染引起的,好的机子开启了更好的效果所以负载大。所以建议所有机子的quality setting设置成一样。quality setting删后会出现bundle资源的显示不正常 各种uv错乱 黑块、应该修改Debug模式下的Second.
28.IOS自动打包设置用XUPorter工具:https://github.com/onevcat/XUPorter。。。http://www.xuanyusong.com/archives/2720/


29 在unity里边使用多线程做一些事情是非常好的,比如解压资源 更新资源等。因为单开线程的话 不会影响主线程卡顿,这样UI就不会卡了。但是开的线程里边不能执行unity主线程的mono代码。线程启动后,执行完毕自动结束该线程、可以同时启动多个线程做事。
代码如下: using System.Threading;
void Awake()
    {
        Thread athread = new Thread(new ThreadStart(goThread));
        athread.Start();
    }
    object lockd = new object();
    void goThread()
    {
        int index = 0;
        while (true)
        {
            lock (lockd)//防止其他线程访问当前线程使用的数据
            {
                Debug.Log("in thread" + index);
                index++;
                if (index == 100)
                {
                    Thread.Sleep(10000);//   将当前线程挂起指定的时间 毫秒  时间结束后 继续执行下一步  和yield类似
                }
                else if (index == 200)
                {
                    break;//该函数执行完自动结束该线程
                }
            }
        }
    }


30 关于事件回调:
EventDelegate.Add(button.onClick, Click);//该委托无参数
EventDelegate.Add(button.onClick,delegate() { Click("click"); });//该委托无参数,但可把参数存在临时变量里边,相当于回调有参数
EventDelegate.Add(button.onClick,delegate() {Debug.Log(UIButton.current.name); });//直接处理


UIEventListener.Get(imageButton.gameObject).onClick=Click; void Click(GameObject go){ }//该委托带参数
UIEventListener.Get(imageButton.gameObject).onClick = delegate(GameObject go ) {Debug.Log(go.name);};//直接写里边 不用函数转换


31 unity资源路径标准情况是斜杠/  Windows资源路径标准是反斜杠\。c#中的反斜杠用转义符\\表示。单纯的反斜杠\被用来做\n换行等字符识别了。


32.
Unity常用目录对应的Android && iOS平台地址
IOS:
Application.dataPath : Application/xxxxx/xxx.app/Data     //ipa安装目录
Application.streamingAssetsPath : Application/xxxxx/xxx.app/Data/Raw   //ipa安装目录;  移动设备不可写 只可读,IOS和Windows用File.Read ,安卓用java代码读;安卓内属于压缩文件类型 读取比persistentDataPath慢
//然后路径问题IOS PC用File.Read路径需要全路径,安卓InputStream in = getResources().getAssets().open(fileName); 用这个读 只需要文件名就可以了。
Application.persistentDataPath : Application/xxxxx/Documents      //文档目录  ;所有设备可写可读,可直接用File.Read读,有些接口读取全路径需要加 "file://" +Application.persistentDataPath,unity 的大部分接口不需要"file://"部分。
Application.temporaryCachePath : Application/xxxxx/Library/Caches   //缓存目录 ;所有设备可写可读
IOS还有tmp目录Application/xxxxx/tmp    //临时缓存目录


Android:
Application.dataPath : /data/app/xxx.xxx.xxx.apk     //apk内
Application.streamingAssetsPath : jar:file:///data/app/xxx.xxx.xxx.apk/!/assets   //apk内
Application.persistentDataPath : Android/data/data/xxx.xxx.xxx/files     //文档目录
Application.temporaryCachePath : /data/data/xxx.xxx.xxx/cache   //缓存目录


PC
Android:
Application.dataPath : C:/Users/guoyuan02.SNDA/Documents/New Notify/Assets     //工程内
Application.streamingAssetsPath : C:/Users/guoyuan02.SNDA/Documents/New Notify/Assets/StreamingAssets   //工程内
Application.persistentDataPath : C:/Users/guoyuan02.SNDA/AppData/LocalLow/DefaultCompany/New Notify    //文档目录
Application.temporaryCachePath : C:/Users/GUOYUA~1.SND/AppData/Local/Temp/DefaultCompany/New Notify   //缓存目录
当然PC下用File.ReadAllText可以读任意目录  移动也可以有其他方法读。


33 .关于Android里边的各个目录的获取方法:
Environment.getDataDirectory() = /data
Environment.getDownloadCacheDirectory() = /cache
Environment.getExternalStorageDirectory() = /mnt/sdcard
Environment.getExternalStoragePublicDirectory(“test”) = /mnt/sdcard/test
Environment.getRootDirectory() = /system
getPackageCodePath() = /data/app/com.my.app-1.apk
getPackageResourcePath() = /data/app/com.my.app-1.apk
getCacheDir() = /data/data/com.my.app/cache
getDatabasePath(“test”) = /data/data/com.my.app/databases/test
getDir(“test”, Context.MODE_PRIVATE) = /data/data/com.my.app/app_test
getExternalCacheDir() = /mnt/sdcard/Android/data/com.my.app/cache
getExternalFilesDir(“test”) = /mnt/sdcard/Android/data/com.my.app/files/test
getExternalFilesDir(null) = /mnt/sdcard/Android/data/com.my.app/files   //对应Application.persistentDataPath的SD卡目录
getFilesDir() = /data/data/com.my.app/files    //可能对应Application.persistentDataPath的本地目录






34排序用List的Sort函数挺好用的:List a=new List(); a.Sort(OnSort); int OnSort(int a,int b){if(a>b)return 1;else return -1; }


35.IOS安装后会把安装文件直接解压到目录,所以包大小会变大,加载速度会变快。Android安装后仍然是用压缩的方式存在目录,所以包大小不会变太多,但是加载会慢些。
这还涉及安装的apk和ipa包里边本身是解压的还是压缩的,如果ipa打包前就是压缩的 在IOS/android下边安装后包变得不会很大。但是ipa打包前是没有经过压缩,而是完全靠ipa和apk本身来压缩大小的话,安装后ios包大小就会差很多。


36.移位问题 2<<1 这是2向左移动1位,2>>1这是2向右移动1位。


37.函数参数传object类型:
一个参数 void BuildA(object para){  List list=para as List;...}  BuildA(list);
多个参数 void BuildB(object para){  object[] ps= para as object[];List list=ps[0] as as List;GameObject b=ps[1] as GameObject;...}  BuildB(new object[]{list,obj});//把多个参数塞到object数组里边 传过去,相当于只传一个参数。


38.stream:其中类Stream为抽象类。由此有三个派生类。 需要引入命名空间:using System.IO。一般调用完stream后 都需要调stream.close()来保证资源回收。
MemoryStream:对内存进行读取与写入 
BufferedStream:对缓冲器进行读取/写入 
FileStream:对文件执行读取与写入 
TextReader/Writer为抽象类。由此派生类: 
StreamReader/StreamWirter 分别用于对流的读取与写入。
File和Directory
File类支持对文件的基本操作,包括创建、拷贝、移动、删除和打开一个文件。Directory类则用于执行常见的各种目录操作,如创建、移动、浏览目录及其子目录。
File类和Directory类都是密封类。不象抽象类Stream,File类和Directory类可以被实例化,
Stream.Read(byte[] buffer, int offset, int count) 从当前流读取字节序列,表示从stream里边 buffer: 此方法返回时该缓冲区包含指定的字符数组,该数组的 offset 和 (offset + count -1) 之间的值由从当前源中读取的字节替换。for循环下返回值为0 代表读完了。每次读前要设Stream.position=offset;这样才能定位流里的位置。
Stream.Write(byte[] buffer, int offset, int count)表示写入该byte数组到流。


39.关于C#中 List和数组 GameObject 结构体等类型传递到函数或赋值给新参数的时候 是引用传递,一边修改数据另外一边也会变。int等数据是值传递,函数传递后就与原数据无关。


40.关于句柄限制 对于persistentDataPath目录 IOS有句柄限制最好不超过200,安卓没有,对于streamingAssetsPath目录IOS还是200个限制,但是安卓只能通过www异步加载或者ja另外va层面同步加载,异步加载有端口限制,同步加载有证据表明句柄数也不能太多。另外对于Http下载的文件流 端口也有数量限制 数量不能超6553个。所以除了可以用File.read方法以外的文件流 最好都及时关闭,若不关闭 一定要处理好引用关系 和防止句柄超。


41.yield return null 和Thread.Sleep(100)的区别 协成是等待一帧 再执行下边的内容;Thread.Sleep是当前线程阻塞0.1秒 然后继续执行,主线程使用会导致其他逻辑也阻塞。所以主线程一般不推荐使用。




----------------------------------------------------------------

new

1.在string中:equals方法对于字符串来说是比较内容的,“==”方法在字符串中比较的是首地址,对于值比较2者完全一样。
2.在c#中不支持中文的时候,要在visual中打开该文件,点文件-高级保存选项,保存为Unicode-1200格式。
3.访问其他脚本的方法,除了声明为static以外,还可以继承:public class FuLi_dailyClick : Fuli_dailyUI {}这样在这个脚本中的方法和变量就可以直接访问Fuli_dailyUI脚本了。继承一般不用,因为继承自己写的方法后,就不能用unity官方的功能了,update等都无效。
还可以采用在脚本中声明一个静态对象,然后其他脚本通过这个对象访问该脚本的其他方法。如:
public class JiusiUI : MonoBehaviour {//一定要保证这个脚本被active后 s_jiusiUI = this;才会生效
    static JiusiUI s_jiusiUI = null;//那么其他脚本可以采用s_jiusiUI.init()的方法来调用这边的函数


    void Awake()
    {
        s_jiusiUI = this;//这个很重要,必须有才能调用,不然不行。
    }
   
   public GameObject item1 = null;
    void OnDestroy()
    {


        s_jiusiUI = null;
    }


    public static void init()
    {//这里可是是实例化的东西
    s_jiusiUI.item1.gameObject.SetActiveRecursively(bShow);//这样静态方法和实例化就结合了。
    }//这儿是指在本脚本中可以将方法定义为静态,其他脚本可以调用该方法。而不是调用对象。
}




4.只有在主界面改变没有prefab的或者增加prefab的时候需要上传scenegame文件,其他的改变prefab的直接上次prefab就可以了。
5.想要自己的数据每次都更新,就不要把初始化数据放在start里边,而是自己定义一个init函数,没次进去都调用。
6.开始等待时间和终止等待
    IEnumerator Start() {
        StartCoroutine("DoSomething", 2.0F);//开始计时
        yield return new WaitForSeconds(1);
        StopCoroutine("DoSomething");//停止计时
    }
    IEnumerator DoSomething(float someParameter) {
        while (true) {
            print("DoSomething Loop");
            yield return null;
        }
    }
7 position和localPosition的区别是position是世界坐标,localpositon是基于父物体的坐标。


8 时间的显示格式:lblWaitTime.Text = string.Format("{0:D2}:{1:D2}:{2:D2}", waitTime.Hours, waitMinutes, waitSeconds);


9 关于叶签和内容最好的搭配方式参考福利。当需要打开第二页的时候,同时使第二页的叶签打开。但都得放在update后执行一次。尽量不要放在update后边,不然会导致显示闪一下。


10 关于等待时间的问题,最好少用协成yield waitforseconds,用updata下Time.time比较本次time和上次time的差值比较好。


11字体text必须要先等它处于active==true状态后再赋值,才能自动适应宽度。如果先赋值后使它active会导致它只会使用上次text数据的宽度显示出问题。


12所有系统要考虑场景切换后能否使用,所以最好存static数据,系统只是显示,每次回场景都要重新联网取数据不好


13对于字体设置 while 比for有效。for有时候根本不起用。


14.DragMove赋给图片,在加上collider后,就可以实现随意拖拽图片了。ngui中用。


15.关于摄像机设置显示的问题无论是否NGUI中,每个物体都有一个Layer层次,
 在摄像机中有一个Culling mask可以选中需要渲染的层次,那么其他层次在game中就
 不会显示了。
16active和enabled的区别:active是只针对物体的有效,enabled是只针对组件有效。


17.碰撞器和触发器要生效最好2个碰撞的物体都加上刚体。若只有一个是刚体,则用刚体去撞另外一个物体才触发事件,反之不触发。这个月layer无关。


18.Unity里边批量选择,鼠标点击一个,再按住SHIFT点另外一个就会批量选择他们之间所有东西。


19 出错时,在调试台打开open Editor Log,搜索Error的就知道哪错了,这是很重要的一个调试手段。


20 打包注意Resource和StreamingAssets目录,这2个目录都会打包,其他的按引用打包。


21.我们的零世界项目库只支持 xp系统的unity3.5.5版本。


22.动画animation的制作方法,在project中建造Animation文件,然后拖到场景中的物体上,然后用动画组件打开,才能编辑和保存。


23.正确的关卡加载做法是,1,showLoad(true),2用协成来异步加载,3.等级协成加载完自动showLoad(false),而不是在start里边showLoad(false),


24.协成最好是只在本地方法使用,不使用static类型。


25.使用多个camera的时候,只有Layer和Depth这2个参数影响各种渲染的内容效果,与各自的z轴无关。
   正常camera:depth=2,layer="UI"//正常相机是先渲染depth小的,也就是depth越大显示越考前。
   load的camera:depth=3,layer="UI"//load要遮住其他UI,所以layer必须和maincamera一样,layer相同的组件,前后关系和z轴有关,所以load的camera整体z=-1200。
   特效的camera:depth=4:layer="Touch"//特效的只要出效果,与其他无关,可以共同显示,所以layer不一样。
   layer由culling Mask来控制渲染。


26 关于回调函数 public delegate void ClickEventHandler();
    public event ClickEventHandler DialogOver = null; 可以把函数名用init传过来赋值给DialogOver,结束了调用DialogOver()就可以回调了。


27 报System.IO.DirectoryInfo错误,是因为平台web上不行,换成 其他平台也许就好了。


28 做战斗节奏加速 或者减速,用Time.timeScale = 1.0F;


29 abstract   override:
 public setUUid(ID){ ...;setInfo(heroInfo) }//父类
 abstract public void setInfo(RCardInfo heroInfo);//父类 用这个得时候 该父类 也得声明成abstract public abstract class HeroBaseDetail : BaseInfoObj{}
 public override void setInfo(RCardInfo heroInfo){}//子类
这个的用法:多个子类都需要调用基类相同的数据部分setUUid,然后也有各自单独的实现部分setInfo就是单独抽象来的。不然setUUid的相同内容得写多遍。




30 设置帧频:先设置quality setting的(垂直同步)vsync count为dont sync,然后用Application.targetFrameRate = 300;就可以了


31 所有会卡的东西,都放在协成里边来做,等一帧执行下一个内容。


32 关于回调的delegate 和event:如果单是回调: public delegate void ClickEventComplete(); ClickEventHandler loadCompleteOnce=null;就可以了。调过来的方法让他等于loadCompleteOnce就可以了。
但很多时候可能会并行使用像msgBox一样:
用法: public delegate void Click();
    public event Click yesClick = null;//event
调用的时候:用btnYes.yesClick += onYesClick; btnYes.yesClick += onYesClick2的形式才调用。+=和=的区别是:+= 的话会调用所有的方法1和2,等于只会调用最后一次覆盖的方法2。参考:http://www.cnblogs.com/haiyang1985/archive/2009/03/10/1407936.html
建议参考UIButtonClick方法:如果2次刷新的id不一样,第二次要调用ClearClick方法。//2者差不多,event更佳严谨。




33 将对象进行序列化 加:[System.Serializable]具体参考FBDtool 和fbddata和AutoHome
如:
[System.Serializable]//加了这句话 下边的public Data[] data = null;这个内容 才能在编辑器中显示出来。
public class Data
{
    public Vector2 ContentRect = Vector2.zero;
    public Transform content = null;
}
public class AutoHome : MonoBehaviour
{
  public Data[] data = null;
}


34 常用   #region A       #endregion A  将代码包裹开来。这样一个脚本里边 各种的功能分开,方便查看。




35.关于次数限制的问题,最好是初始化设为0 ,然后每用一次加1,当最大次数增加后,可以马上生效。
如果你的初始化设为最大值,一直减到0 ,如果最大次数变了 只能明天才能生效,马上生效就比较麻烦。


36 DestroyImmediate 同一帧内如果要destroy一些东西,然后又要生成该物体或要立即生效,就用这个,不要同一帧销毁的话就用Desroy


-----------------------------------------------------

   1、 if(Input.GetKeyDown(KeyCode.W))//指按下W,下面的内容会执行一次,用来调用按钮。按一次执行一次。
       if(Input.GetKey(KeyCode.W))   // 指按住W,下面的内容会一直执行,用于走路等。
       if(Input.GetKey("1") )    //若是引用数字键要用("1")
            Input.GetKeyUp //按下起来后有效一次和上面Down一样


   2、 Vector3(x,y,z)既是:(right,up,forward)
       Vector2(x,y)   做2d游戏用
  
   3、鼠标的光标和滑轮只能监听  不能判断,只读。
   
   4、鼠标键分别是Input.GetMouseButton(0)  左边
                  Input.GetMouseButton(2) 中间
                  Input.GetMouseButton(1)右边
   5、GUIskin可以从 Assets-creat-gui skin创建


   6  直接查找KeyCode可以得到所以键盘码
  
 7  要想实现一个展示品,相机围绕物体旋转。
    则把相机作为那个物体的子物体就可以了,再给物体加上mouselook,
    最好做一个空物体放那个需要展示的物体里边。不过我的mouselook还不是很好用 用官方的吧.


 8  Screen.width,Screen.height既是表示当前运行窗口的宽度与长度,不管是测试和正式运行都是如此。最好在函数中用 不要在定义里用。


 9 、android屏幕的调用,就是相当于windows的鼠标光标的感应,意思就是直接用一个mouselook就可以满足了。


 10  android横屏:Screen.orientation = ScreenOrientation.LandscapeLeft;放在function Start()函数下。


 11导出fbx模型时 要在max里把位图路径设置一样的。fbx和位图在一个文件夹里导入unity


 12 相机:Projection选项:Perspective:正常的个人视角
                          Orthographic:做2D游戏必备的视角,正交视角。


 13 重载本关卡:Application.LoadLevel(Application.loadedLevel);


 14 Rigidbody: Mass质量  Drag阻力,减慢速度  Angular Drag角阻力,减慢旋转。2D游戏还可以把Freeze Position 的z选中,Freeze Rotation 的X Y选中,只能饶z旋转,x y面可移动。


 15 function Start () 用来初始化的,重载场景后,只要初始化就好了,不需要赋值了。


 16 在android上设置开始欢迎界面:Player Settings(Splash Image section)中选择为启动画面准备好的素材


 17 要显示2D效果除了用2d tooltit外还可以用讲cube的scale 调为0,为保持碰撞效果可设为0.1很小(大点好)。


 18 设计蹦床的效果:把2个刚体部分重合,然后固定rigidbody position xyz,那么其他物体来碰撞都有自动弹性效果。质量来调节效果。刚体只是一种效果,要碰撞还是得collider。


 19摩擦力:collider.material.dynamicFriction = 0;//静摩擦与动摩擦 0为摩擦小,1为摩擦大。
           collider.material.staticFriction = 0;
   反弹力:collider.material.bounciness = 0;//反弹力  0为无反弹力  1为大  摩擦和反弹力值越大力越大 值越小力越小
 
 20 在函数里边定义变量的时候不要var,如 function Explosion(explode: Transform){}就可以了。


 21 截屏代码function OnMouseDown(t:Collider) { if(t.gameObject.tag=="") Application.CaptureScreenshot("Screenshot.png");}
 
 22 做绳的效果 :用很多个固定结点fixed joint,用很多cylinder模型,不能要重力,不然力大了会乱反弹。


 23 判断物体不在当前相机(屏幕)显示下就摧毁之:function OnBecameInvisible () {  Destroy(this.gameObject);}//同样还有function OnBecameVisible () { }//相机可见函数


 24 //定义鼠标有效区域:
        Rect screenRect = new Rect(0,0,Screen.width,Screen.height);
        if( !screenRect.Contains(Input.mousePosition)) return;


 26 关于物体材质颜色和透明度:用这个的前提是把材质设为Transparent。透明度改变很容易卡,所以间隔时间要长点:1s。
transform.renderer.material.color = Color.clear;//完全透明; RGB(0,0,0)为黑 (1,1,1)原色,Color32(255,255,255)白色
transform.renderer.material.color =  Color(1, 1, 1, 1);//分别代表RGBA,A代表alpha通道 从0完全透明--- 1不透明;
在做GUI或者2d时要想透明部分不显示成白色也要将材质设为Transparent就可以显示透明色了,前提在ps里面用透明背景。c#要用new Color()


 27 在android上调用退出:     if (Input.GetKey("escape"))
        { Application.Quit(); }


 28 GUI的数组function OnGUI () {
                 GUI.BeginGroup (new Rect (Screen.width / 2 - 400, Screen.height / 2 - 300, 800, 600));
                 GUI.Box (new Rect (0,0,800,600),"");
                 GUI.EndGroup ();} 


 29 GUIText显示参数: GameObject.Find("PETGUI").guiText.text ="收服蚂蚁:"+ PetNum;//放在Update下,""为新建的GUI Text的名字,PetNum为参数。


 30 要在别的脚本里调用其他脚本的变量用static var qq = 0; ///用public var qq =0;也可以在别的脚本调用,但不安全。
    在别人脚本里调用其他的脚本的函数也是static function qq(){} //调用当然都得用Pet.qq 或者Pet.qq();Pet为脚本名。


 31 为保证效率在用while循环时一定要用yield,效率会提高N多倍。不卡


 32 Application.OpenURL(http://www.baidu.com);//打开网页


 33   Destroy (this);删除脚步Destroy (this.gameObject);删除物体或者Destroy (other);//摧毁的物体放在括号内,不是前面,this也不行。Instantiate 


 34要想判断鼠标在屏幕哪一个区域,就在这个区域放一个GUITexture,然后在GUITexture里边放上function OnMouseEnter()就可以判断了,之前怎么没想到了靠!






-----------------------------------------------------

1、获取对象:a = GamoObject.Find("cube");
          b = GamoObject.Find("a/cube");
          c = GamoObject.FindWithTag("cube");
          d = GamoObject.FindGameObjectsWithTag("cube");//获得tag为cube的所有对象
          d[2].tag = "box";//d.length获得数组长度;


2、广播:gameObject.BroadcastMessage ("ApplyDamage", 5.0);向子对象的函数ApplyDamage发送消息5.0
         gameObject.SendMessage ("ApplyDamage", 5.0);//给自己发送消息;
         gameObject.SendMessageUpwards ("ApplyDamage", 5.0);给父对象发送消息 
  function ApplyDamage (damage : float) {
        print (damage);
          }


3、四元素:transform.rotation =Quaternion.Slerp (from.rotation, to.rotation, Time.time * speed);
            //插值旋转,就是慢慢旋转到to.rotation;//Quaternion.Lerp 比Slerp快而且效果没它好
           transform.rotation = Quaternion.Euler(Vector3(0, 30, 0));//立即旋转到这个角度
           var t=transform.eulerAngles.y;   
//返回绕z轴旋转的度数 ,既是读取当前z轴度数
          var angle : float = Quaternion.Angle(transform.rotation, target.rotation);//返回2物体间的角度
        transform.rotation = Quaternion.AngleAxis(30, Vector3.up);//绕y轴旋转30度


4 Mathf函数里边的角度是π方式表示的,欧拉角要转换过来。




5 获得物体的位置,旋转值,缩放值:
 var quare = room.transform.position;
 var rotationQ = room.transform.eulerAngles; 
 var ScaleQ = room.transform.localScale;
 transform.rotation =  Quaternion.identity;//设置旋转角度为(0,0,0)
设置位置和缩放倍数就用=Vector3(2,2,2);就可以了。在c#中是= new Vector3(1f,2f,3f);
设置人物旋转一般用 transform.localEulerAngles = new Vector3(-rotationY, rotationX, 0);//不要用Rotate了




6. Mathf.Clamp (value, min, max); 
限制value的值在min和max之间, 如果value小于min,返回min。 如果value大于max,返回max,否则返回value


7 C#中的等待: StartCoroutine(WAIT()); 这个是接口开始计时,否则直接调用不计时。    IEnumerator WAIT() {yield return new WaitForSeconds(5);}


8 驱动官方角色行走的脚本在animation里边,都是修改Input.GetAxis ("Mouse X");y旋转 Input.GetAxis ("Horizontal");Vertical移动Input.GetAxis (Fire1");开火等


9 颜色问题:Color是0-1范围,Color32范围是0-255;c#用transform.renderer.material.color = new color(,,,);


10 在用官方guitext字体的时候,把Pixel correct 选中,再放大Font Size就可以有最好的效果了。设置颜色: GameObject.Find("PETGUI").guiText.material.color = Color.red;


11,UIAtlas item1 =Resources.Load("head",typeof(UIAtlas) as UIAtlas;如果没有typeof则默认的Resources.Load()只能加载物体(Object)类型的东西。

你可能感兴趣的:(Unity项目经验总结)