Unity游戏开发:对话系统的实现

Posted float_freedom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity游戏开发:对话系统的实现相关的知识,希望对你有一定的参考价值。

在解谜类游戏中,与npc的对话是一个基础且常用的功能。通常来说,在与npc的对话中玩家可以获取一些有价值的信息并对之后的游戏有一定的导向作用。此外,在玩家获取对应物品前后,与npc的对话内容也会发生相应改变。因此,我们需要将对话系统进行封装,以便于在不同的npc身上进行使用,同时要预留接口,后期可以增加或修改对话内容。
这里我们以Cotton Puzzle为例,将对话系统拆分为数据、逻辑和UI三部分来实现。在游戏中,获取物品前后与老太太的对话显示如下:

一、数据

首先,添加数据创建目录

//DialogData.cs
[CreateAssetMenu(fileName = "DailogData", menuName = "Dialog/DailogData")]

创建列表用于存储对话内容

//DialogData.cs
public class DialogData : ScriptableObject

    public List<string> dialogList;

之后,单机右键依次选择Create -> Dialog -> DialogData创建一个空的数据集

在所创建的数据集中输入对话内容,如下:

二、逻辑

利用栈来存储对话数据,每次在需要进行对话时首先将当前状态设置为isTalking并禁用场景切换的操作,之后每一次点击老奶奶便从栈中弹出一条对话内容进行显示。在完成对话之后将isTalking设置为false并重新将数据压入栈中。

1.定义数据存储和状态变量

//DialogController.cs
    public DialogData dialogEmpty;
    public DialogData dialogFinish;

    private Stack<string> dialogEmptyStack = new Stack<string>();
    private Stack<string> dialogFinishStack = new Stack<string>();

    public bool isTalking;

2.在游戏一开始时将数据压入栈中

//DialogController.cs
    private void Awake()
    
        FillDialogStack();
    

    public void FillDialogStack()
    
        for(int i=dialogEmpty.dialogList.Count-1;i>=0;i--)
        
            dialogEmptyStack.Push(dialogEmpty.dialogList[i]);
        
        for (int i = dialogFinish.dialogList.Count - 1; i >= 0; i--)
        
            dialogFinishStack.Push(dialogFinish.dialogList[i]);
        
    

3.定义协程,根据不同状态显示不同对话内容

    public void ShowDialogEmpty()
    
        if (!isTalking)
            StartCoroutine(DialogRoutine(dialogEmptyStack));
    

    public void ShowDialogFinish()
    
        if (!isTalking)
            StartCoroutine(DialogRoutine(dialogFinishStack));
    
    
    private IEnumerator DialogRoutine(Stack<string> data)
    
        isTalking = true;
        //Debug.Log("DialogRoutine协程开始");
        if (data.TryPop(out string result))
        
            //Debug.Log("开始对话");
            EventHandler.CallShowDialogEvent(result);
            yield return null;
            EventHandler.CallGameStateChangedEvent(GameState.Pause);//暂停游戏
        
        else
        
            EventHandler.CallShowDialogEvent(string.Empty);
            FillDialogStack();
            EventHandler.CallGameStateChangedEvent(GameState.GamePlay);//暂停游戏
        
        isTalking = false;
    

三、UI

在DialogUI 中注册ShowDialogEvent ,当对话未完成时(即dialog 不为空)将panel设置为active,当对话结束以后便关闭panel。

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

public class DialogUI : MonoBehaviour

    public GameObject panel;
    public Text dialogText;

    private void OnEnable()
    
        EventHandler.ShowDialogEvent += ShowDialog;
    

    private void OnDisable()
    
        EventHandler.ShowDialogEvent -= ShowDialog;
    


    private void ShowDialog(string dialog)
    
        if (dialog != string.Empty)
            panel.SetActive(true);
        else
            panel.SetActive(false);
        dialogText.text = dialog;
        


Unity3D游戏开发之换装系统的实现

换装有两种:增加式换装、更新式换装

 

 一、增加式换装

             所谓增加式换装,就是指角色模型的身体是一个完整的网格,需要更换的部分只是一个可拆卸的部件,因而换装实际上就是在特定的部位增加或者移除一个模型。这类换装通常用在角色的武器更换中,仙剑、古剑的武侠更换都是这种类型,如图所示的鱼骨头只是在手这个位置更换模型:

 

二、更新式换装

更新式换装是指角色拥有一个公共的骨骼网络和针对该模型的若干组贴图。那么,此时的换装实际上就是将贴图贴到对应的位置实现角色外观的改变,仙剑四严格来讲应该不算是换装,它实际上是做了两套模型(琼华装/野人装)。古剑奇谭的换装实际上就是这种类型的换装,这里我们以下面的一个例子,来一起学习如何实现这种类型的换装。首先我们创建一个Unity项目:

         首先我们去下载官方提供的换装的例子,我们这里只需要里面的模型,因为这个模型提供了多组贴图可以供我们使用。我们将模型拖拽到场景中,我们展开模型可以发现模型是有若干个部分组成的,每一个模型都有一个SkinnedMeshRenderer组件,改变该组件的材质,我们就可以实现对特定部位的换装。由此,我们写出了下面的代码:

 

using UnityEngine;
using System.Collections;

public class ChangeSkin : MonoBehaviour {
    
    //眼睛贴图
    public Texture2D[] TextureEyes;
    //面部贴图-前
    public Texture2D[] TextureFace1;
    //面部贴图-后
    public Texture2D[] TextureFace2;
    //头发贴图-前
    public Texture2D[] TextureHair1;
    //头发贴图-后
    public Texture2D[] TextureHair2;
    //下衣贴图-前
    public Texture2D[] TexturePants1;
    //下衣贴图-后
    public Texture2D[] TexturePants2;
    //上衣贴图-前
    public Texture2D[] TextureTop1;
    //上衣贴图-后
    public Texture2D[] TextureTop2;
    //鞋子贴图-前
    public Texture2D[] TextureShoes1;
    //鞋子贴图-后
    public Texture2D[] TextureShoes2;
    
    //与贴图对应的SkinnedMeshRenderer
    SkinnedMeshRenderer MeshEyes;
    SkinnedMeshRenderer MeshFace1;
    SkinnedMeshRenderer MeshFace2;
    SkinnedMeshRenderer MeshHair1;
    SkinnedMeshRenderer MeshHair2;
    SkinnedMeshRenderer MeshPants1;
    SkinnedMeshRenderer MeshPants2;
    SkinnedMeshRenderer MeshTop1;
    SkinnedMeshRenderer MeshTop2;
    SkinnedMeshRenderer MeshShoes1;
    SkinnedMeshRenderer MeshShoes2;
    
    void Start () 
    {
       //获取SkinnedMeshRenderer
       MeshEyes=transform.Find("eyes").GetComponent<SkinnedMeshRenderer>();
       MeshFace1=transform.Find("face-1").GetComponent<SkinnedMeshRenderer>();
       MeshFace2=transform.Find("face-2").GetComponent<SkinnedMeshRenderer>();
       MeshHair1=transform.Find("hair-1").GetComponent<SkinnedMeshRenderer>();
       MeshHair2=transform.Find("hair-2").GetComponent<SkinnedMeshRenderer>();
       MeshPants1=transform.Find("pants-1").GetComponent<SkinnedMeshRenderer>();
       MeshPants2=transform.Find("pants-2").GetComponent<SkinnedMeshRenderer>();
       MeshTop1=transform.Find("top-1").GetComponent<SkinnedMeshRenderer>();
       MeshTop2=transform.Find("top-2").GetComponent<SkinnedMeshRenderer>();
       MeshShoes1=transform.Find("shoes-1").GetComponent<SkinnedMeshRenderer>();
       MeshShoes2=transform.Find("shoes-2").GetComponent<SkinnedMeshRenderer>();
    }
    
    void OnGUI()
    {
      if(GUILayout.Button("显示外装1",GUILayout.Height(30)))
      {
        SetSkin(MeshEyes,TextureEyes[0]);
        SetSkin(MeshFace1,TextureFace1[0]);
        SetSkin(MeshFace2,TextureFace2[0]);
        SetSkin(MeshHair1,TextureHair1[0]);
        SetSkin(MeshHair2,TextureHair2[0]);
        SetSkin(MeshPants1,TexturePants1[0]);
        SetSkin(MeshPants2,TexturePants1[0]);
        SetSkin(MeshTop1,TextureTop1[0]);
        SetSkin(MeshTop2,TextureTop2[0]);
        SetSkin(MeshShoes1,TextureShoes1[0]);
        SetSkin(MeshShoes2,TextureShoes2[0]);
      }
        
      if(GUILayout.Button("显示外装2",GUILayout.Height(30)))
      {
        SetSkin(MeshEyes,TextureEyes[1]);
        SetSkin(MeshFace1,TextureFace1[1]);
        SetSkin(MeshFace2,TextureFace2[1]);
        SetSkin(MeshHair1,TextureHair1[1]);
        SetSkin(MeshHair2,TextureHair2[1]);
        SetSkin(MeshPants1,TexturePants1[1]);
        SetSkin(MeshPants2,TexturePants1[1]);
        SetSkin(MeshTop1,TextureTop1[1]);
        SetSkin(MeshTop2,TextureTop2[1]);
        SetSkin(MeshShoes1,TextureShoes1[1]);
        SetSkin(MeshShoes2,TextureShoes2[1]);    
      }
    }

    
    private void SetSkin(SkinnedMeshRenderer mRenderer,Texture2D mTexture)
    {
       mRenderer.material.mainTexture=mTexture;
    }
    
    
}

 

这里我们提供两套外装。我们把脚本拖放到模型上,然后编辑贴图数组:

技术分享

 

编辑好贴图后,我们就可以运行程序了,注意贴图要和模型网格匹配。

这种方法需要设计者完全的了解角色身体的内部构造,所以需要和美工有良好的合作才可以实现。

 

 

本文出处:http://blog.csdn.net/qinyuanpei/article/details/24393683

以上是关于Unity游戏开发:对话系统的实现的主要内容,如果未能解决你的问题,请参考以下文章

Unity导航

Unity 游戏黑暗之光笔记第五章 背包系统的实现

强迫我的玩家更新游戏-Unity-C#

Unity3D游戏开发之换装系统的实现

Unity联手腾讯游戏推出防沉迷系统开发工具,共促游戏环境生态建设

Unity3D手机斗地主游戏开发实战(02)_叫地主功能实现(不定期更新中~~~)