using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
using System.Threading;

/*  一.值类型和引用类型的区别:
                             它们的区别在于使用的内存位置不同:值类型数据存储在栈上,而引用类型数据存储在堆上.
                             值类型直接包含值,换言之,变量引用的位置就是值在内存中实际存储的位置.
	                     引用类型并不直接存储值,它们存储的是对一个内存位置的引用(内存地址),要去那个位置才能找到真正的数据.
       此外还有一个可空修饰符 int? 表示可以让int的变量为null,即 int? a = null;
       还有一个int??用于判断并赋值,先判断当前变量是否为null,如果是就可以赋役个新值,否则跳过.
       例如: public int? a=null;
              public int b()
               {
                   return this.a ?? 5;
               }

  二.封装:
          封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类).被封装的对象通常被称为抽象数据类型.
	      封装的意义:
		       封装的意义在于保护或者防止代码(数据)被我们无意中破坏。在面向对象程序设计中数据被看作是一个中心的元素并且和使用它的函数结合的很                      密切,从而保护它不被其它的函数意外的修改。
		        封装提供了一个有效的途径来保护数据不被意外的破坏。相比我们将数据(用域来实现)在程序中定义为公用的(public)我们将它们(friends)定                     义为私有的(private)在很多方面会更好。私有的数据可以用两种方式来间接的控制。第一种方法,我们使用传统的存、取方法。第二种方法我们用属                     性(property)也就是get和set。
		        使用属性不仅可以控制存取数据的合法性,同时也提供了“读写”、“只读”、“只写”灵活的操作方法.
	      访问修饰符:
		    Private:只有类本身能存取.
		    Protected:类和派生类可以存取.
		    Internal:只有同一个项目中的类可以存取.
		    Protected Internal:是Protected和Internal的结合. 
		    Public:完全存取.

   三.继承(主要实现重用代码,节省开发时间):
	   1.C#中的继承符合下列规则:
		               继承是可传递的。如果C从B中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中的成员。Object类作为所有类的基类.                              派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义.
			       构造函数(做初始化)和析构函数(释放内存~+函数名)不能被继承。除此之外的其它成员,不论对它们定义了怎样的访问方式,都能被继                            承。基类中成员的访问方式只能决定派生类能否访问它们.
			       派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这                            些成员.
			       类可以定义虚方法、虚属性以及虚索引指示器,它的派生类能够重载这些成员,从而实现类可以展示出多态性.
	   2.new关键字
		     如果父类中声明了一个没有friend修饰的protected或public方法,子类中也声明了同名的方法。则用new可以隐藏父类中的方法.(不建议使用)
		
	   3.base关键字
		     base 关键字用于从派生类中访问基类的成员:
		                                     调用基类上已被其他方法重写的方法.
	                                             指定创建派生类实例时应调用的基类构造函数.
           4.sealed关键字 
                     写在class前面,表示此类不能被继承.

   四.多态:
        1.多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果.在运行时,可以通过指向基类的指针,来调用实现派生类中的方法.
	        编译时的多态性:
		          编译时的多态性是通过重载来实现的.对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作.
		运行时的多态性:
		          运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作.C#中,运行时的多态性通过虚成员实现.
		编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。
	2.实现多态:
		接口多态性. 这个继承接口的类必须实现接口里面的所有方法,还有就是接口里面的方法和变量都必须是static的.
	        继承多态性.通过抽象类实现的多态性. abstract关键字放在class之前,表示此类为抽象类,它的派生类只用实现它里面的abstract方法就行.
	    3.override关键字(动态多态):
	                重写父类中的virtual修饰的方法,实现多态.

   五.多线程下的单例模式:
      public sealed class Singleton
              {
	                 private static readonly Singleton instance = new Singleton();
	
	                 private Singleton(){}
	
	                 public static Singleton GetInstance()
	                       {
		                        return instance;
	                       }
              }

   六.数据结构-数组和链表:array和list.
                   数组和链表的区别与联系:
                                   1.数组物理上顺序存储,所以查找快(直接下标索引),但插入删除慢(需要移动元素).
	                                 2.链表物理上不一定顺序存储,所以查找慢(不能用下标,只能从表头开始遍历),但插入删除快(直接操作指针,无需移动元素).
		                             这两者都属于线性表。
                   单链表:每个元素除了存储本身的值外还存储了前驱的引用也就是存储了前驱所在的内存地址信息.
                   双链表:就是不仅存储了前驱的引用还存储了后继的引用.

   七:递归思想--它的意思就是自己无限制的调用自己,但终究有一个条件会让它停止下来,一直循环调用堆栈,特别消耗内存.

   八:Unity脚本的生命周期:
                    Awake-Start-Update-FixedUpdate-LateUpdate-OnGUI-Reset-OnDisable(释放)-OnDestroy(销毁)

   九:点乘和叉乘:
            点乘判断两个向量的方向,乘出来的是一个值,如果>0,那么判断在同一方向0-90度;如果=0,那么俩向量垂直;如果<0,那么判断在反方向90-180度.
            点乘还可以计算一个向量在另一个向量上的投影大小: a和b俩向量,a向量在b向量上的投影为|a|*cos(Angels).
            点乘公式: a.b = |a|.|b| cos (Angels)。
            叉乘判断两个向量的垂直向量,也可以说是法向量,乘出来的是一个新的向量,并且垂直于相乘的那两个向量,这三个向量a,b,a*b构成一个右手坐标系.
            叉乘公式:|a*b| = |a|*|b| sin (Angels)。    |a*b|也可以是以a和b为两边构成的平行四边形面积.
            Vector3.Dot 是点乘  ,  Vector3.Cross 是叉乘

   十:Invoke(恳求):
             Invoke("fangfaming",time); 这个表示几秒过后调用哪个方法.
             InvokeRepeating("fangfaming",time,time); 这个表示几秒过后调用哪个方法并且再过几秒重复调用一次,一直循环.
             
   十一: lambda表达式:  (int a,int b) => { int c = a+b ;}  ,它也属于一种匿名函数.
   
   十二: 协同的使用:
                   关键字IEnumerator 名字() {  里面有yield return null 和 yield return new waitforseconds(time);}
                   然后StartCoroutine("名字"); 或者 StopCoroutine("名字");
                   
   十三: private和public变量分别在inspector中的显示和隐藏,分别用[SerializeField]和[HideInInspector].
   
   十四:#define #undef  #if  #endif #elif #else 的使用:
                                         #define必须定义在所有using之前,可以定义任何类型,#if 后面加#define定义的类型,后面必须有
                                         #endif存在,它俩中间的语句就是要输出的语句.
           C++中的宏定义为 #define+类型名+参数(或字符序列,例如:True和Flase用1和0表示).
           
   十五:枚举和结构,都是值类型
        enum+枚举名{},访问的时候直接用枚举名访问.
        struct+结构名{},定义一个结构名类型的变量,访问的时候直接用这个变量访问.结构里面没有默认的构造函数,所以给变量赋值的时候必须得用new
        例如:struct JieGou{ public int numb; }  Class MyClass{public int numb = 0; }
            Class Text{  Static Void Main()
            {
                JieGou a = new JieGou();  JieGou b = a; b.numb = 25;
                MyClass c = new MyClass(); MyClass d = c; d.numb = 25;
            }
        这里看似二者一样,其实因为结构体是值类型,所以改变b的值之后a的值并不发生改变,而类是引用类型,改变d的值,c的值也同样发生变化.
        
   十六:C#中的泛型:
             为了防止装箱和拆箱以及类型的安全才使用泛型,一般都是一些数组和链表,结构,类的泛型,表示为: T[] ,List<T> ,Struct<T> , 类名<T>;
             一般里面用的都是Foreach循环遍历,用CompareTo()<0    互相作比较谁大谁小;
             里面还有Dictionary<T Key,T Value>    用Add添加   Add(2,"hehe");   

   十七:C#中的查询表达式 LINQ:
          1. 所适用的数据库查询:SQL数据库;XML文档;Web服务;ADO.NET数据集;任何支持借口IEnumberable或IEnumberable<T>的集合.
          2.
   十八:c++ 指针的暂时理解: p = &a  p是指针变量,p的值是p所指向的变量的地址,&为取地址符; 
                              *p = a  *p是p所指向的变量,即*p的值就是p所指向变量的值;
                              对于数组而言: int[a] = {1,2,3,4,5}  p = a; p+1 = a+1 因为它是地址,多以等同于p = &a[0]=a  p1 = &a[1] = a+1;     *p = a[0]; *(p+1) = a[1];

          
*/
public class MyProject1 : MonoBehaviour { 

	public System.Action<GameObject> hit;
	void Start() { 

	}   
	void Update() {

	}
    #region
    string text = "hello{0},asdas{1},dasdas{2}";
	void MyText()
	{
		text = string.Format (text, 10, 20, 30);
		Debug.Log (text);
	}
	#endregion //文本占位符操作,输出来的数据为:hello10,asdas20,dasdas30 ;如何debug输出几个对象



    #region
    void xuliehua()
	{
		List<string> inputList = new List<string>();
		FileStream fsWriter = new FileStream (@"D:\test.xml", FileMode.Create, FileAccess.Write);
		XmlSerializer xlh = new XmlSerializer(typeof(List<string>));
		xlh.Serialize (fsWriter, inputList);
		fsWriter.Flush ();
		fsWriter.Close ();
	}
	#endregion //序列化:把对象变成文件内容写入自己创建的文件夹中



    #region
    void fanxuliehua()
	{
		List<string> output = new List<string>();
		FileStream fsRead = new FileStream (@"D:\test.xml", FileMode.Open, FileAccess.Read);
		XmlSerializer fxlh = new XmlSerializer(typeof(List<string>));
		output = (List<string>)fxlh.Deserialize (fsRead);
		fsRead.Close ();
	}
	#endregion //反序列化:把自己创建的文件夹中的文件内容变成对象读出来



    #region 
    int number { get; set; }
    public static MyProject1 operator +(MyProject1 a, MyProject1 b)
	{
        MyProject1 MyNumber = new MyProject1();
        MyNumber.number = a.number + b.number;
		return MyNumber;
	}
	#endregion //重载运算符,必须是静态.必须有返回值.必须有关键字operator, 参数里面的第一个参数类型必须是这个方法所在的那个类名,因为得让这个类知道它重载了.



	#region 
	//一个发布多个监听
	//发布类
    public delegate void Myweituo();
	public event Myweituo _weituo; //定义一个事件,也就是委托变量
	void wt(){if(_weituo!=null){_weituo();}}
	//然后在监听类中获取这个发布类._weituo += 此类中要执行的方法.    之后控制那个wt方法就行.


	//多个发布一个监听
	//发布类
	public delegate void __Myweituo(GameObject obj);
	public event __Myweituo __weituo;
	void yt(){if(__weituo!=null){__weituo(this.gameObject);}}
	//然后在监听类中控制获取这个发布类.yt(); 再在此类中获取这个发布类.__weituo += 此类中要执行的方法


	//委托分为一个发布多个监听和多个发布一个监听两种情况
	public delegate void _Myweituo(GameObject obj);
		public _Myweituo m_weituo;  //这个与这个event是一个概念  event Myweituo m_weituo; 
	//事件可以包含在接口声明中,而字段不能;事件只能在其声明类中调用;而如果是委托,则任何有权访问委托的对象都可以调用委托。
//	public System.Action<GameObject> m__weituo; 这是一种便捷的委托定义,相当于一下定义上面两个
//  但是这个 m__weituo只能=,而上面定义的m_weituo需要+=和-=


	#endregion //委托机制 :使用delegate定义的那个委托就相当于一个新类



    #region
    RaycastHit _hit;
	[SerializeField]
	private float speed = 10f;
	void rayjiance()
	{
		if (Input.GetMouseButtonDown(0)) 
		{
			Ray _ray = Camera.main.ScreenPointToRay (Input.mousePosition);
			Debug.DrawLine(_ray.origin,_hit.point,Color.red,5.0f);
			//  Raycast只能进行单个物体射线检测,重叠不穿透,返回的是一个bool类型
			// 要想多个射线检测,比如游戏拾取物品,得用RaycastAll 返回的是一个RaycastHit[],此时可以一下拾取多个重叠物品
			// 主角打怪的时候用球形碰撞检测  Physics.OverlapSphere方法,返回的是一个collider[]  
			if(Physics.Raycast(_ray, out _hit, 1000))
			{
				GameObject obj = _hit.collider.gameObject;
				if(obj.name.Equals("Ground") && Vector3.Distance(gameObject.transform.position,_hit.point)>0.1f){
					Vector3 direct = _hit.point-gameObject.transform.position;
					direct = direct.normalized;//两坐标向量相减再归一化
					direct = direct * Time.deltaTime*speed; //移动的速度
					direct.y = 0;

					Quaternion m_TargetQua = Quaternion.LookRotation(direct);
					gameObject.transform.rotation = Quaternion.Lerp(gameObject.transform.rotation,
					                                                m_TargetQua,5f); //让角色缓慢转身
					CharacterController a = new CharacterController();
					a.Move(direct);//角色控制器移动到这里
				}
				Debug.Log("Hit objname:"+obj.name+"Hit objlayer:"+obj.layer);  
			}
		}
	}
	#endregion //射线检测鼠标所点击的对象并且移动物体对象



	#region
	void transScale() {
		if(Input.GetAxis("Mouse ScrollWheel")>0) {
			Camera.main.fieldOfView -= 2;
		}
		if(Input.GetAxis("Mouse ScrollWheel")<0) {
			Camera.main.fieldOfView += 2;
		}
	}
	#endregion //鼠标控制缩放



	#region
	Vector3 _cubescreenspacepos; //对象的屏幕坐标
	Vector3 _mousescreenspace; //鼠标的屏幕坐标
	Vector3 _offsetpos; //鼠标与对象在世界坐标系中的偏移坐标
	
	IEnumerator OnMouseDown() {
		_cubescreenspacepos = Camera.main.WorldToScreenPoint(gameObject.transform.position);
		_mousescreenspace = new Vector3(Input.mousePosition.x,Input.mousePosition.y,_cubescreenspacepos.z);
		_offsetpos = gameObject.transform.position - Camera.main.ScreenToWorldPoint(_mousescreenspace);
		
		while(Input.GetMouseButton(0)) {
			_mousescreenspace = new Vector3(Input.mousePosition.x,Input.mousePosition.y,_cubescreenspacepos.z);
			gameObject.transform.position =  _offsetpos + Camera.main.ScreenToWorldPoint(_mousescreenspace);
			yield return new WaitForFixedUpdate();
		}	
	}
	#endregion //鼠标控制物体拖拽



	#region
	void fanxiang() {
		//将动画speed调为-1就行
	}
	#endregion //反向旋转动画



    #region 
    // 两物体都必须有Collider,并且一个必须有rigidbody组件,并且取消刚体组件的重力选项UseGravity。
    // 碰撞检测用Collision;触发用Tigger,并且勾选碰撞组件中的Is Tigger 属性选择框。
	//挂有第一人称控制器的碰撞只能检测到触发信息,有碰撞效果,检测不到碰撞信息
    void OnTiggerEnter(Collider other)
	{
		Debug.Log (other.gameObject.name);
	}
	void OnCollisionEnter(Collision collisionInfo)
	{
		Debug.Log (collisionInfo.gameObject.name);
	}
	#endregion //物体碰撞检测(collider和collision)



    #region 
    void load()
	{
		GameObject obj1 = Resources.Load ("Prefab/Cube") as GameObject;
		if (null != obj1)
		{
			GameObject obj2 = Instantiate (obj1, new Vector3 (1, 0, 0), Quaternion.identity)as GameObject;
			if(null != obj2)
			{
				obj2.name = "Prefab1"; //面板中显示的名字
//				obj2.transform.parent = transform.parent;  因为上面实例化的时候已经定好了坐标和旋转。
//				obj2.transform.position = obj1.transform.position;
//				obj2.transform.rotation = obj1.transform.rotation;
				obj2.transform.localScale = Vector3.one; 
		        Rigidbody rig = obj2.GetComponent<Rigidbody>();//获取组件
				if(null == rig) //如果组件为空
				{
					obj2.AddComponent("Rigidbody"); // 就给它添加组件
				}
			}
		}
	}
	#endregion  //装载预制体和实例化以及调整它的位置旋转和缩放,还有获得和添加组件



	#region //属性(构造器)
	private Transform _name;
	public Transform Name
	{
		get{return _name;}
		set{_name = value;}
	}
	#endregion//属性(也被称之为构造器),私有的private变量,想让在其他脚本里可以访问它并改变它



	#region //单例
	//第一种单例 不继承mono
	private static MyProject1 instance;
	public static MyProject1 GetInstance()
	{
		if (instance == null)
		{
			instance = new MyProject1();
		}
		return instance;
	}

	//第二种单例 继承mono
	private static MyProject1 _instance;
	public static MyProject1 _GetInstance() {
		if(_instance == null) {
			GameObject obj = new GameObject("MyProject");
		    _instance =	obj.AddComponent<MyProject1>();
			DontDestroyOnLoad(obj);//跨场景的时候不释放这个物体
		}
		return _instance;
	}

	//第三种单例  继承mono,并且开发人员指导次脚本只挂在一个物体上面,多一个物体都不行
	private static MyProject1 m_instance;
	void Start2() {
		m_instance = this;
	}
	#endregion//单例,指的就是单例这个类,保证一个类只有一个实例,并且提供一个访问它的全局访问点。单例对象的类必须保证只有一个实例存在。



    #region   
//    void Start2()
//    {
//        Thread thr = new Thread(new ThreadStart(fangfa3));
//        thr.Start();
//        Thread.Sleep(1000);//括号里面的是毫秒,此处还可以写Thread.Sleep(TimeSpan.FromSeconds(30)); timespan是时间间隔的意思。
//        thr.ThreadState();//此刻该线程的状态是运行的还是挂起的还是终止的
//        thr.Suspend();//线程挂起
//        thr.Resume();//线程回复
//        thr.Join();//线程合并
//        thr.Abort();//线程终止
//        thr.IsAlive();//线程是否是执行状态
//        thr.IsBackground();//线程是否后台运行
//        thr.Priority();//线程的优先级
//    }
//    void fangfa3() { }  //方法里面可以写lock(类名);并且用try{}catch{}表示try里面的出现了异常然后执行catch里面的。
//                        //以及finally{} 表示不管前面有没有异常都执行里面的东西,用来处理清理工作和关闭数据库连接等等

	#endregion //线程



    #region 
    public class ObjectPool : MonoBehaviour
    {
        public static ObjectPool instance;
        public GameObject[] objectPrefabs; //所有对象预制体
        public List<GameObject>[] pooledObjects;//现用的池对象
        public int[] amountToBuffer; //场景出现每个对象的数量
        public int defaultBufferAmount = 3;//默认出现的对象数量
        protected GameObject containerObject;//场景容器对象

        void Awake()
        {
            instance = this;
        }
        void Start()
        {
            containerObject = new GameObject("ObjectPool");
            pooledObjects = new List<GameObject>[objectPrefabs.Length];
            int i = 0;
            foreach (GameObject objectPrefab in objectPrefabs)
            {
                pooledObjects[i] = new List<GameObject>();
                int bufferAmount;
                if (i < amountToBuffer.Length) bufferAmount = amountToBuffer[i];
                else bufferAmount = defaultBufferAmount;
                for (int n = 0; n < bufferAmount; n++)
                {
                    GameObject newObj = Instantiate(objectPrefab) as GameObject;
                    newObj.name = objectPrefab.name;
                    PoolObject(newObj);
                }
                i++;
            }
        }
        public GameObject GetObjectForType(string objectType, bool onlyPooled)//根据对象名字和真假做判断是否实例化
        {
            for (int i = 0; i < objectPrefabs.Length; i++)
            {
                GameObject prefab = objectPrefabs[i];
                if (prefab.name == objectType)
                {
                    if (pooledObjects[i].Count > 0)
                    {
                        GameObject pooledObject = pooledObjects[i][0];
                        pooledObjects[i].RemoveAt(0);
                        pooledObject.transform.parent = null;
                        pooledObject.SetActiveRecursively(true);
                        return pooledObject;
                    }
                    else if (!onlyPooled)
                    {
                        return Instantiate(objectPrefabs[i]) as GameObject;
                    }
                    break;
                }
            }
            return null;
        }

        public void PoolObject(GameObject obj) //池对象添加方法
        {
            for (int i = 0; i < objectPrefabs.Length; i++)
            {
                if (objectPrefabs[i].name == obj.name)
                {
                    obj.SetActiveRecursively(false);
                    obj.transform.parent = containerObject.transform;
                    pooledObjects[i].Add(obj);
                    return;
                }
            }
        }
    }
	#endregion //Unity对象池的控制和使用



	#region 
	int[] arr = {30,20,25,40,35,16,78,95,41,26,17};
	void QuickSorf()
	{
		Sorf (arr,0,arr.Length-1);
	}
	
	void Sorf(int[] arr,int i,int j)
	{
		if (i < j)
		{
			/*完成一次单元排序*/
			int mid = MySorf(arr,i,j);
			/*对左边单元进行排序*/
			Sorf(arr,i,mid-1);
			/*对右边单元进行排序*/
			Sorf(arr,mid+1,j);
		}
	}
	
	int MySorf(int[] arr,int i,int j)
	{
		int key = arr [i];
		while (i<j)
		{
			/*从后向前搜索比key小的值*/
			while (arr[j] >= key && j > i)
				--j;
			/*比key小的放左边*/
			arr[i] = arr[j];
			
			/*从前向后搜索比key大的值,比key大的放右边*/
			while (arr[i] <= key && j > i)
				++i;
			/*比key大的放右边*/
			arr[j] = arr[i];
		}
		/*左边都比key小,右边都比key大。//将key放在游标当前位置。//此时low等于high */
		arr[i] = key;
		return i;
	}
	#endregion //快速排序

}