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