working on humbies ai

This commit is contained in:
2025-10-14 16:05:57 +02:00
parent 477ed62c75
commit 1d15377885
6 changed files with 438 additions and 109 deletions

View File

@@ -6,6 +6,7 @@ using RebootKit.Engine.Extensions;
using RebootKit.Engine.Main;
using RebootKit.Engine.Network;
using RebootKit.Engine.Simulation;
using RebootReality.jelycho.Beacons;
using RebootReality.jelycho.Player;
using TriInspector;
using Unity.Mathematics;
@@ -61,33 +62,65 @@ namespace RebootReality.jelycho.Enemies {
}
}
public class ZombieBlackboard {
public ZombieActor Self;
class ZombiePickVictim : IStrategy {
public BehaviourNode.Status Process(Actor target, float dt) {
if (target is not ZombieActor zombie) {
return BehaviourNode.Status.Failure;
}
Debug.Log("Picking victim");
Actor victim = zombie.FindNewVictim();
if (victim == null) {
return BehaviourNode.Status.Failure;
}
return BehaviourNode.Status.Success;
}
}
class ZombieGoToPlayer : IStrategy {
public BehaviourNode.Status Process(Actor target, float dt) {
if (target is not ZombieActor zombie) {
return BehaviourNode.Status.Failure;
}
Debug.Log("Goto player");
if (!zombie.HasTravelDestination) {
float3 victimPos = zombie.Victim.transform.position;
float dstToVictimSq = math.distancesq(victimPos, zombie.transform.position);
if (dstToVictimSq < 1.0f) {
return BehaviourNode.Status.Success;
}
zombie.GoTo(victimPos);
return BehaviourNode.Status.Running;
}
return BehaviourNode.Status.Running;
}
}
[DeclareBoxGroup("Body parts")]
[DeclareBoxGroup("Animations")]
public class ZombieActor : Actor, IKillable {
static readonly Logger s_Logger = new Logger(nameof(ZombieActor));
static readonly int s_MovementSpeedHash = Animator.StringToHash("MovementSpeed");
enum AIState {
Idle,
Dead,
AttackBase,
AttackCharacter,
PanicEscape,
Berserk
}
[SerializeField] AnimancerComponent m_Animancer;
[SerializeField] NavMeshAgent m_NavAgent;
public NavMeshAgent NavAgent {
get {
return m_NavAgent;
}
}
[SerializeField] Collider m_RootCollider;
[SerializeField] Rigidbody[] m_RagdollRigidbodies;
[SerializeField] float m_MaxAttackDistance = 1.0f;
[SerializeField] float m_MaxAttackDistance = 2.0f;
[SerializeField] float m_LoseInterestMinDistance = 10.0f;
[SerializeField] ulong m_BaseDamage = 10;
[SerializeField] float m_AttackDelay = 1.0f;
@@ -97,31 +130,50 @@ namespace RebootReality.jelycho.Enemies {
[SerializeField, Group("Body parts")] ZombieBodyPart m_RightArm;
[SerializeField, Group("Body parts")] ZombieBodyPart m_LeftLeg;
[SerializeField, Group("Body parts")] ZombieBodyPart m_RightLeg;
AIState m_State = AIState.Idle;
PlayerActor m_PlayerTarget;
float m_NextAttackTimer;
public UnityEvent died = new UnityEvent();
[SerializeField, Group("Animations")] AnimationClip[] m_AttackClips;
BehaviourTree m_BehaviourTree;
public enum MindState {
Normal,
RunAway,
Berserk
}
public bool IsRagdoll { get; private set; } = false;
public MindState Mind { get; private set; } = MindState.Normal;
public Actor Victim { get; private set; }
public bool HasTravelDestination { get; private set; }
public float3 TravelDestination { get; private set; }
public UnityEvent died = new UnityEvent();
//
// @MARK: Unity callbacks
//
void Awake() {
SetRagdollLocal(false);
SetRagdollLocal(IsRagdoll);
m_BehaviourTree = new BehaviourTree("Zombie Behaviour");
var rootSelector = new Selector("Root");
m_BehaviourTree.AddChild(rootSelector);
var attackPlayerSequence = new Sequence("Attack Player",
() => m_PlayerTarget != null);
rootSelector.AddChild(attackPlayerSequence);
rootSelector.AddChild(CreateNormalSequence());
}
BehaviourNode CreateNormalSequence() {
var normalSequence = new Sequence("Normal", () => Mind == MindState.Normal);
normalSequence.AddChild(new Leaf("Pick Victim", new ZombiePickVictim()));
var attackPlayerSequence = new Sequence("Attack Player", IsVictimPlayer);
normalSequence.AddChild(attackPlayerSequence);
attackPlayerSequence.AddChild(new Leaf("Go to Player", new ZombieGoToPlayer()));
var attackMotherSequence = new Sequence("Attack Mother", IsVictimMother);
normalSequence.AddChild(attackMotherSequence);
return normalSequence;
}
//
@@ -146,107 +198,69 @@ namespace RebootReality.jelycho.Enemies {
return;
}
m_BehaviourTree.Process(deltaTime);
return;
if (HasTravelDestination) {
float3 pos = transform.position;
switch (m_State) {
case AIState.Idle: {
ServerTickIdle(deltaTime);
break;
}
case AIState.AttackBase: {
ServerTickAttackBase(deltaTime);
break;
}
case AIState.AttackCharacter: {
ServerTickAttackCharacter(deltaTime);
break;
}
case AIState.PanicEscape: {
break;
if (math.distancesq(pos, TravelDestination) <= 1.0f) {
HasTravelDestination = false;
}
}
case AIState.Berserk: {
ServerTickBerserk(deltaTime);
break;
}
}
m_BehaviourTree.Process(this, deltaTime);
}
//
// @MARK: Zombie
//
void ServerTickIdle(float dt) {
public bool GoTo(float3 pos) {
if (!RR.IsServer()) {
s_Logger.Error("Only server can call GoTo");
return false;
}
TravelDestination = pos;
HasTravelDestination = true;
return m_NavAgent.SetDestination(TravelDestination);
}
public bool IsVictimPlayer() {
return Victim is PlayerActor;
}
public bool IsVictimMother() {
return Victim is MotherActor;
}
public Actor FindNewVictim() {
if (!RR.IsServer()) {
s_Logger.Error("Only server can call FindNewVictim");
return null;
}
Victim = null;
(PlayerActor playerActor, float distSqToPlayer) = FindClosestPlayerActor(transform.position);
if (playerActor == null || distSqToPlayer >= m_LoseInterestMinDistance * m_LoseInterestMinDistance) {
return;
if (playerActor != null && distSqToPlayer < m_LoseInterestMinDistance * m_LoseInterestMinDistance) {
Victim = playerActor;
return Victim;
}
m_State = AIState.AttackCharacter;
m_PlayerTarget = playerActor;
if (RR.World.Context is WorldContext ctx) {
Victim = ctx.BaseManager.Mother;
return Victim;
}
s_Logger.Info($"Found player actor to attack: {m_PlayerTarget}");
m_NavAgent.SetDestination(m_PlayerTarget.transform.position);
m_NavAgent.isStopped = false;
return null;
}
void ServerTickAttackCharacter(float dt) {
if (m_PlayerTarget == null || !m_PlayerTarget.IsAlive()) {
SetIdleState();
return;
}
float3 playerPos = m_PlayerTarget.transform.position;
float3 zombiePos = transform.position;
float distToPlayerSq = math.distancesq(playerPos, zombiePos);
if (distToPlayerSq >= m_LoseInterestMinDistance * m_LoseInterestMinDistance) {
SetIdleState();
return;
}
if (distToPlayerSq <= m_MaxAttackDistance * m_MaxAttackDistance) {
m_NextAttackTimer -= dt;
if (m_NextAttackTimer <= 0.0f) {
// m_Animator.CrossFade("Attack_0", 0.0f, 0);
m_NextAttackTimer = m_AttackDelay;
}
if (!m_NavAgent.isStopped) {
m_NavAgent.isStopped = true;
}
return;
}
float distFromDstToTargetSq = math.distancesq(playerPos, m_NavAgent.destination);
if (distFromDstToTargetSq > 1.0f) {
m_NavAgent.isStopped = false;
m_NavAgent.SetDestination(m_PlayerTarget.transform.position);
}
}
void ServerTickAttackBase(float dt) {
}
void ServerTickBerserk(float dt) {
}
void SetIdleState() {
m_PlayerTarget = null;
m_State = AIState.Idle;
public void PerformAttack() {
m_Animancer.Play(m_AttackClips.Random());
}
void Die() {
s_Logger.Info("Die");
EnableRagdoll();
m_NavAgent.enabled = false;
m_State = AIState.Dead;
died.Invoke();
}
@@ -281,7 +295,7 @@ namespace RebootReality.jelycho.Enemies {
}
protected override void OnActorEventClient(ActorEvent actorEvent) {
ZombieActorEvents zombieEvent = (ZombieActorEvents) actorEvent.EventID;
var zombieEvent = (ZombieActorEvents) actorEvent.EventID;
switch (zombieEvent) {
case ZombieActorEvents.EnableRagdoll: {

View File

@@ -1,5 +1,153 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7002381419506676794
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2468028753576640433}
- component: {fileID: 5396192429513740654}
m_Layer: 8
m_Name: Trail
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &2468028753576640433
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7002381419506676794}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.032, y: 0.627, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 711236274234327174}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!96 &5396192429513740654
TrailRenderer:
serializedVersion: 3
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7002381419506676794}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 0
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 0
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: bbf6c47920b8378488083e25f9ab31e1, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_Time: 1
m_PreviewTimeScale: 1
m_Parameters:
serializedVersion: 3
widthMultiplier: 0.74
widthCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: -3.2915306
outSlope: -3.2915306
tangentMode: 0
weightedMode: 0
inWeight: 0.0218094
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
colorGradient:
serializedVersion: 2
key0: {r: 1, g: 1, b: 1, a: 1}
key1: {r: 1, g: 1, b: 1, a: 1}
key2: {r: 0, g: 0, b: 0, a: 0}
key3: {r: 0, g: 0, b: 0, a: 0}
key4: {r: 0, g: 0, b: 0, a: 0}
key5: {r: 0, g: 0, b: 0, a: 0}
key6: {r: 0, g: 0, b: 0, a: 0}
key7: {r: 0, g: 0, b: 0, a: 0}
ctime0: 0
ctime1: 65535
ctime2: 0
ctime3: 0
ctime4: 0
ctime5: 0
ctime6: 0
ctime7: 0
atime0: 0
atime1: 65535
atime2: 0
atime3: 0
atime4: 0
atime5: 0
atime6: 0
atime7: 0
m_Mode: 0
m_ColorSpace: -1
m_NumColorKeys: 2
m_NumAlphaKeys: 2
numCornerVertices: 6
numCapVertices: 0
alignment: 1
textureMode: 0
textureScale: {x: 1, y: 1}
shadowBias: 0.5
generateLightingData: 0
m_MinVertexDistance: 0.1
m_MaskInteraction: 0
m_Autodestruct: 0
m_Emitting: 1
m_ApplyActiveColorSpace: 1
--- !u!1 &7390605608055178527
GameObject:
m_ObjectHideFlags: 0
@@ -34,6 +182,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8232855175872471746}
- {fileID: 2468028753576640433}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &3623051023707969355
@@ -69,8 +218,8 @@ MonoBehaviour:
- actor:
m_AssetGUID: 50a5e87baaedf5b46ba52646e8e88b74
m_SubObjectName:
m_SubObjectGUID:
m_SubObjectType:
m_SubObjectGUID:
m_EditorAssetChanged: 0
slotName: dagger_left
handsAnimationClipsSets:

View File

@@ -0,0 +1,157 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-3534035237265524349
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: m_weapon_trail
m_Shader: {fileID: 4800000, guid: 8516d7a69675844a7a0b7095af7c46af, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords:
- _FLIPBOOKBLENDING_OFF
m_LightmapFlags: 0
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BlendOp: 0
- _BumpScale: 1
- _CameraFadingEnabled: 0
- _CameraFarFadeDistance: 2
- _CameraNearFadeDistance: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _ColorMode: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DistortionBlend: 0.5
- _DistortionEnabled: 0
- _DistortionStrength: 1
- _DistortionStrengthScaled: 0
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EmissionEnabled: 0
- _EnvironmentReflections: 1
- _FlipbookBlending: 0
- _FlipbookMode: 0
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _LightingEnabled: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessSource: 0
- _SmoothnessTextureChannel: 0
- _SoftParticlesEnabled: 0
- _SoftParticlesFarFadeDistance: 1
- _SoftParticlesNearFadeDistance: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _BaseColorAddSubDiff: {r: 0, g: 0, b: 0, a: 0}
- _CameraFadeParams: {r: 0, g: Infinity, b: 0, a: 0}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SoftParticleFadeParams: {r: 0, g: 0, b: 0, a: 0}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bbf6c47920b8378488083e25f9ab31e1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant: