版权声明:本文为博主原创文章,未经允许不得转载。 https://blog.csdn.net/darkrabbit/article/details/87918535
第十一章 地图动作与地图事件(Map Action and Map Event)
我们已经有了剧本,而且可以运行剧本,但我们还缺少对地图的操作控制。
我们这一章来完成地图上的操作,地图的操作将全部由MapAction
控制。
文章目录
九 触发事件与切换回合(Trigger Events and Change Turn)
光有地图事件是不行的,我们还需要知道如何触发它们。
1 触发事件方法(Trigger Events Method)
我们同样的使用一个Coroutine
来触发事件:
protected Coroutine m_EventCoroutine = null;
/// <summary>
/// 触发地图事件
/// </summary>
/// <param name="type"></param>
/// <param name="onTriggerEnd"></param>
public void TriggerEvents(MapEventConditionType type, Action onTriggerEnd)
{
mapStatus = MapStatus.Event;
if (m_EventCoroutine != null)
{
error = "MapAction TriggerEvents -> event is running.";
status = ActionStatus.Error;
Debug.LogError(error);
if (onTriggerEnd != null)
{
onTriggerEnd();
}
return;
}
m_EventCoroutine = GameDirector.instance.StartCoroutine(EventsTriggering(type, onTriggerEnd));
}
public IEnumerator EventsTriggering(MapEventConditionType type, Action onTriggerEnd)
{
yield return null;
// TODO
}
关于触发事件,我们只用可以当做入口的条件来触发相应的事件:
switch (type)
{
case MapEventConditionType.NoneCondition:
yield return m_MapEvents.TriggerStartEvents(
this, onError: eventError => error = eventError);
break;
case MapEventConditionType.TurnCondition:
yield return m_MapEvents.TriggerTurnEvents(
this, onError: eventError => error = eventError);
break;
case MapEventConditionType.PositionCondition:
if (movingEndCell != null)
{
yield return m_MapEvents.TriggerPositionEvents(
this, movingEndCell.position, onError: eventError => error = eventError);
}
break;
case MapEventConditionType.RoleDeadCondition:
if (selectedUnit != null && selectedUnit.role.isDead)
{
yield return m_MapEvents.TriggerDeadEvents(
this, selectedUnit.role.characterId, onError: eventError => error = eventError);
}
if (targetUnit != null && targetUnit.role.isDead)
{
yield return m_MapEvents.TriggerDeadEvents(
this, targetUnit.role.characterId, onError: eventError => error = eventError);
}
break;
case MapEventConditionType.RoleTalkCondition:
if (selectedUnit != null && targetUnit != null)
{
yield return m_MapEvents.TriggerRoleTalkEvents(
this, selectedUnit.role.characterId, targetUnit.role.characterId, onError: eventError => error = eventError);
}
break;
case MapEventConditionType.RoleCombatTalkCondition:
if (selectedUnit != null && targetUnit != null)
{
yield return m_MapEvents.TriggerRoleCombatTalkEvents(
this, selectedUnit.role.characterId, targetUnit.role.characterId, onError: eventError => error = eventError);
}
break;
}
m_EventCoroutine = null;
if (onTriggerEnd != null)
{
onTriggerEnd();
}
2 切换回合(Change Turn)
切换回合的方法,我们命名为NextTurn
。它需要注意几点:
-
每一个回合是指所有阵营结束,并不单指玩家;
-
阵营不是玩家时,光标应该隐藏;
-
在玩家结束后,我们需要将待机的角色都设置成非待机,且要恢复它们的移动力;
-
在切换阵营时,我们需要给出玩家提示。
基于以上,我们创建方法:
/// <summary>
/// 转换回合
/// </summary>
protected void NextTurn()
{
switch (turn)
{
case AttitudeTowards.Player:
turnToken++; // 只有所有阵营结束,回合数才+1
// 将所有玩家回复移动力,并将待机状态重置
string color = GetSwapperColorName(AttitudeTowards.Player);
foreach (MapClass unit in m_UnitDict[AttitudeTowards.Player])
{
unit.role.Holding(false);
unit.role.ResetMovePoint();
unit.swapper.SwapColors(color);
}
// 隐藏光标
DisplayMouseCursor(false);
turn = AttitudeTowards.Enemy;
GameDirector.instance.RunGameAction();
break;
case AttitudeTowards.Enemy:
turn = AttitudeTowards.Ally;
break;
case AttitudeTowards.Ally:
turn = AttitudeTowards.Neutral;
break;
case AttitudeTowards.Neutral:
turn = AttitudeTowards.Player;
break;
}
if (m_UnitDict.ContainsKey(turn))
{
OnChangeTurn(turn);
}
else
{
// 如果不存在MapClass,直接下一组回合
NextTurn();
}
}
private void OnChangeTurn(AttitudeTowards turn)
{
// TODO 给出玩家切换回合的提示,并触发回合事件
}
其中,OnChangeTurn
是在切换回合时运行,它包含:
-
给出玩家切换回合的提示;
-
触发回合事件。
2.1 切换回合面板(UI Change Turn Panel)
我们创建一个面板,让其显示切换回合的提示。
- 图 11.3 UIChangeTurnPanel
我在这里,只是单独改变文字透明度与颜色提示,你可以添加各种动画。
填充OnChangeTurn
:
private void OnChangeTurn(AttitudeTowards turn)
{
// 播放转换回合的UI动画
mapStatus = MapStatus.Animation;
UIChangeTurnPanel panel = UIManager.views.OpenView<UIChangeTurnPanel>(UINames.k_UIChangeTurnPanel, false);
panel.ChangeTurn(turn, () =>
{
UIManager.views.CloseView();
// 触发回合事件
TriggerEvents(MapEventConditionType.TurnCondition, () =>
{
if (turn == AttitudeTowards.Player)
{
// 移动光标到首个玩家单位
MoveCursorCommand(m_UnitDict[AttitudeTowards.Player][0].cellPosition, 0.5f, ClearSelected);
}
else
{
ClearSelected();
}
});
});
}
其中,MoveCursorCommand
是移动光标到首个玩家单位。
2.2 移动光标(Move Cursor)
/// <summary>
/// 移动光标
/// </summary>
/// <param name="targetPos"></param>
/// <param name="time"></param>
public void MoveCursorCommand(Vector3Int targetPos, float time, Action onMoveEnd = null)
{
if (time <= 0f)
{
map.mouseCursor.UpdatePosition(targetPos);
if (onMoveEnd != null)
{
onMoveEnd();
}
}
GameDirector.instance.StartCoroutine(MovingCursorToCell(targetPos, time, onMoveEnd));
}
private IEnumerator MovingCursorToCell(Vector3Int targetPos, float time, Action onMoveEnd)
{
mapStatus = MapStatus.Animation;
Vector3 start = map.mouseCursor.transform.position;
Vector3 end = map.GetCellPosition(targetPos);
float t = 0f;
while (t < time)
{
t += Time.deltaTime;
Vector3 pos = Vector3.Lerp(start, end, t / time);
map.mouseCursor.transform.position = pos;
yield return null;
}
map.mouseCursor.UpdatePosition(targetPos);
// 最后延迟0.5秒
yield return new WaitForSeconds(0.5f);
mapStatus = MapStatus.Normal;
if (onMoveEnd != null)
{
onMoveEnd();
}
}
2.3 补充菜单(Change Turn Button)
这个操作,我们在MapMenu_OnButtonClick
中进行补充:
case UIMapMenuPanel.MenuTextID.TurnEnd:
NextTurn();
break;
3 触发事件(Trigger Events)
我们要在需要触发事件的地方,添加触发事件的方法。
-
地图开始事件(
LoadMapEvent
)/// <summary> /// 读取事件 /// </summary> /// <param name="info"></param> /// <returns></returns> private bool LoadMapEvent(MapEventInfo info) { // 省略代码 // 执行地图加载事件 TriggerEvents(MapEventConditionType.NoneCondition, () => { OnChangeTurn(AttitudeTowards.Player); }); return true; }
-
位置事件(
HoldingMapClass
)/// <summary> /// 待机 /// </summary> /// <param name="mapClass"></param> protected void HoldingMapClass(MapClass mapClass) { // 只有玩家才会设置待机状态与颜色 if (mapClass.role.attitudeTowards == AttitudeTowards.Player) { mapClass.role.Holding(true); mapClass.swapper.SwapColors(GetSwapperHoldingColorName()); } // 如果是独有角色,触发坐标事件 if (mapClass.role.roleType == RoleType.Unique) { TriggerEvents(MapEventConditionType.PositionCondition, ClearSelected); } else { ClearSelected(); } }
-
战斗对话事件(
AttackMapClass
)/// <summary> /// 攻击 /// </summary> /// <param name="mapClass"></param> /// <param name="target"></param> protected void AttackMapClass(MapClass mapClass, MapClass target) { // 省略代码 TriggerEvents(MapEventConditionType.RoleCombatTalkCondition, () => { mapStatus = MapStatus.Animation; combatAnima.PlayAnimas(true); }); }
-
死亡事件(
MapClass_OnCombatAnimaStop
)
/// <summary>
/// 战斗结束回调
/// </summary>
/// <param name="combatAnima"></param>
/// <param name="inMap"></param>
private void MapClass_OnCombatAnimaStop(CombatAnimaController combatAnima, bool inMap)
{
// 省略代码
if (unit0.role.isDead)
{
TriggerEvents(MapEventConditionType.RoleDeadCondition, () =>
{
ClearSelected();
OnMapClassDead(unit0);
UIManager.views.CloseView();
});
}
else if (unit1.role.isDead)
{
TriggerEvents(MapEventConditionType.RoleDeadCondition, () =>
{
HoldingMapClass(unit0);
OnMapClassDead(unit1);
UIManager.views.CloseView();
});
}
else
{
HoldingMapClass(unit0);
UIManager.views.CloseView();
}
}
4 释放资源(Dispose)
同样的,我们的MapAction
还没有重写Dispose
方法。
public override void Dispose()
{
base.Dispose();
m_Status = ActionStatus.Error;
if (m_ScenarioAction != null)
{
m_ScenarioAction.Dispose();
m_ScenarioAction = null;
}
m_Turn = AttitudeTowards.Player;
m_TurnToken = 0;
m_SelectedCell = null;
m_SelectedUnit = null;
m_MapStatus = MapStatus.Normal;
m_MovingEndCell = null;
m_TargetUnit = null;
m_NpcIndex = 0;
foreach (KeyValuePair<AttitudeTowards, List<MapClass>> kvp in m_UnitDict)
{
if (kvp.Value == null)
{
continue;
}
foreach (MapClass unit in kvp.Value)
{
ObjectPool.DespawnUnsafe(unit.gameObject, true);
}
}
m_UnitDict.Clear();
foreach (MapObstacle obstacle in m_Obstacles)
{
ObjectPool.DespawnUnsafe(obstacle.gameObject, true);
}
m_Obstacles.Clear();
if (m_EventCoroutine != null)
{
GameDirector.instance.StopCoroutine(m_EventCoroutine);
m_EventCoroutine = null;
}
m_MapEvents.Clear();
if (m_Map != null)
{
m_Map.HideRangeCursors();
ObjectPool.DespawnUnsafe(m_Map.mouseCursor.gameObject);
m_Map.mapCursorPool.DestroyRecycledImmediate();
m_Map.mapObjectPool.DestroyRecycledImmediate();
m_Map = null;
}
}