SRPG游戏开发(五十九)第十一章 地图动作与地图事件 - 八 地图事件(Map Event)

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

返回总目录

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

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

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



八 地图事件(Map Event)

地图事件是我们地图中所发生的事情,包括:

  • 触发条件;

  • 触发结果。

它也比较常见,例如:

条件 结果 FE4中实例
地图开始 创建各种地图对象 战斗开始时的剧情,然后创建地图对象
角色到达某个地点 获取物品 第一章时斧骑士到达海边获取二回攻击斧
某回合开始 创建各种地图对象 某些剧情需要
对应角色对话 获取物品、提升属性等 某些章节对话

等等诸如此类的事件。

这一节,我们挑选一些典型的事件来说明。


1 地图事件类(Class MapEvent)

我们的地图事件必须包含:

  • 入口条件(主条件);

  • 一些子条件;

  • 触发结果。

当然,还有一些帮助类的属性:

  • 唯一标识(id);

  • 是否只触发一次;

  • 是否触发过。

创建类:

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

namespace DR.Book.SRPG_Dev.ScriptManagement
{
    [Serializable]
    public class MapEvent
    {
        [XmlAttribute]
        public int id;

        /// <summary>
        /// 是否只能触发一次
        /// </summary>
        [XmlAttribute]
        public bool onlyonce;

        /// <summary>
        /// 进入事件条件的类型
        /// </summary>
        [XmlIgnore]
        public MapEventConditionType entryConditionType;

        /// <summary>
        /// 进入事件条件
        /// </summary>
        [XmlChoiceIdentifier("entryConditionType")]
        [XmlElement("NoneCondition", typeof(Condition)),
            XmlElement("TurnCondition", typeof(TurnCondition)),
            XmlElement("PositionCondition", typeof(PositionCondition)),
            XmlElement("RoleCondition", typeof(RoleCondition), IsNullable = true),
            XmlElement("RoleDeadCondition", typeof(RoleDeadCondition)),
            XmlElement("RoleTalkCondition", typeof(RoleTalkCondition)),
            XmlElement("RoleCombatTalkCondition", typeof(RoleCombatTalkCondition)),
            XmlElement("PropertyCondition", typeof(PropertyCondition), IsNullable = true),
            XmlElement("ItemCondition", typeof(ItemCondition), IsNullable = true)]
        public Condition entryCondition;

        /// <summary>
        /// 额外的事件条件
        /// </summary>
        [XmlArray]
        [XmlArrayItem(typeof(TurnCondition)),
            XmlArrayItem(typeof(PositionCondition)),
            XmlArrayItem(typeof(RoleCondition)),
            XmlArrayItem(typeof(RoleDeadCondition)),
            XmlArrayItem(typeof(PropertyCondition)),
            XmlArrayItem(typeof(ItemCondition))]
        public List<Condition> conditions = new List<Condition>();

        /// <summary>
        /// 事件结果
        /// </summary>
        [XmlArray]
        [XmlArrayItem(typeof(ScenarioResult)),
            XmlArrayItem(typeof(CreateObjectResult)),
            XmlArrayItem(typeof(PositionResult)),
            XmlArrayItem(typeof(PropertyResult)),
            XmlArrayItem(typeof(ItemResult)),
            XmlArrayItem(typeof(WinResult)),
            XmlArrayItem(typeof(LoseResult))]
        public List<Result> triggers = new List<Result>();

        /// <summary>
        /// 是否已经触发过
        /// </summary>
        [XmlIgnore]
        public bool isTriggered { get; private set; }

        public MapEvent()
        {
            isTriggered = false;
        }

        // TODO 方法

    }
}

在触发事件之前,我们应该判断每一个条件。

        /// <summary>
        /// 是否满足单个事件条件
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        protected bool CanConditionTrigger(Condition condition, MapAction action)
        {
            if (condition == null)
            {
                return true;
            }

            return condition.GetResult(action);
        }

        /// <summary>
        /// 是否满足所有事件条件
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        public virtual bool CanTrigger(MapAction action)
        {
            if (!CanConditionTrigger(entryCondition, action))
            {
                return false;
            }

            if (conditions != null && conditions.Count != 0)
            {
                for (int i = 0; i < conditions.Count; i++)
                {
                    if (!CanConditionTrigger(conditions[i], action))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

最后,触发我们的事件。

要注意, 触发时,我们规定每帧执行一个结果,而结果可能包含剧情,这时应该等待剧情运行结束后,再执行下面的结果。

        /// <summary>
        /// 触发事件
        /// </summary>
        /// <param name="action"></param>
        /// <param name="onTriggered"></param>
        /// <param name="onError"></param>
        /// <returns></returns>
        public virtual 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 (onlyonce)
            {
                isTriggered = true;
            }

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

2 地图事件集合(Map Event Collection)

我们建立一个集合,将所有的MapEvent都存储到这里面,且它还负责执行事件。

创建类:

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

namespace DR.Book.SRPG_Dev.ScriptManagement
{
    /// <summary>
    /// 事件集合
    /// </summary>
    public class MapEventCollection
    {
        #region Fields
        /// <summary>
        /// 所有事件
        /// </summary>
        protected readonly Dictionary<int, MapEvent> m_Events = new Dictionary<int, MapEvent>();

        /// <summary>
        /// 触发过的事件
        /// </summary>
        protected readonly Dictionary<int, MapEvent> m_TriggeredEvents = new Dictionary<int, MapEvent>();

        /// <summary>
        /// 进入地图事件
        /// </summary>
        public readonly List<MapEvent> startEvents = new List<MapEvent>();

        /// <summary>
        /// 每回合开始事件
        /// </summary>
        public readonly List<MapEvent> turnEvents = new List<MapEvent>();

        /// <summary>
        /// 角色死亡事件
        /// </summary>
        public readonly Dictionary<int, MapEvent> deadEvents = new Dictionary<int, MapEvent>();

        /// <summary>
        /// 对话事件
        /// </summary>
        public readonly Dictionary<Vector2Int, MapEvent> roleTalkEvents = new Dictionary<Vector2Int, MapEvent>();

        /// <summary>
        /// 战斗对话事件
        /// </summary>
        public readonly Dictionary<Vector2Int, MapEvent> roleCombatTalkEvents = new Dictionary<Vector2Int, MapEvent>();

        /// <summary>
        /// 移动到坐标事件
        /// </summary>
        public readonly Dictionary<Vector3Int, MapEvent> posEvents = new Dictionary<Vector3Int, MapEvent>();
        #endregion

        // TODO
    }
}

2.1 添加(Add)

我们在添加事件时,要注意它们的唯一标识。

        /// <summary>
        /// 添加事件
        /// </summary>
        /// <param name="me"></param>
        /// <returns></returns>
        public bool Add(MapEvent me)
        {
            if (m_Events.ContainsKey(me.id))
            {
                return false;
            }

            m_Events.Add(me.id, me);

            switch (me.entryConditionType)
            {
                case MapEventConditionType.NoneCondition:
                    startEvents.Add(me);
                    break;
                case MapEventConditionType.TurnCondition:
                    turnEvents.Add(me);
                    break;
                case MapEventConditionType.PositionCondition:
                    MapEvent.PositionCondition pc = me.entryCondition as MapEvent.PositionCondition;
                    posEvents.Add(new Vector3Int(pc.x, pc.y, 0), me);
                    break;
                case MapEventConditionType.RoleDeadCondition:
                    MapEvent.RoleDeadCondition rdc = me.entryCondition as MapEvent.RoleDeadCondition;
                    deadEvents.Add(rdc.characterId, me);
                    break;
                case MapEventConditionType.RoleTalkCondition:
                    MapEvent.RoleTalkCondition rtc = me.entryCondition as MapEvent.RoleTalkCondition;
                    if (rtc.characterId == rtc.targetId)
                    {
                        return false;
                    }
                    roleTalkEvents.Add(new Vector2Int(rtc.characterId, rtc.targetId), me);
                    break;
                case MapEventConditionType.RoleCombatTalkCondition:
                    MapEvent.RoleCombatTalkCondition rctc = me.entryCondition as MapEvent.RoleCombatTalkCondition;
                    if (rctc.characterId == rctc.targetId)
                    {
                        return false;
                    }
                    roleCombatTalkEvents.Add(new Vector2Int(rctc.characterId, rctc.targetId), me);
                    break;
                default:
                    return false;
            }

            return true;
        }

2.2 触发事件(Trigger Events)

在触发事件后,我们要将触发过的事件加入到m_TriggeredEvents中。而可以重复触发的事件则不动。

为每一个入口条件创建触发方法:

        public virtual IEnumerator TriggerStartEvents(MapAction action, Action<MapEvent> onTriggered = null, Action<string> onError = null)
        {
            int i = 0;
            while (i < startEvents.Count)
            {
                MapEvent me = startEvents[i];
                yield return me.Trigger(action, onTriggered, onError);
                if (me.isTriggered)
                {
                    startEvents.RemoveAt(i);
                    m_TriggeredEvents.Add(me.id, me);
                }
                else
                {
                    i++;
                }
            }
        }

        public virtual IEnumerator TriggerTurnEvents(MapAction action, Action<MapEvent> onTriggered = null, Action<string> onError = null)
        {
            int i = 0;
            while (i < turnEvents.Count)
            {
                MapEvent me = turnEvents[i];
                yield return me.Trigger(action, onTriggered, onError);
                if (me.isTriggered)
                {
                    turnEvents.RemoveAt(i);
                    m_TriggeredEvents.Add(me.id, me);
                }
                else
                {
                    i++;
                }
            }
        }

        public virtual IEnumerator TriggerDeadEvents(MapAction action, int id, Action<MapEvent> onTriggered = null, Action<string> onError = null)
        {
            MapEvent me;
            if (!deadEvents.TryGetValue(id, out me))
            {
                yield break;
            }

            yield return me.Trigger(action, onTriggered, onError);
            if (me.isTriggered)
            {
                deadEvents.Remove(id);
                m_TriggeredEvents.Add(me.id, me);
            }
        }

        public virtual IEnumerator TriggerRoleTalkEvents(MapAction action, int id, int target, Action<MapEvent> onTriggered = null, Action<string> onError = null)
        {
            Vector2Int key = new Vector2Int(id, target);
            MapEvent me;
            if (!roleTalkEvents.TryGetValue(key, out me))
            {
                yield break;
            }

            yield return me.Trigger(action, onTriggered, onError);
            if (me.isTriggered)
            {
                roleTalkEvents.Remove(key);
                m_TriggeredEvents.Add(me.id, me);
            }
        }

        public virtual IEnumerator TriggerRoleCombatTalkEvents(MapAction action, int id1, int id2, Action<MapEvent> onTriggered = null, Action<string> onError = null)
        {
            Vector2Int key = new Vector2Int(id1, id2);
            MapEvent me;
            if (!roleCombatTalkEvents.TryGetValue(key, out me))
            {
                key = new Vector2Int(id2, id1);
                if (!roleCombatTalkEvents.TryGetValue(key, out me))
                {
                    yield break;
                }
            }

            yield return me.Trigger(action, onTriggered, onError);
            if (me.isTriggered)
            {
                roleCombatTalkEvents.Remove(key);
                m_TriggeredEvents.Add(me.id, me);
            }
        }

        public virtual IEnumerator TriggerPositionEvents(MapAction action, Vector3Int position, Action<MapEvent> onTriggered = null, Action<string> onError = null)
        {
            MapEvent me;
            if (!posEvents.TryGetValue(position, out me))
            {
                yield break;
            }

            yield return me.Trigger(action, onTriggered, onError);
            if (me.isTriggered)
            {
                posEvents.Remove(position);
                m_TriggeredEvents.Add(me.id, me);
            }
        }

2.3 清空事件(Clear Events)

        /// <summary>
        /// 删除所有事件
        /// </summary>
        public void Clear()
        {
            m_Events.Clear();
            m_TriggeredEvents.Clear();
            startEvents.Clear();
            turnEvents.Clear();
            deadEvents.Clear();
            posEvents.Clear();
            roleTalkEvents.Clear();
            roleCombatTalkEvents.Clear();
            posEvents.Clear();
        }

3 读取事件(Load Map Events)

我们在MapEventInfo中,将事件添加进去:

        [XmlArray, XmlArrayItem]
        public MapEvent[] events;

然后在MapAction中,填充我们的LoadMapEvent

        private bool LoadMapEvent(MapEventInfo info)
        {
            // 地图中的事件
            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;
                    }
                }
            }

            // TODO 触发开始事件

            return true;
        }

猜你喜欢

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