浏览代码

Simple AI based on Actions

- added AIBrain which can switch between Idle and Attack. Owned by ServerCharacter
- renamed Action enum to ActionType to prevent name conflicts with Action class
- gave NetworkingManager its own prefab (this is the only change to MainMenu)
- testing hack: press the "E" key on the server/host to spawn an enemy at 0,0,0
/main
eheimburg 4 年前
当前提交
3bee0ea3
共有 22 个文件被更改,包括 808 次插入177 次删除
  1. 1
      Assets/BossRoom/Prefabs/State/BossRoomState.prefab
  2. 226
      Assets/BossRoom/Scenes/MainMenu.unity
  3. 6
      Assets/BossRoom/Scripts/Client/ClientInputSender.cs
  4. 17
      Assets/BossRoom/Scripts/Server/Game/Action/ActionPlayer.cs
  5. 38
      Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
  6. 25
      Assets/BossRoom/Scripts/Server/Game/State/ServerBossRoomState.cs
  7. 12
      Assets/BossRoom/Scripts/Shared/Game/Action/ActionRequestData.cs
  8. 2
      Assets/BossRoom/Scripts/Shared/NetworkCharacterState.cs
  9. 75
      Assets/BossRoom/Prefabs/Enemy.prefab
  10. 7
      Assets/BossRoom/Prefabs/Enemy.prefab.meta
  11. 172
      Assets/BossRoom/Prefabs/NetworkingManager.prefab
  12. 7
      Assets/BossRoom/Prefabs/NetworkingManager.prefab.meta
  13. 8
      Assets/BossRoom/Scripts/Server/Game/AIState.meta
  14. 119
      Assets/BossRoom/Scripts/Server/Game/Character/AIBrain.cs
  15. 11
      Assets/BossRoom/Scripts/Server/Game/Character/AIBrain.cs.meta
  16. 32
      Assets/BossRoom/Scripts/Server/Game/AIState/AIState.cs
  17. 11
      Assets/BossRoom/Scripts/Server/Game/AIState/AIState.cs.meta
  18. 146
      Assets/BossRoom/Scripts/Server/Game/AIState/AttackAIState.cs
  19. 11
      Assets/BossRoom/Scripts/Server/Game/AIState/AttackAIState.cs.meta
  20. 48
      Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
  21. 11
      Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs.meta

1
Assets/BossRoom/Prefabs/State/BossRoomState.prefab


m_Name:
m_EditorClassIdentifier:
PlayerPrefab: {fileID: 4600110157238723790, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
EnemyPrefab: {fileID: 2147136737739531989, guid: febd58976027e484884aa5357092da15, type: 3}

226
Assets/BossRoom/Scenes/MainMenu.unity


m_EditorClassIdentifier:
GameHubGO: {fileID: 308991157}
InputTextGO: {fileID: 714400691}
--- !u!1 &1359040525
--- !u!1 &1359040525 stripped
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_CorrespondingSourceObject: {fileID: 5436007408952557947, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
m_PrefabInstance: {fileID: 5436007408130527606}
serializedVersion: 6
m_Component:
- component: {fileID: 1359040531}
- component: {fileID: 1359040530}
- component: {fileID: 1359040529}
- component: {fileID: 1359040528}
- component: {fileID: 1359040527}
m_Layer: 0
m_Name: NetworkingManager
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1359040527
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1359040525}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 52c005b32a68a254cbe502a4e5cb8eb6, type: 3}
m_Name:
m_EditorClassIdentifier:
appId: bc5b8b0d-edf3-4c61-9593-ce38da7f0c79
gameVersion: 0.0.0
region: EU
nickName:
roomName:
maxPlayers: 4
batchedTransportEventCode: 129
channelIdCodesStartRange: 130
attachSupportLogger: 1
sendQueueBatchSize: 4096
--- !u!114 &1359040528
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1359040525}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: db28424c2ae12f64da25c9ecccded6b1, type: 3}
m_Name:
m_EditorClassIdentifier:
Port: 7777
Address: 127.0.0.1
PingInterval: 1
DisconnectTimeout: 5
ReconnectDelay: 0.5
MaxConnectAttempts: 10
channels: []
MessageBufferSize: 5120
SimulatePacketLossChance: 0
SimulateMinLatency: 0
SimulateMaxLatency: 0
--- !u!114 &1359040529
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1359040525}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b84c2d8dfe509a34fb59e2b81f8e1319, type: 3}
m_Name:
m_EditorClassIdentifier:
MessageBufferSize: 5120
MaxConnections: 100
MaxSentMessageQueueSize: 128
ConnectAddress: 127.0.0.1
ConnectPort: 7777
ServerListenPort: 7777
ServerWebsocketListenPort: 8887
SupportWebsocket: 0
Channels: []
UseMLAPIRelay: 0
MLAPIRelayAddress: 184.72.104.138
MLAPIRelayPort: 8888
MessageSendMode: 0
--- !u!114 &1359040530
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1359040525}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3}
m_Name:
m_EditorClassIdentifier:
DontDestroy: 1
RunInBackground: 1
LogLevel: 1
NetworkConfig:
ProtocolVersion: 0
NetworkTransport: {fileID: 1359040529}
RegisteredScenes:
- MainMenu
- CharSelect
- SampleScene
- DungeonTest
AllowRuntimeSceneChanges: 1
NetworkedPrefabs:
- Prefab: {fileID: 4600110157238723781, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
PlayerPrefab: 1
- Prefab: {fileID: 3565665953789623672, guid: 1a58a2c4657fe6d4890d9ad39f43894e, type: 3}
PlayerPrefab: 0
- Prefab: {fileID: 297185343939699586, guid: 8b9c63e7d70c5ff48a03aad51e17103c, type: 3}
PlayerPrefab: 0
PlayerPrefabHash:
id: 0
CreatePlayerPrefab: 0
ReceiveTickrate: 0
MaxReceiveEventsPerTickRate: 1000
EventTickrate: 100
ClientConnectionBufferTimeout: 10
ConnectionApproval: 1
ConnectionData:
SecondsHistory: 0
EnableTimeResync: 0
TimeResyncInterval: 30
EnableNetworkedVar: 1
EnsureNetworkedVarLengthSafety: 0
EnableSceneManagement: 1
ForceSamePrefabs: 0
UsePrefabSync: 0
RecycleNetworkIds: 1
NetworkIdRecycleDelay: 120
RpcHashSize: 0
LoadSceneTimeOut: 120
EnableMessageBuffering: 0
MessageBufferTimeout: 20
EnableNetworkLogs: 1
EnableEncryption: 0
SignKeyExchange: 0
ServerBase64PfxCertificate:
references:
version: 1
00000000:
type: {class: NullableBoolSerializable, ns: MLAPI.Configuration, asm: Unity.Multiplayer.MLAPI.Runtime}
data:
Value: 1897319656204293034
--- !u!4 &1359040531
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1359040525}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1767010205
GameObject:
m_ObjectHideFlags: 0

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1985907275}
m_CullTransparentMesh: 1
--- !u!1001 &5436007408130527606
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_RootOrder
value: 2
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalPosition.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalPosition.z
value: -10
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557925, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5436007408952557947, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}
propertyPath: m_Name
value: NetworkingManager
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b, type: 3}

6
Assets/BossRoom/Scripts/Client/ClientInputSender.cs


{
//these two actions will queue one after the other, causing us to run over to our target and take a swing.
var chase_data = new ActionRequestData();
chase_data.ActionTypeEnum = Action.GENERAL_CHASE;
chase_data.ActionTypeEnum = ActionType.GENERAL_CHASE;
chase_data.Amount = 3f;
chase_data.TargetIds = new ulong[] { GetTargetObject(ref hit) };
m_NetworkCharacter.C2S_DoAction(ref chase_data);

hit_data.ActionTypeEnum = Action.TANK_BASEATTACK;
hit_data.ActionTypeEnum = ActionType.TANK_BASEATTACK;
data.ActionTypeEnum = Action.TANK_BASEATTACK;
data.ActionTypeEnum = ActionType.TANK_BASEATTACK;
m_NetworkCharacter.C2S_DoAction(ref data);
}

17
Assets/BossRoom/Scripts/Server/Game/Action/ActionPlayer.cs


}
/// <summary>
/// If an Action is active, fills out 'data' param and returns true. If no Action is active, returns false
/// </summary>
public bool GetActiveActionInfo(out ActionRequestData data)
{
if (m_queue.Count > 0)
{
data = m_queue[ 0 ].Data;
return true;
}
else
{
data = new ActionRequestData();
return false;
}
}
/// <summary>
/// Optionally end the currently playing action, and advance to the next Action that wants to play.
/// </summary>
/// <param name="expireFirstElement">Pass true to remove the first element and advance to the next element. Pass false to "advance" to the 0th element</param>

38
Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs


{
public NetworkCharacterState NetState { get; private set; }
[SerializeField]
[Tooltip("If enabled, this character has an AIBrain and behaves as an enemy")]
public bool IsNPC;
[SerializeField]
[Tooltip("If IsNPC, this is how far the npc can detect others (in meters)")]
public float DetectRange = 10;
private AIBrain m_aiBrain;
/// <summary>
/// Temp place to store all the active characters (to avoid having to
/// perform insanely-expensive GameObject.Find operations during Update)
/// </summary>
private static List<ServerCharacter> g_activeServerCharacters = new List<ServerCharacter>();
private void OnEnable()
{
g_activeServerCharacters.Add(this);
}
private void OnDisable()
{
g_activeServerCharacters.Remove(this);
}
public static List<ServerCharacter> GetAllActiveServerCharacters()
{
return g_activeServerCharacters;
}
// Start is called before the first frame update
void Start()

if (IsNPC)
{
m_aiBrain = new AIBrain(this, m_actionPlayer);
}
}
public override void NetworkStart()

void Update()
{
m_actionPlayer.Update();
if (m_aiBrain != null)
{
m_aiBrain.Update();
}
}
}
}

25
Assets/BossRoom/Scripts/Server/Game/State/ServerBossRoomState.cs


/// </summary>
public class ServerBossRoomState : GameStateBehaviour
{
[SerializeField] private NetworkedObject PlayerPrefab;
[SerializeField]
[Tooltip("Make sure this is included in the NetworkingManager's list of prefabs!")]
private NetworkedObject PlayerPrefab;
[SerializeField]
[Tooltip("Make sure this is included in the NetworkingManager's list of prefabs!")]
private NetworkedObject EnemyPrefab;
public override GameState ActiveState { get { return GameState.BOSSROOM; } }

{
var NewPlayer = Instantiate(PlayerPrefab);
NewPlayer.SpawnAsPlayerObject(clientId);
}
private void SpawnEnemy()
{
var NewEnemy = Instantiate(EnemyPrefab);
NewEnemy.SpawnWithOwnership(NetworkingManager.Singleton.LocalClientId);
}
/// <summary>
/// Temp code to spawn an enemy
/// </summary>
private void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
SpawnEnemy();
}
}
}
}

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


/// <summary>
/// List of all Actions supported in the game.
/// </summary>
public enum Action
public enum ActionType
{
TANK_BASEATTACK,
ARCHER_BASEATTACK,

{ActionLogic.CHASE, new ActionLogicInfo{HasTarget=true, HasAmount=true} },
};
public static Dictionary<Action, List<ActionDescription>> ActionDescriptions = new Dictionary<Action, List<ActionDescription>>
public static Dictionary<ActionType, List<ActionDescription>> ActionDescriptions = new Dictionary<ActionType, List<ActionDescription>>
{ Action.TANK_BASEATTACK , new List<ActionDescription>
{ ActionType.TANK_BASEATTACK , new List<ActionDescription>
{
{new ActionDescription{Logic=ActionLogic.MELEE, Amount=10, ManaCost=2, Duration_s=0.5f, Range=1f, Anim="Todo" } }, //level 1
{new ActionDescription{Logic=ActionLogic.MELEE, Amount=15, ManaCost=2, Duration_s=0.5f, Range=1f, Anim="Todo" } }, //level 2

{ Action.ARCHER_BASEATTACK, new List<ActionDescription>
{ ActionType.ARCHER_BASEATTACK, new List<ActionDescription>
{
{new ActionDescription{Logic=ActionLogic.RANGED, Amount=7, ManaCost=2, Duration_s=0.5f, Range=12f, Anim="Todo" } }, //Level 1
{new ActionDescription{Logic=ActionLogic.RANGED, Amount=12, ManaCost=2, Duration_s=0.5f, Range=15f, Anim="Todo" } }, //Level 2

{ Action.GENERAL_CHASE, new List<ActionDescription>
{ ActionType.GENERAL_CHASE, new List<ActionDescription>
{
{new ActionDescription{Logic=ActionLogic.CHASE } }
}

/// </summary>
public struct ActionRequestData
{
public Action ActionTypeEnum; //the action to play.
public ActionType ActionTypeEnum; //the action to play.
public Vector3 Position; //center position of skill, e.g. "ground zero" of a fireball skill.
public Vector3 Direction; //direction of skill, if not inferrable from the character's current facing.
public ulong[] TargetIds; //networkIds of targets, or null if untargeted.

2
Assets/BossRoom/Scripts/Shared/NetworkCharacterState.cs


using (PooledBitReader reader = PooledBitReader.Get(stream))
{
data.ActionTypeEnum = (Action)reader.ReadInt16();
data.ActionTypeEnum = (ActionType)reader.ReadInt16();
data.ShouldQueue = reader.ReadBool();
var Logic = ActionData.ActionDescriptions[data.ActionTypeEnum][0].Logic;

75
Assets/BossRoom/Prefabs/Enemy.prefab


%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &2457547766902456859
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 4600110157238723776, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_Enabled
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723781, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_Name
value: Enemy
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723790, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: PrefabHash
value: 7042126347811011599
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723790, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: PrefabHashGenerator
value: Enemy
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_RootOrder
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4600110157238723791, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4602672899881656135, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
propertyPath: IsNPC
value: 1
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}

7
Assets/BossRoom/Prefabs/Enemy.prefab.meta


fileFormatVersion: 2
guid: febd58976027e484884aa5357092da15
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

172
Assets/BossRoom/Prefabs/NetworkingManager.prefab


%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &5436007408952557947
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5436007408952557925}
- component: {fileID: 5436007408952557924}
- component: {fileID: 5436007408952557927}
- component: {fileID: 5436007408952557926}
- component: {fileID: 5436007408952557945}
m_Layer: 0
m_Name: NetworkingManager
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &5436007408952557925
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5436007408952557947}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &5436007408952557924
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5436007408952557947}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3}
m_Name:
m_EditorClassIdentifier:
DontDestroy: 1
RunInBackground: 1
LogLevel: 1
NetworkConfig:
ProtocolVersion: 0
NetworkTransport: {fileID: 5436007408952557927}
RegisteredScenes:
- MainMenu
- CharSelect
- SampleScene
- DungeonTest
AllowRuntimeSceneChanges: 1
NetworkedPrefabs:
- Prefab: {fileID: 4600110157238723781, guid: bb87f9bac2595f8499c048016c4b2e1d, type: 3}
PlayerPrefab: 1
- Prefab: {fileID: 3565665953789623672, guid: 1a58a2c4657fe6d4890d9ad39f43894e, type: 3}
PlayerPrefab: 0
- Prefab: {fileID: 297185343939699586, guid: 8b9c63e7d70c5ff48a03aad51e17103c, type: 3}
PlayerPrefab: 0
- Prefab: {fileID: 2147136737739531998, guid: febd58976027e484884aa5357092da15, type: 3}
PlayerPrefab: 0
PlayerPrefabHash:
id: 0
CreatePlayerPrefab: 0
ReceiveTickrate: 0
MaxReceiveEventsPerTickRate: 1000
EventTickrate: 100
ClientConnectionBufferTimeout: 10
ConnectionApproval: 1
ConnectionData:
SecondsHistory: 0
EnableTimeResync: 0
TimeResyncInterval: 30
EnableNetworkedVar: 1
EnsureNetworkedVarLengthSafety: 0
EnableSceneManagement: 1
ForceSamePrefabs: 0
UsePrefabSync: 0
RecycleNetworkIds: 1
NetworkIdRecycleDelay: 120
RpcHashSize: 0
LoadSceneTimeOut: 120
EnableMessageBuffering: 0
MessageBufferTimeout: 20
EnableNetworkLogs: 1
EnableEncryption: 0
SignKeyExchange: 0
ServerBase64PfxCertificate:
references:
version: 1
00000000:
type: {class: NullableBoolSerializable, ns: MLAPI.Configuration, asm: Unity.Multiplayer.MLAPI.Runtime}
data:
Value: 1897319656204293034
--- !u!114 &5436007408952557927
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5436007408952557947}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b84c2d8dfe509a34fb59e2b81f8e1319, type: 3}
m_Name:
m_EditorClassIdentifier:
MessageBufferSize: 5120
MaxConnections: 100
MaxSentMessageQueueSize: 128
ConnectAddress: 127.0.0.1
ConnectPort: 7777
ServerListenPort: 7777
ServerWebsocketListenPort: 8887
SupportWebsocket: 0
Channels: []
UseMLAPIRelay: 0
MLAPIRelayAddress: 184.72.104.138
MLAPIRelayPort: 8888
MessageSendMode: 0
--- !u!114 &5436007408952557926
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5436007408952557947}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: db28424c2ae12f64da25c9ecccded6b1, type: 3}
m_Name:
m_EditorClassIdentifier:
Port: 7777
Address: 127.0.0.1
PingInterval: 1
DisconnectTimeout: 5
ReconnectDelay: 0.5
MaxConnectAttempts: 10
channels: []
MessageBufferSize: 5120
SimulatePacketLossChance: 0
SimulateMinLatency: 0
SimulateMaxLatency: 0
--- !u!114 &5436007408952557945
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5436007408952557947}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 52c005b32a68a254cbe502a4e5cb8eb6, type: 3}
m_Name:
m_EditorClassIdentifier:
appId: bc5b8b0d-edf3-4c61-9593-ce38da7f0c79
gameVersion: 0.0.0
region: EU
nickName:
roomName:
maxPlayers: 4
batchedTransportEventCode: 129
channelIdCodesStartRange: 130
attachSupportLogger: 1
sendQueueBatchSize: 4096

7
Assets/BossRoom/Prefabs/NetworkingManager.prefab.meta


fileFormatVersion: 2
guid: 8f5ddd70561bc0b42bbbe5a8a155bb7b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/BossRoom/Scripts/Server/Game/AIState.meta


fileFormatVersion: 2
guid: 1f710d234d4cd2c4da99bedcd533b6d7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

119
Assets/BossRoom/Scripts/Server/Game/Character/AIBrain.cs


using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BossRoom.Server
{
/// <summary>
/// Handles enemy AI. Contains AIStateLogics that handle some of the details,
/// and has various utility functions that are called by those AIStateLogics
/// </summary>
public class AIBrain
{
private enum AIStateType
{
ATTACK,
//WANDER,
IDLE,
}
private ServerCharacter m_serverCharacter;
private ActionPlayer m_actionPlayer;
private AIStateType m_currentState;
private Dictionary<AIStateType, AIState> m_logics;
private List<ServerCharacter> m_hatedEnemies;
public AIBrain(ServerCharacter me, ActionPlayer myActionPlayer)
{
m_serverCharacter = me;
m_actionPlayer = myActionPlayer;
m_logics = new Dictionary<AIStateType, AIState>
{
[ AIStateType.IDLE ] = new IdleAIState(this),
//[ AIStateType.WANDER ] = new WanderAIState(this), // not written yet
[ AIStateType.ATTACK ] = new AttackAIState(this, m_actionPlayer),
};
m_hatedEnemies = new List<ServerCharacter>();
m_currentState = AIStateType.IDLE;
}
/// <summary>
/// Should be called by the AIBrain's owner each Update()
/// </summary>
public void Update()
{
AIStateType newState = FindBestEligibleAIState();
if (m_currentState != newState)
{
m_logics[ newState ].Initialize();
}
m_currentState = newState;
m_logics[ m_currentState ].Update();
}
private AIStateType FindBestEligibleAIState()
{
// for now we assume the AI states are in order of appropriateness,
// which may be nonsensical when there are more states
foreach (AIStateType type in Enum.GetValues(typeof(AIStateType)))
{
if (m_logics[ type ].IsEligible())
{
return type;
}
}
Debug.LogError("No AI states are valid!?!");
return AIStateType.IDLE;
}
#region Functions for AIStateLogics
/// <summary>
/// Returns true if it be appropriate for us to murder this character, starting right now!
/// </summary>
public bool IsAppropriateFoe(ServerCharacter potentialFoe)
{
if (potentialFoe == null || potentialFoe.IsNPC)
{
return false;
}
// FIXME: check for dead!
// Also, we could use NavMesh.Raycast() to see if we have line of sight to foe?
return true;
}
/// <summary>
/// Notify the AIBrain that we should consider this character an enemy.
/// </summary>
/// <param name="character"></param>
public void Hate(ServerCharacter character)
{
if (!m_hatedEnemies.Contains(character))
{
m_hatedEnemies.Add(character);
}
}
/// <summary>
/// Return the raw list of hated enemies -- treat as read-only!
/// </summary>
public List<ServerCharacter> GetHatedEnemies()
{
// first we clean the list -- remove any enemies that have disappeared (became null), are dead, etc.
m_hatedEnemies.RemoveAll(enemy => !IsAppropriateFoe(enemy));
return m_hatedEnemies;
}
/// <summary>
/// Retrieve info about who we are. Treat as read-only!
/// </summary>
/// <returns></returns>
public ServerCharacter GetMyServerCharacter()
{
return m_serverCharacter;
}
#endregion
}
}

11
Assets/BossRoom/Scripts/Server/Game/Character/AIBrain.cs.meta


fileFormatVersion: 2
guid: 25fa6461eb4b4fc4e8ec96487989fb8b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

32
Assets/BossRoom/Scripts/Server/Game/AIState/AIState.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BossRoom.Server
{
/// <summary>
/// Base class for all AIStates
/// </summary>
public abstract class AIState
{
/// <summary>
/// Indicates whether this state thinks it can become/continue to be the active state.
/// </summary>
/// <returns></returns>
public abstract bool IsEligible();
/// <summary>
/// Called once each time this state becomes the active state.
/// (This will only happen if IsEligible() has returned true for this state)
/// </summary>
public abstract void Initialize();
/// <summary>
/// Called once per frame while this is the active state. Initialize() will have
/// already been called prior to Update() being called
/// </summary>
public abstract void Update();
}
}

11
Assets/BossRoom/Scripts/Server/Game/AIState/AIState.cs.meta


fileFormatVersion: 2
guid: cebcb66dc48def84abd1cfbc995f7c6b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

146
Assets/BossRoom/Scripts/Server/Game/AIState/AttackAIState.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BossRoom.Server
{
public class AttackAIState : AIState
{
private AIBrain m_brain;
private ActionPlayer m_actionPlayer;
private ServerCharacter m_foe;
private ActionType m_curAttackAction;
public AttackAIState(AIBrain brain, ActionPlayer actionPlayer)
{
m_brain = brain;
m_actionPlayer = actionPlayer;
}
public override bool IsEligible()
{
return m_foe != null || ChooseFoe() != null;
}
public override void Initialize()
{
m_curAttackAction = ActionType.TANK_BASEATTACK;
// clear any old foe info; we'll choose a new one in Update()
m_foe = null;
}
public override void Update()
{
if (!m_brain.IsAppropriateFoe(m_foe))
{
// time for a new foe!
m_foe = ChooseFoe();
// whatever we used to be doing, stop that. New plan is coming!
m_actionPlayer.ClearActions();
}
// if we're out of foes, stop! IsEligible() will now return false so we'll soon switch to a new state
if (!m_foe)
{
return;
}
// see if we're already chasing or attacking our active foe!
if (m_actionPlayer.GetActiveActionInfo(out var info))
{
if (info.ActionTypeEnum == ActionType.GENERAL_CHASE)
{
foreach (var id in info.TargetIds)
{
if (id == m_foe.NetworkId)
{
// yep we're chasing our foe; all set! (The attack is enqueued after it)
return;
}
}
}
else if (info.ActionTypeEnum == m_curAttackAction)
{
foreach (var id in info.TargetIds)
{
if (id == m_foe.NetworkId)
{
// yep we're attacking our foe; all set!
return;
}
}
}
}
// Choose whether we can attack our foe directly, or if we need to get closer first
var attackInfo = GetCurrentAttackInfo();
Vector3 diff = m_brain.GetMyServerCharacter().transform.position - m_foe.transform.position;
if (diff.sqrMagnitude < attackInfo.Range * attackInfo.Range)
{
// yes! We are in range
var attack_data = new ActionRequestData
{
ActionTypeEnum = m_curAttackAction,
Amount = attackInfo.Amount,
ShouldQueue = false,
TargetIds = new ulong[] { m_foe.NetworkId }
};
m_actionPlayer.PlayAction(ref attack_data);
}
else
{
// we are not in range so we will need to chase them
var chase_data = new ActionRequestData
{
ActionTypeEnum = ActionType.GENERAL_CHASE,
Amount = attackInfo.Range,
ShouldQueue = false,
TargetIds = new ulong[] { m_foe.NetworkId }
};
m_actionPlayer.PlayAction(ref chase_data);
// queue up the actual attack for when we're in range
var attack_data = new ActionRequestData
{
ActionTypeEnum = m_curAttackAction,
Amount = attackInfo.Amount,
ShouldQueue = true,
TargetIds = new ulong[] { m_foe.NetworkId }
};
m_actionPlayer.PlayAction(ref attack_data);
}
}
/// <summary>
/// Picks the most appropriate foe for us to attack right now, or null if none are appropriate
/// (Currently just chooses the foe closest to us in distance)
/// </summary>
/// <returns></returns>
private ServerCharacter ChooseFoe()
{
Vector3 myPosition = m_brain.GetMyServerCharacter().transform.position;
float closestDistanceSqr = int.MaxValue;
ServerCharacter closestFoe = null;
foreach (var foe in m_brain.GetHatedEnemies())
{
float distanceSqr = (myPosition - foe.transform.position).sqrMagnitude;
if (distanceSqr < closestDistanceSqr)
{
closestDistanceSqr = distanceSqr;
closestFoe = foe;
}
}
return closestFoe;
}
private ActionDescription GetCurrentAttackInfo()
{
List<ActionDescription> actionLevels = ActionData.ActionDescriptions[ m_curAttackAction ];
int level = 0; // FIXME: pull this level from some character state var?
return actionLevels[ level ];
}
}
}

11
Assets/BossRoom/Scripts/Server/Game/AIState/AttackAIState.cs.meta


fileFormatVersion: 2
guid: 53ec60f5ec1af1549b282973368a6f19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

48
Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BossRoom.Server
{
public class IdleAIState : AIState
{
private AIBrain m_brain;
public IdleAIState(AIBrain brain)
{
m_brain = brain;
}
public override bool IsEligible()
{
return m_brain.GetHatedEnemies().Count == 0;
}
public override void Initialize()
{
}
public override void Update()
{
// while idle, we are scanning for jerks to hate
DetectFoes();
}
protected void DetectFoes()
{
float detectionRange = m_brain.GetMyServerCharacter().DetectRange;
// we are doing this check every Update, so we'll use square-magnitude distance to avoid the expensive sqrt (that's implicit in Vector3.magnitude)
float detectionRangeSqr = detectionRange * detectionRange;
Vector3 position = m_brain.GetMyServerCharacter().transform.position;
foreach (ServerCharacter character in ServerCharacter.GetAllActiveServerCharacters())
{
if (m_brain.IsAppropriateFoe(character)
&& (character.transform.position - position).sqrMagnitude <= detectionRangeSqr)
{
m_brain.Hate(character);
}
}
}
}
}

11
Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs.meta


fileFormatVersion: 2
guid: e12014cb6bdbcb54eb08524c9d9301cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存