新Unity学习笔记3D RPG项目(没钱教教程费站告一段落)

新Unity学习笔记

URP(通用渲染管线)

安装URP

新建URP Setting文件

在project setting中设置渲染管线为新建的URPSetting

在Edit中将项目升级为URP(需不需要安装包UPR场景导入支持不清楚)

Polybrush(类似terrain的地形调整)

根据RP选择导入

image-20210414124126359

Probuilder(提高顶点数)

跟Polybrush一样,注意选择URP支持

图标显示按shife键可以显示功能

增强PreGrid(这是一个preview,所有需要额外设置,2021版的Unity目前不支持)

Edit中(下面是2021的,2020版显示不一样)

image-20210414143628262

人物移动以及地形

1 先调出Navigation(导航)窗口

image-20210414205829468

2.将不可移动物体设置为Nvaigation静态,

image-20210414205917894

3.将其设置为不可以行走

image-20210414210030390

4.烘焙后可见地图发现了一些变化,一些无法行走的以及不符合人物行走框架的(比如斜度、高度、宽度)

image-20210414210100352

5.若是可移动物体,比如敌人或者一些可被主角移动的道具,就不能使用这方法,需要对于物体添加一个组件(障碍物)

image-20210414210444639

6.最后,人物添加一个组件

第一个框是行走属性,包括速度,角速度,灵敏度,距离目标点停止距离(一些武器比较大的,就需要提前停止),是否自动降速

image-20210414210536902

第二个红框是角色碰撞体大小,高度以及半径

鼠标控制物体移动

方法一 采用委托

1.用一个物体装C#代码

image-20210414210917384

2.编写C#代码

onMouseClicked是委托,不是事件,干。

image-20210414212031389

Unity 编辑器中,放入对应的方法

image-20210414212113781

其他

Physics

RaycastHit

struct in UnityEngine

描述

用于从射线投射获取信息的结构。

另请参阅:Physics.RaycastPhysics.LinecastPhysics.RaycastAll

变量

articulationBodyThe 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。

image-20210414222853080

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后,创建虚拟相机

image-20210415102234572

2.为什么这么设,不清楚

image-20210415102630794

3.设置观察点,在游戏对象新建一个空对象,然后让虚拟摄像机跟随这个空对象即可。

4.设计距离虚拟相机中Body有距离设置。

FreeLook Camera

这货可以绕圈圈移动的,所以懂了吗

image-20210415174010446

Axis是轴的意思。invent反向,YAxis是观察的高度,作用和LOL的滚轮一样,所以这里使用滚轮。

XAxis是左右,可以用AD或者QE

image-20210415174359809

圈圈,三个圈圈决定了观察的的高度,宽度,以及底部

image-20210415174427554

场景迷雾(Fog)

image-20210415104210451

后处理(滤镜?)

创建Global Volume,相机(不是虚拟相机)的Render中的Post-Processing改为启用。

然后再Global Volueme中设置吧。需要懂Post-Processing。

动画控制

1.选了一个动画效果后,可以选择模型进行展现。

image-20210415105254890

2.创建一个动画控制器。右键新建一个混合树(blend Tree)Tree

image-20210415112655185

3.双击进入其中,

BlendType,可以选择参数数量,参数类型只允许int或者float。bool和Trigger无法再Blend中使用。

这里只使用了一个参数,就是Speed。

Automate Thresholds开启后自动设置阈值,目前由于加速度是80,而速度顶峰才3.5,使用基本上很难发现Walk的动画。

image-20210415113054089

image-20210415112747737

4.C#代码

主要代码如下,设置角色中的animator中的参数,

NavMeshAgent

navMeshAgent.velocity.magnitude获得速度。

    void SwitchAnimation()
    {
        animator.SetFloat("Speed",navMeshAgent.velocity.magnitude);
    }

遮挡剔除(目前我这水平shader编程就当玩玩吧)

1.新建一个通用着色器图lit shader Graph

image-20210415121909957

2.创建一个Material,采用上面的shader渲染。

3.编辑shader

右键新建一个Fresnel Effect 费尼尔材质

image-20210415163502707

再右键新建一个Multiply,将A和B两个效果结合

image-20210415163547455

这是B

image-20210415163649279

将Multiply的输出连到Fragment中的Base Color

4.透明通道Alpha Clip,选上操作和上面差不多

image-20210415164237354

5.shader属性

image-20210415164304467

使用这个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

image-20210416210225076

额外: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;
        }
    }

动画设置

image-20210416222815559

额外:

代码

image-20210416214736258

效果

image-20210416214751499

敌人追踪玩家

Layer 通过设置不同的层级。表示动作阶段

image-20210417225044789

base state是一个空对象,表示当前阶段不运动,避免与其他层级冲突。

image-20210417225138624

设置参数表示不同层级的动作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值