Unity3D入门学习记录

本文记录了一些个人学习Unity3D的笔记,如有问题欢迎指出

一、物体操作快捷键

  • 手性工具:Q
  • 移动工具:W
  • 旋转工具:E
  • 缩放工具:R
  • 矩形工具:T
  • 移动旋转或缩放选定对象:Y

二、地形

1.地形刷

  • 抬高或降低地形
  • Paints Holes
  • Paint Texture
  • Set Height
  • Smooth Height
  • Stamp Terrain

2.树木

3.草

三、游戏脚本基础

1.组件类MonoBehaviour(重要!)

  • 每个游戏物体的功能都是由组件组成的,使用脚本可以任意添加修改组组件,前提是该脚本的类的父类为MonoBehaviour
  • 脚本本身也可以是一个组件,能挂在物体上

2.脚本生命周期

  • void Awake()<void Start()<void OnEnable()<void Update()/void FixedUpdate()<void LateUpdate()<void OnDestory()

3.多个脚本执行顺序

  • 打开项目设置,在“脚本执行顺序”设置界面中单击“创建”添加脚本,然后通过拖曳来设定脚本的正确执行顺序。

四、虚拟轴

  • 虚拟轴可将不同的游戏设备按键映射到一套虚拟轴
  • 虚拟轴的本质是一个数值在-1~1内的数轴,数轴上最重要的数值是-1,0,1
  • 支持自定义

五、场景灯光

  • 定向光
  • 点光源
  • 聚光灯
  • 区域光

六、光照烘培

  • 将灯光制作为光照贴图,节省性能

七、Transform和RigidBody的区别

  • Transform将重点放在物体的位置和旋转上,而RigidBody重点放在物体的力、速度和扭矩上
  • Transform组件持续撞向墙体时可能会发生穿过墙体或抖动等现象

八、射线

  • 从一条固定的点发出的一条直线,在发射过程中需要判断该条射线有没有与游戏物体发生碰撞
  • 射线可以判断在该射线上的所有物品

九、粒子系统

  • 粒子发射器
    •  形状
    • 粒子渲染效果
    • 粒子速度
    • 粒子颜色大小与旋转
  • 线条效果
    • 线段渲染器
    • 拖尾渲染器

十、动画效果

  • Animation(动画组件)
    • 通过属性的变化制作动画
    • 帧事件:在某个动画帧上调用一个功能
  • Animator
    • 过渡条件
  • 混合树
  • 动画层
    • 对动画进行分类
  • Avatar遮罩
    • 对人物的不同部位进行多层动画的混合
  • 反向动力学
    • 让角色更有真实感(击打敌人,脚踩斜坡)
    • 举例:由原本的手臂来决定手的位置,现在由手的位置决定手臂的位置

十一、导航系统

1.导航网格

  • 先要在检查器面板中展开的”静态的“下拉列表并选择Navigation Static,随后进行烘培
  • 导航网格就是物体可移动的区域

2.导航代理

  • 导航代理就是指定物体能在导航网格区域内移动
  • 动态障碍物:Nav Mesh Obstacle,在检查器中取消勾选”静态的“,节约性能可以增大移动阈值和增加静止时间,还可以勾选仅在静止时切割

3.导航网格链接

  • 可以指定有高低差的地方具有掉落和跳跃的功能
  • Off Mesh Link可自定义网格链接

4.导航区域

  • 区域成本越高,经过的优先级就越低

十二、UI系统

1.画布的渲染模式

  • 屏幕空间-覆盖
  • 屏幕空间-摄像机
  • 世界空间

2.UI位置布局

  • 轴心点:控制UI图像的中心
  • 锚点:UI对于画布的固定位置
    • 普通
    • 分散
    • 缩放
    • 预设

3.UI控件

  • 图像
  • 文本
  • 按钮
  • 文本框
  • 选项
  • 下拉列表框
  • 滚动视图
  • 滑动条

4.UI组件

  • 遮罩组件:Mask
  • 内容尺寸适应器:组件Content Size Fitter
  • 垂直和水平布局组:组件Horizontal LayOut Group
  • 网格布局组:Grid LayOut Group

十二、2D游戏

1.瓦片地图

  • 瓦片地图碰撞器

十三、游戏中的数据与碰撞器

1.常用数据结构

  •   JSON
    • using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      [SerializeField]
      public class Persons
      {
          public Person[] persons;
      }
      [SerializeField]
      public  class Person
      {
          public string name;
          public int age;
      }
      public class JsonTest :MonoBehaviour
      {
          private void Start()
          {
              Person p1 = new Person();
              p1.name = "Tom";
              p1.age = 40;
              Person p2 = new Person();
              p2.name = "Jerry";
              p2.age = 30;
              Persons persons= new Persons();
              persons.persons = new Person[] { p1, p2 };
              string JSONStr = JsonUtility.ToJson(persons.persons);
              Debug.Log(JSONStr);
              Persons newPersons = JsonUtility.FromJson<Persons>(JSONStr);
              Person person = newPersons.persons[0];
              Debug.Log(person.name);
          }
      
      }
  • XML
    • <?xml version="1.0" encoding="utf-8"?>
      <items>
        <item id="1000">
          <name>小血瓶</name>
          <description>使用后增加100血量</description>
          <type>消耗品</type>
          <hp>100</hp>
          <attack>0</attack>
        </item>
        <item id="1000">
          <name>铁剑</name>
          <description>使用后增加10点攻击力</description>
          <type>武器</type>
          <hp>0</hp>
          <attack>10</attack>
        </item>
      </items>
    • 脚本调用
      using System.Xml;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class XMLTest : MonoBehaviour
      {
          // Start is called before the first frame update
          void Start()
          {
              //解析XML
              XmlDocument doc = new XmlDocument();
              //读取XML文件,也可以用doc.LoadXml加载XML字符串
              doc.Load(Application.dataPath + "/test.xml");
              //获取XML根节点,这里为了方便,强制转换类型为XmlElement
              XmlElement rootElement =doc.LastChild as XmlElement;
              //获取根节点的第一个子节点
              XmlElement persons = rootElement.FirstChild as XmlElement;
              //因为persons有多个子节点,所以这里我们遍历子节点
              foreach(XmlElement xmlElement in persons)
              {
                  //获取属性id
                  string id = xmlElement.GetAttribute("id");
                  //获取名称
                  string name = xmlElement.ChildNodes[0].InnerText;
                  //获取年龄
                  string age = xmlElement.ChildNodes[1].InnerText;
                  Debug.Log("id:" + id + "name:" + name + "age:" + age);
              }
          }
      }
      
  • 多线程
    • 并发操作,即同时处理不同的事物
    • using System.Threading;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class ThreadTest : MonoBehaviour
      {
          // Start is called before the first frame update
          void Start()
          {
              Debug.Log("正在忙工作");
              Debug.Log("正在忙工作");
              Debug.Log("正在忙工作");
              //执行协程
              ThreadStart ts = new ThreadStart(Test);
              Thread thread = new Thread(ts);
              thread.Start();
              Debug.Log("继续工作");
              Debug.Log("继续工作");
              Debug.Log("继续工作");
          }
      
          // Update is called once per frame
          void  Test()
          {
              Debug.Log("开始烧水");
              Thread.Sleep(5000);
              Debug.Log("可以使用热水啦!");
          }
      }
  • 协程
    • 即顺序处理,一个一个执行,当有一个进程需要处理很长时间时,可以去处理别的进程
    • public class Corutine : MonoBehaviour
      {
          // Start is called before the first frame update
          void Start()
          {
              Debug.Log("正在忙工作");
              Debug.Log("正在忙工作");
              Debug.Log("正在忙工作");
              //执行协程
              StartCoroutine(Test());
              Debug.Log("继续工作");
              Debug.Log("继续工作");
              Debug.Log("继续工作");
          }
      
          IEnumerator Test()
          {
              Debug.Log("开始烧水");
              yield return new WaitForSeconds(5);
              Debug.Log("可以使用热水了");
          }
      
          // Update is called once per frame
          
      }
  • 网络的请求
    • 动态获取数据
    • 客户端发送请求,服务端接受响应,数据便传输完成
    • 利用简易服务器调用数据
    • using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      using UnityEngine.Networking;
      
      public class WebServe : MonoBehaviour
      {
          // Start is called before the first frame update
          void Start()
          {
              //开启协程,进行get请求
              StartCoroutine(GetTest());
              //如果接口是Post类型,开启协程,进行Post请求
              //StartCoroutine(PostTest());
          }
      
          IEnumerator GetTest()
          {
              //创建一个Get类型的请求,请求的内容其实就是我们的test.JSON
              UnityWebRequest request = UnityWebRequest.Get("http://127.0.0.1/text.xml");
              //发送请求并开始等待响应
              yield return request.SendWebRequest();
              if (request.isHttpError || request.isNetworkError)
              {
                  Debug.Log(request.error);
              }else
              {
                  //如果没有错误,输出响应中的内容,这里会输出JSON文件中的内容
                  Debug.Log(request.downloadHandler.text);
                  //这里开始就可以对XML开始解析
              }
          }
      
          IEnumerator PostTest()
          {
              WWWForm form = new WWWForm();
              //添加Post参数
              form.AddField("key", "value");
              //form.AddBinaryData("key", data);
              //创建一个Pose类型的请求
              //创建一个Get类型的请求,请求的内容其实就是我们的test.JSON
              UnityWebRequest request = UnityWebRequest.Post("http://xxx.xxx.xxx",form);
              //发送请求并开始等待响应
              yield return request.SendWebRequest();
              if (request.isHttpError || request.isNetworkError)
              {
                  Debug.Log(request.error);
              }
              else
              {
                  //如果没有错误,输出响应中的内容,这里会输出JSON文件中的内容
                  Debug.Log(request.downloadHandler.text);
                  //这里开始就可以对XML开始解析
              }
      
          }
      }
  • Socket
    • 采用TCP/IP协议
    • 客户端响应一次,服务端响应一次j
    •  

      using System;
      using System.Net;
      using System.Net.Sockets;
      using System.Text;
      using System.Threading;
      namespace Send
      {
          class Program
          {
              //监听套接字
              static Socket listenSocket;
              static void Main(string[] args)
              {
                  //创建一个TCP模式监听套接字
                  listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                  //如果要创建UDP模式
                  //listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                  IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5556);
                  listenSocket.Bind(ip);
                  listenSocket.Listen(10);
                  Thread thread = new Thread(Listen);
                  thread.IsBackground = true;
                  thread.Start();
                  Console.ReadKey();
              }
      
              static void Listen()
              {
                  Console.WriteLine("开始监听");
                  //循环监听
                  while(true)
                  {
                      Socket newSocket = listenSocket.Accept();
                      Console.WriteLine("有客户端链接");
                      //数据缓存
                      byte[] bs = new byte[1024];
                      //接受数据
                      newSocket.Receive(bs);
                      //将数据转换为字符串并输出
                      Console.WriteLine(newSocket.RemoteEndPoint.ToString() + ":" + new UTF8Encoding().GetString(bs) + "\n");
                  }
              }
          }
      }
      
    • 上图是接受端
    • 下图是发送端
    • using System.Net;
      using System.Net.Sockets;
      using System.Text;
      namespace Recieve
      {
      	public class Program
      	{
      		static Socket socket;
      		static void Main(string[] args)
      		{
      			//创建Socket
      			socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      			//连接要发送消息的目标ip与端口,这里ip与端口一定要与监听消息端相同
      			IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5556);
      			socket.Connect(ip);
      			//发送消息
      			socket.Send(new UTF8Encoding().GetBytes("你好"));
      		}
      	}
      }

十四、设计模式

1.状态模式

  • 先写一个抽象状态类来封装状态,然后将每个状态分别继承于这个类
  • using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public abstract class State
    {
        public abstract void Run();
    }
    
    public class SleepState:State
    {
        public override void Run()
        {
            Debug.Log("学习");
        }
    }
    
    public class PlayState : State
    {
        public override void Run()
        {
            Debug.Log("学习");
        }
    }
    public class StudyState : State
    {
        public override void Run()
        {
            Debug.Log("学习");
        }
    }
    
    public class NewStudent
    {
        public State state;
        //接收时间并切换学生该时间段的状态
        public void Run(int time)
        {
            if(time>22||time<6)
            {
                state = new SleepState();
            }
            else if(time>=7&&time<=18)
            {
                state = new StudyState();
            }
            else
            {
                state = new PlayState();
            }
            state.Run();
        }
    }
    public class StateStyle : MonoBehaviour
    {
    
        private void Start()
        {
            NewStudent student = new NewStudent();
            student.Run(8);
            student.Run(22);
                }
    }

2.外观模式

  • 为子系统提供一组统一的高层接口,使子系统更容易使用,这就是外观模式
  • using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class StudentSystem
    {
        public void Run()
        {
            Debug.Log("我是学生系统");
        }
    }
    
    public class TeacherSystem
    {
        public void Run()
        {
            Debug.Log("我是教师系统");
        }
    }
    
    public class WorkerSystem
    {
        public void Run()
        {
            Debug.Log("我是工人系统");
        }
    }
    
    public class Facade
    {
        StudentSystem studentSystem;
        TeacherSystem teacherSystem;
        WorkerSystem workerSystem;
        public Facade()
        {
            studentSystem = new StudentSystem();
            teacherSystem = new TeacherSystem();
            workerSystem = new WorkerSystem();
        }
    
        public void StudentRun()
        {
            studentSystem.Run();
        }
    
        public void TeacherRun()
        {
            teacherSystem.Run();
        }
        public void WorkerRun()
        {
            workerSystem.Run();
        }
    
    }
    public class FacadeTest : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
            Facade facade = new Facade();
            facade.StudentRun();
            facade.TeacherRun();
        }
    
        
    }

3.单例模式

  • 保证一个类只有一个对应的对象,并提供一个访问该对象的方法
  • using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Worker
    {
        public string name;
    }
    
    public class Leader
    {
        //唯一的领导对象
        private static Leader instance = null;
        private List<Worker> workers = new List<Worker>();
        //给外界开放一个接口,用于访问唯一的领导对象
        public static Leader Instance
        {
            get
            {
                //如果还没有领导对象,则实例化一个领导对象
                if (instance == null)
                {
                    instance = new Leader();
                }
                return instance;
            }
        }
    
        private Leader() { }
    
        public void AddWorker(string WorkerName)
        {
            Worker worker = new Worker();
            worker.name = WorkerName;
            workers.Add(worker);
        }
    
        public void Check()
        {
            foreach(Worker worker in workers)
            {
                Debug.Log(worker.name);
            }
        }
    }
    public class SingleTest : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
            func1();
            func2();
        }
    
        // Update is called once per frame
        void func1()
        {
            Leader.Instance.AddWorker("员工1");
            Leader.Instance.AddWorker("员工2");
        }
        
    
        void func2()
        {
            Leader.Instance.Check();
        }
    }

4.观察者模式

  • 定义了一对多的依赖关系,让多个观察者监听同一个主体对象,当主题对象发生变化时,会通知所有的观察者,使观察者可以自行更新
  • using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public interface Viewer
    {
        void Run();
    }
    
    public class Viewer1:Viewer
    {
        public void Run()
        {
            Debug.Log("大笑");
        }
    }
    
    public class Viewer2:Viewer
    {
        public void Run()
        {
            Debug.Log("难过");
        }
    }
    
    public class Viewer3 : Viewer
    {
        public void Run()
        {
            Debug.Log("关掉电视");
        }
    }
    
    public class TV
    {
        //监听的观众
        public List<Viewer> viewers = new List<Viewer>();
    
        //添加观察者
        public void Add(Viewer viewer)
        {
            viewers.Add(viewer);
        }
    
        public void Run()
        {
            foreach(Viewer viewer in viewers)
            {
                viewer.Run();
            }
        }
    
        public void Remove(Viewer viewer)
        {
            viewers.Remove(viewer);
        }
    }
    public class ViewerStyle : MonoBehaviour
    {
        TV tv;
        // Start is called before the first frame update
        void Start()
        {
            tv = new TV();
            tv.Add(new Viewer1());
            tv.Add(new Viewer2());
            tv.Add(new Viewer3());
        }
    
        // Update is called once per frame
        void Update()
        {
            if(Input.GetMouseButtonDown(0))
            {
                tv.Run();
            }
        }
    }

5.工厂模式

  • 定义一个创建对象的接口,让子类决定实例化哪一类,让类的实例化延迟到子类中进行
  • 比如选汽车,选定车子型号,就能得到相应型号的汽车
  • using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public abstract class Car
    {
        public abstract void Run();
    }
    
    public class Bmw:Car
    {
        public override void Run()
        {
            Debug.Log("宝马");
        }
    }
    
    public class Benz : Car
    {
        public override void Run()
        {
            Debug.Log("奔驰");
        }
    }
    
    public class Audi:Car
    {
        public override void Run()
        {
            Debug.Log("奥迪");
        }
    }
    
    public enum CarType
    {
        Bmw,
        Benz,
        Audi
    }
    
    public class Factory
    {
        public static Car Create(CarType type)
        {
            Car car = null;
            switch(type)
            {
                case CarType.Bmw:
                    car = new Bmw();
                    break;
                case CarType.Benz:
                    car = new Benz();
                    break;
                case CarType.Audi:
                    car = new Audi();
                    break;
            }
            return car;
        }
    
    }
    
    public class FactoryStyle : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
            Car bmw = Factory.Create(CarType.Bmw);
            bmw.Run();
            Car benz = Factory.Create(CarType.Benz);
            benz.Run();
            Car audi = Factory.Create(CarType.Audi);
            audi.Run();
        }
    
        // Update is called once per frame
        void Update()
        {
            
        }
    }

十五、Lua

1.Lua

  • 用来进行热更新,能快速修改程序的内容
  • 详细可查看
  • using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using XLua;
    public  class NPC
    {
        public string Name;
        public NPC( string name)
        {
            Name=name;
        }
    }
    public class LuaTest : MonoBehaviour
    {
        //Lua环境
        LuaEnv lua;
        // Start is called before the first frame update
        void Start()
        {
            //创建一个Lua虚拟机
            lua = new LuaEnv();
            //执行lua语句
            lua.DoString("print('你好,我是lua')");
            //在lua语句中调用c#方法
            lua.DoString("CS.UnityEngine.Debug.Log('你好,我是c#')");
            //在lua中创建一个c#类对象
            lua.DoString(@"
             local npc =CS.NPC('杰瑞')
             print(npc.Name)
            ");
        }
        private void OnDestroy()
        {
            lua.Dispose();
        }
    }

2.人工智能

  •    有限状态机:表现有限个状态及在这些状态之间的转移和动作等行为的数学模型
  • 状态抽象类
  • using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    //状态抽象类
    public  abstract class FSMState
    {
        public abstract void OnEnter();
        public abstract void OnUpdate();
    }
    
    public class State1:FSMState
    {
        public override void OnEnter()
        {
            Debug.Log("进入状态1");
        }
        public override void OnUpdate()
        {
            Debug.Log("状态1中");
        }
    }
    
    public class State2 : FSMState
    {
        public override void OnEnter()
        {
            Debug.Log("进入状态2");
        }
        public override void OnUpdate()
        {
            Debug.Log("状态2中");
        }
    }
  • 状态管理类
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class FSMManager 
    {
        private FSMState currentState = null;
        // Start is called before the first frame update
        public void ChangeState(FSMState state)
        {
            currentState = state;
            currentState.OnEnter();
        }
    
        public void OnUpdate()
        {
            if(currentState!=null)
            {
                currentState.OnUpdate();
            }
        }
    }
    
  • 测试
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class FSMTest : MonoBehaviour
    {
        public FSMManager manager;
        // Start is called before the first frame update
        void Start()
        {
            manager = new FSMManager();
            manager.ChangeState(new State1());
        }
    
        // Update is called once per frame
        void Update()
        {
            if(Input.GetMouseButtonDown(0))
            {
                manager.ChangeState(new State1());
            }
    
            if(Input.GetMouseButtonDown(1))
            {
                manager.ChangeState(new State2());
            }
    
            manager.OnUpdate();
        }
    }

十六、资源管理

1.AssertBundle

  • 是一个资源文件压缩包,可以动态加载资源,也能将项目中用到的模型、纹理、场景、预制件和媒体文件等资源压缩到单个文件中,常用于热更新
  • 需要用脚本编辑
    public class BuildBundle : Editor
    {
        //在Unity菜单栏上创建一个Custom菜单,并给其添加一个菜单项BuildBundles
        //给其绑定一个方法build(),当单机BuildBundle时会调用Bundle()方法
        [MenuItem("Custom/BuildBundles")]
        static void Build()
        {
            BuildPipeline.BuildAssetBundles(Application.dataPath + "/AssetBundle", BuildAssetBundleOptions.None,
                BuildTarget.StandaloneWindows64);
            Debug.Log("调试");
        }
    }

2.使用AssertBundle加载资源

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AssertBundleTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        string path = Application.dataPath + "/AssertBundle/custom.ab";
        //读取AssertBundle
        AssetBundle ab = AssetBundle.LoadFromFile(path);
        //从AssertBundle文件中加载Cube预制件
        GameObject CubePre = ab.LoadAsset<GameObject>("Cube");
        //实例化Cube
        Instantiate(CubePre);
    }
}

3.使用AssertBundle加载依赖

  • 材质和模型分开打包,便会产生依赖(材质依赖于模型)
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class AssertBundleTest2 : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
            //获取路径
            string path = Application.dataPath + "/AssertBundle/AssertBundle";
            //加载文件
            AssetBundle ab = AssetBundle.LoadFromFile(path);
            //读取MANIFEST文件查看依赖关系
            AssetBundleManifest mf = ab.LoadAsset<AssetBundleManifest>("AssertBundleManifest");
            //获取custom.ab的依赖
            string[] strs = mf.GetAllDependencies("custom.ab");
            //遍历依赖名称
            foreach(string name in strs)
            {
                //加载所有依赖
                AssetBundle.LoadFromFile(Application.dataPath + "/AssertBundle/" + name);
            }
            //获取custom.ab
            path = Application.dataPath + "/AssertBundle/custom.ab";
            //读取AssetBundle文件
            ab = AssetBundle.LoadFromFile(path);
            GameObject CubePre = ab.LoadAsset<GameObject>("Cube");
            //实例化Cube
            Instantiate(CubePre);
        }
    }
posted @ 2021-11-29 10:59  不进育碧不改名  阅读(199)  评论(0)    收藏  举报