SRPG游戏开发(六十三)第十一章 地图动作与地图事件 - 十二 完善地图信息与测试(Perfect MapEventInfo and Testing)

版权声明:本文为博主原创文章,未经允许不得转载。 https://blog.csdn.net/darkrabbit/article/details/88122131

返回《SRPG游戏开发》导航

第十一章 地图动作与地图事件(Map Action and Map Event)

我们已经有了剧本,而且可以运行剧本,但我们还缺少对地图的操作控制。

我们这一章来完成地图上的操作,地图的操作将全部由MapAction控制。



十二 完善地图信息与测试(Perfect MapEventInfo and Testing)

我们来测试我们的成果。


1 完善地图信息(Perfect MapEventInfo)

首先,我们先来完善一下地图信息。

我们之前将所有的地图事件都放在了一起,在这里,我们将地图开始和地图结束的事件分开。

1.1 地图开始事件(Map Start Event)

这个事件是在读取地图时触发的。且它没有条件,是直接触发,且只触发一次。

我们在CanTrigger中,让它直接返回true,这样就直接屏蔽了条件。

我们单独建立一个类:

using System;

namespace DR.Book.SRPG_Dev.ScriptManagement
{
    [Serializable]
    public class MapEventNoCondition : MapEvent
    {
        public override bool CanTrigger(MapAction action)
        {
            if (!onlyonce)
            {
                onlyonce = true;
            }
            return true;
        }
    }
}

1.2 地图结束事件(Map End Event)

这个事件是在地图结束时触发的。无论它触发什么事件,在结尾都应该触发WinResult或者是LoseResult,同样它们只能触发一次。

所以这个事件,我们添加它们:

using System;
using System.Collections;
using System.Xml.Serialization;

namespace DR.Book.SRPG_Dev.ScriptManagement
{
    [Serializable]
    public class MapEventWinLose : MapEvent
    {
        [XmlElement("WinResult", typeof(WinResult))]
        [XmlElement("LoseResult", typeof(LoseResult))]
        public Result resultTrigger;

        public override bool CanTrigger(MapAction action)
        {
            if (!onlyonce)
            {
                onlyonce = true;
            }
            return base.CanTrigger(action);
        }

        public override IEnumerator Trigger(MapAction action, Action<MapEvent> onTriggered = null, Action<string> onError = null)
        {
            // 触发过了
            if (isTriggered)
            {
                yield break;
            }

            // 是否满足所有触发条件
            if (!CanTrigger(action))
            {
                yield break;
            }

            // 触发事件
            if (triggers != null && triggers.Count != 0)
            {
                int i = 0;
                while (i < triggers.Count)
                {
                    Result trigger = triggers[i++];
                    if (trigger == null)
                    {
                        continue;
                    }

                    if (!trigger.Trigger(action))
                    {
                        if (onError != null)
                        {
                            onError(string.Format("MapEvent {0} -> Event Trigger error.", id));
                        }
                        isTriggered = true;
                        yield break;
                    }

                    do
                    {
                        yield return null;
                    } while (action.status != ActionStatus.WaitInput);
                }
            }

            if ((resultTrigger == null)
                || (resultTrigger.type != MapEventResultType.WinResult && resultTrigger.type != MapEventResultType.LoseResult)
                || (!resultTrigger.Trigger(action)))
            {
                if (onError != null)
                {
                    onError(string.Format("MapEventWinLose {0} -> Event Trigger error.", id));
                }
                isTriggered = true;
                action.MapEndCommand(-1);
                yield break;
            }

            // 如果只能触发一次,则设置触发过事件了
            if (onlyonce)
            {
                isTriggered = true;
            }

            if (onTriggered != null)
            {
                onTriggered(this);
            }
        }
    }
}

1.3 胜利结果(Win Result)

我们结束地图的事件是可以有多个分支,

例如胜利条件有多种:

  • 到达某地

  • 杀死某个角色

失败条件类似,也可以有多种。

我们在这里规定失败时的参数就是-1,而胜利可以有多种状态。在剧本中,判断就可以有多种选择。

例如

    battle Stage0Scene map0;
    if Stage0Scene == 1 goto #Stage0WinPos;
    if Stage0Scene == 2 goto #Stage0WinRole;
    // other
    goto #Stage0Lose;

所以我们的胜利结果需要一个参数:

        [Serializable]
        public class WinResult : Result
        {
            [XmlAttribute]
            public int result;

            public sealed override MapEventResultType type
            {
                get { return MapEventResultType.WinResult; }
            }

            public override bool Trigger(MapAction action)
            {
                action.MapEndCommand(result);
                return true;
            }
        }

提示:加入参数后,可以不使用LoseResult(它显得多余了),只使用WinResult

1.4 地图信息(Map Event Info)

类似于胜利结果,状态有多种,所以我们也可能有多个胜利结果。

using System;
using System.Xml.Serialization;

namespace DR.Book.SRPG_Dev.ScriptManagement
{
    [Serializable]
    public class MapEventInfo
    {
        // 省略其它参数

        [XmlElement]
        public MapEventNoCondition startEvent;

        [XmlArray, XmlArrayItem]
        public MapEvent[] events;

        [XmlArray, XmlArrayItem]
        public MapEventWinLose[] resultEvents;
    }
}

不要忘记,在MapAction中也要做相应的修改:

        /// <summary>
        /// 读取事件
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        private bool LoadMapEvent(MapEventInfo info)
        {
            // 地图加载事件
            if (info.startEvent != null)
            {
                if (info.startEvent.entryConditionType != MapEventConditionType.NoneCondition)
                {
                    info.startEvent.entryConditionType = MapEventConditionType.NoneCondition;
                }
                m_MapEvents.Add(info.startEvent);
            }

            // 地图中的事件
            if (info.events != null)
            {
                for (int i = 0; i < info.events.Length; i++)
                {
                    MapEvent me = info.events[i];
                    if (me == null)
                    {
                        continue;
                    }

                    if (!m_MapEvents.Add(me))
                    {
                        error = string.Format(
                            "MapAction -> Load map event failure. Entry type `{0}` is not supported."
                            + " Or, id of map event is already exist.",
                            me.entryConditionType);
                        return false;
                    }
                }
            }

            // 地图结束事件
            if (info.resultEvents != null)
            {
                for (int i = 0; i < info.resultEvents.Length; i++)
                {
                    MapEvent me = info.resultEvents[i];
                    if (me == null)
                    {
                        continue;
                    }

                    if (!m_MapEvents.Add(me))
                    {
                        error = string.Format(
                            "MapAction -> Load map event failure. Entry type `{0}` is not supported."
                            + " Or, id of map event is already exist.",
                            me.entryConditionType);
                        return false;
                    }
                }
            }

            // 执行地图加载事件
            TriggerEvents(MapEventConditionType.NoneCondition, () =>
            {
                OnChangeTurn(AttitudeTowards.Player);
            });

            return true;
        }

2 测试(Testing)

在测试阶段,我们可以多些一些地图的测试脚本进行测试。

2.1 地图信息与地图剧本(Map Event Info and Map Script)

最基本的脚本map0.xml

<?xml version="1.0" encoding="utf-8"?>
<MapEventInfo nextScene="" scenarioName="stage0">
  <mouseCursor prefab="MouseCursor" x="5" y="5" />
  <cursor>Cursor</cursor>
  <startEvent id="0" onlyonce="true">
    <triggers>
      <CreateObjectResult>
        <ClassUnique x="5" y="5" attitudeTowards="Player" id="0" />
        <ClassUnique x="18" y="5" attitudeTowards="Enemy" id="1" />
        <ClassFollowing x="19" y="5" attitudeTowards="Enemy" id="0" level="1" items="0" />
      </CreateObjectResult>
      <ScenarioResult flag="#Start" />
    </triggers>
  </startEvent>
  <resultEvents>
    <MapEventWinLose id="10" onlyonce="true">
      <PositionCondition x="2" y="4" />
      <triggers>
        <ScenarioResult flag="#Win" />
      </triggers>
      <WinResult result="1" />
    </MapEventWinLose>
    <MapEventWinLose id="20" onlyonce="true">
      <PositionCondition x="2" y="6" />
      <LoseResult />
    </MapEventWinLose>
  </resultEvents>
</MapEventInfo>
  • 地图开始事件:

    • 创建3个角色(1个玩家,2个敌人)(2个独有角色,1个杂兵);

    • 从剧本stage0的剧情标识符#Start开始运行剧本;

  • 地图事件:无;

  • 地图结束事件:

    • 到达坐标(2,4)从剧本stage0的剧情标识符#Win开始运行剧本,之后战斗胜利;

    • 到达坐标(2,6)战斗失败。

这里只是举了个例子,你可以自己编写条件,并尝试更多的条件与结果。

地图剧本为stage0.txt

// stage0.txt
// 首关

#Start;	// 剧本开始

    text top
        "战斗好激烈啊,我们行不行?";
    text bottom
        "没问题的,放心吧。";
    clear text;
    back;

#Win;

    text top
        "我们赢了";

    clear text;
    back;

2.2 测试剧情剧本(Test Script)

同样的,我们的测试剧本test.txt也需要修改:

// 测试剧本
// test.txt

#Testing; // 测试剧本

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

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

    // 省略中间剧本代码

    #TestMapAction;
        text global
            "3:测试MapAction:"
            "场景:Stage0Scene;"
            "剧本:map0。";

        battle Stage0Scene map0;
        if Stage0Scene == 1 goto #Stage0Win;
        goto #Stage0Lose;

        #Stage0Win;
            text global
                "战斗胜利";
            goto #TestMapActionEnd;

        #Stage0Lose;
            text global
                "战斗失败";
            goto #TestMapActionEnd;

        #TestMapActionEnd;
            text global
                "测试MapAction结束";
            goto #Testing;

2.3 测试结果(Testing Result)

我们直接在Unity中运行游戏,查看测试结果,如 图 11.12 所示。

Testing

  • 图 11.12 Testing

2.4 测试完整代码(Testing Code)

修改测试代码(EditorTestGamePlot.cs):

#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;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor;
#endif

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

    public class EditorTestGamePlot : MonoBehaviour
    {
        public const string testSceneName = "TestGamePlotScene";

        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));
        }

        private void Start()
        {
            GameDirector.instance.debugInfo = m_DebugInfo;
            GameDirector.instance.firstScenario = m_TestScript;
            GameDirector.instance.firstScenarioIsTxt = m_IsTxt;

            SceneManager.sceneLoaded += SceneManager_sceneLoaded;
            SceneManager.LoadSceneAsync("InterludeScene", LoadSceneMode.Single);
        }

        private void SceneManager_sceneLoaded(Scene scene, LoadSceneMode mode)
        {
            Debug.LogFormat("Load Scene: {0}.", scene.name);

            SceneManager.sceneLoaded -= SceneManager_sceneLoaded;

            if (!GameDirector.instance.LoadFirstScenario())
            {
                Debug.LogError("Load first scenario error.");
                EditorApplication.isPlaying = false;
                return;
            }

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

至此,本章内容全部完成。


猜你喜欢

转载自blog.csdn.net/darkrabbit/article/details/88122131