3D RPG Course | Core | Unity学习笔记(七)

这篇具有很好参考价值的文章主要介绍了3D RPG Course | Core | Unity学习笔记(七)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

(一)制作石头人boss

        希望为石头人设置近战击飞和远程两种攻击动作。由于动画逻辑与兽人敌人一致,所以可以使用override方式设计AnimatorController。

        脚本也继承自EnemyController,首先实现近战击飞效果。

//代码结构与之前兽人的技能类似
[Header("skill")]
public float kickForce = 20;//击飞的力

public void KickOff()//近战并击飞玩家
{
    if (attackTarget != null && transform.IsFacingTarget(attackTarget.transform))//先判断攻击对象是否为空,防止玩家已经离开,并判断攻击目标是否在前方扇形范围内
    {
        
        //实现击飞
        if(attackTarget!=null)
        {
            var TargetStats = attackTarget.GetComponent<CharacterStats>();
            Vector3 direction = attackTarget.transform.position - transform.position;//玩家被击飞的方向
            direction.Normalize();//量化
            attackTarget.GetComponent<NavMeshAgent>().isStopped = true;//目标被击飞之前先被打断动作
            attackTarget.GetComponent<NavMeshAgent>().velocity = direction*kickForce;
            //attackTarget.GetComponent<Animator>().SetTrigger("dizzy");
            TargetStats.TakeDamage(characterStats, TargetStats);
        }
    }
}

        其他部分也像其他单位一样设置,这样近战机能就可以正常运行了。

        (注:这里修复一下player因为敌人半径大于agent的停止距离导致一直移动的问题,教程采用在攻击时修改停止距离的方式,但由于代码实现逻辑会在到达目标后停止agent,所以也可以采用让移动判断条件改为Vector3.Distance(attackTarget.transform.position,transform.position) > characterStats.attackData.attackRange+attackTarget.GetComponent<NavMeshAgent>().radius的形式。此外记得在受伤的动画中加上StopAgent行为脚本来保证受伤动画也可以打断移动。)

(二)制作可以扔出的石头

        为这颗石头创建代码,使用刚体组件来让石头拥有速度等物理属性,并添加MeshCollider来让石头能够碰撞到物体。构建好石头后,修改石头人的代码,以“判定距离-控制动画-触发动画事件-调用技能方法”的逻辑来完成一次攻击的实现。(如果测试时发现石头人只远程不近战,则改变攻击逻辑。)当发现石头人和兽人的击退效果无效时,在击退生效前先将player的agent.autoBraking关闭,player移动时再开启即可。

        教程希望让石头也能够成为玩家攻击boss的方法,所以需要让石头拥有多种状态。使用OnCollisionEnter方法可以在发生碰撞时被调用,获得被碰撞体的信息来切换石头状态并采取不同的处理。

        脚本中在控制游戏数据的CharacterStats中已经有了伤害方法,但石头的脚本没有必要的参数取调用,为了减少工作量可以对方法进行重载来实现石头对撞击目标的伤害:

    public void TakeDamage(int damage,CharacterStats defener)
    {
        int CurrentDamage = Mathf.Max(damage - defener.CurrentDefence, 0);
        CurrentHealth = Mathf.Max(CurrentHealth - CurrentDamage, 0);
    }

        接着实现石头用于攻击boss的状态(脚本中的gameObject代表所挂载的游戏对象),包括造成伤害和销毁石头自身,并修改MouseManager的点击方法和PlayerController的攻击方法,让玩家在攻击到tag为Attackable的对象时,能够改变石头的状态并将其击飞出去:

void Hit()//动画事件调用此方法来完成攻击扣血
{
    if(attackTarget.CompareTag("Attackable"))
    {
        if(attackTarget.GetComponent<Rock>() && attackTarget.GetComponent<Rock>().rockState== Rock.RockStates.HitNothing)//当攻击了已经不再攻击玩家的石头时,把石头打回去
        {
            attackTarget.GetComponent<Rock>().rockState = Rock.RockStates.HitEnemy;
            attackTarget.GetComponent<Rigidbody>().velocity = Vector3.one;//防止速度为0的帧,使得状态被设置为HitNothing
            attackTarget.GetComponent<Rigidbody>().AddForce(transform.forward * kickForce,ForceMode.Impulse);//击飞石头
        }
    }
    else
    {
        var TargetStats = attackTarget.GetComponent<CharacterStats>();
        TargetStats.TakeDamage(characterStats, TargetStats);
    }

}

        想要击飞石头,就需要石头保持在HitNothing状态,由于希望通过物理属性判断石头的情况来转换状态,所以要采用FixedUpdate()方法来代替Update()方法(unity中游戏暂停是ctrl+shift+p,使用暂停加逐帧运行可以方便的观察数值变化)。由于之前采用了攻击距离加半径的方式来改正敌人半径大于agent停止距离的问题,所以石头上也需要添加NavMeshAgent组件防止报错,并停用NavMeshAgent防止与刚体组件冲突。,如果采用原教程的方法就不需要。为了防止player直接穿过石头,需要给player也加上刚体,并勾选"is Kinematic"防止与NavMeshAgent冲突。

        希望让石头的消失更加真实,可以在Hierarchy中创建 Effects-Particle System (粒子系统,详见中文手册)实现石头碎裂效果。参数设置完成后,保存为预制体并在Rock脚本中调用。

        最终的石头脚本:

public class Rock : MonoBehaviour
{
    private Rigidbody rb;
    public enum RockStates { HitPlayer,HitEnemy,HitNothing}//石头的不同状态
    public RockStates rockState;

    [Header("basic setting")]
    public float force;
    public int damage;//基础伤害
    public GameObject target;//砸向的目标
    private Vector3 direction;//飞行方向

    public GameObject breakEffect;//消失的粒子效果

    private void Start()
    {
        rb = GetComponent<Rigidbody>();//生成时执行
        rb.velocity = Vector3.one;//防止刚生成时速度为0,使得状态被设置为HitNothing从而无法攻击玩家
        rockState = RockStates.HitPlayer;//初始状态为攻击玩家
        FlyToTarget();//从生成开始就要飞向目标
    }

    private void FixedUpdate()
    {
        if (rb.velocity.sqrMagnitude<1f)//向量平方长度,比Magnitude计算更快
        {
            rockState = RockStates.HitNothing;
        }
    }

    private void FlyToTarget()
    {
        if(target== null)//石头刚生成出来玩家就跑出了范围时
        {
            target=FindObjectOfType<PlayerController>().gameObject;
        }
        direction = (target.transform.position - transform.position+Vector3.up).normalized;//为了让石头能够有更长的滞空时间,为方向向量加一个向上的分量。
        rb.AddForce(direction*force,ForceMode.Impulse);//采用冲击力的模式让石头一下飞出去。

    }

    private void OnCollisionEnter(Collision other)//此函数能够在发生碰撞时获得被撞击的目标的信息并执行
    {
        switch (rockState)
        {
            case RockStates.HitPlayer:
                if (other.gameObject.CompareTag("Player"))//撞到player时将其击退
                {
                    other.gameObject.GetComponent<NavMeshAgent>().autoBraking = false;
                    other.gameObject.GetComponent<NavMeshAgent>().isStopped = true;
                    //击退方向已经定义过了,可以重复利用
                    other.gameObject.GetComponent<NavMeshAgent>().velocity = direction * force;
                    other.gameObject.GetComponent<Animator>().SetTrigger("dizzy");
                    other.gameObject.GetComponent<CharacterStats>().TakeDamage(damage, other.gameObject.GetComponent<CharacterStats>());
                    rockState = RockStates.HitNothing;
                }
                break;
            case RockStates.HitEnemy: 
                if(other.gameObject.GetComponent<Golem>())//石头在此状态碰到石头人,则造成伤害并销毁
                {
                    var otherStats = other.gameObject.GetComponent<CharacterStats>();
                    otherStats.TakeDamage(damage, otherStats);
                    Instantiate(breakEffect, transform.position, Quaternion.identity);//消失前生成粒子特效
                    Destroy(gameObject);//销毁石头
                }
                break;
            case RockStates.HitNothing: 
                break;
        }
    }
}

(三)制作血条显示

        使用Unity UI来创建血条。UI制作首先需要创建canvas,画布 (Canvas) 是应该容纳所有 UI 元素的区域。画布是一种带有画布组件的游戏对象,所有 UI 元素都必须是此类画布的子项。创建一个大小合适的image作为第一个血条。在包管理器中安装2D sprite来使用其中的2D素材,从中复制一份Squre到资源文件夹中。通过放置两个相覆盖的红绿矩形来代表血条,上层可变的绿色血条以 Filled模式从左侧填充,这样,只要在脚本中实现实时控制上层图片的覆盖范围就可以实现血条变化。(需要prefab的脚本可以直接在unity的project窗口中配置prefab)

        想设置血条在敌人的头顶的话,可以在敌人对象身上创建代表头顶的子对象来获得位置。在数据脚本CharacterStats中产生伤害的方法里需要在扣血后更新UI,类似于鼠标的点击功能,可以采用事件的形式实现。在 CharacterStats中设置事件“public event Action<int,int> UpdateHealthBarOnAttack;”,并在造成伤害后触发事件"UpdateHealthBarOnAttack?.Invoke(CurrentHealth, MaxHealth);"。回到血条脚本,将方法注册到事件中。并在Awake和OnEnable中完成血条的生成。由于场景树基于transform构建,因此获得血条子对象需要通过其transform获得。

        接下来实现血条的跟随和显示时间。Update在每一帧执行,而LateUpdate会在上一帧结束后执行,两者之间存在一定时间差,在血条实现中采用后者来体现血条跟随在人物后移动与面朝摄像机。

        最终的血条脚本如下:文章来源地址https://www.toymoban.com/news/detail-770684.html

public class HealthBarUI : MonoBehaviour
{
    public GameObject healthUIPrefab;//血条UI预制体
    public Transform barPoint;//血条位置

    public bool alwaysVisible;//控制血条是否始终可见
    public float visibleTime;//最大可见时间
    private float timeLeft;//剩余可见时间

    Image healthSlider;
    Transform UIbar;//血条位置变量
    Transform cam;//用于保持面朝摄像机!

    CharacterStats currentStats;

    private void Awake()
    {
        currentStats = GetComponent<CharacterStats>();
        currentStats.UpdateHealthBarOnAttack += UpdateHealthBar;//将方法登记在事件中
    }

    private void OnEnable()//启动时调用
    {
        cam = Camera.main.transform;//获得主相机位置
        foreach(Canvas canvas in FindObjectsOfType<Canvas>())//在所有canvas对象中搜索属于血条的HealthBar Canvas
        {
            //在对应Canvas上生成血条
            if(canvas.name== "HealthBar Canvas")//或canvas.renderMode==RenderMode.WorldSpace
            {
                UIbar = Instantiate(healthUIPrefab, canvas.transform).transform;//生成血条对象并获得坐标变量
                healthSlider = UIbar.GetChild(0).GetComponent<Image>();//获得绿色血量滑块子对象
                UIbar.gameObject.SetActive(alwaysVisible);//设置是否随时可见
            }
        }
    }

    private void UpdateHealthBar(int CurrentHealth, int MaxHealth)
    {
        if(CurrentHealth==0)
        {
            Destroy(UIbar.gameObject);//敌人死亡时血条也一同消失。
        }
        UIbar.gameObject.SetActive(true);//受伤时血条应为可见
        timeLeft = visibleTime;//恢复可见时间
        float sliderPercent = (float) CurrentHealth / MaxHealth;
        healthSlider.fillAmount=sliderPercent;//将绿色血条图像设置为当前生命值百分比

    }

    private void LateUpdate()
    {
        if(UIbar!=null)//血条没有被销毁,防止空指针报错
        {
            UIbar.position = barPoint.position;//血条追踪单位
            UIbar.forward = - cam.forward;//始终面朝相机
            if (timeLeft <= 0&&!alwaysVisible)//控制剩余显示时间
            {
                UIbar.gameObject.SetActive(false);
            }
            else if(timeLeft>0)
            {
                timeLeft-=Time.deltaTime;
            }
        }
    }
}

到了这里,关于3D RPG Course | Core | Unity学习笔记(七)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity 3D RPG游戏的设计与实现

    public class PlayerInput : MonoBehaviour {     public float horizontalInput;     public float verticalInput;    public bool mouseDown;             // Update is called once per frame     void Update()     { ///在游戏运行且鼠标当前未被按下的情况下,检测鼠标左键是否被用户按下,并记录这一交互状态。    

    2024年03月14日
    浏览(55)
  • Unity2D RPG开发笔记 P1 - Unity界面基础操作和知识

    按下 QWERTY 可以选择不同的工具进行 旋转、定位、缩放 按下 Ctrl + D 可以复制物体 16:9 为最常见的分辨率 Transform 组件 物体在空间中的位置、缩放、旋转 点击这里可以进行 reset 操作,位置将会被重置 不知道算不算冷知识的冷知识:鼠标拖动这里可以移动该数值 Sprite Renderer

    2024年02月13日
    浏览(46)
  • Unity 3D 学习笔记(1)

    Unity 3D简介 :Unity 3D是虚拟现实行业中使用率较高的一款开发引擎,由Unity Technology公司开发。通过Unity,开发人员可以制作三维视频游戏、建筑可视化和实时三维动画等内容。 引擎的概念 :引擎为设计者提供了编写程序所需的工具,而并非从零开始对项目进行开发。这样可以

    2024年02月02日
    浏览(34)
  • Unity3D学习笔记——物理引擎

    1简介 刚体可以为游戏对象赋予物理特性,是游戏对象在物理系统的控制下接受推力和扭力,从而实现现实世界的物理学现象。 2属性 1简介 碰撞器是物理组件的一类,他与刚体一起促使碰撞发生 碰撞体是简单形状,如方块、球形或者胶囊形,在 Unity 3D 中每当一个 GameObjects

    2023年04月12日
    浏览(49)
  • sheng的学习笔记-【中文】【吴恩达课后测验】Course 1 - 神经网络和深度学习 - 第一周测验

    目录:目录 1.“人工智能是新电力” 这个比喻指的是什么? A. 【  】人工智能为我们的家庭和办公室的个人设备供电,类似于电力。 B. 【  】通过“智能电网”,人工智能正在传递新一波的电力。 C. 【  】人工智能在计算机上运行,因此由电力驱动,但它让计算机做以前

    2024年02月07日
    浏览(36)
  • Unity3D学习笔记8——GPU实例化(3)

    在前两篇文章《Unity3D学习笔记6——GPU实例化(1)》《Unity3D学习笔记6——GPU实例化(2)》分别介绍了通过简单的顶点着色器+片元着色器,以及通过表面着色器实现GPU实例化的过程。而在Unity的官方文档Creating shaders that support GPU instancing里,也提供了一个GPU实例化的案例,这里就详

    2023年04月22日
    浏览(43)
  • Unity3D高级编程主程手记 学习笔记二:C#技术要点

    1.Untiy3D中C#的底层原理 Unity底层在运行C#程序时有两种机制:一种是Mono,另一种是IL2CPP。 Mono存在的目的是为了跨平台 ,因为最初C#只支持Windows。而IL可以看成是一种汇编语言且完全基于堆栈,必须运行在虚拟机上。也就是说C#会被编译器编译成IL,当需要他们时就会被实时的

    2024年02月08日
    浏览(62)
  • 【Unity学习笔记】b站Unity架构课Unity3D 商业化的网络游戏架构(高级/主程级别)

    自己跟着学完了,写了不少代码,会放在CSDN代码库,因为老师并没有提供源码,录屏也不是完全连续,所以难免 有代码缺少、无法运行 的情况,但是确实学到了不少真本事,主要是了解老师的架构思想。 b站课程地址 课程我自己是跟着学完了的,本文是个人笔记记录和好课

    2024年02月02日
    浏览(52)
  • 3D~RPG游戏的制作

    @作者 : SYFStrive @博客首页 : HomePage @创建时间 : 2022/7/29 10:00 📜: UnityRPG核心文章(代码加起来7万字🤮) 📌: 个人社区(欢迎大佬们加入) 👉: 社区链接🔗 📌: 觉得文章不错可以点点关注 👉: 专栏连接🔗 💃: 程序员每天坚持锻炼💪 🔗: 点击直接阅读文章

    2024年02月03日
    浏览(47)
  • 用Unity3D制作FPS游戏的学习笔记————人物移动、利用鼠标实现视角转动和人物跳跃(含人物悬空不掉落修复)

    前言: 这是我第一次发布文章,此文章仅供参考,我也是刚学习接触untiy,在制作项目的过程中将有用的写下来记一记,以便自己之后能回头看看,各位大佬轻点喷,若有错误请麻烦积极提谢谢各位。该文章参考自B站UP主蔡先森_rm-rf发布的 【第一人称射击游戏教程2.0【已完结

    2024年04月27日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包