unity 完成NPC对话

2015年01月29日 09:52 1 点赞 0 评论 更新于 2017-05-09 06:13

这篇博客将详细讲解如何在Unity中实现NPC对话系统。

一、RPG游戏与NPC对话的意义

相信每一位RPG游戏爱好者,都会被其精彩的剧情所吸引。在RPG游戏里,剧情主要通过人物间的对话串联起来,玩家通过整合这些对话信息来理解整个故事的来龙去脉。从故事性角度而言,RPG游戏能让玩家产生更强的共鸣和代入感。相比之下,当下的很多网游充斥着厮杀、咆哮和谩骂,缺乏单机游戏那样完整的叙事性。

博主本人对网游比较厌倦,尤其是腾讯代理的部分网游对青少年可能产生的不良影响。虽然国产单机游戏在某些方面或许比不上同时代的国外大作,但武侠、仙侠题材若离开了中国传统文化,便失却了其独特的韵味。博主认为,游戏应是一种艺术,它能让玩家心中存有信念,明白什么才是重要的。像《仙剑奇侠传》和《古剑奇谭》,表面说剑,实则说人。游戏主角们面临着各种选择,他们为了信念不断突破自我。这也提醒着现实中的我们,二十几岁正是努力的年纪,不应荒废时光。如果一款游戏不能带来有益的启示和思考,玩家便无法理解战斗的意义。游戏与现实虽有区别,但道理是相通的,不努力就会有人超越自己。

二、更新文章频率降低的原因

大家可能发现博主近期文章更新频率降低了,这是因为宿舍环境嘈杂,整天充满厮杀声,难以安静写博客。所以选择在晚上更新这篇文章。

三、实现RPG游戏中的NPC对话系统

(一)实现思路

我们希望实现的是RPG游戏中的NPC对话系统。具体思路如下:首先,根据用户鼠标的位置向NPC发射射线,若射线击中NPC,则进入对话状态。当用户按下空格键或者单击鼠标左键时,开始显示事先定义好的NPC对话数据。由于传统的GUI方式效率较低,我们采用在场景中使用GUI Text ,并在脚本中更新其内容的方式来实现与NPC对话。我们继续使用之前文章中搭建的场景。

(二)场景搭建

  1. 向场景中拖入一个熊的模型作为NPC,在Tag中添加NPC并为其设置NPC标签,同时加上一个合适的触发器,这里使用球体触发器。
  2. 创建一个GUI Text,命名为Description,它将用于显示NPC对话。至此,场景搭建完成。

(三)编写控制NPC对话的脚本NPC.cs

using UnityEngine;
using System.Collections;

public class NPC : MonoBehaviour
{
// 定义NPC对话数据
private string[] mData = { "你好,我是NPC", "这是一个Unity3D编写的脚本",
"对话框是基于GUI实现的", "博主是一个喜欢游戏的人", "这是一个关于NPC对话的简单实现",
"大家就不要笑话这个界面丑陋了啊" };

// 当前对话索引
private int index = 0;

// 用于显示对话的GUI Text
public GUIText mText;

// 对话标示贴图
public Texture mTalkIcon;

// 是否显示对话标示贴图
private bool isTalk = false;

void Update()
{
// 从角色位置向NPC发射一条经过鼠标位置的射线
Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit mHi;

// 判断是否击中了NPC
if (Physics.Raycast(mRay, out mHi))
{
// 如果击中了NPC
if (mHi.collider.gameObject.tag == "NPC")
{
// 进入对话状态
isTalk = true;

// 允许绘制
if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0))
{
// 绘制指定索引的对话文本
if (index < mData.Length)
{
mText.text = "NPC:" + mData[index];
index = index + 1;
}
else
{
index = 0;
mText.text = "NPC:" + mData[index];
}
}
}
}
}

void OnGUI()
{
if (isTalk)
{
// 禁用系统鼠标指针
Screen.showCursor = false;
Rect mRect = new Rect(Input.mousePosition.x - mTalkIcon.width,
Screen.height - Input.mousePosition.y - mTalkIcon.height,
mTalkIcon.width, mTalkIcon.height);

// 绘制自定义鼠标指针
GUI.DrawTexture(mRect, mTalkIcon);
}
}
}

(四)脚本逻辑解释

在上述脚本中,我们通过isTalk标志判断是否进入对话状态。若进入对话状态,将系统默认的鼠标指针修改为自定义的鼠标指针,提示用户即将进入对话。在此基础上,若用户按下空格键或鼠标左键,NPC会依次显示对话数据,当所有对话显示完毕后,将索引清零,让对话从头开始循环。

(五)最终效果及后续问题

我们给对话文本加上了背景,效果有所提升。不过,博主目前还未找到使对话框背景响应用户操作的方法,待研究好具体细节后会补充到文章中,希望大家持续关注。

(六)对话框问题的解决方法

博主当时写文章时提到对话框的问题,现在给出一个可行的方法:将对话框背景及对话框文字制作成预设,在屏幕底部使用克隆物体的方法生成预设对象。当用户按下空格键时,先判断当前场景中是否存在预设对象,若存在则修改文本,若不存在则先创建预设对象再修改文本。当一组对话显示完后,判断玩家与NPC的距离,若距离大于设定的对话距离,则销毁对话;否则,暂时隐藏对话框。

希望大家喜欢这篇文章,有问题可留言,博主会尽力解答。

作者信息

feifeila

feifeila

共发布了 570 篇文章