Merge pull request #438 from SHADE-DP/Navigation

Added Navigation System. Added basic AI FSM
This commit is contained in:
XiaoQiDigipen 2023-03-24 16:11:13 +08:00 committed by GitHub
commit 346f2b2fac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 17114 additions and 10 deletions

View File

@ -1,13 +1,13 @@
0 StaticObject 1100000000000000 0 StaticObject 1101000000000000
1 Player 1100000000000000 1 Player 1101000000000000
2 Food 1000000000000000 2 Food 1000000000000000
3 Breakable 1100000000000000 3 Breakable 1100000000000000
4 ScoringWallCollider 0110000000000000 4 ScoringWallCollider 0110000000000000
5 Homeowner 1100000000000000 5 Homeowner 1100000000000000
6 Camera 0010000000000000 6 Camera 0010000000000000
7 StaticWithCameraCollision 1110000000000000 7 StaticWithCameraCollision 1111000000000000
8 9 0000000000000000 8 Floor 1100000000000000
9 10 0000000000000000 9 Navigation 0001000000000000
10 11 0000000000000000 10 11 0000000000000000
11 12 0000000000000000 11 12 0000000000000000
12 13 0000000000000000 12 13 0000000000000000

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
Name: Level2_AITest
ID: 86300248
Type: 5

View File

@ -530,7 +530,6 @@
Scripts: Scripts:
- Type: SHADE_Scripting.UI.FadeInOnActive - Type: SHADE_Scripting.UI.FadeInOnActive
Enabled: true Enabled: true
alpha: 0
- EID: 19 - EID: 19
Name: Options Canvas Name: Options Canvas
IsActive: false IsActive: false
@ -1236,7 +1235,6 @@
Scripts: Scripts:
- Type: SHADE_Scripting.UI.FadeInOnActive - Type: SHADE_Scripting.UI.FadeInOnActive
Enabled: true Enabled: true
alpha: 0
- EID: 46 - EID: 46
Name: Credits Canvas Name: Credits Canvas
IsActive: false IsActive: false
@ -1461,7 +1459,6 @@
Scripts: Scripts:
- Type: SHADE_Scripting.UI.FadeInOnActive - Type: SHADE_Scripting.UI.FadeInOnActive
Enabled: true Enabled: true
alpha: 0
- EID: 441 - EID: 441
Name: Level Select Buttons Name: Level Select Buttons
IsActive: true IsActive: true

View File

@ -0,0 +1,320 @@
- EID: 0
Name: Camera
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 10.2736483, z: 0}
Rotate: {x: -1.48352981, y: 1.5, z: 0.5}
Scale: {x: 1, y: 1, z: 1}
IsActive: true
Scripts: ~
- EID: 1
Name: Floor
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 0, z: 0}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 12.8000002, y: 1, z: 12.8000002}
IsActive: true
Renderable Component:
Mesh: 142686872
Material: 126223465
IsActive: true
Scripts: ~
- EID: 2
Name: Light
IsActive: true
NumberOfChildren: 0
Components:
Light Component:
Position: {x: 0, y: 0, z: 0}
Type: Directional
Direction: {x: 0, y: -1, z: 1}
Color: {x: 1, y: 1, z: 1, w: 1}
Layer: 4294967295
Strength: 0
Casting Shadows: false
IsActive: true
Scripts: ~
- EID: 3
Name: Wall
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 11, y: 0, z: 0}
Rotate: {x: -0, y: 1.57079637, z: -0}
Scale: {x: 10.6225176, y: 1, z: 1}
IsActive: true
Renderable Component:
Mesh: 142652392
Material: 126223465
IsActive: true
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 0
Type: Box
Half Extents: {x: 2.5, y: 1, z: 1.60000002}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~
- EID: 4
Name: Wall
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 0, z: -11.2958097}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 10.6225176, y: 1, z: 1}
IsActive: true
Renderable Component:
Mesh: 142652392
Material: 126223465
IsActive: true
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 0
Type: Box
Half Extents: {x: 2.5, y: 1, z: 1.60000002}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~
- EID: 5
Name: Wall
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 0, z: 11}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 10.6225176, y: 1, z: 1}
IsActive: true
Renderable Component:
Mesh: 142652392
Material: 126223465
IsActive: true
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 0
Type: Box
Half Extents: {x: 2.5, y: 1, z: 1.60000002}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~
- EID: 6
Name: Wall
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 6.06437206, y: 0, z: -5.60716629}
Rotate: {x: -0, y: 1.57079601, z: -0}
Scale: {x: 4.99989605, y: 1, z: 0.999986947}
IsActive: true
Renderable Component:
Mesh: 142652392
Material: 126223465
IsActive: true
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 0
Type: Box
Half Extents: {x: 2.5, y: 1, z: 1.60000002}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~
- EID: 7
Name: Wall
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: -11, y: 0, z: 0}
Rotate: {x: -0, y: 1.57079637, z: -0}
Scale: {x: 10.6225176, y: 1, z: 1}
IsActive: true
Renderable Component:
Mesh: 142652392
Material: 126223465
IsActive: true
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 0
Type: Box
Half Extents: {x: 2.5, y: 1, z: 1.60000002}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~
- EID: 8
Name: Wall
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: -4.70212746, y: 0, z: -0.00226712227}
Rotate: {x: -0, y: 1.57079601, z: -0}
Scale: {x: 4.99982977, y: 1, z: 0.999978602}
IsActive: true
Renderable Component:
Mesh: 142652392
Material: 126223465
IsActive: true
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 0
Type: Box
Half Extents: {x: 2.5, y: 1, z: 1.60000002}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~
- EID: 9
Name: Wall
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 1.46194017, y: 0, z: 0.71579361}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 4.99985838, y: 1, z: 0.999982238}
IsActive: true
Renderable Component:
Mesh: 142652392
Material: 126223465
IsActive: true
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 0
Type: Box
Half Extents: {x: 2.5, y: 1, z: 1.60000002}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~
- EID: 10
Name: Wall
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: -2.30867815, y: 0, z: -6.02849674}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 4, y: 1, z: 0.999974966}
IsActive: true
Renderable Component:
Mesh: 142652392
Material: 126223465
IsActive: true
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 0
Type: Box
Half Extents: {x: 2.5, y: 1, z: 1.60000002}
Friction: 0.400000006
Bounciness: 0
Density: 1
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
IsActive: true
Scripts: ~
- EID: 11
Name: TestAI
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 8.5, y: -1, z: -6.41661787}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 5, y: 5, z: 5}
IsActive: true
Renderable Component:
Mesh: 140639624
Material: 131956078
IsActive: true
Navigation Component:
Target: {x: 0, y: 0, z: 0}
Forward: {x: 0, y: 0, z: 0}
Recalculate Path: true
Unreachable Target: false
Acceptance threshold: 1
IsActive: true
Scripts:
- Type: SHADE_Scripting.Gameplay.AIBehaviour.AIRework.NavigationTestScript
Enabled: true
endPoint: 12
speed: 3
- EID: 12
Name: EndPoint
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: -0.853474855, y: -1, z: 0.354041398}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 5, y: 5, z: 5}
IsActive: true
Renderable Component:
Mesh: 140639624
Material: 131956078
IsActive: true
Scripts: ~
- EID: 13
Name: Camera
IsActive: true
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: 10.2736483, z: 0}
Rotate: {x: -1.48352981, y: 0, z: 0}
Scale: {x: 1, y: 1, z: 1}
IsActive: true
Camera Component:
Position: {x: 0, y: 0, z: 0}
Pitch: -85
Yaw: 0
Roll: 0
Width: 1920
Near: 0.00999999978
Far: 10000
Perspective: true
FOV: 90
IsActive: true
Scripts: ~

View File

@ -0,0 +1,3 @@
Name: NavigationTest
ID: 100246336
Type: 5

View File

@ -0,0 +1,107 @@
using SHADE;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework
{
public class AILineOfSight:Script
{
public GameObject player;
public float range = 2.0f;
[Tooltip("Angle between player and forward to be within sight")]
public float angle = 30.0f;
[Tooltip("For debug")]
public float angleBetween = 0.0f;
[Tooltip("For debug")]
public float distance = 0.0f;
public float heightLimit = 1.0f;
public Vector3 rayOffset;
public bool withinRange;
public bool withinSight;
public Vector3 lastFoundPos;
public float lastFoundTimer = 0.0f;
protected override void update()
{
if (player == GameObject.Null)
return;
Transform transform = GetComponent<Transform>();
Transform playerTransform = player.GetComponent<Transform>();
Collider playerCollider = player.GetComponent<Collider>();
withinRange = false;
withinSight = false;
if(transform && playerTransform && playerCollider)
{
Vector3 pos = transform.GlobalPosition + rayOffset;
Vector3 playerPos = playerTransform.GlobalPosition ;
Vector3 d = playerPos - pos;
distance = d.GetMagnitude();
if(distance < range)
{
Vector3 fwdHorizontal = transform.Forward;
fwdHorizontal.y = 0;
fwdHorizontal.Normalise();
Vector3 dHorizontal = d;
dHorizontal.y = 0;
float dot = Vector3.Dot(fwdHorizontal, dHorizontal);
angleBetween = SHADE.Math.Rad2Deg * MathF.Acos(dot / (dHorizontal.GetMagnitude()));
if (angleBetween < angle && playerPos.y < pos.y + heightLimit)
{
withinRange = true;
withinSight = true;
Ray sightRay = new Ray(pos, d.GetNormalised());
List<RaycastHit> hitResults = Physics.Raycast(sightRay, distance,false, (ushort)65535);
foreach(RaycastHit hit in hitResults)
{
if (hit.Hit && hit.Other != player)
{
Debug.Log("AI LOS: HIT OTHER");
withinSight = false;
break;
}
}
}
}
if (withinSight == true)
{
lastFoundPos = playerTransform.GlobalPosition;
lastFoundTimer = 0.0f;
}
else
{
lastFoundTimer += Time.DeltaTimeF;
}
}
}
}
}

View File

@ -0,0 +1,3 @@
Name: AILineOfSight
ID: 152074687
Type: 9

View File

@ -0,0 +1,3 @@
Name: BaseState
ID: 167091082
Type: 9

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
using SHADE;
using SHADE_Scripting.Gameplay.AIBehaviour.AIRework.States;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework
{
public class HomeOwnerAI:Script
{
public float idleDuration = 1.0f;
public float timeoutDuration = 2.0f;
public GameObject patrolPointParent;
public float patrolSpeed = 2.0f;
public float chaseSpeed = 3.0f;
public float alertCooldown = 0.0f;
public GameObject player;
[NonSerialized]
public IEnumerable<Transform> patrolPointPool;
protected override void awake()
{
StateMachine machine = GetScript<StateMachine>();
if(machine)
{
Dictionary<Type, BaseState> dictionary = new Dictionary<Type, BaseState>();
dictionary.Add(typeof(IdleState), new IdleState(machine));
dictionary.Add(typeof(PatrolState), new PatrolState(machine));
dictionary.Add(typeof(TimeoutState), new TimeoutState(machine));
dictionary.Add(typeof(ChaseState), new ChaseState(machine));
dictionary.Add(typeof(AlertState), new AlertState(machine));
machine.InitStateMachine(dictionary);
}
patrolPointPool = patrolPointParent.GetComponentsInChildren<Transform>();
}
protected override void update()
{
if(alertCooldown > 0.0f)
{
alertCooldown -= Time.DeltaTimeF;
}
else
{
alertCooldown = 0.0f;
}
RigidBody rigid = GetComponent<RigidBody>();
if(rigid)
{
rigid.AngularVelocity = Vector3.Zero;
}
}
public bool ShouldTransitAlert()
{
AILineOfSight los = GetScript<AILineOfSight>();
if (los)
{
if (los.withinSight && alertCooldown <= 0.0f)
{
return true;
}
}
return false;
}
public void RotateToPlayer()
{
//Transform playerTransform = player.GetComponent<Transform>();
////Rotate to face player.
//Transform aiTransform = GetComponent<Transform>();
//if(playerTransform && aiTransform)
//{
// Vector3 direction = playerTransform.GlobalPosition - aiTransform.GlobalPosition;
// Quaternion currentRotation = aiTransform.LocalRotation;
// Quaternion targetRotation = Quaternion.Euler(0.0f, MathF.Atan2(direction.x, direction.z), 0.0f);
// aiTransform.LocalRotation = Quaternion.Slerp(currentRotation, targetRotation, 5.0f * (float)Time.FixedDeltaTime);
//}
}
}
}

View File

@ -0,0 +1,3 @@
Name: HomeOwnerAI
ID: 162553450
Type: 9

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SHADE;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework
{
public class NavigationTestScript :Script
{
public GameObject endPoint;
public float speed = 1.0f;
float timer = 0.0f;
protected override void start()
{
Navigation nav = GetComponent<Navigation>();
Transform endTransform = endPoint.GetComponent<Transform>();
if (endTransform)
nav.MoveTo(endTransform.GlobalPosition);
}
protected override void update()
{
timer += Time.DeltaTimeF;
Navigation nav = GetComponent<Navigation>();
Transform transform = GetComponent<Transform>();
if (timer >= 1.0f)
{
timer = 0.0f;
Transform endTransform = endPoint.GetComponent<Transform>();
if (endTransform)
nav.MoveTo(endTransform.GlobalPosition);
}
if (nav && transform)
{
transform.LocalPosition = transform.LocalPosition + ( nav.GetForward() * Time.DeltaTimeF * speed);
}
}
}
}

View File

@ -0,0 +1,3 @@
Name: NavigationTestScript
ID: 162476480
Type: 9

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SHADE;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework
{
public class RotateToVelocity : Script
{
public float rotationPerSecond = 2.0f;
protected override void update()
{
RigidBody rigid = GetComponent<RigidBody>();
Transform transform = GetComponent<Transform>();
if(rigid && transform)
{
Vector3 vel = rigid.LinearVelocity;
if(vel.GetSqrMagnitude() > 1.0f)
{
Quaternion currentRotation = transform.LocalRotation;
Quaternion targetRotation = Quaternion.Euler(0.0f, MathF.Atan2(vel.x, vel.z), 0.0f);
transform.LocalRotation = Quaternion.Slerp(currentRotation, targetRotation, rotationPerSecond * (float)Time.FixedDeltaTime);
}
}
}
}
}

View File

@ -0,0 +1,3 @@
Name: RotateToVelocity
ID: 163814533
Type: 9

View File

@ -0,0 +1,3 @@
Name: StateMachine
ID: 165140157
Type: 9

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework.States
{
public abstract class AIBaseState: BaseState
{
protected HomeOwnerAI ai;
public AIBaseState(StateMachine stateMachine): base(stateMachine, "")
{
stateName = "AI Base State";
ai = stateMachine.GetScript<HomeOwnerAI>();
}
}
}

View File

@ -0,0 +1,3 @@
Name: AIBaseState
ID: 160233845
Type: 9

View File

@ -0,0 +1,42 @@
using SHADE;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework.States
{
public class AlertState: AIBaseState
{
const float alertDuration = 2.0f;
float alertTimer = alertDuration;
public AlertState(StateMachine machine): base(machine)
{
stateName = "Alert";
}
public override void OnEnter()
{
alertTimer = alertDuration;
}
public override void update()
{
alertTimer -= Time.DeltaTimeF;
if (alertTimer <= 0.0f)
{
machine.SetState(typeof(ChaseState));
}
}
public override void fixedUpdate()
{
}
}
}

View File

@ -0,0 +1,3 @@
Name: AlertState
ID: 159828775
Type: 9

View File

@ -0,0 +1,77 @@
using SHADE;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework.States
{
public class ChaseState :AIBaseState
{
float giveUpDuration = 10.0f;
float giveUpTimer = 0.0f;
public ChaseState(StateMachine machine): base(machine)
{
stateName = "Chase";
}
public override void OnEnter()
{
giveUpTimer = giveUpDuration;
}
public override void update()
{
Navigation nav = machine.GetComponent<Navigation>();
AILineOfSight los = ai.GetScript<AILineOfSight>();
if(los && nav)
{
Transform playerTransform = los.player.GetComponent<Transform>();
if (los.withinSight)
{
nav.MoveTo(playerTransform.GlobalPosition);
}
else
{
nav.MoveTo(los.lastFoundPos);
giveUpTimer -= Time.DeltaTimeF;
}
if(los.lastFoundTimer>= 10.0f || giveUpTimer <= 0.0f)
{
machine.SetState(typeof(TimeoutState));
}
RigidBody rigid = machine.GetComponent<RigidBody>();
if(rigid)
{
if (los.withinSight)
rigid.LinearVelocity = nav.GetForward() * ai.chaseSpeed;
else
rigid.LinearVelocity = nav.GetForward() * ai.patrolSpeed;
}
if(nav.ReachedTarget())
{
giveUpTimer -= Time.DeltaTimeF;
ai.RotateToPlayer();
}
}
}
public override void fixedUpdate()
{
}
}
}

View File

@ -0,0 +1,3 @@
Name: ChaseState
ID: 166357249
Type: 9

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SHADE;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework.States
{
public class IdleState: AIBaseState
{
float timer = 0.0f;
public IdleState(StateMachine machine): base(machine)
{
stateName = "Idle";
}
public override void OnEnter()
{
timer = 0.0f;
}
public override void update()
{
timer += Time.DeltaTimeF;
if(timer >= ai.idleDuration)
{
machine.SetState(typeof(PatrolState));
}
if(ai.ShouldTransitAlert())
{
machine.SetState(typeof(AlertState));
}
}
public override void fixedUpdate()
{
}
}
}

View File

@ -0,0 +1,3 @@
Name: IdleState
ID: 164902316
Type: 9

View File

@ -0,0 +1,88 @@
using SHADE;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework.States
{
public class PatrolState: AIBaseState
{
Random rand;
Vector3 lastFramePos;
float stuckTimer ;
public PatrolState(StateMachine machine) : base(machine)
{
stateName = "Patrol";
rand = new Random();
}
public override void OnEnter()
{
Navigation nav = machine.GetComponent<Navigation>();
Transform transform = machine.GetComponent<Transform>();
if (nav && transform)
{
int index = rand.Next(0, ai.patrolPointPool.Count() - 1);
Transform dest = ai.patrolPointPool.ElementAt(index);
if (dest)
{
nav.MoveTo(dest.GlobalPosition);
Debug.Log("Moving to" + dest.GlobalPosition.ToString());
}
lastFramePos = transform.GlobalPosition;
stuckTimer = 0.0f;
}
}
public override void update()
{
Navigation nav = machine.GetComponent<Navigation>();
Transform transform = machine.GetComponent<Transform>();
RigidBody rigid = machine.GetComponent<RigidBody>();
if(nav && transform && rigid)
{
rigid.LinearVelocity = nav.GetForward() * ai.patrolSpeed;
Vector3 d = lastFramePos - transform.GlobalPosition;
if (d.GetSqrMagnitude() < 0.001f)
{
stuckTimer += Time.DeltaTimeF;
}
if (nav.ReachedTarget())
{
machine.SetState(typeof(IdleState));
}
lastFramePos = transform.GlobalPosition;
}
if(stuckTimer >= 0.5f)
{
machine.SetState(typeof(IdleState));
}
if (ai.ShouldTransitAlert())
{
machine.SetState(typeof(AlertState));
}
}
public override void fixedUpdate()
{
}
}
}

View File

@ -0,0 +1,3 @@
Name: PatrolState
ID: 155844701
Type: 9

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SHADE;
namespace SHADE_Scripting.Gameplay.AIBehaviour.AIRework.States
{
public class TimeoutState : AIBaseState
{
float timer = 0.0f;
float alertCooldown = 10.0f;
public TimeoutState(StateMachine machine) : base(machine)
{
stateName = "Timeout";
}
public override void OnEnter()
{
timer = 0.0f;
}
public override void update()
{
timer += Time.DeltaTimeF;
if (timer >= ai.timeoutDuration)
{
machine.SetState(typeof(PatrolState));
}
}
public override void fixedUpdate()
{
}
public override void OnExit()
{
ai.alertCooldown = alertCooldown;
}
}
}

View File

@ -0,0 +1,3 @@
Name: TimeoutState
ID: 167077719
Type: 9

View File

@ -35,6 +35,7 @@
#include "Scripting/SHScriptEngine.h" #include "Scripting/SHScriptEngine.h"
#include "UI/SHUISystem.h" #include "UI/SHUISystem.h"
#include "Animation/SHAnimationSystem.h" #include "Animation/SHAnimationSystem.h"
#include "Navigation/SHNavigationSystem.h"
// Components // Components
#include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h"
@ -89,6 +90,7 @@ namespace Sandbox
SHSystemManager::CreateSystem<SHAudioSystem>(); SHSystemManager::CreateSystem<SHAudioSystem>();
SHSystemManager::CreateSystem<SHCameraSystem>(); SHSystemManager::CreateSystem<SHCameraSystem>();
SHSystemManager::CreateSystem<SHUISystem>(); SHSystemManager::CreateSystem<SHUISystem>();
SHSystemManager::CreateSystem<SHNavigationSystem>();
//std::system("FontCompiler.exe ../../Assets/Fonts/SegoeUI.ttf"); //std::system("FontCompiler.exe ../../Assets/Fonts/SegoeUI.ttf");
//std::system("FontCompiler.exe ../../Assets/Fonts/ALGER.ttf"); //std::system("FontCompiler.exe ../../Assets/Fonts/ALGER.ttf");
@ -127,7 +129,9 @@ namespace Sandbox
#ifdef SHEDITOR #ifdef SHEDITOR
SHSystemManager::RegisterRoutine<SHPhysicsDebugDrawSystem, SHPhysicsDebugDrawSystem::PhysicsDebugDraw>(); SHSystemManager::RegisterRoutine<SHPhysicsDebugDrawSystem, SHPhysicsDebugDrawSystem::PhysicsDebugDraw>();
SHSystemManager::RegisterRoutine<SHNavigationSystem, SHNavigationSystem::NavigationSystemGenerateRoutine>();
#endif #endif
SHSystemManager::RegisterRoutine<SHNavigationSystem, SHNavigationSystem::UpdateNavigationRoutine>();
SHSystemManager::RegisterRoutine<SHTransformSystem, SHTransformSystem::TransformPostPhysicsUpdate>(); SHSystemManager::RegisterRoutine<SHTransformSystem, SHTransformSystem::TransformPostPhysicsUpdate>();
SHSystemManager::RegisterRoutine<SHDebugDrawSystem, SHDebugDrawSystem::ProcessPointsRoutine>(); SHSystemManager::RegisterRoutine<SHDebugDrawSystem, SHDebugDrawSystem::ProcessPointsRoutine>();

View File

@ -16,3 +16,4 @@
#include "AudioSystem/SHAudioListenerComponent.h" #include "AudioSystem/SHAudioListenerComponent.h"
#include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h" #include "Graphics/MiddleEnd/TrajectoryRendering/SHTrajectoryRenderableComponent.h"
#include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h" #include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h"
#include "Navigation/SHNavigationComponent.h"

View File

@ -27,6 +27,7 @@
#include "AudioSystem/SHAudioListenerComponent.h" #include "AudioSystem/SHAudioListenerComponent.h"
#include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h"
#include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h" #include "Graphics/MiddleEnd/Particles/SHParticleEmitterComponent.h"
#include "Navigation/SHNavigationComponent.h"
#include "Camera/SHCameraSystem.h" #include "Camera/SHCameraSystem.h"
#include "FRC/SHFramerateController.h" #include "FRC/SHFramerateController.h"
@ -193,6 +194,10 @@ namespace SHADE
{ {
DrawComponent(particleComponent); DrawComponent(particleComponent);
} }
if (auto navigationComponent = SHComponentManager::GetComponent_s<SHNavigationComponent>(eid))
{
DrawComponent(navigationComponent);
}
ImGui::Separator(); ImGui::Separator();
// Render Scripts // Render Scripts
SHScriptEngine* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>()); SHScriptEngine* scriptEngine = static_cast<SHScriptEngine*>(SHSystemManager::GetSystem<SHScriptEngine>());
@ -219,6 +224,7 @@ namespace SHADE
DrawAddComponentWithEnforcedComponentButton<SHTextRenderableComponent, SHTransformComponent>(eid); DrawAddComponentWithEnforcedComponentButton<SHTextRenderableComponent, SHTransformComponent>(eid);
DrawAddComponentWithEnforcedComponentButton<SHAnimatorComponent, SHTransformComponent, SHRenderable>(eid); DrawAddComponentWithEnforcedComponentButton<SHAnimatorComponent, SHTransformComponent, SHRenderable>(eid);
DrawAddComponentWithEnforcedComponentButton<SHAudioListenerComponent, SHTransformComponent>(eid); DrawAddComponentWithEnforcedComponentButton<SHAudioListenerComponent, SHTransformComponent>(eid);
DrawAddComponentWithEnforcedComponentButton<SHNavigationComponent, SHTransformComponent>(eid);
//DrawAddComponentWithEnforcedComponentButton<SHParticleEmitterComponent, SHTransformComponent>(eid); //DrawAddComponentWithEnforcedComponentButton<SHParticleEmitterComponent, SHTransformComponent>(eid);
ImGui::EndMenu(); ImGui::EndMenu();

View File

@ -0,0 +1,78 @@
#include "SHpch.h"
#include "SHNavigationComponent.h"
namespace SHADE
{
uint16_t NavigationGridIndex::numColumns = 8;
SHNavigationComponent::SHNavigationComponent()
:target{ 0.0f }, path{}, threshold{ 0.1f }, recalculatePath{ false }, unreachableTarget{ false }, forward{0.0f}, tolerance{0}
{
}
SHVec3 SHNavigationComponent::GetTarget() const noexcept
{
return target;
}
SHVec3 SHNavigationComponent::GetForward() const noexcept
{
return forward;
}
bool SHNavigationComponent::GetRecalculatePath() const noexcept
{
return recalculatePath;
}
bool SHNavigationComponent::GetUnreachableTarget() const noexcept
{
return unreachableTarget;
}
bool SHNavigationComponent::ReachedTarget() const noexcept
{
return path.empty();
}
void SHNavigationComponent::SetTarget(SHVec3 value) noexcept
{
recalculatePath = true;
target = value;
}
void SHNavigationComponent::EmptySetForward(SHVec3 value) noexcept
{
}
void SHNavigationComponent::EmptySetBool(bool value) noexcept
{
}
}//namespace SHADE
RTTR_REGISTRATION
{
using namespace SHADE;
using namespace rttr;
registration::class_<SHNavigationComponent>("Navigation Component")
.property("Target", &SHNavigationComponent::GetTarget, &SHNavigationComponent::SetTarget)
.property("Forward", &SHNavigationComponent::GetForward, &SHNavigationComponent::EmptySetForward)
.property("Recalculate Path", &SHNavigationComponent::GetRecalculatePath, &SHNavigationComponent::EmptySetBool)
.property("Unreachable Target", &SHNavigationComponent::GetUnreachableTarget, &SHNavigationComponent::EmptySetBool)
.property("Tolerance", &SHNavigationComponent::tolerance)
.property("Acceptance threshold", &SHNavigationComponent::threshold)
;
}

View File

@ -0,0 +1,123 @@
#pragma once
#include <rttr/registration>
#include "ECS_Base/Components/SHComponent.h"
#include "Math/Vector/SHVec2.h"
#include "Math/Vector/SHVec3.h"
#include <queue>
#include "SH_API.h"
namespace SHADE
{
struct NavigationGridIndex
{
uint16_t row;
uint16_t column;
static uint16_t numColumns;
bool operator==(NavigationGridIndex const& rhs) const noexcept
{
return row == rhs.row && column == rhs.column;
}
bool operator!=(NavigationGridIndex const& rhs) const noexcept
{
return row != rhs.row || column != rhs.column;
}
bool operator<(NavigationGridIndex const& rhs) const noexcept
{
return (row * numColumns + column) < (rhs.row * numColumns + rhs.column);
}
};
#define NullGridIndex 9999;
class SH_API SHNavigationComponent final: public SHComponent
{
private:
//Target to move to
SHVec3 target;
//Direction to mvoe towards to get to the target position.
SHVec3 forward;
//The path to follow to reach the target.
std::queue<NavigationGridIndex> path;
//The flag to check against to indicate whether to recalculate path
bool recalculatePath;
//The flag to set if the target is unreachable.
bool unreachableTarget;
public:
friend class SHNavigationSystem;
//The distance threshold that indicates when the entity has reached the navigation grid.
float threshold;
//Number of grid cells surrounding colliding cells to also mark as colliding.
uint16_t tolerance;
SHNavigationComponent();
virtual ~SHNavigationComponent() = default;
/********************************************************************
* \brief
* Getter for the target position. This should be in world position.
* \return
* SHVec3
********************************************************************/
SHVec3 GetTarget() const noexcept;
/********************************************************************
* \brief
* Getter for the forward vector. This shows the direction to move
* in order to reach the target.
* \return
* SHVec3
********************************************************************/
SHVec3 GetForward() const noexcept;
bool GetRecalculatePath() const noexcept;
bool GetUnreachableTarget() const noexcept;
bool ReachedTarget() const noexcept;
/********************************************************************
* \brief
* Setter for the target variable. Also sets recalculatePath boolean
* to true.
* \param value
* The target world location. The y-axis will be ignored.
* \return
* void
********************************************************************/
void SetTarget(SHVec3 value) noexcept;
/********************************************************************
* \brief
* An empty setter so we can see this variable in inspector but
* can't set it.
* \param value
* value to set to.
* \return
* void
********************************************************************/
void EmptySetForward(SHVec3 value) noexcept;
void EmptySetBool(bool value) noexcept;
RTTR_ENABLE()
protected:
};
}

View File

@ -0,0 +1,671 @@
#include "SHpch.h"
#include "SHNavigationSystem.h"
#include "ECS_Base/Managers/SHSystemManager.h"
#include "Physics/System/SHPhysicsSystem.h"
#include "Math/Geometry/SHAABB.h"
#include "Input/SHInputManager.h"
#include "Editor/SHEditor.h"
#include "Scene/SHSceneManager.h"
#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h"
#include <vector>
#include <stack>
#include <map>
#include <list>
#define DRAW_NAVIGATION_DATA
#define DRAW_NAVIGATION_PATH
namespace SHADE
{
void SHNavigationSystem::Init()
{
auto id = ComponentFamily::GetID<SHNavigationComponent>();
SHComponentManager::CreateComponentSparseSet<SHNavigationComponent>();
SystemID i = SystemFamily::GetID<SHNavigationSystem>();
}
void SHNavigationSystem::Exit()
{
}
uint16_t SHNavigationSystem::GetIndex(uint16_t row, uint16_t col) noexcept
{
size_t rowOffset = numRows / NumGridDataTypeBits;
return row * rowOffset + (col / NumGridDataTypeBits );
}
bool SHNavigationSystem::GetNavigationData(uint16_t row, uint16_t col) noexcept
{
size_t index = GetIndex(row, col);
GridDataType bitMask = 1 << (col % NumGridDataTypeBits);
if ( index < navigationGrid.size())
{
return navigationGrid[index] & bitMask;
}
else
{
return true;
}
}
bool SHNavigationSystem::GetNavigationData(NavigationGridIndex index) noexcept
{
return GetNavigationData(index.row, index.column);
}
SHVec2 SHNavigationSystem::GetGridSize() noexcept
{
return SHVec2{size.x / numCols, size.z / numRows};
}
NavigationGridIndex SHNavigationSystem::GetNavigationGridIndex(SHVec3 const& worldPosition) noexcept
{
NavigationGridIndex result;
SHVec3 topleft{ origin.x - (size.x / 2.0f), origin.y, origin.z - (size.z / 2.0f) };
SHVec3 pos = worldPosition - topleft;
SHVec2 gridSize = GetGridSize();
result.row = (pos.z )/ gridSize.y;
result.column = (pos.x )/ gridSize.x;
return result;
}
SHVec3 SHNavigationSystem::GetGridWorldPos(NavigationGridIndex index) noexcept
{
SHVec3 result{0.0f};
SHVec3 topleft{ origin.x - (size.x / 2.0f), origin.y, origin.z - (size.z / 2.0f) };
SHVec2 gridSize = GetGridSize();
result.x = (index.column * gridSize.x);
result.z = (index.row * gridSize.y) ;
result += topleft;
return result;
}
void SHNavigationSystem::GenerateNavigationGridData(SHVec3 origin, SHVec3 size, uint16_t nr, uint16_t nc) noexcept
{
if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f || nr == 0 || nc == 0 )
{
return;
}
this->origin = origin;
this->size = size;
numRows = nr;
numCols = nc;
navigationGrid.clear();
GridDataType temp = 0;
for (size_t r = 0; r < numRows; ++r)
{
for (size_t c = 0; c < numCols; ++c)
{
GridDataType bitmask = 1 << (c % NumGridDataTypeBits);
auto physics = SHSystemManager::GetSystem<SHPhysicsSystem>();
//Does the AABB calculation
if (physics != nullptr)
{
//Get the top left position
SHVec3 topleft{ origin.x - (size.x / 2.0f), origin.y, origin.z - (size.z / 2.0f) };
SHVec2 halfGridSize = GetGridSize() * 0.5f;
//offset it by row and column and center it with half grid size.
topleft += SHVec3{ c * GetGridSize().x, 0.0f, r * GetGridSize().y} + SHVec3{halfGridSize.x,0.0f,halfGridSize.y};
//Get half extents.
SHVec3 halfExtents{ halfGridSize.x, size.y, halfGridSize.y };
SHAABB aabb{ topleft,halfExtents };
if (physics->TestAABBOverlap(aabb, (uint16_t)(SHCollisionTagMatrix::GetTag("Navigation")->GetMask())))
{
temp |= bitmask;
}
}
if ((c % NumGridDataTypeBits) == NumGridDataTypeBits - 1)
{
navigationGrid.push_back(temp);
temp = 0;
}
}
//if the number of column dont fit perfectly to the NumGridDataTypeBits. We push in what we have with the rest as 0s.
if (numCols % NumGridDataTypeBits != 0)
{
navigationGrid.push_back(temp);
temp = 0;
}
}
}
void SHNavigationSystem::NavigationSystemGenerateRoutine::Execute(double dt) noexcept
{
#ifdef SHEDITOR
SHVec3 navigationAreaSize{ 10.5f, 1.0f, 10.5f };
auto editor = SHSystemManager::GetSystem<SHEditor>();
if (editor->editorState != SHEditor::State::PLAY)
{
SHNavigationSystem* system = static_cast<SHNavigationSystem*>(GetSystem());
if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::H))
{
system->GenerateNavigationGridData(SHVec3{ 0.0f }, navigationAreaSize, 80, 80);
}
auto debugDrawSystem = SHSystemManager::GetSystem<SHDebugDrawSystem>();
if (debugDrawSystem)
{
SHTransform trans;
trans.position = SHVec3{ 0.0f };
trans.scale = navigationAreaSize;
trans.ComputeTRS();
debugDrawSystem->DrawWireCube(trans.trs, SHColour::YELLOW, false);
#ifdef DRAW_NAVIGATION_DATA
for (uint16_t r = 0; r < system->numRows; ++r)
{
for (uint16_t c = 0; c < system->numCols; ++c)
{
if (system->GetNavigationData(r, c) == true)
{
SHVec3 topleft{ system->origin.x - (system->size.x / 2.0f), system->origin.y, system->origin.z - (system->size.z / 2.0f) };
SHVec2 halfGridSize = system->GetGridSize() * 0.5f;
//offset it by row and column and center it with half grid size.
topleft += SHVec3{ c * system->GetGridSize().x, 0.0f, r * system->GetGridSize().y } + SHVec3{ halfGridSize.x,0.0f,halfGridSize.y };
SHTransform t;
t.position = system->GetGridWorldPos({r,c});
t.scale = SHVec3{ halfGridSize.x * 2.0f, 1.0f, halfGridSize.y * 2.0f};
t.ComputeTRS();
debugDrawSystem->DrawCube(t.trs, SHColour::RED, true);
}
else
{
SHVec3 topleft{ system->origin.x - (system->size.x / 2.0f), system->origin.y, system->origin.z - (system->size.z / 2.0f) };
SHVec2 halfGridSize = system->GetGridSize() * 0.5f;
//offset it by row and column and center it with half grid size.
topleft += SHVec3{ c * system->GetGridSize().x, 0.0f, r * system->GetGridSize().y } + SHVec3{ halfGridSize.x,0.0f,halfGridSize.y };
SHTransform t;
t.position = system->GetGridWorldPos({ r,c });
t.scale = SHVec3{ halfGridSize.x * 2.0f, 1.0f, halfGridSize.y * 2.0f };
t.ComputeTRS();
debugDrawSystem->DrawCube(t.trs, SHColour::WHITE, true);
}
}
}
#endif
}
}
#endif
}
void SHNavigationSystem::UpdateNavigationRoutine::Execute(double dt) noexcept
{
SHNavigationSystem* system = static_cast<SHNavigationSystem*>(GetSystem());
auto& dense = SHComponentManager::GetDense<SHNavigationComponent>();
for (auto& comp : dense)
{
if (SHSceneManager::CheckNodeAndComponentsActive<SHNavigationComponent>(comp.GetEID()))
{
if (comp.recalculatePath)
system->GeneratePath(comp);
system->UpdateNavigationComponent(comp);
}
}
}
void SHNavigationSystem::UpdateNavigationComponent(SHNavigationComponent& comp) noexcept
{
if (comp.unreachableTarget == true || comp.path.empty())
{
comp.forward = SHVec3::Zero;
return; // no point continuing because target can't be reached / path is empty.
}
SHTransformComponent* transform = SHComponentManager::GetComponent_s<SHTransformComponent>(comp.GetEID());
if (transform)
{
NavigationGridIndex nxtPoint = comp.path.front();
SHVec3 nxtPointPos = GetGridWorldPos(nxtPoint);
SHVec3 direction = nxtPointPos - transform->GetWorldPosition();
direction.y = 0.0f;
if (direction.LengthSquared() <= comp.threshold * comp.threshold || direction.LengthSquared() <= GetGridSize().LengthSquared() * 0.5f)
{
comp.path.pop();
//Rerun this function
UpdateNavigationComponent(comp);
//return so we don't run the parts after this twice.
return;
}
comp.forward = SHVec3::Normalise(direction);
#ifdef DRAW_NAVIGATION_PATH
auto debugDrawSystem = SHSystemManager::GetSystem<SHDebugDrawSystem>();
auto queue = comp.path;
while (!queue.empty())
{
uint16_t r = queue.front().row;
uint16_t c = queue.front().column;
{
SHVec3 topleft{ origin.x - (size.x / 2.0f), origin.y, origin.z - (size.z / 2.0f) };
SHVec2 halfGridSize = GetGridSize() * 0.5f;
//offset it by row and column and center it with half grid size.
topleft += SHVec3{ c * GetGridSize().x, 0.0f, r * GetGridSize().y } + SHVec3{ halfGridSize.x,0.0f,halfGridSize.y };
SHTransform t;
t.position = GetGridWorldPos({ r,c });
t.scale = SHVec3{ halfGridSize.x * 2.0f, 1.0f, halfGridSize.y * 2.0f };
t.ComputeTRS();
debugDrawSystem->DrawCube(t.trs, SHColour::YELLOW, true);
}
queue.pop();
}
#endif
}
}
void SHNavigationSystem::GeneratePath(SHNavigationComponent& comp) noexcept
{
//Check that the entity has a transform.
if (SHComponentManager::HasComponent<SHTransformComponent>(comp.GetEID()) == false)
{
comp.unreachableTarget = true;
return;
}
std::list<NavigationNode> openList;
std::map<NavigationGridIndex, NavigationNode> closedList;
closedList.clear();
NavigationGridIndex::numColumns = numCols;
//Check if ending position is set to true in navigation data.
NavigationGridIndex endIndex = GetNavigationGridIndex(comp.target);
//if (GetNavigationData(endIndex) == true)
//{
// //Target position is unreachable.
// SHLOG_WARNING("Navigation System: GeneratePath() target position is unreachable EID: {}", comp.GetEID())
// comp.unreachableTarget = true;
// return;
//}
auto transform = SHComponentManager::GetComponent<SHTransformComponent>(comp.GetEID());
NavigationNode startingNode;
startingNode.index = GetNavigationGridIndex(transform->GetWorldPosition());
startingNode.parent.row = NullGridIndex;
startingNode.parent.column = NullGridIndex;
startingNode.h = std::numeric_limits<uint32_t>::max();
startingNode.g = 0;
startingNode.f = 0;
openList.push_back(startingNode);
NavigationNode endNode;
endNode.index.row = NullGridIndex;
endNode.index.column = NullGridIndex;
endNode.g = std::numeric_limits<uint32_t>::max();
endNode.h = std::numeric_limits<uint32_t>::max();
endNode.f = std::numeric_limits<uint32_t>::max();
while (!openList.empty())
{
NavigationNode currNode;
std::list<NavigationNode>::iterator lowestNode = openList.end();
uint32_t lowestFValue = std::numeric_limits<uint32_t>::max();
uint32_t lowestHValue = std::numeric_limits<uint32_t>::max();
for (std::list<NavigationNode>::iterator node = openList.begin(); node != openList.end(); ++node)
{
if (node->f < lowestFValue)
{
lowestNode = node;
lowestHValue = node->h;
lowestFValue = node->f;
}
if (node->f == lowestFValue && node->h < lowestHValue)
{
lowestNode = node;
lowestHValue = node->h;
}
}
currNode = *lowestNode;
openList.erase(lowestNode);
closedList.emplace(currNode.index, currNode);
if (currNode.index == endIndex)
{
endNode = currNode;
break;
}
//Add the surrounding 8 tiles into the open list
{
//Top
if (currNode.index.column > 0)
{
NavigationNode topNode;
topNode.index = currNode.index;
topNode.index.column -= 1;
topNode.parent = currNode.index;
topNode.g = currNode.g + 10;
topNode.h = HCostCalculation(topNode.index, endIndex);
topNode.f = topNode.g + topNode.h;
AddNodeToOpenList(topNode, openList, closedList, comp.tolerance);
//TopLeft
if (currNode.index.row > 0)
{
NavigationNode newNode;
newNode.index = currNode.index;
newNode.index.column -= 1;
newNode.index.row -= 1;
newNode.parent = currNode.index;
newNode.g = currNode.g + 14;
newNode.h = HCostCalculation(newNode.index, endIndex);
newNode.f = newNode.g + newNode.h;
AddNodeToOpenList(newNode, openList, closedList, comp.tolerance);
}
}
//Bottom
if (currNode.index.column < numCols - 1)
{
NavigationNode btmNode;
btmNode.index = currNode.index;
btmNode.index.column += 1;
btmNode.parent = currNode.index;
btmNode.g = currNode.g + 10;
btmNode.h = HCostCalculation(btmNode.index, endIndex);
btmNode.f = btmNode.g + btmNode.h;
AddNodeToOpenList(btmNode, openList, closedList, comp.tolerance);
//BottomRight
if (currNode.index.row < numRows - 1)
{
NavigationNode newNode;
newNode.index = currNode.index;
newNode.index.column += 1;
newNode.index.row += 1;
newNode.parent = currNode.index;
newNode.g = currNode.g + 14;
newNode.h = HCostCalculation(newNode.index, endIndex);
newNode.f = newNode.g + newNode.h;
AddNodeToOpenList(newNode, openList, closedList, comp.tolerance);
}
}
//Left
if (currNode.index.row > 0)
{
NavigationNode leftNode;
leftNode.index = currNode.index;
leftNode.index.row -= 1;
leftNode.parent = currNode.index;
leftNode.g = currNode.g + 10;
leftNode.h = HCostCalculation(leftNode.index, endIndex);
leftNode.f = leftNode.g + leftNode.h;
AddNodeToOpenList(leftNode, openList, closedList, comp.tolerance);
//BottomLeft
if (currNode.index.column < numCols - 1)
{
NavigationNode newNode;
newNode.index = currNode.index;
newNode.index.column += 1;
newNode.index.row -= 1;
newNode.parent = currNode.index;
newNode.g = currNode.g + 14;
newNode.h = HCostCalculation(newNode.index, endIndex);
newNode.f = newNode.g + newNode.h;
AddNodeToOpenList(newNode, openList, closedList, comp.tolerance);
}
}
//Right
if (currNode.index.row < numRows - 1)
{
NavigationNode rightNode;
rightNode.index = currNode.index;
rightNode.index.row += 1;
rightNode.parent = currNode.index;
rightNode.g = currNode.g + 10;
rightNode.h = HCostCalculation(rightNode.index, endIndex);
rightNode.f = rightNode.g + rightNode.h;
AddNodeToOpenList(rightNode, openList, closedList, comp.tolerance);
//TopRight
if (currNode.index.column > 0)
{
NavigationNode newNode;
newNode.index = currNode.index;
newNode.index.column -= 1;
newNode.index.row += 1;
newNode.parent = currNode.index;
newNode.g = currNode.g + 14;
newNode.h = HCostCalculation(newNode.index, endIndex);
newNode.f = newNode.g + newNode.h;
AddNodeToOpenList(newNode, openList, closedList, comp.tolerance);
}
}
}
}
//Check if there is a path.
if (endNode.g == std::numeric_limits<uint32_t>::max() || endNode.h == std::numeric_limits<uint32_t>::max() || endNode.f == std::numeric_limits<uint32_t>::max())
{
//SHLOG_WARNING("Navigation System: End Node not found after running through algo EID: {}", comp.GetEID())
//comp.unreachableTarget = true;
uint32_t lowestH = std::numeric_limits<uint32_t>::max();
for (std::map<NavigationGridIndex, NavigationNode>::iterator it = closedList.begin(); it != closedList.end(); ++it)
{
if (it->second.h < lowestH)
{
lowestH = it->second.h;
endNode = (it->second);
}
}
//return;
}
//Generate the path using end node.
std::stack<NavigationGridIndex> reversePath;
NavigationNode pathNode = endNode;
while (pathNode.index != startingNode.index)
{
reversePath.push(pathNode.index);
if (closedList.find(pathNode.parent) == closedList.end())
{
SHLOG_WARNING("Navigation System: GeneratePath() reverse path parent not in closed list EID: {}", comp.GetEID())
comp.unreachableTarget = true;
return;
}
pathNode = closedList.find(pathNode.parent)->second;
}
//Clear the queue.
while (!comp.path.empty())
{
comp.path.pop();
}
while (!reversePath.empty())
{
comp.path.push(reversePath.top());
reversePath.pop();
}
comp.recalculatePath = false;
comp.unreachableTarget = false;
}//End GeneratePath
bool SHNavigationSystem::AddNodeToOpenList(NavigationNode node, std::list<NavigationNode>& openList, std::map<NavigationGridIndex, NavigationNode>& closedList, uint16_t tolerance) noexcept
{
if (closedList.find(node.index) != closedList.end())
{
//Node is already in closed list.
return false;
}
//Check if the node is pointing to a non movable tile.
if (GetNavigationData(node.index) == true)
{
return false;
}
//Check that node surrounding this is also movable. (Controlled by tolerance).
for (uint16_t i = 0; i < tolerance; ++i)
{
NavigationGridIndex tolCheckIndex = node.index;
tolCheckIndex.row += i;
if (GetNavigationData(tolCheckIndex) == true)
return false;
tolCheckIndex = node.index;
tolCheckIndex.row -= i;
if (GetNavigationData(tolCheckIndex) == true)
return false;
tolCheckIndex = node.index;
tolCheckIndex.column += i;
if (GetNavigationData(tolCheckIndex) == true)
return false;
tolCheckIndex = node.index;
tolCheckIndex.column -= i;
if (GetNavigationData(tolCheckIndex) == true)
return false;
tolCheckIndex = node.index;
tolCheckIndex.row += i;
tolCheckIndex.column += i;
if (GetNavigationData(tolCheckIndex) == true)
return false;
tolCheckIndex = node.index;
tolCheckIndex.row -= i;
tolCheckIndex.column -= i;
if (GetNavigationData(tolCheckIndex) == true)
return false;
tolCheckIndex = node.index;
tolCheckIndex.row -= i;
tolCheckIndex.column += i;
if (GetNavigationData(tolCheckIndex) == true)
return false;
tolCheckIndex = node.index;
tolCheckIndex.row += i;
tolCheckIndex.column -= i;
if (GetNavigationData(tolCheckIndex) == true)
return false;
}
//Check if node exist in open list already
for (auto& n : openList)
{
if (n.index == node.index )
{
if (n.f > node.f)
{
n = node;
}
return false;
}
}
openList.push_back(node);
return true;
}
uint32_t SHNavigationSystem::HCostCalculation(NavigationGridIndex first, NavigationGridIndex second) noexcept
{
uint16_t rDiff = (first.row > second.row)?first.row - second.row: second.row - first.row;
uint16_t cDiff = (first.column > second.column) ? first.column - second.column : second.column - first.column;
if (rDiff > cDiff)
{
return (cDiff * 14) + ((rDiff - cDiff) * 10);
}
else
{
return (rDiff * 14 )+ ((cDiff - rDiff) * 10);
}
}
}

View File

@ -0,0 +1,98 @@
#pragma once
#include "ECS_Base/System/SHSystem.h"
#include "ECS_Base/System/SHSystemRoutine.h"
#include "SHNavigationComponent.h"
#include "Math/Vector/SHVec2.h"
#include "Math/Vector/SHVec3.h"
#include "SH_API.h"
namespace SHADE
{
struct SH_API NavigationNode
{
NavigationGridIndex index;
NavigationGridIndex parent;
uint32_t g;
uint32_t h;
uint32_t f;
};
class SH_API SHNavigationSystem final: public SHSystem
{
private:
using GridDataType = uint8_t;
const size_t NumGridDataTypeBits = sizeof(GridDataType) * CHAR_BIT;
std::vector<uint8_t> navigationGrid;
//Number of subdivision on the x axis
uint16_t numRows{0};
//Number of subdivision on the z axis
uint16_t numCols{0};
//The center of the navigation area.
SHVec3 origin{0.0f};
//Size of the navigation area
SHVec3 size{0.0f};
SHVec2 GetGridSize() noexcept;
uint16_t GetIndex(uint16_t row, uint16_t col) noexcept;
uint32_t HCostCalculation(NavigationGridIndex first, NavigationGridIndex second) noexcept; //TO DO
bool AddNodeToOpenList(NavigationNode node, std::list<NavigationNode>& openList, std::map<NavigationGridIndex, NavigationNode>& closedList, uint16_t tolerance) noexcept;
void UpdateNavigationComponent(SHNavigationComponent& comp) noexcept;
SHVec3 GetGridWorldPos(NavigationGridIndex index) noexcept;
public:
SHNavigationSystem() = default;
virtual ~SHNavigationSystem() = default;
virtual void Init();
virtual void Exit();
void GenerateNavigationGridData(SHVec3 origin, SHVec3 size, uint16_t numRow, uint16_t numCol) noexcept;
void GeneratePath(SHNavigationComponent& comp) noexcept;
bool GetNavigationData(uint16_t row, uint16_t col) noexcept;
bool GetNavigationData(NavigationGridIndex index) noexcept;
NavigationGridIndex GetNavigationGridIndex(SHVec3 const& worldPosition) noexcept;
class SH_API NavigationSystemGenerateRoutine final: public SHSystemRoutine
{
public:
NavigationSystemGenerateRoutine() : SHSystemRoutine("Navigation System Generate", true) {};
virtual void Execute(double dt)noexcept override final;
};
friend class NavigationSystemGenerateRoutine;
class SH_API UpdateNavigationRoutine final: public SHSystemRoutine
{
public:
UpdateNavigationRoutine() : SHSystemRoutine("Update Navigation Routine", false) {};
virtual void Execute(double dt)noexcept override final;
};
friend class UpdateNavigationRoutine;
};
}

View File

@ -243,6 +243,8 @@ namespace SHADE
AddComponentToComponentNode<SHToggleButtonComponent>(components, eid); AddComponentToComponentNode<SHToggleButtonComponent>(components, eid);
AddComponentToComponentNode<SHSliderComponent>(components, eid); AddComponentToComponentNode<SHSliderComponent>(components, eid);
AddComponentToComponentNode<SHNavigationComponent>(components, eid);
AddComponentToComponentNode<SHTextRenderableComponent>(components, eid); AddComponentToComponentNode<SHTextRenderableComponent>(components, eid);
AddComponentToComponentNode<SHAnimatorComponent>(components, eid); AddComponentToComponentNode<SHAnimatorComponent>(components, eid);
AddComponentToComponentNode<SHUIComponent>(components, eid); AddComponentToComponentNode<SHUIComponent>(components, eid);
@ -306,6 +308,9 @@ namespace SHADE
AddComponentID<SHButtonComponent>(componentIDList, componentsNode); AddComponentID<SHButtonComponent>(componentIDList, componentsNode);
AddComponentID<SHToggleButtonComponent>(componentIDList, componentsNode); AddComponentID<SHToggleButtonComponent>(componentIDList, componentsNode);
AddComponentID<SHSliderComponent>(componentIDList, componentsNode); AddComponentID<SHSliderComponent>(componentIDList, componentsNode);
AddComponentID<SHNavigationComponent>(componentIDList, componentsNode);
AddComponentID<SHTextRenderableComponent>(componentIDList, componentsNode); AddComponentID<SHTextRenderableComponent>(componentIDList, componentsNode);
AddComponentID<SHAnimatorComponent>(componentIDList, componentsNode); AddComponentID<SHAnimatorComponent>(componentIDList, componentsNode);
AddComponentID<SHUIComponent>(componentIDList, componentsNode); AddComponentID<SHUIComponent>(componentIDList, componentsNode);
@ -394,6 +399,9 @@ namespace SHADE
SHSerializationHelper::InitializeComponentFromNode<SHButtonComponent>(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode<SHButtonComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHToggleButtonComponent>(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode<SHToggleButtonComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHSliderComponent>(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode<SHSliderComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHNavigationComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHTextRenderableComponent>(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode<SHTextRenderableComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHLightComponent>(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode<SHLightComponent>(componentsNode, eid);
SHSerializationHelper::InitializeComponentFromNode<SHAnimatorComponent>(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode<SHAnimatorComponent>(componentsNode, eid);

View File

@ -0,0 +1,63 @@
#include "SHpch.h"
#include "Navigation.hxx"
#include "ECS_Base/Managers/SHSystemManager.h"
#include "Navigation/SHNavigationSystem.h"
namespace SHADE
{
Navigation::Navigation(Entity entity)
:Component(entity)
{
}
float Navigation::Threshold::get()
{
return GetNativeComponent()->threshold;
}
void Navigation::Threshold::set(float val)
{
GetNativeComponent()->threshold = val;
}
Vector3 Navigation::Target::get()
{
return Convert::ToCLI(GetNativeComponent()->GetTarget());
}
void Navigation::Target::set(Vector3 val)
{
GetNativeComponent()->SetTarget(Convert::ToNative(val));
}
Vector3 Navigation::GetForward()
{
return Convert::ToCLI(GetNativeComponent()->GetForward());
}
bool Navigation::GetUnreachableTarget()
{
return GetNativeComponent()->GetUnreachableTarget();
}
void Navigation::MoveTo(Vector3 val)
{
GetNativeComponent()->SetTarget(Convert::ToNative(val));
auto system = SHSystemManager::GetSystem<SHNavigationSystem>();
if (system)
{
system->GeneratePath(*GetNativeComponent());
}
}
bool Navigation::ReachedTarget()
{
return GetNativeComponent()->ReachedTarget();
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "Components/Component.hxx"
#include "Math/Vector3.hxx"
//External Dependencies
#include "Navigation/SHNavigationComponent.h"
namespace SHADE
{
public ref class Navigation : public Component<SHNavigationComponent>
{
internal:
Navigation(Entity entity);
public:
property float Threshold
{
float get();
void set(float val);
}
property Vector3 Target
{
Vector3 get();
void set(Vector3 val);
}
Vector3 GetForward();
bool GetUnreachableTarget();
bool ReachedTarget();
void MoveTo(Vector3 val);
};
}

View File

@ -52,7 +52,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Components\TrajectoryRenderable.hxx" #include "Components\TrajectoryRenderable.hxx"
#include "Components\Animator.hxx" #include "Components\Animator.hxx"
#include "Components\ParticleEmitter.hxx" #include "Components\ParticleEmitter.hxx"
#include "Components\Navigation.hxx"
namespace SHADE namespace SHADE
@ -344,6 +344,7 @@ namespace SHADE
componentMap.Add(createComponentSet<SHTrajectoryRenderableComponent, TrajectoryRenderable>()); componentMap.Add(createComponentSet<SHTrajectoryRenderableComponent, TrajectoryRenderable>());
componentMap.Add(createComponentSet<SHAnimatorComponent, Animator>()); componentMap.Add(createComponentSet<SHAnimatorComponent, Animator>());
componentMap.Add(createComponentSet<SHParticleEmitterComponent, ParticleEmitter>()); componentMap.Add(createComponentSet<SHParticleEmitterComponent, ParticleEmitter>());
componentMap.Add(createComponentSet<SHNavigationComponent, Navigation>());
} }
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/