SRPG游戏开发(五十一)第十章 游戏剧情 - 九 游戏导演与测试 (Game Director and Testing)

论坛 期权论坛 脚本     
匿名技术用户   2020-12-21 12:42   141   0

返回总目录

第十章 游戏剧情(Game Plot)

在大部分的RPG中,故事剧情是非常重要的。例如某些播放某些过场动画,人物台词等文字叙述的显示。这些可以推动整个游戏流程。

在Unity商店中,有一些剧情类的插件。我们编写的这个可以配合那些插件使用。



九 游戏导演与测试 (Game Director and Testing)

我们在之前已经完成了许多典型命令的编写,其它命令不再说明,大同小异。

现在,我们有了一个剧情动作ScenarioAction,它用来指导我们剧情的走向;

以后还会有MapAction,它用来指导我们地图是如何建立的,并将地图事件指向剧情;

不过在这之前我们还没有“导演”来统筹规划。

这一节,我们创建这个导演,并且测试剧情。


1 游戏导演类(Class GameDirector)

我们的导演是统筹规划游戏所有剧情与事件的,属于管理器一类,所以它应该是个单例。

我们再来看看它的基本功能:

  • 读取剧本;

  • 执行剧本;

  • 停止执行剧本;

基本功能基本上只有以上三个。但不要忘记,在游戏进行初期需要一个入口,这个入口就是“首次运行的剧本”。而执行剧本时,我们需要每帧都运行游戏,你可以使用Update(),也可以使用Coroutine

基于以上,我们来创建导演类:

using System;
using System.Collections;
using UnityEngine;

namespace DR.Book.SRPG_Dev.ScriptManagement
{
    using DR.Book.SRPG_Dev.Framework;
    using DR.Book.SRPG_Dev.Models;

    public class GameDirector : UnitySingleton<GameDirector>
    {
        #region Fields
        [SerializeField]
        private bool m_DebugInfo = true;
        [SerializeField]
        private string m_FirstScenario = "main";
        [SerializeField]
        private bool m_FirstScenarioIsTxt = true;

        private IGameAction m_GameAction = null;
        private Coroutine m_Coroutine = null;
        #endregion

        #region Properties
        // 省略Properties
        #endregion

        // TODO 读取剧本、运行剧本、停止剧本

    }
}

1.1 读取剧本动作 (Load Scenario Action)

关于读取剧本,我们新建一个Model称为ScenarioModel(第八章已经写过多次,具体不再重复),里面包含了获取剧本的方法IScenario Get(string name, bool txt = true)(我们的剧本只使用了两种格式)。

我们读取剧本后需要创建我们的ScenarioAction

所以我们的步骤是:

  • ScenarioModel读取剧本文本;

  • 创建ScenarioAction

  • 读取命令执行器;

  • 并让ScenarioAction读取剧本文本。

创建方法:

        /// <summary>
        /// 读取剧本
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual bool LoadScenario(string name, bool txt = true)
        {
            ScenarioModel model = ModelManager.models.Get<ScenarioModel>();
            IScenario scenario = model.Get(name, txt);
            if (scenario == null)
            {
                return false;
            }

            ScenarioAction action = new ScenarioAction(currentAction)
            {
                debugInfo = debugInfo
            };

            Type[] executorTypes = new Type[]
            {
                typeof(DebugExecutor),
                typeof(VarExecutor), typeof(CalcExecutor),
                typeof(GotoExecutor), typeof(IfGotoExecutor),
                typeof(TextExecutor), typeof(MenuExecutor)
                // 其它执行器类型
            };
            action.LoadExecutors(executorTypes);

            if (!action.LoadScenario(scenario))
            {
                Debug.LogError(action.error);
                action.Dispose();
                return false;
            }

            currentAction = action;

            if (debugInfo)
            {
                Debug.LogFormat("GameDirector Load Scenario: {0}", scenario.name);
            }

            return true;
        }

1.2 运行/停止游戏动作 (Run/Stop Game Action)

在这里,我不使用Update(),而是使用Coroutine

如果你要使用Update(),最好是使用LateUpdate(),而Update()处理输入。

在这里,我们的每帧处理返回一个布尔变量:

  • true:执行成功,等待一帧;

  • false:执行失败。

创建方法:

        /// <summary>
        /// 开始运行
        /// </summary>
        public void RunGameAction()
        {
            m_Coroutine = StartCoroutine(RunningGameAction());
        }

        /// <summary>
        /// 运行中
        /// </summary>
        /// <returns></returns>
        private IEnumerator RunningGameAction()
        {
            yield return null;

            while (true)
            {
                if (currentAction == null)
                {
                    yield return null;
                    continue;
                }

                if (currentAction.Update())
                {
                    yield return null;
                }
                else
                {
                    break;
                }
            }

            m_Coroutine = null;
        }

停止运行:

        /// <summary>
        /// 停止运行
        /// </summary>
        public void StopGameAction()
        {
            if (m_Coroutine == null)
            {
                return;
            }

            StopCoroutine(m_Coroutine);
            m_Coroutine = null;
        }

1.3 输入处理 (Input)

我们的输入处理使用Update()

        protected virtual void Update()
        {
            if (currentAction == null)
            {
                return;
            }

            currentAction.OnMouseMove(Input.mousePosition);

            if (Input.GetMouseButtonDown(0))
            {
                currentAction.OnMouseLButtonDown(Input.mousePosition);
            }
            else if (Input.GetMouseButtonUp(0))
            {
                currentAction.OnMouseLButtonUp(Input.mousePosition);
            }
            else if (Input.GetMouseButtonDown(1))
            {
                currentAction.OnMouseRButtonDown(Input.mousePosition);
            }
            else if (Input.GetMouseButtonUp(1))
            {
                currentAction.OnMouseRButtonUp(Input.mousePosition);
            }

            // TODO Keypress
        }

2 测试 (Testing)

2.1 测试文本剧本 (test.txt)

我们要测试程序是否写的正确,最好先有一个剧本,我取名为test.txt

这是我的剧本:

// 测试剧本

#Testing; // 测试剧本

 #TestMenuIfGotoCmd;
  clear text;
  menu option
   "测试var、calc和debug命令"
   "测试text命令";

  if option == 0 goto #TestVarCalcDebugCmd;
  if option == 1 goto #TestTextCmd;
  goto #Testing;

 #TestVarCalcDebugCmd;
  text global
   "1:测试变量var、calc和debug命令。"
   "按下按键后,变量值将打印在Unity的Console面板中。";

  var a;
  var b = 20;
  var c = 30;
  calc c += b; // c = 50
  var d = b; // d = 20

  debug log "Test Debug Log \"a\" \"b\" \"c\" \"d\":";
  debug log 
   "a = " a 
   ", b = " b 
   ", c = " c 
   ", d = " d;

  text global
   "测试变量结束";
  goto #Testing;
 
 #TestTextCmd;
  text global 
   "2:测试文本text命令,将按以下顺序:"
   "1 测试字符串和从TextInfo读取id=1,显示在上面板;"
   "2 测试字符串和从TextInfo读取id=2,显示在下面板;"
   "3 测试富文本,显示在全局面板。";

  text top 
   "字符串测试,上面板: '我被单引号括起来了'。"
   "下面一行是从TextInfo读取id=1:"
   1;
  text bottom 
   "字符串测试,下面板:\"我是被双引号括起来了\"。"
   "下面一行是从TextInfo读取id=2:"
   2;
  text global 
   "富文本测试,富文本暂不支持嵌套:"
   "<color=#ff0000>红色</color> <color=#00ff00>绿色</color>"
   "<color=#0000ff>蓝色</color> <color=#808080>灰色</color>"
   "<b>加粗</b> <i>斜体</i>"
   "<size=40>大小40</size> <size=16>大小16</size>"
   "双引号内是空的富文本格式,测试是否报错:\"<b></b>\"";

  text global 
   "测试文本结束";
  goto #Testing;

在之前,我们并没有说过带"的字符串处理,不过在这段测试剧本中,为了方便我使用了字符串,而在正式游戏中应该不会用到字符串,所有文本都取自TextInfoConfig,你可以将以上文本替换成对应的id

2.2 测试步骤 (Testing Procedure)

我们测试按一下步骤:

  • 其一,将所有其它测试的GameObjectactiveSelf设置成false;

  • 其二,创建GameObject并重命名为Test Game Plot,并添加EditorTestGamePlot组件;

  • 其三,调整EditorTestGamePlot的参数,如 图 10.3 ;
    Test Game Plot Inspector

    • 图 10.3:Test Game Plot Inspector
  • 其四,查看游戏界面是否正常,如 图 10.4 ;
    Test Game Plot Scene

    • 图 10.4:Test Game Plot Scene
  • 其五,查看Console面板输出,如 图 10.5 。
    Test Game Plot Console

    • 图 10.5:Test Game Plot Console

2.3 测试完整代码 (EditorTestGamePlot.cs)

我们的测试代码也非常简单,只是让GameDirector播放测试剧本就可以了。

#region ---------- File Info ----------
/// **********************************************************************
/// Copyright (C) 2019 DarkRabbit(ZhangHan)
///
/// File Name:    EditorTestGamePlot.cs
/// Author:     DarkRabbit
/// Create Time:   Sat, 12 Jan 2019 23:56:52 GMT
/// Modifier:
/// Module Description:
/// Version:    V1.0.0
/// **********************************************************************
#endregion ---------- File Info ----------

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

namespace DR.Book.SRPG_Dev.ScriptManagement.Testing
{
    using DR.Book.SRPG_Dev.Framework;
    using DR.Book.SRPG_Dev.Models;

    public class EditorTestGamePlot : MonoBehaviour
    {
        public bool m_DebugInfo = true;
        public string m_TestScript = "test";
        public bool m_IsTxt = true;

        #region Unity Callback
#if UNITY_EDITOR
        private void Awake()
        {
            ConfigLoader.rootDirectory = Application.streamingAssetsPath + "/Config";
            ConfigLoader.LoadConfig(typeof(TextInfoConfig));

            GameDirector.instance.debugInfo = m_DebugInfo;
            GameDirector.instance.firstScenario = m_TestScript;
            GameDirector.instance.firstScenarioIsTxt = m_IsTxt;
        }

        private void Start()
        {
            if (!GameDirector.instance.LoadFirstScenario())
            {
                Debug.LogError("Load first scenario error.");
                return;
            }

            GameDirector.instance.RunGameAction();
        }
#endif
        #endregion
    }
}

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP