目录
声明
6:SetCursor 设置鼠标指针
7:Cinemachine & Post Processing 摄像机跟踪和后处理
8:Animator 动画控制器
9:Shader Graph 遮挡剔除
10:Enemy Set States 设置敌人的基本属性和状态
声明
本教程学习均来自U3D中文课堂麦扣老师
6:SetCursor 设置鼠标指针
如果使用拖拽的方式来控制人物移动的话,在未来后面的游戏设计和开发的时候都不适用,所以我们要舍弃这样的方式,更改MouseManager,为了希望不拖拽的方式就可以调用里面的一些方法,就要把它写成一个单例模式,
先创建一个static的自身变量
public static MouseManager Instance;
然后再Awake中判断
private void Awake()
{
if(Instance != null)
{
Destroy(gameObject);
}
Instance = this;
}
创建第二个代码来调用:
想要使用NavMeshAgent需要使用using UnityEngine.AI;命名空间,初始化NavMeshAgent变量
在MouseManager中舍弃UnityEvent方法,使用unity自带的event:添加命名空间using System;
创建event事件:public event Action<Vector3> onMouseClicked;
Player接收鼠标点击发送的Vector3的值,创建方法MoveToTarget,onMouseClicked事件一起用就会调用这个函数
代码如下:
PlayerController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class PlayerController : MonoBehaviour
{
private NavMeshAgent agent;
private void Awake()
{
agent = GetComponent<NavMeshAgent>();
}
private void Start()
{
MouseManager.Instance.onMouseClicked += MoveToTarget; //onMouseClicked事件添加注册 MoveToTarget()方法
}
public void MoveToTarget(Vector3 target) //必须包含参数Vector3,保证函数命名方式定义方式是和onMouseClicked完全一致
{
agent.destination = target;
}
}
MouseManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class MouseManager : MonoBehaviour
{
public static MouseManager Instance;
RaycastHit hitInfo; //保存射线碰撞到的物体的相关信息
public event Action<Vector3> onMouseClicked;
private void Awake()
{
if(Instance != null)
{
Destroy(gameObject);
}
Instance = this;
}
private void Update()
{
SetCursorTexture();//设置指针的贴图
MouseControl();//返回鼠标左键点击返回值
}
void SetCursorTexture() //设置指针的贴图
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray,out hitInfo))
{
//切换鼠标贴图
}
}
void MouseControl()//返回鼠标左键点击返回值
{
if(Input.GetMouseButtonDown(0)&&hitInfo.collider != null)
{
if(hitInfo.collider.gameObject.CompareTag("Ground"))
{
onMouseClicked?.Invoke(hitInfo.point); //当前onMouseClicked事件如果不为空,将点击到地面上的坐标传回给这个事件(执行所有加入到onMouseClicked的函数方法)
}
}
}
}
这样就实现了没有用拖拽的方式就将人物添加到MouseManager中去控制了
下面设置鼠标指针:
指针有 :手指、传送门、攻击、目标、箭头;
创建5个Texture2D贴图变量:public Texture2D point, doorway, attack, target, arrow;
补充SetCursorTexture() 方法:
void SetCursorTexture() //设置指针的贴图
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray,out hitInfo))
{
//切换鼠标贴图
switch(hitInfo.collider.gameObject.tag)
{
case "Ground":
Cursor.SetCursor(target, new Vector2(16, 16),CursorMode.Auto);
break;
}
}
}
这样鼠标指向地面贴图就变为target了
7:Cinemachine & Post Processing 摄像机跟踪和后处理
安装Cinemachine做摄像机跟随,
创建虚拟相机
调整摄像机属性
这样就能跟随人物移动了
为远处设置迷雾效果会更好,选择Lighting,勾选Environment中的迷雾Fog,
调整属性,画面就有迷雾效果了
Post Processing 后处理提升画质,创建GlobalVolume
在MainCamera中勾选 Post Processing
添加属性:
8:Animator 动画控制器
创建PlayerController
PlayerController 中建立BlendTree,编辑BlendTree,创建一个Parameters名为Speed,Float类型,来根据人物移动速度切换走路和跑步的状态,BlendTree中添加3个Motion动画
下面用脚本来给Animator中的Speed变量赋值:先获取Player的Animator组件,接下来写一个方法来实时切换动画,
private void SwitchAnimation()//实时切换动画
{
anim.SetFloat("Speed", agent.velocity.sqrMagnitude);
//sqrMagnitude将velocity转换为浮点数值
}
这样角色移动就有动作了
9:Shader Graph 遮挡剔除
新建一个Shader Graph
并基于Shader创建一个材质
点开Shader Graph,新建一个Fresnel Effect
应用费尼尔现象,为了后面能修改颜色,新建一个Color,设置颜色,然后拖入面板,进行乘法运算,输出到Base Color.
添加噪点效果:新建一个Dither,
新建一个Float型变量
进行输入,输出到Alpha值
Save保存,这样就可以在材质外面更改了
现在需要创建一个阈值来控制alpha。在创建一个Float变量AlphaThreshold
Save保存,返回游戏,
要将人物移动到遮挡物后面的时候,就让它应用这个材质,那样的话就只显示一个轮廓了,这就涉及到我们的pipeline的setting了,urp就是可编辑的渲染管线,所以我们要在渲染管线当中进行编辑了,找到pipeline settings,找到UniversalRenderPipelineAsset_Renderer,新建RenderObjects,因为人物在后面和在前面的时候是2种不同的方式显示的,将第一个命名为CharacterBehind
应用depth判断人物是在后面还是前面,将第一个RenderObject命名为CharacterBehind,将Player图层设置为Player图层,选择Player进行剔除
在添加一个Render Objects,取名为CharacterInFront,选择应用Player图层
这样只有在人物被其它物品挡住的时候,它才会应用上面的材质,没有遮挡的情况下就保持不变
解决问题:由于树木遮挡了鼠标的射线导致人物没法移动到树的位置,第一种方法是可以将所有的树忽略射线,就直接解决这个问题了,第二种方法是将所有的树的MeshCollider关闭掉
10:Enemy Set States 设置敌人的基本属性和状态
下载资源
导入素材
将所有材质升级到URP材质,
将史莱姆放置到场景中
创建一个EnemyController脚本:因为很多敌人都会挂载Enemy的脚本,希望这个脚本里获取的所有变量都能够让它能够自动添加到我们物体上,先写这个代码:
EnemyController:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))] //确保NavMeshAgent组件一定存在
public class EnemyController : MonoBehaviour
{
private NavMeshAgent agent;
private void Awake()
{
agent = GetComponent<NavMeshAgent>();
}
}
为史莱姆添加BoxCollider组件,调整各组件的属性值
为了切换敌人不同的状态,我们要先想我们的敌人可能会有哪几种状态,首先是Guard(站桩)的状态,它是一个站桩的敌人,当我们的player接近的时候它才会追击我们的player,另外一种它是一个(PATROL)巡逻的敌人,他会按照自己巡逻的点不断移动,一旦发现我们的玩家就会追击,那么顺理成章我们也有一个(CHASE)追击的状态,然后他还有一个(DEAD)死亡的状态。
我们用一个enum枚举来切换控制它的状态,
声明枚举变量:public enum EnemyStates { GUARD,PATROL,CHASE,DEAD}
生成变量: public EnemyStates ememyStates;
现在代码中就有一个下拉菜单选择是那种怪物了,先选择为站桩怪
接下来写一个函数方法来切换它的不同状态:
void SwitchStates() //切换状态
{
switch(ememyStates)
{
case EnemyStates.GUARD:
break;
case EnemyStates.PATROL:
break;
case EnemyStates.CHASE:
break;
case EnemyStates.DEAD:
break;
}
}
并在Update方法中调用它
EnemyController:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public enum EnemyStates { GUARD,PATROL,CHASE,DEAD} //站桩,巡逻,追击,死亡
[RequireComponent(typeof(NavMeshAgent))] //确保NavMeshAgent组件一定存在
public class EnemyController : MonoBehaviour
{
public EnemyStates ememyStates;
private NavMeshAgent agent;
private void Awake()
{
agent = GetComponent<NavMeshAgent>();
}
private void Update()
{
SwitchStates();//切换状态
}
void SwitchStates() //切换状态
{
switch(ememyStates)
{
case EnemyStates.GUARD:
break;
case EnemyStates.PATROL:
break;
case EnemyStates.CHASE:
break;
case EnemyStates.DEAD:
break;
}
}
}
这样敌人基本的框架就有了
下面让鼠标指向敌人可以切换鼠标的样子,为敌人添加一个Layer和Tag,取名都为Enemy
留意:我们的敌人也是希望和Player一样可以发生遮挡剔除的,不然它们跑到树林当中我们无法看见它们并且选中它们产生攻击效果的,所以我们回到pipeline settings当中将Enemy图层也选上
返回到MouseManager当中,修改我们的Cursor:
void SetCursorTexture() //设置指针的贴图
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray,out hitInfo))
{
//切换鼠标贴图
switch(hitInfo.collider.gameObject.tag)
{
case "Ground":
Cursor.SetCursor(target, new Vector2(16, 16),CursorMode.Auto); //偏移(16,16)
break;
case "Enemy":
Cursor.SetCursor(attack, new Vector2(16, 16), CursorMode.Auto); //偏移(16,16)
break;
}
}
}
下期将会实现敌人的所有功能了,赶紧操作起来吧!