Building Turret AI: Part 2
I've expanded the turret AI to include a visibility test. This is performed initially when selecting a target, and is also performed on a continuous basis to make sure the target stays in view. To avoid overloading the engine I am only performing the visibility test at most once every 500 milliseconds. This provides near-instantaneous updating while preventing the picking from becoming a bottleneck and slowing down performance.
Adding bullets was mostly a copy and paste job from the SoldierAI script. Because we have a self-contained tracer prefab already assembled, it is easy to reassign this prefab for use with the turret.
Still left to do:
- The FPSGun script expects hit objects to either be animated nemies or physics objects. Some revision will be required to handle the turret objects, so that the bullets will hurt the object and also apply a force at the hit point, so that it can potentially tip over the turrets.
- Smoke needs to emit from the turret once it is killed, to signify that it is no longer operational.
- The script needs to detect when the turret is tipped over and deactivate the AI when this occurs.
- The model needs to be adjusted so the pivots point in the direction I want for the gun to point at the target object.
My updated script is below.
--Public
Script.target = nil--enemy to shoot
Script.range = 20
Script.health=100
Script.projectilepath = "Prefabs/Projectiles/tracer.pfb"
Script.shoot1sound=""--path "Fire 1 sound" "Wav file (*.wav):wav|Sound"
Script.shoot2sound=""--path "Fire 2 sound" "Wav file (*.wav):wav|Sound"
Script.shoot3sound=""--path "Fire 3 sound" "Wav file (*.wav):wav|Sound"
--Private
Script.lastfiretime=0
Script.mode = "idle"
Script.lastsearchtime=0
Script.searchfrequency = 500
Script.firefrequency = 50
function Script:Start()
--Load sounds
self.sound = {}
self.sound.shoot = {}
if self.shoot1sound~="" then self.sound.shoot[1] = Sound:Load(self.shoot1sound) end
if self.shoot2sound~="" then self.sound.shoot[2] = Sound:Load(self.shoot2sound) end
if self.shoot3sound~="" then self.sound.shoot[3] = Sound:Load(self.shoot3sound) end
self.projectile = Prefab:Load(self.projectilepath)
if self.projectile~=nil then
self.projectile:Hide()
end
self.turret = self.entity:FindChild("turret")
--Add muzzleflash to gun
self.muzzle = self.entity:FindChild("muzzle")
if self.muzzle~=nil then
local mtl=Material:Create()
mtl:SetBlendMode(5)
self.muzzle:SetMaterial(mtl)
mtl:Release()
self.muzzleflash = Sprite:Create()
self.muzzleflash:SetSize(0.35,0.35)
local pos=self.muzzle:GetPosition(true)
self.muzzleflash:SetPosition(pos,true)
self.muzzleflash:SetParent(self.muzzle,true)
mtl = Material:Load("Materials/Effects/muzzleflash.mat")
if mtl then
self.muzzleflash:SetMaterial(mtl)
mtl:Release()
mtl=nil
end
local light = PointLight:Create()
light:SetRange(5)
light:SetColor(1,0.75,0)
light:SetParent(self.muzzleflash,flash)
if light.world:GetLightQuality()<2 then
light:SetShadowMode(0)
end
self.muzzleflash:Hide()
end
end
function Script:Release()
if self.projectile~=nil then
self.projectile:Release()
self.projectile = nil
end
end
function TurretSearchHook(entity,extra)
if entity~=extra then
if entity.script~=nil then
if type(entity.script.health)=="number" then
if entity.script.health>0 then
local d = extra:GetDistance(entity)
if d<extra.script.range then
if extra.script.target~=nil then
if extra.script:GetDistance(extra.script.target)>d then
if extra.script:GetTargetVisible(entity.script) then
extra.script.target = entity.script
end
end
else
if extra.script:GetTargetVisible(entity.script) then
extra.script.target = entity.script
end
end
end
end
end
end
end
end
function Script:GetTargetVisible(target)
if target==nil then
target = self.target
end
if target==nil then
return false
end
local pickinfo = PickInfo()
local p0 = self.entity:GetAABB().center
local p1 = target.entity:GetAABB().center
local pickmode0 = self.entity:GetPickMode()
local pickmode1 = target.entity:GetPickMode()
self.entity:SetPickMode(0)
target.entity:SetPickMode(0)
local result = not self.entity.world:Pick(p0,p1,pickinfo,0,false,Collision.LineOfSight)
self.entity:SetPickMode(pickmode0)
target.entity:SetPickMode(pickmode1)
return result
end
function Script:UpdateWorld()
if self.health>=0 then
local currenttime = Time:GetCurrent()
--Stop shooting if target is dead
if self.target~=nil then
if self.target.health<=0 then
self.target = nil
end
end
--Search for target
if self.target==nil then
if currenttime - self.lastsearchtime > self.searchfrequency then
self.lastsearchtime = currenttime
local pos = self.entity:GetPosition(true)
local aabb = AABB(pos - Vec3(self.range), pos + Vec3(self.range))
self.entity.world:ForEachEntityInAABBDo(aabb,"TurretSearchHook",self.entity)
end
end
--Continuous visibility test
if self.target~=nil then
if currenttime - self.lastsearchtime > self.searchfrequency then
self.lastsearchtime = currenttime
if self:GetTargetVisible(self.target)==false then
self.target = nil
return
end
end
end
if self.target~=nil then
--Motion tracking
if self.turret~=nil then
local p0 = self.turret:GetPosition(true)
local p1 = self.target.entity:GetAABB().center
local yplane = p1 - p0
yplane.y=0
self.turret:AlignToVector(yplane,1,0.1 / Time:GetSpeed(),0)
end
--Shoot
if self.muzzle~=nil and self.projectile~=nil then
if currenttime - self.lastfiretime > self.firefrequency then
self.lastfiretime = currenttime
local bullet = self.projectile:Instance()
self.muzzleflash:Show()
self.muzzleflash:EmitSound(self.sound.shoot[#self.sound.shoot])
self.muzzleflash:SetAngle(math.random(0,360))
self.muzzleflashtime=currenttime
if bullet~=nil then
bullet:Show()
bullet:SetPosition(self.muzzle:GetPosition(true),true)
bullet:SetRotation(self.muzzle:GetRotation(true),true)
if bullet.script~=nil then
bullet.script.owner = self
if type(bullet.script.Enable)=="function" then
bullet.script:Enable()
end
bullet:Turn(Math:Random(-3,3),Math:Random(-3,3),0)
end
end
end
end
end
end
self:UpdateMuzzleFlash()
end
function Script:UpdateMuzzleFlash()
if self.muzzleflashtime then
if Time:GetCurrent()-self.muzzleflashtime<30 then
self.muzzleflash:Show()
else
self.muzzleflash:Hide()
end
end
end
-
1
5 Comments
Recommended Comments