新Unity学习笔记
URP(通用渲染管线)
安装URP
新建URP Setting文件
在project setting中设置渲染管线为新建的URPSetting
在Edit中将项目升级为URP(需不需要安装包UPR场景导入支持不清楚)
Polybrush(类似terrain的地形调整)
根据RP选择导入
Probuilder(提高顶点数)
跟Polybrush一样,注意选择URP支持
图标显示按shife键可以显示功能
增强PreGrid(这是一个preview,所有需要额外设置,2021版的Unity目前不支持)
Edit中(下面是2021的,2020版显示不一样)
人物移动以及地形
1 先调出Navigation(导航)窗口
2.将不可移动物体设置为Nvaigation静态,
3.将其设置为不可以行走
4.烘焙后可见地图发现了一些变化,一些无法行走的以及不符合人物行走框架的(比如斜度、高度、宽度)
5.若是可移动物体,比如敌人或者一些可被主角移动的道具,就不能使用这方法,需要对于物体添加一个组件(障碍物)
6.最后,人物添加一个组件
第一个框是行走属性,包括速度,角速度,灵敏度,距离目标点停止距离(一些武器比较大的,就需要提前停止),是否自动降速
第二个红框是角色碰撞体大小,高度以及半径
鼠标控制物体移动
方法一 采用委托
1.用一个物体装C#代码
2.编写C#代码
onMouseClicked是委托,不是事件,干。
Unity 编辑器中,放入对应的方法
其他
RaycastHit
struct in UnityEngine
描述
用于从射线投射获取信息的结构。
另请参阅:Physics.Raycast、Physics.Linecast、Physics.RaycastAll。
变量
articulationBody | The ArticulationBody of the collider that was hit. If the collider is not attached to an articulation body then it is null. |
---|---|
barycentricCoordinate | 命中的三角形的重心坐标。 |
collider | 命中的 Collider。 |
distance | 从射线原点到撞击点的距离。 |
lightmapCoord | 撞击点处的 UV 光照贴图坐标。 |
normal | 射线命中的表面的法线。 |
point | 世界空间中射线命中碰撞体的撞击点。 |
rigidbody | 命中的碰撞体的 Rigidbody。如果该碰撞体未附加到刚体,则值为 /null/。 |
textureCoord | 碰撞位置处的 UV 纹理坐标。 |
textureCoord2 | 撞击点处的辅助 UV 纹理坐标。 |
transform | 命中的刚体或碰撞体的 Transform。 |
triangleIndex | 命中的三角形的索引。 |
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class MouseManager : MonoBehaviour
{
private RaycastHit hitInfo;
//这是委托类型啊,哪里是事件啊
public UnityEvent<Vector3> onMouseClicked;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
SetCursorTexture();
MouseControl();
}
void SetCursorTexture()
{
//ScreenPointToRay返回从摄像机通过屏幕点的光线。
//产生的光线位于世界空间中,从摄像机的近平面开始,并通过屏幕上 位置的(x, y) 像素坐标(忽略 position.z)
//Input.mousePosition返回鼠标屏幕中位置
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//向场景中的所有碰撞体投射一条射线,该射线起点为 /origin/,朝向 /direction/,长度为 /maxDistance/。
if (Physics.Raycast(ray, out hitInfo))
{
}
}
private void MouseControl()
{
//点击右键时,如果hitInfo的collider存在,也就说这货是存在的且有碰撞体。
if (Input.GetMouseButtonDown(0) && hitInfo.collider != null)
{
//如果是地形则
if (hitInfo.collider.gameObject.CompareTag("Ground"))
{
onMouseClicked?.Invoke(hitInfo.point);
}
}
}
}
方法二 采用事件
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class MouseManagerByEvent : MonoBehaviour
{
public static MouseManagerByEvent Instance;
private RaycastHit hitInfo;
public event Action<Vector3> MouseClicked;
public event Action<GameObject> AttackClicked;
public Texture2D point, doorway, attack, target, arrow;
// Start is called before the first frame update
void Start()
{
if (Instance != null)
{
Destroy(gameObject);
}
Instance = this;
}
// Update is called once per frame
void Update()
{
SetCursorTexture();
OnMouseControl();
}
void SetCursorTexture()
{
//ScreenPointToRay返回从摄像机通过屏幕点的光线。
//产生的光线位于世界空间中,从摄像机的近平面开始,并通过屏幕上 位置的(x, y) 像素坐标(忽略 position.z)
//Input.mousePosition返回鼠标屏幕中位置
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//向场景中的所有碰撞体投射一条射线,该射线起点为 /origin/,朝向 /direction/,长度为 /maxDistance/。
if (Physics.Raycast(ray, out hitInfo))
{
}
}
//触发事件的方法
private void OnMouseControl()
{
//点击右键时,如果hitInfo的collider存在,也就说这货是存在的且有碰撞体。
if (Input.GetMouseButtonDown(0) && hitInfo.collider != null)
{
//如果是地形则
print(hitInfo.point);
if (hitInfo.collider.gameObject.CompareTag("Ground"))
{
MouseClicked?.Invoke(hitInfo.point);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class PlayerController : MonoBehaviour
{
// Start is called before the first frame update
private NavMeshAgent navMeshAgent;
void Start()
{
navMeshAgent = GetComponent<NavMeshAgent>();
MouseManagerByEvent.MouseClicked += MoveToTarget;
}
// Update is called once per frame
void Update()
{
}
//事件处理器
void MoveToTarget(Vector3 vector3)
{
navMeshAgent.destination = vector3;
}
}
射线射到树木咋办
下面两种方法各有优缺点
方法一:
将树木的Layer改为Ignore Raycast
方法二:
取消树木的碰撞体
修改鼠标图片
1.选择图片后,第一个是材质类型,选为鼠标类型。图片质量不搞,使用FilterMode为Point,以及Compression为None,不需要压缩。Alpha isTransparence,自动透明。MaxSize是图片最大显示大小(偏移量可以根据这个和图片形状设置),最后apply。
2.代码编写,先设置Texture2D字段,然后设置Update中图片更改,完成
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class MouseManagerByEvent : MonoBehaviour
{
private RaycastHit hitInfo;
public static Action<Vector3> MouseClicked;
public Texture2D point, doorway, attack, target, arrow;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
SetCursorTexture();
OnMouseControl();
}
void SetCursorTexture()
{
//ScreenPointToRay返回从摄像机通过屏幕点的光线。
//产生的光线位于世界空间中,从摄像机的近平面开始,并通过屏幕上 位置的(x, y) 像素坐标(忽略 position.z)
//Input.mousePosition返回鼠标屏幕中位置
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//向场景中的所有碰撞体投射一条射线,该射线起点为 /origin/,朝向 /direction/,长度为 /maxDistance/。
if (Physics.Raycast(ray, out hitInfo))
{
switch (hitInfo.collider.gameObject.tag)
{
case "Ground":
//第二个是图片偏移量,比如普通的鼠标,那么偏移量的位置就是鼠标尖尖的那部分
//如果鼠标图片是圆圈,偏移量应该指向图片的中心。
Cursor.SetCursor(target,new Vector2(32,32),CursorMode.Auto);
break;
default:
Cursor.SetCursor(target, new Vector2(32, 32), CursorMode.Auto);
break;
}
}
}
//触发事件的方法,最高使用protect访问级别
private void OnMouseControl()
{
//点击右键时,如果hitInfo的collider存在,也就说这货是存在的且有碰撞体。
if (Input.GetMouseButtonDown(0) && hitInfo.collider != null)
{
//如果是地形则
print(hitInfo.point);
if (hitInfo.collider.gameObject.CompareTag("Ground"))
{
MouseClicked?.Invoke(hitInfo.point);
}
}
}
}
摄像机Cinemachine
对Camara的一个增强,
Virtual Camera
1.搜索安装Cinemachine后,创建虚拟相机
2.为什么这么设,不清楚
3.设置观察点,在游戏对象新建一个空对象,然后让虚拟摄像机跟随这个空对象即可。
4.设计距离虚拟相机中Body有距离设置。
FreeLook Camera
这货可以绕圈圈移动的,所以懂了吗
Axis是轴的意思。invent反向,YAxis是观察的高度,作用和LOL的滚轮一样,所以这里使用滚轮。
XAxis是左右,可以用AD或者QE
圈圈,三个圈圈决定了观察的的高度,宽度,以及底部
场景迷雾(Fog)
后处理(滤镜?)
创建Global Volume,相机(不是虚拟相机)的Render中的Post-Processing改为启用。
然后再Global Volueme中设置吧。需要懂Post-Processing。
动画控制
1.选了一个动画效果后,可以选择模型进行展现。
2.创建一个动画控制器。右键新建一个混合树(blend Tree)Tree
3.双击进入其中,
BlendType,可以选择参数数量,参数类型只允许int或者float。bool和Trigger无法再Blend中使用。
这里只使用了一个参数,就是Speed。
Automate Thresholds开启后自动设置阈值,目前由于加速度是80,而速度顶峰才3.5,使用基本上很难发现Walk的动画。
4.C#代码
主要代码如下,设置角色中的animator中的参数,
navMeshAgent.velocity.magnitude获得速度。
void SwitchAnimation()
{
animator.SetFloat("Speed",navMeshAgent.velocity.magnitude);
}
遮挡剔除(目前我这水平shader编程就当玩玩吧)
1.新建一个通用着色器图lit shader Graph
2.创建一个Material,采用上面的shader渲染。
3.编辑shader
右键新建一个Fresnel Effect 费尼尔材质
再右键新建一个Multiply,将A和B两个效果结合
这是B
将Multiply的输出连到Fragment中的Base Color
4.透明通道Alpha Clip,选上操作和上面差不多
5.shader属性
使用这个shader的,都可以使用这些属性改变渲染效果。
敌人怎么搞,靠自己
需求,敌人要会动,具有player的遮挡剔除
追踪敌人和攻击
MouseManagerByEvent类
//增加一个事件,参数是攻击的对象
public event Action<GameObject> AttackClicked;
//OnMouseControl增加敌人判断,触发攻击事件
if (hitInfo.collider.gameObject.CompareTag("Empty"))
{
AttackClicked?.Invoke(hitInfo.collider.gameObject);
}
PlayerController 类
攻击事件绑定事件处理器
MouseManagerByEvent.Instance.AttackClicked += MoveAndAttackHandle;
//设攻击对象,并且触发协程
private void MoveAndAttackHandle(GameObject target)
{
if (target != null)
{
attackGameObject = target;
//启动协程,不要传方法,传方法StopCoroutine无法停止协程
StartCoroutine("MoveToAttackTarget");
}
}
//移动事件处理器,停止协程,并且navMeshAgent.isStopped = false;
void MoveToTargetHandle(Vector3 vector3)
{
StopCoroutine("MoveToAttackTarget");
//StopAllCoroutines();
navMeshAgent.isStopped = false;
navMeshAgent.destination = vector3;
}
//协程首先将navMeshAgent.isStopped = false,避免因为上次攻击设置navMeshAgent.isStopped =true,导致无法移动
IEnumerator MoveToAttackTarget()
{
navMeshAgent.isStopped = false;
//转向敌人
transform.LookAt(attackGameObject.transform);
//循环判断是否满足攻击,不满足则走向敌人,并且下一帧还是在这方法中执行
while (Vector3.Distance(attackGameObject.transform.position,transform.position) > 1)
{
navMeshAgent.destination = attackGameObject.transform.position;
//如果满足条件,下一帧还在这里执行
yield return null;
}
//靠近了目标了所以需要停止行动
navMeshAgent.isStopped = true;
//攻击CD完全后,攻击,否则不攻击
if (nextAttackTime < Time.unscaledTime)
{
animator.SetTrigger("Attack");
nextAttackTime = Time.unscaledTime + 0.5f;
}
}
将攻击动画触发条件设为Attack
额外:TODO Tree
在需要提醒未完成的。注释//TODO:这件事未完成
Vs studio可以直接使用,Vs code 需要安装ToDoTree
敌人发现玩家(自己设计的攻击代码和动画:简略的)
判断玩家是否在视野范围内,是的话返回True,Player记得增加碰撞体以及设置Tag
Collider[] colliders = Physics.OverlapSphere(transform.position, ViewDistance);
foreach (Collider c in colliders)
{
if (c.gameObject.CompareTag("Player"))
{
attackGameObject=c.gameObject;
return true;
}
}
return false;
如果找到敌人,启动协程进行追击和攻击
void Update()
{
SwitchState();
if (FoundPlay())
{
StartCoroutine("PatrolAndAttack");
}
}
IEnumerator PatrolAndAttack()
{
navMeshAgent.isStopped = false;
transform.LookAt(attackGameObject.transform);
while (Vector3.Distance(attackGameObject.transform.position, transform.position) > 1)
{
animator.SetTrigger(getEnemyName(EnemyStates.PATROL));
navMeshAgent.destination = attackGameObject.transform.position;
//如果满足条件,下一帧还在这里执行
yield return null;
}
navMeshAgent.isStopped = true;
if (nextAttackTime < Time.unscaledTime)
{
animator.SetTrigger(getEnemyName(EnemyStates.ATTACK));
nextAttackTime = Time.unscaledTime + 0.5f;
}
}
动画设置
额外:
代码
效果
敌人追踪玩家
Layer 通过设置不同的层级。表示动作阶段
base state是一个空对象,表示当前阶段不运动,避免与其他层级冲突。
设置参数表示不同层级的动作。