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); } }

浙公网安备 33010602011771号