SRPG游戏开发(五十八)第十一章 地图动作与地图事件 - 七 地图事件的结果(Result of Map Event)

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

返回总目录

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

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

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



七 地图事件的结果(Result of Map Event)

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

  • 触发条件;

  • 触发结果。

它也比较常见,例如:

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

等等诸如此类的事件。

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


1 结果类(Class Result)

和条件类似的,每个事件触发的结果可能是多种。

首先,我们先来归类一下结果类型:

创建一个枚举MapEventResultType来表示结果类型(它们不是必须的,按需来):

using System;

namespace DR.Book.SRPG_Dev.ScriptManagement
{
    [Serializable]
    public enum MapEventResultType
    {
        NoneResult = 0,

        /// <summary>
        /// 剧本
        /// </summary>
        ScenarioResult,

        /// <summary>
        /// 创建MapObject
        /// </summary>
        CreateObjectResult,

        /// <summary>
        /// 传送位置
        /// </summary>
        PositionResult,

        /// <summary>
        /// 增加/减少属性
        /// </summary>
        PropertyResult,

        /// <summary>
        /// 获得/遗失物品
        /// </summary>
        ItemResult,

        /// <summary>
        /// 战斗胜利
        /// </summary>
        WinResult,

        /// <summary>
        /// 战斗失败
        /// </summary>
        LoseResult
    }
}

这些结果,我们规定:

  • NoneResult:没有任何结果;

  • ScenarioResult:剧本结果,触发剧情;

  • CreateObjectResult:创建对象结果,创建地图障碍物或角色等(你也可以添加一个变量,判断是创建还是消除,我在这里只是创建);

  • PositionResult:位置结果,它用来表示将角色传送到的位置;

  • ItemResult:物品结果,获取或遗失某些物品;

  • WinResult:战斗胜利结果;

  • LoseResult:战斗失败结果。

  • 其它:包含移动角色,摄像机等,这些我并没有写,你可以添加。

这些结果需要返回一个布尔类型,来表示是否运行成功。

创建类:

        [Serializable]
        public class Result
        {
            [XmlIgnore]
            public virtual MapEventResultType type
            {
                get { return MapEventResultType.NoneResult; }
            }

            public virtual bool Trigger(MapAction action)
            {
                return true;
            }
        }

2 剧本结果(Scenario Result)

剧本的结果,是用来触发某些剧情的,自然要使用MapScenarioAction

而触发哪段剧情,需要用到剧情标识符。

        [Serializable]
        public class ScenarioResult : Result
        {
            [XmlAttribute]
            public string flag;

            [XmlIgnore]
            public override MapEventResultType type
            {
                get { return MapEventResultType.ScenarioResult; }
            }

            public override bool Trigger(MapAction action)
            {
                return action.ScenarioCommand(flag);
            }
        }

3 创建对象结果(Create Object Result)

我们要在地图创建的对象包含:

  • 地图障碍物(MapObstacle)

  • 地图职业(MapClass)

    • 独有角色
    • 杂兵角色

所以,我们要先有其对应的方法才可以。

3.1 创建地图障碍物(Create Map Obstacle)

MapAction中,我们创建方法:

        protected readonly HashSet<MapObstacle> m_Obstacles = 
            new HashSet<MapObstacle>();

        /// <summary>
        /// 创建Obstacle
        /// </summary>
        /// <param name="prefab"></param>
        /// <param name="position"></param>
        /// <param name="cmdError"></param>
        /// <returns></returns>
        public ActionStatus ObjectCommandCreateObstacle(
            string prefab, 
            Vector3Int position, 
            out string cmdError)
        {
            // TODO

            cmdError = null;
            return ActionStatus.Continue;
        }

提示:这些方法是在地图事件之前完成的,所以返回值使用了ActionStatus,这里使用bool也可以,下同。

首先,我们应该检测其合法性:

  • 位置必须在地图内;

  • 位置网格中不能存在其它地图对象;

  • 创建的prefab必须是障碍物。

            CellData cellData = map.GetCellData(position);
            if (cellData == null)
            {
                cmdError = string.Format(
                    "{0} Create Obstacle -> position `{1}` is out of range. prefab: {2}",
                    "ObjectExecutor",
                    position.ToString(),
                    prefab);
                return ActionStatus.Error;
            }

            if (cellData.hasMapObject)
            {
                cmdError = string.Format(
                    "{0} Create Obstacle -> the object in position `{1}` is already exist. prefab: {2}",
                    "ObjectExecutor",
                    position.ToString(),
                    prefab);
                return ActionStatus.Error;
            }

            MapObject mapObject = map.CreateMapObject(prefab, position);
            if (mapObject == null || mapObject.mapObjectType != MapObjectType.Obstacle)
            {
                cmdError = string.Format(
                    "{0} Create Obstacle -> create map object error. prefab: {1}",
                    "ObjectExecutor",
                    prefab);

                if (mapObject != null)
                {
                    ObjectPool.DespawnUnsafe(mapObject.gameObject, true);
                }
                return ActionStatus.Error;
            }

最后,将它添加到我们的集合中,并返回结果:

            m_Obstacles.Add(mapObject as MapObstacle);

            cellData.mapObject = mapObject;
            cmdError = null;
            return ActionStatus.Continue;

3.2 创建地图职业(Create Map Class)

在创建地图职业时,我们需知:

  • 关于等级与物品,在独有角色时我们放在了CharacterInfoConfig中,而杂兵角色我们将放在这里;

  • 独有角色时,id表示角色id,而在杂兵角色时,它是指职业id;

  • 玩家阵营不应出现杂兵角色。

创建方法:

        /// <summary>
        /// 创建MapClass
        /// </summary>
        /// <param name="attitudeTowards"></param>
        /// <param name="roleType"></param>
        /// <param name="id"></param>
        /// <param name="position"></param>
        /// <param name="level"></param>
        /// <param name="items"></param>
        /// <param name="cmdError"></param>
        /// <returns></returns>
        public ActionStatus ObjectCommandCreateClass(
            AttitudeTowards attitudeTowards,
            RoleType roleType,
            int id,
            Vector3Int position,
            int level,
            int[] items,
            out string cmdError)
        {
            if (roleType == RoleType.Following && attitudeTowards == AttitudeTowards.Player)
            {
                cmdError = string.Format(
                    "{0} Create Class -> role type of player can only be `Unique`",
                    "ObjectExecutor");
                return ActionStatus.Error;
            }

            CellData cellData = map.GetCellData(position);
            if (cellData == null)
            {
                cmdError = string.Format(
                    "{0} Create Class -> position `{1}` is out of range. id: {2}",
                    "ObjectExecutor",
                    position.ToString(),
                    id.ToString());
                return ActionStatus.Error;
            }

            if (cellData.hasMapObject)
            {
                cmdError = string.Format(
                    "{0} Create Class -> the object in position `{1}` is already exist. id: {2}",
                    "ObjectExecutor",
                    position.ToString(),
                    id.ToString());
                return ActionStatus.Error;
            }

            // TODO
        }

然后,关于prefab也同上,独有角色和杂兵角色是不同的:

            RoleModel model = ModelManager.models.Get<RoleModel>();
            string prefab;
            if (roleType == RoleType.Unique)
            {
                prefab = model.GetOrCreateClass(model.GetOrCreateCharacter(id).info.classId).info.prefab;
            }
            else
            {
                prefab = model.GetOrCreateClass(id).info.prefab;
            }

之后,是创建并读取角色:

            MapObject mapObject = map.CreateMapObject(prefab, position);
            if (mapObject == null)
            {
                cmdError = string.Format(
                    "{0} Create Class -> create map object error",
                    "ObjectExecutor");
                return ActionStatus.Error;
            }

            if (mapObject.mapObjectType != MapObjectType.Class)
            {
                cmdError = string.Format(
                    "{0} Create Class -> create map object error, type error. id: {1}",
                    "ObjectExecutor",
                    id.ToString());
                ObjectPool.DespawnUnsafe(mapObject.gameObject, true);
                return ActionStatus.Error;
            }

            MapClass mapCls = mapObject as MapClass;
            if (!mapCls.Load(id, roleType))
            {
                cmdError = string.Format(
                    "{0} Run -> load role error. id: {1}, role type: {2}.",
                    "ObjectExecutor",
                    id.ToString(),
                    roleType.ToString());
                return ActionStatus.Error;
            }

还不要忘记设置其阵营所属,和改变颜色:

            mapCls.role.attitudeTowards = attitudeTowards;
            mapCls.swapper.SwapColors(GetSwapperColorName(attitudeTowards));
            if (roleType == RoleType.Following)
            {
                mapCls.role.level = Mathf.Max(1, level);
                
                if (items != null && items.Length > 0)
                {
                    ItemModel itemModel = ModelManager.models.Get<ItemModel>();
                    for (int i = 0; i < items.Length; i++)
                    {
                        mapCls.role.AddItem(itemModel.CreateItem(items[i]));
                    }
                }
            }

最后,添加到我们的集合中,并返回:

            List<MapClass> classes;
            if (!m_UnitDict.TryGetValue(attitudeTowards, out classes))
            {
                classes = new List<MapClass>();
                m_UnitDict.Add(attitudeTowards, classes);
            }

            classes.Add(mapCls);

            cellData.mapObject = mapCls;
            cmdError = null;
            return ActionStatus.Continue;

有了这个方法后,我们来创建帮助方法:

        /// <summary>
        /// 为独有角色创建MapClass
        /// </summary>
        /// <param name="attitudeTowards"></param>
        /// <param name="id"></param>
        /// <param name="position"></param>
        /// <param name="cmdError"></param>
        /// <returns></returns>
        public ActionStatus ObjectCommandCreateClassUnique(
            AttitudeTowards attitudeTowards, 
            int id, 
            Vector3Int position, 
            out string cmdError)
        {
            return ObjectCommandCreateClass(
                attitudeTowards,
                RoleType.Unique,
                id,
                position,
                0,
                null,
                out cmdError);
        }

        /// <summary>
        /// 为部下创建MapClass
        /// </summary>
        /// <param name="attitudeTowards"></param>
        /// <param name="id"></param>
        /// <param name="position"></param>
        /// <param name="level"></param>
        /// <param name="items"></param>
        /// <param name="cmdError"></param>
        /// <returns></returns>
        public ActionStatus ObjectCommandCreateClassFollowing(
            AttitudeTowards attitudeTowards,
            int id,
            Vector3Int position,
            int level,
            int[] items,
            out string cmdError)
        {
            return ObjectCommandCreateClass(
                attitudeTowards,
                RoleType.Following,
                id,
                position,
                level,
                items,
                out cmdError);
        }

3.3 创建对象结果(Create Object Result)

我们在有了创建方法之后,就可以添加参数了:

            [Serializable]
            public abstract class MapObjectInfo
            {
                [XmlIgnore]
                public abstract MapObjectType objectType { get; }

                [XmlAttribute]
                public int x;
                [XmlAttribute]
                public int y;
            }

            [Serializable]
            public class ObstacleInfo : MapObjectInfo
            {
                [XmlIgnore]
                public override MapObjectType objectType
                {
                    get { return MapObjectType.Obstacle; }
                }

                [XmlAttribute]
                public string prefab;
            }

            [Serializable]
            public abstract class ClassInfo : MapObjectInfo
            {
                [XmlIgnore]
                public override MapObjectType objectType
                {
                    get { return MapObjectType.Class; }
                }

                [XmlAttribute]
                public AttitudeTowards attitudeTowards;

                [XmlIgnore]
                public abstract RoleType roleType { get; }

                [XmlAttribute]
                public int id;
            }

            [Serializable]
            public class ClassUniqueInfo : ClassInfo
            {
                [XmlIgnore]
                public override RoleType roleType
                {
                    get { return RoleType.Unique; }
                }
            }

            [Serializable]
            public class ClassFollowingInfo : ClassInfo
            {
                [XmlIgnore]
                public override RoleType roleType
                {
                    get { return RoleType.Following; }
                }

                [XmlAttribute]
                public int level;
                [XmlAttribute]
                public int[] items;
            }

这样,我们的主类:

        [Serializable]
        public class CreateObjectResult : Result
        {
            [XmlElement("Obstacle", typeof(ObstacleInfo))]
            [XmlElement("ClassUnique", typeof(ClassUniqueInfo))]
            [XmlElement("ClassFollowing", typeof(ClassFollowingInfo))]
            public MapObjectInfo[] objects;

            [XmlIgnore]
            public string error;

            [XmlIgnore]
            public override MapEventResultType type
            {
                get { return MapEventResultType.CreateObjectResult; }
            }

            public override bool Trigger(MapAction action)
            {
                if (objects == null || objects.Length == 0)
                {
                    return true;
                }

                ActionStatus status = ActionStatus.Continue;
                for (int i = 0; i < objects.Length; i++)
                {
                    if (objects[i].objectType == MapObjectType.Obstacle)
                    {
                        ObstacleInfo info = objects[i] as ObstacleInfo;
                        status = action.ObjectCommandCreateObstacle(
                            info.prefab,
                            new Vector3Int(info.x, info.y, 0),
                            out error);
                    }
                    else if (objects[i].objectType == MapObjectType.Class)
                    {
                        ClassInfo info = objects[i] as ClassInfo;
                        if (info.roleType == RoleType.Unique)
                        {
                            status = action.ObjectCommandCreateClassUnique(
                                info.attitudeTowards,
                                info.id,
                                new Vector3Int(info.x, info.y, 0),
                                out error);
                        }
                        else
                        {
                            status = action.ObjectCommandCreateClassFollowing(
                                info.attitudeTowards,
                                info.id,
                                new Vector3Int(info.x, info.y, 0),
                                (info as ClassFollowingInfo).level,
                                (info as ClassFollowingInfo).items,
                                out error);
                        }
                    }
                    else
                    {
                        error = "CreateObjectResult -> object type can not be `Cursor` or `MouseCursor`.";
                    }

                    if (status == ActionStatus.Error)
                    {
                        return false;
                    }
                }

                return true;
            }
        }

4 位置结果(Position Result)

        [Serializable]
        public class PositionResult : Result
        {
            [XmlAttribute]
            public int x;
            [XmlAttribute]
            public int y;

            [XmlIgnore]
            public sealed override MapEventResultType type
            {
                get { return MapEventResultType.PositionResult; }
            }

            public override bool Trigger(MapAction action)
            {
                if (action.selectedUnit == null)
                {
                    return false;
                }

                Vector3Int position = new Vector3Int(x, y, 0);
                CellData cellData = action.map.GetCellData(position);
                if (cellData == null || cellData.hasMapObject)
                {
                    return false;
                }

                action.selectedCell.mapObject = null;
                action.selectedUnit.UpdatePosition(position);
                cellData.mapObject = action.selectedUnit;

                return true;
            }
        }

5 属性结果(Property Result)

        [Serializable]
        public class PropertyResult : Result
        {
            [XmlAttribute]
            public FightPropertyType propertyType;
            [XmlAttribute]
            public int value;

            [XmlIgnore]
            public sealed override MapEventResultType type
            {
                get { return MapEventResultType.PropertyResult; }
            }

            public override bool Trigger(MapAction action)
            {
                if (action.selectedUnit == null)
                {
                    return false;
                }

                Role role = action.selectedUnit.role;
                role.AddFightProperty(propertyType, value);

                return true;
            }
        }

6 物品结果(Item Result)

        [Serializable]
        public class ItemResult : Result
        {
            [XmlAttribute]
            public int characterId = -1;
            [XmlAttribute]
            public bool give;
            [XmlAttribute]
            public int id;

            [XmlIgnore]
            public sealed override MapEventResultType type
            {
                get { return MapEventResultType.ItemResult; }
            }

            public override bool Trigger(MapAction action)
            {
                Role role;
                if (characterId < 0)
                {
                    if (action.selectedUnit == null)
                    {
                        return false;
                    }
                    role = action.selectedUnit.role;
                }
                else
                {
                    RoleModel roleModel = ModelManager.models.Get<RoleModel>();
                    if (!roleModel.ContainsKey(characterId))
                    {
                        return false;
                    }

                    role = roleModel.GetOrCreateRole(characterId, RoleType.Unique);
                }
                
                ItemModel itemModel = ModelManager.models.Get<ItemModel>();

                if (give)
                {
                    Item item = itemModel.CreateItem(id);
                    int index = role.AddItem(item);
                    if (index == -1)
                    {
                        item.Dispose();
                        return false;
                    }
                }
                else
                {
                    int index = -1;
                    for (int i = 0; i < role.items.Length; i++)
                    {
                        if (role.items[i] != null && role.items[i].itemId == id)
                        {
                            index = i;
                            break;
                        }
                    }

                    if (index == -1)
                    {
                        return false;
                    }

                    Item item = role.RemoveItem(index);
                    item.Dispose();
                }

                return true;
            }
        }

7 胜利结果与失败结果(Win/Lose Result)

MapAction中,我们添加一个结束战斗的方法:

        /// <summary>
        /// 游戏结束
        /// </summary>
        public void MapEndCommand(int result)
        {
            if (m_EventCoroutine != null)
            {
                GameDirector.instance.StopCoroutine(m_EventCoroutine);
                m_EventCoroutine = null;
            }
            GameDirector.instance.StopGameAction();

            if (previous != null)
            {
                ScenarioBlackboard.Set(ScenarioBlackboard.battleMapScene, result);
                (previous as ScenarioAction).BattleMapDone(error);
                GameDirector.instance.BackGameAction();

                MessageCenter.AddListener(GameMain.k_Event_OnSceneLoaded, MapEndCommand_OnSceneLoaded);
                if (string.IsNullOrEmpty(nextScene))
                {
                    GameMain.instance.LoadSceneAsync(ScenarioBlackboard.lastScenarioScene, mode: LoadSceneMode.Additive);
                }
                else
                {
                    GameMain.instance.LoadSceneAsync(nextScene, mode: LoadSceneMode.Additive);
                }
            }
            else
            {
                Debug.LogError("MapAction -> no previous game action.");
                GameDirector.instance.BackGameAction();
            }
        }

        private void MapEndCommand_OnSceneLoaded(string message, object sender, MessageArgs messageArgs, params object[] messageParams)
        {
            MessageCenter.RemoveListener(GameMain.k_Event_OnSceneLoaded, MapEndCommand_OnSceneLoaded);

            OnSceneLoadedArgs args = messageArgs as OnSceneLoadedArgs;
            GameMain.instance.SetActiveScene(args.scene.name);
            GameMain.instance.UnloadSceneAsync(ScenarioBlackboard.battleMapScene);
            ScenarioBlackboard.battleMapScene = string.Empty;
            ScenarioBlackboard.mapScript = string.Empty;

            GameDirector.instance.RunGameAction();
        }

这样,我们触发:

        [Serializable]
        public class WinResult : Result
        {
            public sealed override MapEventResultType type
            {
                get { return MapEventResultType.WinResult; }
            }

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

        [Serializable]
        public class LoseResult : Result
        {
            public sealed override MapEventResultType type
            {
                get { return MapEventResultType.LoseResult; }
            }

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

猜你喜欢

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