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

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

(一)设计玩家升级系统

        在属性代码CharacterData_SO中添加代表经验值系统的变量,对于不同类型的对象,比如敌人没有升级系统而玩家有,则不需要的变量不对其赋值即可。设计好等级、经验值、等级增幅和击败敌人获得的经验值,并在造成伤害的方法中判断死亡并提供经验值(由于逻辑是承担伤害所以可以这样调用)。修改后的数据代码:

public class CharacterData_SO : ScriptableObject
{
    [Header("Stats Info")]
    public int maxHealth;
    public int currentHealth;
    public int maxDefence;
    public int currentDefence;

    [Header("kill")]
    public int killPoint;

    [Header("Level")]
    public int currentLevel;
    public int maxLevel;
    public int baseExp;//升级所需经验值
    public int currentExp;
    public float levelBuff;//每次升级属性的提升幅度

    public float LevelMultiplier//每级具体的提升
    {
        get
        {
            return 1 + (currentLevel - 1) * levelBuff;
        }
    }

    public void UpdateExp(int point)//用于将杀敌获得的point加入到currentExp中
    {
        currentExp += point;
        if (currentExp >= baseExp)
        {
            LevelUp();
        }
    }

    private void LevelUp()
    {
        currentLevel=Mathf.Clamp(currentLevel+1, 0,maxLevel);//将等级提升限制在范围内
        baseExp += (int)(baseExp * LevelMultiplier);//每一级所需经验值增加

        maxHealth=(int)(maxHealth*LevelMultiplier);
        currentHealth = maxHealth;

        //Debug.Log("Level up:" + currentLevel + "\tMax Health:" + maxHealth);//test

    }
}

(二)制作玩家生命值UI

        就像敌人血条UI一样,玩家生命值UI也从创建Canvas开始,其RenderMode设置为Screen Space来保证UI显示在屏幕平面上(毕竟不需要像敌人的一样跟随)。在创建的Image中设置锚点位置:3D RPG Course | Core | Unity学习笔记(八),学习,笔记在此界面按住alt+shift去选择即可保证无论屏幕如何缩放都可保持image距离边缘的距离。同样在Canvas中创建代表经验值的条和文本,将Canvas的UI Scale Mode改为根据屏幕尺寸调整,从而保证在不同大小分辨率下能够保持相同的效果。

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

        编写的控制脚本需要挂载在对应的Canvas上。由于player的UI需要在各个场景中都有显示,所以要将UI保存为预制体,并在脚本中获得Canvas的所有子对象。获取对象后,在Update方法中同步数据与UI。之前制作怪物血条时,由于敌人血条UI脚本是挂载到敌人对象上的,因此可以通过.GetComponent<>()来获得数据,而玩家在创建时在GameManager中注册过,且GameManager是一个单例调用方便,所以可以从GameManager中获得数据。

        完整的playerUI:

public class PlayerHealthUI : MonoBehaviour
{
    Text levelText;//等级文本
    Image healthSlider;
    Image expSlider;

    private void Awake()
    {
        //获取子物体
        levelText = transform.GetChild(2).GetComponent<Text>();//子物体按顺序从0开始编号
        healthSlider=transform.GetChild(0).GetChild(0).GetComponent<Image>();
        expSlider= transform.GetChild(1).GetChild(0).GetComponent<Image>();
    }

    private void Update()
    {
        levelText.text = "Level: "+GameManager.Instance.playerStats.characterData.currentLevel.ToString("00");//使用ToString可以改变字符串转化方法与样式
        UpdateHealth();
        UpdateExp();
    }

    void UpdateHealth()//更新血条方法
    {
        float sliderPercent = (float)((float)GameManager.Instance.playerStats.CurrentHealth / (float)GameManager.Instance.playerStats.MaxHealth);
        healthSlider.fillAmount = sliderPercent;
    }

    void UpdateExp()//更新经验值方法
    {
        float expPercent = (float)((float)GameManager.Instance.playerStats.characterData.currentExp / (float)GameManager.Instance.playerStats.characterData.baseExp);
        expSlider.fillAmount = expPercent;
    }

}

(三)制作传送门

        使用shader graph创建一个传送门。先在projects中创建一个lit shader graph,通过为shader graph添加节点来不断改变其图形效果。传送门的动态效果首先需要旋转,将twirl节点和voronoi节点相连接形成效果更好的漩涡状。

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

节点的参数中,UV是与xyz对应的参数,其匹配关系为:(图片引自教程视频)

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

        在游戏中,传送门会不断地旋转,因此还要添加时间节点,并添加速度参数,与Time相乘将结果连接到旋转上来控制旋转动画。旋转的强度也需要另设变量来控制。结果输出前需要先对图像的形状等进行处理。创建Sample Texture2D节点和相关变量并配置初始值,将结果与旋转结果相乘,能够得到成圆形的旋转图案,再创建color变量来控制图案的颜色。最终结果连接到主节点Emission(自发光)上,并设置透明通道。如果将结果连接到主节点的Alpha上后发现预览图中没有结果,则可以尝试改为将上一层Multiply结果连到Alpha上。shadergraph结果:

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

        使用做好的shader创建材质,挂载到quad对象上,调整相关参数并在MeshRender中取消其阴影。在shader中调整颜色模式为HDR并修改亮度(注意shader需要手动保存)。效果仍然不满意的话,也可以在shader中继续添加参数进行修改,如使用power节点增强强度。

        创建其他传送门时,要先基于shader生成新的材质,再在新材质上修改。保存传送门预制体时需要将子物体图标设为none,否则会报错。

        下面设计传送门的传送功能,让玩家在传送门处按e能够转移位置到传送门的目的地。创建传送脚本和传送目的地脚本。每个传送门由门和目的地两部分组成,传送的逻辑则是搜索同场景或目标场景下与门的目的地Tag相同的目的地,并在那里将player生成。

        将门的碰撞体的isTrigger打开以用于判断是否碰撞。当Player在碰撞范围内时将传送门表示允许传送的bool变量打开,反之关闭。

        在进行下一步之前,利用已有素材创建新的场景。后面实现不同场景传送时将采用异步加载的形式。

        

(四)实现同场景传送

        创建SceneController脚本来专门管理场景转换(注意SceneManager为unity自带类型,取名时要注意)。在TransitionPoint中检测键盘是否按下e键且为可传送状态,在Update中实现此功能,而传送的具体实现则在SceneController单例中进行。用SceneManager.LoadSceneAsync来对场景进行异步加载,并使用AsyncOperation来获得加载场景的进度,具体使用方法见此接口的官方文档。获取到玩家对象后,使用Transform的Set型方法来设置坐标实现传送的移动效果。

        由于传送门的碰撞体挡住了射线,无法移动到传送门处,因此需要在MouseManager处修改来增加到传送门的点击寻路。由于传送不会使NavMeshAgent重置,因此在传送方法实现中也需要使agent停止。

(五)实现跨场景传送

        在协程中异步加载传送到的场景,并使用yield return等待异步加载完成后再进行后面的传送。出于游戏设计,每次转换场景时应当把原本的玩家角色生成出来而非在每个场景都创建一个玩家,所以在加载完成后创建角色。完成后跳出协程,后续工作中会补充在传送前保存当前状态的功能。场景需要在buildsettings中进行设置才能被识别到。

        在切换场景后,原本场景的管理器脚本就会随对象一起消失,因此需要在各个Manager的Awake中加入。而此时也需要在PlayerController中将原本订阅的事件取消,即需要将玩家对事件的订阅和取消订阅放在玩家生成和销毁时执行,这样就可以防止传送后玩家无法控制player。但是切换场景经历了player的销毁和构建,此时摄像机跟随会失效,由于GameManager会第一时间获得生成出的player信息,所以可以在GameManager中,在player注册后查找场景中是否有对应的CinemaChine摄像机,如果存在则将其follow对象重设为player。

        在测试时发现如下错误:“NullReferenceException: Object reference not set to an instance of an object PlayerController.OnEnable () (at Assets/Script/characters/PlayerController.cs:39)“,此句原功能为将方法登记到事件中,查看运行过程发现是在MouseManager加载之前PlayerController.OnEnable ()就已经运行,根据评论方法,可以通过在project settings 的Execution Order中设置执行顺序来解决。

        TransitionPoint传送门脚本:

        

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TransitionPoint : MonoBehaviour
{
    public enum TransitionType//是同场景传送还是不同场景
    {
        SameScene,DifferentScene
    }

    [Header("Transition Info")]
    public string sceneName;//传送到的场景名
    public TransitionType transitionType;
    public TransitionDestination.DestinationTag destinationTag;//传送目的地的类型,入口或其他

    private bool canTrans;//判断能否传送

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.E) && canTrans)
        {
            //调用SceneController的传送方法。
            SceneController.Instance.TransitionToDestination(this);
        }
    }

    private void OnTriggerStay(Collider other)//isTrigget的碰撞体碰撞期间执行
    {
        if(other.CompareTag("Player"))//使传送门成为可传送状态
        {
            canTrans = true;
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            canTrans = false;
        }
    }
}

        TransitionDestination传送目的地类:

public class TransitionDestination : MonoBehaviour
{
    public enum DestinationTag
    {
        Enter,A,B,C,Z
    }
    public DestinationTag destinationTag;//传送点类型
}

        SceneController场景管理脚本:文章来源地址https://www.toymoban.com/news/detail-752641.html

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;

//设置为单例模式,本质为场景管理Manager
public class SceneController : Singleton<SceneController>
{
    public GameObject PlayerPrefab;//用于跨场景时生成玩家
    GameObject player;//被传送的对象
    NavMeshAgent playerAgent;


    protected override void Awake()
    {
        base.Awake();
        DontDestroyOnLoad(this);//防止Manager被销毁而无法继续执行功能
    }


    public void TransitionToDestination(TransitionPoint transitionPoint)
    {
        switch (transitionPoint.transitionType)
        {
            case TransitionPoint.TransitionType.SameScene://同场景传送
                StartCoroutine(Transition(SceneManager.GetActiveScene().name, transitionPoint.destinationTag));//以当前场景名和目的地作为参数
                break;
            case TransitionPoint.TransitionType.DifferentScene://不同场景传送
                StartCoroutine(Transition(transitionPoint.sceneName, transitionPoint.destinationTag)); //以目标场景和目的地作为参数
                break;
        }
    }

    IEnumerator Transition(string sceneName , TransitionDestination.DestinationTag destinationTag)//进行传送的方法。
    {
        if (SceneManager.GetActiveScene().name == sceneName)//同场景传送
        {
            //Debug.Log("SceneName:" + sceneName);//test
            player = GameManager.Instance.playerStats.gameObject;//先获得玩家的游戏对象
            //再将玩家位置设置为终点位置
            playerAgent = player.GetComponent<NavMeshAgent>();
            playerAgent.enabled = false;//暂停Agent防止玩家自动导航回传送门处
            player.transform.SetPositionAndRotation(GetDestination(destinationTag).transform.position, GetDestination(destinationTag).transform.rotation);
            playerAgent.enabled = true;
            yield return null;
        }
        else//跨场景传送
        {
            //TODO: Save data
            yield return SceneManager.LoadSceneAsync(sceneName);//异步加载场景
            yield return Instantiate(PlayerPrefab, GetDestination(destinationTag).transform.position,GetDestination(destinationTag).transform.rotation);
            yield break;//跳出协程
        }
        
    }

    private TransitionDestination GetDestination(TransitionDestination.DestinationTag destinationTag)//搜索符合标签的传送终点并返回。
    {
        var entrances = FindObjectsOfType<TransitionDestination>();//找到所有可作为终点的对象
        for(int i = 0; i < entrances.Length; i++)
        {
            if (entrances[i].destinationTag== destinationTag)
            {
                return entrances[i];
            }
        }
        return null;
    }

}

到了这里,关于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日
    浏览(56)
  • Unity2D RPG开发笔记 P1 - Unity界面基础操作和知识

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2024年04月27日
    浏览(70)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包