浏览代码

Melee Improved. Hit Reacts and bugfixes

-- added HitReact state to CharacterSetController (triggered by "BeginHitReact")
    -- Made everybody have a ClientCharacter component.
    -- Added a ClientCharacter->ClientCharacterVisualization reference, so that you can
       get the visual GameObject from a NetworkId.
    -- Fixed issue where placed Boss wasn't on the right Layer, making him invincible.
    -- Hooked up some very preliminary HitReact logic in ClientCharacterVisualization (will be refactored shortly as part of ActionVisualizer).
    -- ChaseAction now rotates you to target even if you don't move.
    -- ChaseAction no longer "inches forward" every time you attack.
    -- MeleeAction now does a preliminary DetectFoe in Start. There's a lengthy comment explaining this, for educational purposes.
    -- The TANK_BASEATTACK range has been tuned to look better.
    -- ActionRequestData now checks which fields are "non-default" and uses that to populate a flags byte,...
/main
GitHub 3 年前
当前提交
bd7c4caf
共有 11 个文件被更改,包括 520 次插入94 次删除
  1. 221
      Assets/BossRoom/Models/CharacterSetController.controller
  2. 13
      Assets/BossRoom/Prefabs/Enemy.prefab
  3. 13
      Assets/BossRoom/Prefabs/Imp.prefab
  4. 13
      Assets/BossRoom/Prefabs/Player.prefab
  5. 2
      Assets/BossRoom/Scenes/DungeonTest.unity
  6. 56
      Assets/BossRoom/Scripts/Client/ClientCharacterVisualization.cs
  7. 10
      Assets/BossRoom/Scripts/Client/Game/Character/ClientCharacter.cs
  8. 40
      Assets/BossRoom/Scripts/Server/Game/Action/ChaseAction.cs
  9. 79
      Assets/BossRoom/Scripts/Server/Game/Action/MeleeAction.cs
  10. 91
      Assets/BossRoom/Scripts/Shared/Game/Action/ActionRequestData.cs
  11. 76
      .editorconfig

221
Assets/BossRoom/Models/CharacterSetController.controller


%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1102 &-9059899351118470251
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Nothing
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: -6270680072761177303}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 0}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!206 &-8988982740543935465
BlendTree:
m_ObjectHideFlags: 1

m_UseAutomaticThresholds: 1
m_NormalizedBlendValues: 0
m_BlendType: 0
--- !u!1102 &-7345246596832709857
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: HitReact
m_Speed: 1
m_CycleOffset: 0
m_Transitions:
- {fileID: 4957634337843930872}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 7894786494464805379, guid: 2115c4661f55eff45a5a0f91fc0a12f0, type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &-6270680072761177303
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: BeginHitReact
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -7345246596832709857}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0
m_TransitionOffset: 0
m_ExitTime: 2.5632382e-10
m_HasExitTime: 1
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &-5808022421118276268
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: BeginHitReact
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 0}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.25
m_TransitionOffset: 0
m_ExitTime: 0.75
m_HasExitTime: 1
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &-5603320215398779609
AnimatorStateTransition:
m_ObjectHideFlags: 1

m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 0}
- m_Name: BeginHitReact
m_Type: 9
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 0}
m_AnimatorLayers:
- serializedVersion: 5
m_Name: Base Layer

m_IKPass: 0
m_SyncedLayerAffectsTiming: 0
m_Controller: {fileID: 9100000}
- serializedVersion: 5
m_Name: HitReacts
m_StateMachine: {fileID: 3532665691827284609}
m_Mask: {fileID: 0}
m_Motions: []
m_Behaviours: []
m_BlendingMode: 1
m_SyncedLayerIndex: -1
m_DefaultWeight: 1
m_IKPass: 0
m_SyncedLayerAffectsTiming: 0
m_Controller: {fileID: 9100000}
--- !u!1107 &2782526268344220699
AnimatorStateMachine:
serializedVersion: 6

m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1107 &3532665691827284609
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: HitReacts
m_ChildStates:
- serializedVersion: 1
m_State: {fileID: -9059899351118470251}
m_Position: {x: 280, y: 70, z: 0}
- serializedVersion: 1
m_State: {fileID: -7345246596832709857}
m_Position: {x: 290, y: -30, z: 0}
m_ChildStateMachines: []
m_AnyStateTransitions: []
m_EntryTransitions: []
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: -9059899351118470251}
--- !u!1101 &4660115808215194994
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 6
m_ConditionEvent: AttackID
m_EventTreshold: 1
- m_ConditionMode: 1
m_ConditionEvent: BeginAttack
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 6299315480180736668}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.25
m_TransitionOffset: 0
m_ExitTime: 0.75
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &4957634337843930872
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions: []
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -9059899351118470251}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.25
m_TransitionOffset: 0
m_ExitTime: 0.5
m_HasExitTime: 1
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &5593412509193387231
AnimatorStateTransition:
m_ObjectHideFlags: 1

m_Position: {x: 240, y: 90, z: 0}
- serializedVersion: 1
m_State: {fileID: 6299315480180736668}
m_Position: {x: 40, y: 220, z: 0}
m_Position: {x: 250, y: 240, z: 0}
m_ChildStateMachines: []
m_AnyStateTransitions: []
m_EntryTransitions: []

m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: -1068572555509609370}
--- !u!1101 &8468659891928091302
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions: []
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -1068572555509609370}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.25
m_TransitionOffset: 0
m_ExitTime: 0.5
m_HasExitTime: 1
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1

13
Assets/BossRoom/Prefabs/Enemy.prefab


- component: {fileID: -7347463593668903068}
- component: {fileID: -2661490869309078486}
- component: {fileID: -3025025970778375211}
- component: {fileID: -4759928719674634496}
m_Layer: 6
m_Name: Enemy
m_TagString: Untagged

m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 797d92969c575574d868e069887e8486, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &-4759928719674634496
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4340979889802297118}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ae9a06a3585027044ababe9f9cb7b8e6, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1001 &1692449221005613434

13
Assets/BossRoom/Prefabs/Imp.prefab


- component: {fileID: 1241790024788381460}
- component: {fileID: 5774553290016453722}
- component: {fileID: 6717078508665899941}
- component: {fileID: 3887528011308980450}
m_Layer: 6
m_Name: Imp
m_TagString: Untagged

m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 797d92969c575574d868e069887e8486, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &3887528011308980450
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3966945101575016302}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ae9a06a3585027044ababe9f9cb7b8e6, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &4344372015230948628

13
Assets/BossRoom/Prefabs/Player.prefab


- component: {fileID: 6970334877957368017}
- component: {fileID: 4602672899881656135}
- component: {fileID: 7690172137830037487}
- component: {fileID: -909640835356089789}
m_Layer: 3
m_Name: Player
m_TagString: Player

m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 797d92969c575574d868e069887e8486, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &-909640835356089789
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4600110157238723781}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ae9a06a3585027044ababe9f9cb7b8e6, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1001 &7872000410579295758

2
Assets/BossRoom/Scenes/DungeonTest.unity


objectReference: {fileID: 0}
- target: {fileID: 4340979889802297118, guid: 5f8daaa54415e5e41b5162cd13121359, type: 3}
propertyPath: m_Layer
value: 7
value: 6
objectReference: {fileID: 0}
- target: {fileID: 4578434794519654631, guid: 5f8daaa54415e5e41b5162cd13121359, type: 3}
propertyPath: m_Layer

56
Assets/BossRoom/Scripts/Client/ClientCharacterVisualization.cs


using Cinemachine;
using Cinemachine;
namespace BossRoom.Visual
{

[SerializeField]
private Animator m_ClientVisualsAnimator;
public Animator OurAnimator { get { return m_ClientVisualsAnimator; } }
private CinemachineVirtualCamera m_MainCamera;
private Transform m_Parent;

private const float MAX_VIZ_SPEED = 4; //max speed at which we will chase the parent transform.
private const float MAX_ROT_SPEED = 280; //max angular speed at which we will rotate, in degrees/second.
private const float k_MaxVizSpeed = 4; //max speed at which we will chase the parent transform.
private const float x_MaxRotSpeed = 280; //max angular speed at which we will rotate, in degrees/second.
/// <inheritdoc />
public override void NetworkStart()

//we want to follow our parent on a spring, which means it can't be directly in the transform hierarchy.
m_Parent = transform.parent;
m_Parent.GetComponent<BossRoom.Client.ClientCharacter>().ChildVizObject = this;
transform.parent = null;
if (IsLocalPlayer)

}
private void PerformActionFX(ActionRequestData data )
private void PerformActionFX(ActionRequestData data)
if (data.TargetIds != null && data.TargetIds.Length > 0)
{
NetworkedObject targetObject = MLAPI.Spawning.SpawnManager.SpawnedObjects[data.TargetIds[0]];
if (targetObject != null)
{
var targetAnimator = targetObject.GetComponent<BossRoom.Client.ClientCharacter>().ChildVizObject.OurAnimator;
if (targetAnimator != null)
{
targetAnimator.SetTrigger("BeginHitReact");
}
}
}
}
void Update()

}
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0 && m_MainCamera )
if (scroll != 0 && m_MainCamera)
{
ZoomCamera(scroll);
}

private void SmoothMove()
{
var pos_diff = m_Parent.transform.position - transform.position;
var angle_diff = Quaternion.Angle(m_Parent.transform.rotation, transform.rotation);
var posDiff = m_Parent.transform.position - transform.position;
var angleDiff = Quaternion.Angle(m_Parent.transform.rotation, transform.rotation);
float time_delta = Time.deltaTime;
float timeDelta = Time.deltaTime;
float pos_diff_mag = pos_diff.magnitude;
if( pos_diff_mag > 0 )
float posDiffMag = posDiff.magnitude;
if (posDiffMag > 0)
float max_move = time_delta * MAX_VIZ_SPEED;
float move_dist = Mathf.Min(max_move, pos_diff_mag);
pos_diff *= (move_dist / pos_diff_mag);
float maxMove = timeDelta * k_MaxVizSpeed;
float moveDist = Mathf.Min(maxMove, posDiffMag);
posDiff *= (moveDist / posDiffMag);
transform.position += pos_diff;
transform.position += posDiff;
if( angle_diff > 0 )
if (angleDiff > 0)
float max_angle_move = time_delta * MAX_ROT_SPEED;
float angle_move = Mathf.Min(max_angle_move, angle_diff);
float t = angle_move / angle_diff;
float maxAngleMove = timeDelta * x_MaxRotSpeed;
float angleMove = Mathf.Min(maxAngleMove, angleDiff);
float t = angleMove / angleDiff;
transform.rotation = Quaternion.Slerp(transform.rotation, m_Parent.transform.rotation, t);
}
}

var cameraGO = GameObject.FindGameObjectWithTag("CMCamera");
if( cameraGO == null ) { return; }
if (cameraGO == null) { return; }
m_MainCamera = cameraGO.GetComponent<CinemachineVirtualCamera>();
if (m_MainCamera)

}
}
}
}
}

10
Assets/BossRoom/Scripts/Client/Game/Character/ClientCharacter.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BossRoom.Client

{
//!!STUB. Client Character gamelogic will go here.
/// <summary>
/// The Vizualization GameObject isn't in the same transform hierarchy as the object, but it registers itself here
/// so that the visual GameObject can be found from a NetworkId.
/// </summary>
public BossRoom.Visual.ClientCharacterVisualization ChildVizObject { get; set; }
if( !IsClient ) { this.enabled = false; }
if (!IsClient) { this.enabled = false; }
}
}

40
Assets/BossRoom/Scripts/Server/Game/Action/ChaseAction.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine;
namespace BossRoom.Server
{

{
if (!HasValidTarget())
{
Debug.Log("Failed to start ChaseAction. The target entity wasn't submitted or doesn't exist anymore" );
Debug.Log("Failed to start ChaseAction. The target entity wasn't submitted or doesn't exist anymore");
return false;
}

m_Movement.SetMovementTarget(m_Target.transform.position);
if (StopIfDone())
{
m_Parent.transform.LookAt(m_CurrentTargetPos); //even if we didn't move, snap to face the target!
return false;
}
m_Movement.SetMovementTarget(m_Target.transform.position);
return true;
}

/// </summary>
private bool HasValidTarget()
{
return m_Data.TargetIds != null &&
m_Data.TargetIds.Length > 0 &&
return m_Data.TargetIds != null &&
m_Data.TargetIds.Length > 0 &&
/// Called each frame while the action is running.
/// Tests to see if we've reached our target. Returns true if we've reached our target, false otherwise (in which case it also stops our movement).
/// <returns>true to keep running, false to stop. The Action will stop by default when its duration expires, if it has a duration set. </returns>
public override bool Update()
private bool StopIfDone()
if( (m_Data.Amount*m_Data.Amount) > distToTarget2 )
if ((m_Data.Amount * m_Data.Amount) > distToTarget2)
return false;
return true;
return false;
}
/// <summary>
/// Called each frame while the action is running.
/// </summary>
/// <returns>true to keep running, false to stop. The Action will stop by default when its duration expires, if it has a duration set. </returns>
public override bool Update()
{
if (StopIfDone()) { return false; }
if( (m_Data.Amount*m_Data.Amount) < targetMoved2 )
if ((m_Data.Amount * m_Data.Amount) < targetMoved2)
{
//target has moved past our range tolerance. Must repath.
this.m_Movement.SetMovementTarget(m_Target.transform.position);

79
Assets/BossRoom/Scripts/Server/Game/Action/MeleeAction.cs


using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Action that represents a swing of a melee weapon. It is not explicitly targeted, but rather detects the foe that was hit with a physics check.
/// </summary>
/// <remarks>
/// Q: Why do we DetectFoe twice, once in Start, once when we actually connect?
/// A: The weapon swing doesn't happen instantaneously. We want to broadcast the action to other clients as fast as possible to minimize latency,
/// but this poses a conundrum. At the moment the swing starts, you don't know for sure if you've hit anybody yet. There are a few possible resolutions to this:
/// 1. Do the DetectFoe operation once--in Start.
/// Pros: Simple! Only one physics cast per swing--saves on perf.
/// Cons: Is unfair. You can step out of the swing of an attack, but no matter how far you go, you'll still be hit. The reverse is also true--you can
/// "step into an attack", and it won't affect you. This will feel terrible to the attacker.
/// 2. Do the DetectFoe operation once--in Update. Send a separate RPC to the targeted entity telling it to play its hit react.
/// Pros: Always shows the correct behavior. The entity that gets hit plays its hit react (if any).
/// Cons: You need another RPC. Adds code complexity and bandwidth. You also don't have enough information when you start visualizing the swing on
/// the client to do any intelligent animation handshaking. If your server->client latency is even a little uneven, your "attack" animation
/// won't line up correctly with the hit react, making combat look floaty and disjointed.
/// 3. Do the DetectFoe operation twice, once in Start and once in Update.
/// Pros: Is fair--you do the hit-detect at the moment of the swing striking home. And will generally play the hit react on the right target.
/// Cons: Requires more complicated visualization logic. The initial broadcast foe can only ever be treated as a "hint". The graphics logic
/// needs to do its own range checking to pick the best candidate to play the hit react on.
///
/// As so often happens in networked games (and games in general), there's no perfect solution--just sets of tradeoffs. For our example, we're showing option "3".
/// </remarks>
//cache Physics Cast hits, to minimize allocs.
private static RaycastHit[] s_Hits;
private const int k_MaxDetects = 4;
private ulong m_ProvisionalTarget;
if (s_Hits == null)
{
s_Hits = new RaycastHit[k_MaxDetects];
}
ServerCharacter foe = DetectFoe();
if (foe != null)
{
m_ProvisionalTarget = foe.NetworkId;
Data.TargetIds = new ulong[] { foe.NetworkId };
}
m_Parent.NetState.ServerBroadcastAction(ref Data);
return true;
}

if( !m_ExecFired && (Time.time-TimeStarted) >= Description.ExecTime_s )
if (!m_ExecFired && (Time.time - TimeStarted) >= Description.ExecTime_s)
var foe = DetectFoe();
if(foe != null )
var foe = DetectFoe(m_ProvisionalTarget);
if (foe != null)
return true;
return true;
}

/// </summary>
/// <returns></returns>
private ServerCharacter DetectFoe()
private ServerCharacter DetectFoe(ulong foeHint = 0)
var my_bounds = this.m_Parent.GetComponent<Collider>().bounds;
RaycastHit hit;
var myBounds = this.m_Parent.GetComponent<Collider>().bounds;
if( Physics.BoxCast(m_Parent.transform.position, my_bounds.extents, m_Parent.transform.forward, out hit, Quaternion.identity,
Description.Range, mask ))
int numResults = Physics.BoxCastNonAlloc(m_Parent.transform.position, myBounds.extents,
m_Parent.transform.forward, s_Hits, Quaternion.identity, Description.Range, mask);
if (numResults == 0) { return null; }
//everything that passes the mask should have a ServerCharacter component.
ServerCharacter foundFoe = s_Hits[0].collider.GetComponent<ServerCharacter>();
//we always prefer the hinted foe. If he's still in range, he should take the damage, because he's who the client visualization
//system will play the hit-react on (in case there's any ambiguity).
for (int i = 0; i < numResults; i++)
var foe_character = hit.collider.GetComponent<ServerCharacter>();
return foe_character;
var serverChar = s_Hits[i].collider.GetComponent<ServerCharacter>();
if (serverChar.NetworkId == foeHint)
{
foundFoe = serverChar;
break;
}
return null;
return foundFoe;
}
}
}

91
Assets/BossRoom/Scripts/Shared/Game/Action/ActionRequestData.cs


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

}
/// <summary>
/// metadata about each kind of ActionLogic. This basically just informs us what fields to serialize for each kind of ActionLogic.
/// </summary>
public class ActionLogicInfo
{
public bool HasPosition;
public bool HasDirection;
public bool HasTarget;
public bool HasAmount;
}
/// <summary>
public static Dictionary<ActionLogic, ActionLogicInfo> LogicInfos = new Dictionary<ActionLogic, ActionLogicInfo>
{
{ActionLogic.MELEE, new ActionLogicInfo{} },
{ActionLogic.RANGED, new ActionLogicInfo{HasDirection=true} },
{ActionLogic.RANGEDTARGETED, new ActionLogicInfo{HasTarget=true} },
{ActionLogic.CHASE, new ActionLogicInfo{HasTarget=true, HasAmount=true} },
};
{new ActionDescription{Logic=ActionLogic.MELEE, Amount=10, ManaCost=2, ExecTime_s=0.3f, Duration_s=0.5f, Range=4f, Anim="Todo" } }, //level 1
{new ActionDescription{Logic=ActionLogic.MELEE, Amount=15, ManaCost=2, ExecTime_s=0.3f, Duration_s=0.5f, Range=4f, Anim="Todo" } }, //level 2
{new ActionDescription{Logic=ActionLogic.MELEE, Amount=20, ManaCost=2, ExecTime_s=0.3f, Duration_s=0.5f, Range=4f, Anim="Todo" } }, //level 3
{new ActionDescription{Logic=ActionLogic.MELEE, Amount=10, ManaCost=2, ExecTime_s=0.3f, Duration_s=0.5f, Range=2f, Anim="Todo" } }, //level 1
{new ActionDescription{Logic=ActionLogic.MELEE, Amount=15, ManaCost=2, ExecTime_s=0.3f, Duration_s=0.5f, Range=2f, Anim="Todo" } }, //level 2
{new ActionDescription{Logic=ActionLogic.MELEE, Amount=20, ManaCost=2, ExecTime_s=0.3f, Duration_s=0.5f, Range=2f, Anim="Todo" } }, //level 3
}
},

}
},
{ ActionType.GENERAL_CHASE, new List<ActionDescription>
{ ActionType.GENERAL_CHASE, new List<ActionDescription>
}
}
}
};
}

//O__O Hey, are you adding something? Be sure to update ActionLogicInfo, as well as the methods below.
//[System.Flags]
private enum PackFlags
{
None = 0,
HasPosition = 1,
HasDirection = 1 << 1,
HasTargetIds = 1 << 2,
HasLevel = 1 << 3,
HasAmount = 1 << 4,
ShouldQueue = 1 << 5
//currently serialized with a byte. Change Read/Write if you add more than 8 fields.
}
private PackFlags GetPackFlags()
{
PackFlags flags = PackFlags.None;
if (Position != Vector3.zero) { flags |= PackFlags.HasPosition; }
if (Direction != Vector3.zero) { flags |= PackFlags.HasDirection; }
if (TargetIds != null) { flags |= PackFlags.HasTargetIds; }
if (Level != 0) { flags |= PackFlags.HasLevel; }
if (Amount != 0) { flags |= PackFlags.HasAmount; }
if (ShouldQueue) { flags |= PackFlags.ShouldQueue; }
return flags;
}
ShouldQueue = reader.ReadBool();
PackFlags flags = (PackFlags)reader.ReadByte();
var Logic = ActionData.ActionDescriptions[ActionTypeEnum][0].Logic;
var Info = ActionData.LogicInfos[Logic];
ShouldQueue = (flags & PackFlags.ShouldQueue) != 0;
if (Info.HasPosition)
if ((flags & PackFlags.HasPosition) != 0)
if (Info.HasDirection)
if ((flags & PackFlags.HasDirection) != 0)
if (Info.HasTarget)
if ((flags & PackFlags.HasTargetIds) != 0)
if (Info.HasAmount)
if ((flags & PackFlags.HasLevel) != 0)
{
Level = reader.ReadInt32();
}
if ((flags & PackFlags.HasAmount) != 0)
{
Amount = reader.ReadSingle();
}

{
using (var writer = MLAPI.Serialization.Pooled.PooledBitWriter.Get(stream))
{
ActionLogic Logic = ActionData.ActionDescriptions[ActionTypeEnum][0].Logic;
ActionLogicInfo Info = ActionData.LogicInfos[Logic];
PackFlags flags = GetPackFlags();
writer.WriteBool(ShouldQueue);
if (Info.HasPosition)
writer.WriteByte((byte)flags);
if ((flags & PackFlags.HasPosition) != 0)
if (Info.HasDirection)
if ((flags & PackFlags.HasDirection) != 0)
if (Info.HasTarget)
if ((flags & PackFlags.HasTargetIds) != 0)
if (Info.HasAmount)
if ((flags & PackFlags.HasLevel) != 0)
{
writer.WriteInt32(Level);
}
if ((flags & PackFlags.HasAmount) != 0)
{
writer.WriteSingle(Amount);
}

76
.editorconfig
文件差异内容过多而无法显示
查看文件

正在加载...
取消
保存