burgelkat Posted February 11 Share Posted February 11 MonsterSkeleton = {} MonsterSkeleton.name = "MonsterSkeleton" MonsterSkeleton.team = 2 MonsterSkeleton.alive = true MonsterSkeleton.nextscanfortargettime = 0 MonsterSkeleton.scanrange = 100 MonsterSkeleton.eyeheight = 1.65 MonsterSkeleton.sound_attack = {} MonsterSkeleton.navmeshindex = 1 MonsterSkeleton.damage = 20 MonsterSkeleton.health = 100 MonsterSkeleton.meleedamage = 20 -- Animationssteuerung für das Skelett MonsterSkeleton.enabled = false MonsterSkeleton.idle = false MonsterSkeleton.animation = false MonsterSkeleton.GetUpPlayed = false MonsterSkeleton.getUpDelay = 0 MonsterSkeleton.getUpStart = 0 MonsterSkeleton.idleDelay = 6000 function MonsterSkeleton:Enable() self.enabled = true self.animation = true self.getUpStart = Millisecs() -- Startzeit für GetUp setzen Component(self):Enable() end function MonsterSkeleton:Start() -- Sicherstellen, dass self.entity existiert if not self.entity then DebugLog("Fehler: self.entity ist nil!") return end -- Skelett-Modell laden und in liegender Position starten self.skeleton = LoadModel(world, "Models/Characters/Skeletton/Skeleton_complete.mdl") self.skeleton:SetRotation(0, 180, 0) -- Liegt auf dem Rücken self.skeleton:SetPosition(self.entity:GetPosition()) local entity = Model(self.skeleton) local model = Model(self.skeleton) entity.team = self.team entity.health = self.health self.health = nil -- GetUp Verzögerung setzen self.getUpDelay = math.random(1000, 8000) -- Initiale Animation stoppen self.skeleton:Animate(1, 0.25, 2000, ANIMATION_STOP, 0) -- Sound laden und Lautstärke setzen local soundFile = self.soundPath self.skeletonsound = CreateSpeaker(LoadSound(soundFile)) self.skeletonsound:SetLooping(false) -- Navigation if self.navmesh then self.agent = CreateNavAgent(self.navmesh, 0.5, 1.8) self.agent:SetPosition(entity:GetPosition(true)) self.agent:SetRotation(entity:GetRotation(true).y) entity:SetPosition(0, 0, 0) entity:SetRotation(0, 180, 0) entity:Attach(self.agent) end entity:SetNavObstacle(false) entity:SetCollisionType(COLLISION_PLAYER) entity:SetMass(0) entity:SetPhysicsMode(PHYSICS_RIGIDBODY) entity:AddTag("player") if model then local seq = model:FindAnimation("Attack1") if seq ~= 0 then local count = model:CountAnimationFrames(seq) model.skeleton:AddHook(seq, count - 1, Monster.EndAttackHook, self) end seq = model:FindAnimation("Attack1") if seq ~= 0 then local count = model:CountAnimationFrames(seq) model.skeleton:AddHook(seq, count - 1, Monster.EndAttackHook, self) end end end function MonsterSkeleton:Load(properties, binstream, scene, flags, extra) if type(properties.alertsound) == "string" then self.sound_alert = LoadSound(properties.alertsound) end for n = 1, 2 do local key = "attacksound" .. tostring(n) if type(properties[key]) == "string" then self.sound_attack[n] = LoadSound(properties[key]) end end self.navmesh = nil if #scene.navmeshes > 0 then self.navmesh = scene.navmeshes[self.navmeshindex] end return true end -- "GetUp" Animation nach zufälliger Verzögerung starten function MonsterSkeleton:GetUp() if self.GetUpPlayed then return end -- Falls bereits abgespielt, nichts tun local currentTime = Millisecs() if currentTime - self.getUpStart >= self.getUpDelay then self.skeletonsound:Play() self.skeleton:Animate(1, 0.5, 2000, ANIMATION_ONCE) self.GetUpPlayed = true self.startTime = Millisecs() -- Startzeit für Idle-Übergang setzen end end -- Wechsel zu Idle-Animation function MonsterSkeleton:Idle() if not self.idle then return end if self.idle then self.skeleton:Animate(0, 0.7, 2000, ANIMATION_LOOP) end end function MonsterSkeleton.EndAttackHook(skeleton, monster) -- Uncomment the following block if you want to implement the attack logic -- if not monster.attackfinished then -- monster.attackfinished = true -- local target = monster.target:lock() -- local entity = monster:GetEntity() -- local attackrange = 2.5 -- if entity:GetDistance(target) < attackrange then -- for _, c in ipairs(target.components) do -- local base = c:As("BaseComponent") -- if base then base:Damage(1, entity) end -- end -- end -- end local monster = Component(monster) monster.attacking = false end function MonsterSkeleton:Kill() if not self.alive then return end self.alive = false self.skeleton:Animate("Death", 1, 250, ANIMATION_ONCE) if self.agent then self.agent:Stop() self.entity:Detach() self.agent = nil end self.entity:SetCollisionType(COLLISION_NONE) self.entity:SetPickMode(PICK_NONE) self:Disable() end -- Update-Funktion: Steuerung von GetUp und Idle function MonsterSkeleton:Update() local entity = Model(self.skeleton) local model = Model(self.skeleton) if not self.enabled then return end if self.enabled then if self.animation and not self.GetUpPlayed then self:GetUp() elseif self.GetUpPlayed and not self.target and Millisecs() - self.startTime >= self.idleDelay then self:Idle() self.idle=true --self.ScanForTarget() end if not self.target then self:ScanForTarget() end if entity.health <= 0 then self:Disable() return end local entity = Model(self.skeleton) local model = Model(self.skeleton) -- Stop attacking if target is dead if self.target then if self.target.health ~= nil and type(self.target.health) == "number" and self.target.health <= 0 then self.target = nil if self.agent then self.agent:Stop() self.Idle() self.idle=true --self.ScanForTarget() end end end if self.attacking and self.target then if self.agent then local a = ATan(entity.matrix.t.x - self.target.matrix.t.x, entity.matrix.t.z - self.target.matrix.t.z) self.agent:SetRotation(a + 180) end if not self.attackfinished then local world = self.entity:GetWorld() if world then if world:GetTime() - self.meleeattacktime > 300 then self.attackfinished = true local pos = entity:GetPosition(true) local dest = self.target:GetPosition(true) + self.target:GetVelocity() local attackrange = 3.0 if pos:DistanceToPoint(dest) < attackrange then self.target.health = self.target.health - self.meleedamage for _, c in ipairs(self.target.components) do if c.Damage and type(c.Damage) == "function" then c:Damage(self.meleedamage, entity) if self.target.health <= 0 and type(c.Kill) == "function" then c:Kill(entity) end end end if self.target.health <= 0 then self.target = nil end end end end end return end if not self.target then self:ScanForTarget() end if self.target then self.idle=false if self.attacking == false and entity:GetDistance(self.target) > 3.0 then if self.agent then self.agent:Navigate(self.target:GetPosition(true)) self.agent:SetMaxSpeed(6) end if model then model:Animate("Walk") end else if self.agent then self.agent:Stop() end if not self.attacking then local world = entity:GetWorld() if world then self.meleeattacktime = world:GetTime() end local index = Ceil(math.random(1, 2)) if model then model:Animate("Attack" .. tostring(index), 1, 100, ANIMATION_ONCE) end if Ceil(Random(0, 2)) == 0 then index = Ceil(Random(1, 2)) if self.sound_attack[index] then entity:EmitSound(self.sound_attack[index]) end end self.attacking = true self.attackfinished = false end end else if not self.idle then self:Idle() end end end end -- Kampflogik für das Monster bleibt erhalten function MonsterSkeleton:Damage(amount, attacker) if entity.health <= 0 then return end if entity.health <= 0 then local n --[[for n = 1, #self.entity.components do if type(self.entity.components[n].Kill) == "function" then self.entity.components[n]:Kill(attacker) end end]] return end if self.target == nil and attacker ~= Model(self.skeleton) then self.target = attacker end --Make him angry if someone hurts him! end function MonsterSkeleton.RayFilter(entity, extra) local ctype = entity:GetCollisionType() return ctype == COLLISION_SCENE or ctype == COLLISION_PROP end function MonsterSkeleton:ScanForTarget() local entity = Model(self.skeleton) local world = entity:GetWorld() if world then local now = world:GetTime() if now < self.nextscanfortargettime then return end self.nextscanfortargettime = now + math.random(300, 500) local temp = entity:GetPickMode() entity:SetPickMode(PICK_NONE) local players = world:GetTaggedEntities("good") for _, player in ipairs(players) do if player.health ~= nil and type(player.health) == "number" and player.health > 0 then if player:GetDistance(entity) < self.scanrange then local temp2 = player:GetPickMode() player:SetPickMode(PICK_NONE) local A = entity:GetPosition(true) + Vec3(0, self.eyeheight, 0) local B = player:GetPosition(true) + Vec3(0, 0.5, 0) local pick = world:Pick(A, B, 0, false) player:SetPickMode(temp2) if not pick.success then self.target = player break end end end end entity:SetPickMode(temp) end end function MonsterSkeleton:Save(properties, binstream, scene, flags, extra) properties.health = self.entity.health end RegisterComponent("MonsterSkeleton", MonsterSkeleton) return MonsterSkeleton Quote Hello, maybe someone can help. I tryed to combine monster AI Script with my sceleton get up Script. In the script, the skeleton is activated and immediately executes the Attack function. However, it should first play the GetUp function completely and then execute the Scan for Target function. And, the Attack function only be executed once, and the Walk function run continuously. sometimes the sceleton get up and do nothing . what i do wrong here and or where i get lost in that code 😅 second question. I have a build a nav mesh. But the monster ai don’t use it. Even not if i use josh‘s Monster AI script on my sceleton model . Is there something to pay attention ? (It seems ultraengine is heredifferent to leadwerks ) Sorry but i try to learn all in Ultraengine again 😅 Quote Link to comment Share on other sites More sharing options...
Dreikblack Posted February 11 Share Posted February 11 29 minutes ago, burgelkat said: have a build a nav mesh. But the monster ai don’t use it. Even not if i use josh‘s Monster AI script on my sceleton model Do you use it as prefab? If yes, then you need to "break" it. Quote Link to comment Share on other sites More sharing options...
burgelkat Posted February 11 Author Share Posted February 11 No it is no prefab Quote Link to comment Share on other sites More sharing options...
burgelkat Posted February 12 Author Share Posted February 12 MonsterSkeleton = {} MonsterSkeleton.name = "MonsterSkeleton" MonsterSkeleton.team = 2 MonsterSkeleton.alive = true MonsterSkeleton.nextscanfortargettime = 0 MonsterSkeleton.scanrange = 100 MonsterSkeleton.eyeheight = 1.65 MonsterSkeleton.sound_attack = {} MonsterSkeleton.navmeshindex = 1 MonsterSkeleton.damage = 20 MonsterSkeleton.health = 100 MonsterSkeleton.meleedamage = 20 -- Animationssteuerung für das Skelett MonsterSkeleton.enabled = false MonsterSkeleton.idle = false MonsterSkeleton.animation = false MonsterSkeleton.GetUpPlayed = false MonsterSkeleton.getUpDelay = 0 MonsterSkeleton.getUpStart = 0 MonsterSkeleton.idleDelay = 6000 MonsterSkeleton.combatready = false function MonsterSkeleton:Enable() self.enabled = true self.animation = true self.getUpStart = Millisecs() -- Startzeit für GetUp setzen Component(self):Enable() end function MonsterSkeleton:Start() -- Skelett-Modell laden und in liegender Position starten self.skeleton = LoadModel(world, "Models/Characters/Skeletton/Skeleton_complete.mdl") self.skeleton:SetRotation(0, 0, 0) -- Liegt auf dem Rücken self.skeleton:SetPosition(self.entity:GetPosition()) local entity = Model(self.skeleton) local model = Model(self.skeleton) entity.team = self.team entity.health = self.health self.health = nil -- GetUp Verzögerung setzen self.getUpDelay = math.random(1000, 8000) -- Initiale Animation stoppen self.skeleton:Animate(1, 0.25, 2000, ANIMATION_STOP, 0) -- Sound laden und Lautstärke setzen local soundFile = self.soundPath self.skeletonsound = CreateSpeaker(LoadSound(soundFile)) self.skeletonsound:SetLooping(false) -- Navigation if self.navmesh then self.agent = CreateNavAgent(self.navmesh, 0.5, 1.8) self.agent:SetPosition(entity:GetPosition(true)) self.agent:SetRotation(entity:GetRotation(true).y) entity:SetPosition(0, 0, 0) entity:SetRotation(0, 180, 0) entity:Attach(self.agent) end entity:SetNavObstacle(false) entity:SetCollisionType(COLLISION_PLAYER) entity:SetMass(0) entity:SetPhysicsMode(PHYSICS_RIGIDBODY) entity:AddTag("player") if model then local seq = model:FindAnimation("Attack1") if seq ~= 0 then local count = model:CountAnimationFrames(seq) model.skeleton:AddHook(seq, count - 1, Monster.EndAttackHook, self) end end end function MonsterSkeleton:Load(properties, binstream, scene, flags, extra) if type(properties.alertsound) == "string" then self.sound_alert = LoadSound(properties.alertsound) end for n = 1, 2 do local key = "attacksound" .. tostring(n) if type(properties[key]) == "string" then self.sound_attack[n] = LoadSound(properties[key]) end end self.navmesh = nil if #scene.navmeshes > 0 then self.navmesh = scene.navmeshes[self.navmeshindex] end return true end -- "GetUp" Animation nach zufälliger Verzögerung starten function MonsterSkeleton:GetUp() if not self.GetUpPlayed then local currentTime = Millisecs() if currentTime - self.getUpStart >= self.getUpDelay then self.skeletonsound:Play() self.skeleton:Animate(1, 0.5, 2000, ANIMATION_ONCE) self.GetUpPlayed = true self.startTime = Millisecs() -- Startzeit für Idle-Übergang setzen end end end -- Wechsel zu Idle-Animation function MonsterSkeleton:Idle() if self.GetUpPlayed then self.skeleton:Animate(0, 0.7, 2000, ANIMATION_LOOP) -- if self.idle==false then self.idle=true --end end end function MonsterSkeleton.EndAttackHook(skeleton, monster) local monster = Component(monster) monster.attacking = false end function MonsterSkeleton:Kill() if not self.alive then return end self.alive = false self.skeleton:Animate("Death", 1, 250, ANIMATION_ONCE) if self.agent then self.agent:Stop() self.skeleton:Detach() self.agent = nil end self.skeleton:SetCollisionType(COLLISION_NONE) self.skeleton:SetPickMode(PICK_NONE) self:Disable() end -- Update-Funktion: Steuerung von GetUp und Idle function MonsterSkeleton:Update() local entity = Model(self.skeleton) local model = Model(self.skeleton) if entity.health <= 0 then self:Kill() return end if not self.enabled then return end if self.enabled then if not self.combatready then if self.animation and not self.GetUpPlayed then self:GetUp() end if self.GetUpPlayed and Millisecs() - self.startTime >= self.idleDelay then self:Idle() end if self.GetUpPlayed and self.idle then self.combatready=true end end if self.combatready then if self.combatready then self:ScanForTarget() end -- Stop attacking if target is dead if self.target then if self.target.health ~= nil and type(self.target.health) == "number" and self.target.health <= 0 then self.target = nil if self.agent then self.agent:Stop() end end end if self.attacking and self.target then if self.agent then local a = ATan(entity.matrix.t.x - self.target.matrix.t.x, entity.matrix.t.z - self.target.matrix.t.z) self.agent:SetRotation(a + 180) end if not self.attackfinished then local world = self.entity:GetWorld() if world then if world:GetTime() - self.meleeattacktime > 300 then self.attackfinished = true local pos = entity:GetPosition(true) local dest = self.target:GetPosition(true) + self.target:GetVelocity() local attackrange = 3.0 if pos:DistanceToPoint(dest) < attackrange then self.target.health = self.target.health - self.meleedamage for _, c in ipairs(self.target.components) do if c.Damage and type(c.Damage) == "function" then c:Damage(self.meleedamage, entity) if self.target.health <= 0 and type(c.Kill) == "function" then c:Kill(entity) end end end if self.target.health <= 0 then self.target = nil self:Idle() end end end end end return end --if not self.target and self.combatready then self:ScanForTarget() end if self.target then self.idle=false if self.attacking == false and entity:GetDistance(self.target) > 3.0 then if self.agent then self.agent:Navigate(self.target:GetPosition(true)) self.agent:SetMaxSpeed(10) end if model then model:Animate("Walk") end else if self.agent then self.agent:Stop() end if not self.attacking then local world = entity:GetWorld() if world then self.meleeattacktime = world:GetTime() end local index = Ceil(math.random(1, 1)) if model then model:Animate("Attack" .. tostring(index), 1, 100, ANIMATION_ONCE) end if Ceil(Random(0, 1)) == 0 then index = Ceil(Random(1, 1)) -- if self.sound_attack[index] then entity:EmitSound(self.sound_attack[index]) end end self.attacking = true self.attackfinished = false end end else -- self:Idle() end end end end -- Kampflogik für das Monster bleibt erhalten function MonsterSkeleton:Damage(amount, attacker) if entity.health <= 0 then return end if entity.health <= 0 then local n return end if self.target == nil and attacker ~= Model(self.skeleton) then self.target = attacker end --Make him angry if someone hurts him! end function MonsterSkeleton.RayFilter(entity, extra) local ctype = self.skeleton:GetCollisionType() return ctype == COLLISION_SCENE or ctype == COLLISION_PROP end function MonsterSkeleton:ScanForTarget() local entity = Model(self.skeleton) local world = entity:GetWorld() if world then local now = world:GetTime() if now < self.nextscanfortargettime then return end self.nextscanfortargettime = now + math.random(300, 500) local temp = entity:GetPickMode() entity:SetPickMode(PICK_NONE) local players = world:GetTaggedEntities("good") for _, player in ipairs(players) do if player.health ~= nil and type(player.health) == "number" and player.health > 0 then if player:GetDistance(entity) < self.scanrange then local temp2 = player:GetPickMode() player:SetPickMode(PICK_NONE) local A = entity:GetPosition(true) + Vec3(0, self.eyeheight, 0) local B = player:GetPosition(true) + Vec3(0, 0.5, 0) local pick = world:Pick(A, B, 0, false) player:SetPickMode(temp2) if not pick.success then self.target = player break end end end end entity:SetPickMode(temp) end end function MonsterSkeleton:Save(properties, binstream, scene, flags, extra) properties.health = self.entity.health end RegisterComponent("MonsterSkeleton", MonsterSkeleton) return MonsterSkeleton so far the script is now ok . get up--> idle --> find target--> and attack also if target is not in range walk animation start. Only with navmesh the Model walks on place and dont move to target. i saw this too in the shooter template too. the monster in the room runs in place. The other Monster run to me Quote Link to comment Share on other sites More sharing options...
Josh Posted February 13 Share Posted February 13 You have to use the debugger to check if the monster is actually ever calling Navigate(), and make sure the method returns true, indicating that it can find a path. Screenshot looks great! Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.