From 4db20c6ea509a9686883d66ab6671943363f299d Mon Sep 17 00:00:00 2001 From: Jens Pitkanen Date: Sun, 4 Aug 2019 16:44:44 +0300 Subject: [PATCH] Add wandering behaviour --- Assets/Scenes/MainScene.unity | 204 +++++++++++++++++--- Assets/Scripts/AI/AIBehaviour.cs | 4 +- Assets/Scripts/AI/BehaviourBranch.cs | 10 +- Assets/Scripts/AI/BehaviourLeaf.cs | 12 +- Assets/Scripts/AI/BehaviourNode.cs | 2 + Assets/Scripts/AI/Behaviours/Follow.cs | 2 +- Assets/Scripts/AI/Behaviours/Wander.cs | 60 ++++++ Assets/Scripts/AI/Behaviours/Wander.cs.meta | 11 ++ Assets/Scripts/Enemy.cs | 3 + ProjectSettings/GraphicsSettings.asset | 3 + 10 files changed, 284 insertions(+), 27 deletions(-) create mode 100644 Assets/Scripts/AI/Behaviours/Wander.cs create mode 100644 Assets/Scripts/AI/Behaviours/Wander.cs.meta diff --git a/Assets/Scenes/MainScene.unity b/Assets/Scenes/MainScene.unity index e0b7b5b..ed5cbe4 100644 --- a/Assets/Scenes/MainScene.unity +++ b/Assets/Scenes/MainScene.unity @@ -184,6 +184,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f7e23822f429fcc46a8fd9163e68f332, type: 3} m_Name: m_EditorClassIdentifier: + CurrentStatus: Target: {fileID: 0} CloseEnoughRadius: 1 --- !u!114 &162528940 @@ -230,6 +231,7 @@ MonoBehaviour: m_EditorClassIdentifier: MoveSpeed: 2 BehaviourTree: {fileID: 162528941} + CurrentBehavior: Nothing --- !u!61 &162528943 BoxCollider2D: m_ObjectHideFlags: 0 @@ -285,8 +287,8 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 162528936} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 4, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalPosition: {x: 4.19, y: 1.47, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} m_Children: - {fileID: 1237792229} m_Father: {fileID: 0} @@ -1303,12 +1305,12 @@ PrefabInstance: propertyPath: MoveSpeed value: 5 objectReference: {fileID: 0} - - target: {fileID: 8489029732241002784, guid: 14a47f86f9e45da45863a81716f2378b, + - target: {fileID: 8489029732599905344, guid: 14a47f86f9e45da45863a81716f2378b, type: 3} propertyPath: m_SortingLayer value: 3 objectReference: {fileID: 0} - - target: {fileID: 8489029732599905344, guid: 14a47f86f9e45da45863a81716f2378b, + - target: {fileID: 8489029732241002784, guid: 14a47f86f9e45da45863a81716f2378b, type: 3} propertyPath: m_SortingLayer value: 3 @@ -1614,6 +1616,15 @@ Tilemap: m_TileFlags: 1 m_ColliderType: 1 - first: {x: -2, y: 0, z: 0} + second: + m_TileIndex: 11 + m_TileSpriteIndex: 11 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 + - first: {x: -1, y: 0, z: 0} second: m_TileIndex: 7 m_TileSpriteIndex: 7 @@ -1622,6 +1633,69 @@ Tilemap: m_ObjectToInstantiate: {fileID: 0} m_TileFlags: 1 m_ColliderType: 1 + - first: {x: 0, y: 0, z: 0} + second: + m_TileIndex: 7 + m_TileSpriteIndex: 7 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 + - first: {x: 1, y: 0, z: 0} + second: + m_TileIndex: 7 + m_TileSpriteIndex: 7 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 + - first: {x: 2, y: 0, z: 0} + second: + m_TileIndex: 7 + m_TileSpriteIndex: 7 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 + - first: {x: 3, y: 0, z: 0} + second: + m_TileIndex: 7 + m_TileSpriteIndex: 7 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 + - first: {x: 4, y: 0, z: 0} + second: + m_TileIndex: 7 + m_TileSpriteIndex: 7 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 + - first: {x: 5, y: 0, z: 0} + second: + m_TileIndex: 7 + m_TileSpriteIndex: 7 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 + - first: {x: 6, y: 0, z: 0} + second: + m_TileIndex: 14 + m_TileSpriteIndex: 14 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 - first: {x: -6, y: 1, z: 0} second: m_TileIndex: 10 @@ -1640,6 +1714,24 @@ Tilemap: m_ObjectToInstantiate: {fileID: 0} m_TileFlags: 1 m_ColliderType: 1 + - first: {x: -2, y: 1, z: 0} + second: + m_TileIndex: 10 + m_TileSpriteIndex: 10 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 + - first: {x: 6, y: 1, z: 0} + second: + m_TileIndex: 13 + m_TileSpriteIndex: 13 + m_TileMatrixIndex: 0 + m_TileColorIndex: 8 + m_ObjectToInstantiate: {fileID: 0} + m_TileFlags: 1 + m_ColliderType: 1 - first: {x: -6, y: 2, z: 0} second: m_TileIndex: 9 @@ -1755,21 +1847,21 @@ Tilemap: m_Data: {fileID: 0} - m_RefCount: 0 m_Data: {fileID: 0} - - m_RefCount: 9 + - m_RefCount: 15 m_Data: {fileID: 11400000, guid: 2004c99dc125dd542aafd24f4ad842f4, type: 2} - m_RefCount: 0 m_Data: {fileID: 0} - m_RefCount: 1 m_Data: {fileID: 11400000, guid: d71c3f314fe54f141b9df7c5699b66ab, type: 2} - - m_RefCount: 3 + - m_RefCount: 4 m_Data: {fileID: 11400000, guid: 3a266bd53dfebcf479f723737979e62d, type: 2} - - m_RefCount: 2 + - m_RefCount: 3 m_Data: {fileID: 11400000, guid: bf9ccb7cbc24ca346944cfd6efb15bf6, type: 2} - m_RefCount: 1 m_Data: {fileID: 11400000, guid: b673d03227bc8f74eab4645216e9d2a4, type: 2} - - m_RefCount: 3 + - m_RefCount: 4 m_Data: {fileID: 11400000, guid: 607b380a0644eb9438d6f72c3c8434c5, type: 2} - - m_RefCount: 2 + - m_RefCount: 3 m_Data: {fileID: 11400000, guid: 4bbf78a58bd67f246927f19ce84be3fd, type: 2} m_TileSpriteArray: - m_RefCount: 0 @@ -1786,7 +1878,7 @@ Tilemap: m_Data: {fileID: 0} - m_RefCount: 0 m_Data: {fileID: 0} - - m_RefCount: 9 + - m_RefCount: 15 m_Data: {fileID: 6408231970667830406, guid: 3b5f43ce65db7e74f9d3a906dfc2c460, type: 3} - m_RefCount: 0 @@ -1794,22 +1886,22 @@ Tilemap: - m_RefCount: 1 m_Data: {fileID: 5441223297315994465, guid: 3b5f43ce65db7e74f9d3a906dfc2c460, type: 3} - - m_RefCount: 3 + - m_RefCount: 4 m_Data: {fileID: -8025435653959805260, guid: 3b5f43ce65db7e74f9d3a906dfc2c460, type: 3} - - m_RefCount: 2 + - m_RefCount: 3 m_Data: {fileID: -7869403223043187030, guid: 3b5f43ce65db7e74f9d3a906dfc2c460, type: 3} - m_RefCount: 1 m_Data: {fileID: -6108889420429456849, guid: 3b5f43ce65db7e74f9d3a906dfc2c460, type: 3} - - m_RefCount: 3 + - m_RefCount: 4 m_Data: {fileID: -6315978382755026507, guid: 3b5f43ce65db7e74f9d3a906dfc2c460, type: 3} - - m_RefCount: 2 + - m_RefCount: 3 m_Data: {fileID: 511961965738296472, guid: 3b5f43ce65db7e74f9d3a906dfc2c460, type: 3} m_TileMatrixArray: - - m_RefCount: 21 + - m_RefCount: 31 m_Data: e00: 1 e01: 0 @@ -1844,7 +1936,7 @@ Tilemap: m_Data: {r: 0, g: 0, b: 0, a: 0} - m_RefCount: 0 m_Data: {r: 0, g: 0, b: 0, a: 0} - - m_RefCount: 21 + - m_RefCount: 31 m_Data: {r: 1, g: 1, b: 1, a: 1} m_AnimationFrameRate: 1 m_Color: {r: 1, g: 1, b: 1, a: 1} @@ -1923,8 +2015,8 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 867100008} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalPosition: {x: 0.19, y: 1.47, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} m_Children: - {fileID: 1000260803} m_Father: {fileID: 0} @@ -1944,6 +2036,7 @@ MonoBehaviour: m_EditorClassIdentifier: MoveSpeed: 2 BehaviourTree: {fileID: 867100012} + CurrentBehavior: Nothing --- !u!50 &867100011 Rigidbody2D: serializedVersion: 4 @@ -2046,6 +2139,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f7e23822f429fcc46a8fd9163e68f332, type: 3} m_Name: m_EditorClassIdentifier: + CurrentStatus: Target: {fileID: 0} CloseEnoughRadius: 1 --- !u!114 &867100017 @@ -2091,8 +2185,12 @@ GameObject: - component: {fileID: 895097235} - component: {fileID: 895097234} - component: {fileID: 895097233} + - component: {fileID: 895097240} + - component: {fileID: 895097239} + - component: {fileID: 895097238} + - component: {fileID: 895097237} m_Layer: 0 - m_Name: Enemy (2) + m_Name: Enemy (Default Melee) m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -2154,8 +2252,8 @@ Rigidbody2D: m_UseFullKinematicContacts: 1 m_UseAutoMass: 0 m_Mass: 5 - m_LinearDrag: 2 - m_AngularDrag: 2 + m_LinearDrag: 8 + m_AngularDrag: 0 m_GravityScale: 1 m_Material: {fileID: 0} m_Interpolate: 1 @@ -2170,13 +2268,75 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 895097232} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -3.43, y: -0.6, z: 0} + m_LocalPosition: {x: -0.43, y: -2.53, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 490037238} m_Father: {fileID: 0} m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &895097237 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 895097232} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bf838f73fb74f0046a5b6a86994c54ed, type: 3} + m_Name: + m_EditorClassIdentifier: + CurrentStatus: + WalkingDistance: 2 + MinWalkingDistance: 1 + SubjectRadius: 0.7 + StopLength: 2 +--- !u!114 &895097238 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 895097232} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 163f55033e69e2e4282c151e1a806bc6, type: 3} + m_Name: + m_EditorClassIdentifier: + Behaviours: + - {fileID: 895097237} +--- !u!114 &895097239 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 895097232} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bbf2158cd27572e4599b4c813f555fd8, type: 3} + m_Name: + m_EditorClassIdentifier: + Trigger: {fileID: 0} + TriggeredNodes: [] + NotTriggeredNodes: + - {fileID: 895097238} +--- !u!114 &895097240 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 895097232} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 978bec54408eb8f42a36fd68d7447f5a, type: 3} + m_Name: + m_EditorClassIdentifier: + MoveSpeed: 1 + BehaviourTree: {fileID: 895097239} + CurrentBehavior: Nothing --- !u!1 &963194225 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/AI/AIBehaviour.cs b/Assets/Scripts/AI/AIBehaviour.cs index c3f9a48..adb4c1a 100644 --- a/Assets/Scripts/AI/AIBehaviour.cs +++ b/Assets/Scripts/AI/AIBehaviour.cs @@ -1,7 +1,9 @@ using UnityEngine; namespace Saltosion.OneWeapon.AI { - public abstract class Behaviour : MonoBehaviour { + public abstract class AIBehaviour : MonoBehaviour { + public string CurrentStatus = ""; + /* Returns whether or not Execute() should be called this frame */ public abstract bool CanBehave(Enemy subject); /* Returns whether or not this was a blocking behaviour, ie. should the behaviour processing be stopped here */ diff --git a/Assets/Scripts/AI/BehaviourBranch.cs b/Assets/Scripts/AI/BehaviourBranch.cs index 84c6b2f..66a0009 100644 --- a/Assets/Scripts/AI/BehaviourBranch.cs +++ b/Assets/Scripts/AI/BehaviourBranch.cs @@ -8,22 +8,30 @@ namespace Saltosion.OneWeapon.AI { public BehaviourNode[] TriggeredNodes; public BehaviourNode[] NotTriggeredNodes; + private String MostRecentExecution; + /* Returns true if any action was taken. */ public override bool Execute(Enemy subject) { - if (Trigger.IsTriggered(subject)) { + if (Trigger != null && Trigger.IsTriggered(subject)) { foreach (BehaviourNode Node in TriggeredNodes) { if (Node.Execute(subject)) { + MostRecentExecution = Node.GetExecutedName(); return true; } } } else { foreach (BehaviourNode Node in NotTriggeredNodes) { if (Node.Execute(subject)) { + MostRecentExecution = Node.GetExecutedName(); return true; } } } return false; } + + public override string GetExecutedName() { + return MostRecentExecution; + } } } diff --git a/Assets/Scripts/AI/BehaviourLeaf.cs b/Assets/Scripts/AI/BehaviourLeaf.cs index 09bf101..fe04371 100644 --- a/Assets/Scripts/AI/BehaviourLeaf.cs +++ b/Assets/Scripts/AI/BehaviourLeaf.cs @@ -4,14 +4,18 @@ using UnityEngine; namespace Saltosion.OneWeapon.AI { [Serializable] public class BehaviourLeaf : BehaviourNode { - public Behaviour[] Behaviours; + public AIBehaviour[] Behaviours; public bool IsLeaf { get { return Behaviours.Length > 0; } } + private string ExecutedBehaviours = ""; + /* Returns true if any action was taken. */ public override bool Execute(Enemy subject) { bool Acted = false; - foreach (Behaviour Behaviour in Behaviours) { + ExecutedBehaviours = ""; + foreach (AIBehaviour Behaviour in Behaviours) { if (Behaviour.CanBehave(subject)) { + ExecutedBehaviours += Behaviour.GetType().Name + ":" + Behaviour.CurrentStatus + ", "; if (Behaviour.Execute(subject)) { return true; } else { @@ -21,5 +25,9 @@ namespace Saltosion.OneWeapon.AI { } return Acted; } + + public override string GetExecutedName() { + return ExecutedBehaviours; + } } } diff --git a/Assets/Scripts/AI/BehaviourNode.cs b/Assets/Scripts/AI/BehaviourNode.cs index e245b25..86cb203 100644 --- a/Assets/Scripts/AI/BehaviourNode.cs +++ b/Assets/Scripts/AI/BehaviourNode.cs @@ -6,5 +6,7 @@ namespace Saltosion.OneWeapon.AI { public abstract class BehaviourNode : MonoBehaviour { /* Returns true if any action was taken. */ public abstract bool Execute(Enemy subject); + + public abstract string GetExecutedName(); } } diff --git a/Assets/Scripts/AI/Behaviours/Follow.cs b/Assets/Scripts/AI/Behaviours/Follow.cs index 16cd51f..97ec6df 100644 --- a/Assets/Scripts/AI/Behaviours/Follow.cs +++ b/Assets/Scripts/AI/Behaviours/Follow.cs @@ -1,7 +1,7 @@ using UnityEngine; namespace Saltosion.OneWeapon.AI.Behaviours { - public class Follow : Behaviour { + public class Follow : AIBehaviour { public Transform Target; public float CloseEnoughRadius; diff --git a/Assets/Scripts/AI/Behaviours/Wander.cs b/Assets/Scripts/AI/Behaviours/Wander.cs new file mode 100644 index 0000000..802d113 --- /dev/null +++ b/Assets/Scripts/AI/Behaviours/Wander.cs @@ -0,0 +1,60 @@ +using UnityEngine; + +namespace Saltosion.OneWeapon.AI.Behaviours { + public class Wander : AIBehaviour { + public float WalkingDistance; + public float MinWalkingDistance; + public float SubjectRadius; + public float StopLength; + + private Vector2 Target; + private float StopTime; + private bool Stopped = true; + + private void Start() { + StopTime = Time.time; + } + + public override bool CanBehave(Enemy subject) { + return true; + } + + public override bool Execute(Enemy subject) { + Vector2 SubjectPosition = (Vector2)subject.transform.position; + if ((Target - SubjectPosition).magnitude > SubjectRadius) { + subject.StartMovingTo(Target); + CurrentStatus = "Move"; + Stopped = false; + return true; + } else if (!Stopped) { + Stopped = true; + StopTime = Time.time; + Target = SubjectPosition; + } else if (Time.time - StopTime > StopLength) { + // The AI is near the target, and has waited for StopTime. + // Now we pick a new target. + CurrentStatus = "Search"; + int i = 0; + while ((Target - SubjectPosition).magnitude <= SubjectRadius) { + i++; + if (i > 100) { + Debug.LogWarning($"Wander behaviour took over 100 tries to find a wander position, '{subject.name}' is stuck!"); + break; + } + float Distance = MinWalkingDistance + (WalkingDistance - MinWalkingDistance) * Random.value; + float Direction = Random.value * Mathf.PI * 2f; + Vector2 Delta = new Vector2(Mathf.Cos(Direction), Mathf.Sin(Direction)) * Distance; + RaycastHit2D Hit = Physics2D.Raycast(SubjectPosition + Delta.normalized * SubjectRadius, Delta.normalized, Distance); + if (Hit.rigidbody == null) { + Target = SubjectPosition + Delta; + } + } + } else { + CurrentStatus = "Stop"; + } + + subject.StopMoving(); + return false; + } + } +} diff --git a/Assets/Scripts/AI/Behaviours/Wander.cs.meta b/Assets/Scripts/AI/Behaviours/Wander.cs.meta new file mode 100644 index 0000000..26da0c0 --- /dev/null +++ b/Assets/Scripts/AI/Behaviours/Wander.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf838f73fb74f0046a5b6a86994c54ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Enemy.cs b/Assets/Scripts/Enemy.cs index 3f8f7b5..857cbd3 100644 --- a/Assets/Scripts/Enemy.cs +++ b/Assets/Scripts/Enemy.cs @@ -6,6 +6,8 @@ namespace Saltosion.OneWeapon { public class Enemy : MonoBehaviour { public float MoveSpeed; public BehaviourNode BehaviourTree; + [Space(20)] + public string CurrentBehavior = "Nothing"; private Rigidbody2D Body; private bool MovingToTarget = false; @@ -17,6 +19,7 @@ namespace Saltosion.OneWeapon { private void Update() { BehaviourTree.Execute(this); + CurrentBehavior = BehaviourTree.GetExecutedName(); } private void FixedUpdate() { diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset index 74d7b53..f3874f6 100644 --- a/ProjectSettings/GraphicsSettings.asset +++ b/ProjectSettings/GraphicsSettings.asset @@ -35,6 +35,9 @@ GraphicsSettings: - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} m_PreloadedShaders: [] m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}