For any feature requests to either the Skyline Engine or the websites and forums please leave your suggestion here.

AI Request

AI Request

Postby TattieBoJangle » 28 Jan 2015, 16:47

Hi guys this will be my only request as everything is great better than a lot of other engines i have used you should get this on steam!

Anyway my only request will be for better character support because as it stand you almost have to wright a script from scratch and a lot of other engines have a genetic script/input panel this asks for frame ranges so for example.

Image

This would make the engine usable for all levels :)
User avatar
TattieBoJangle
Community Manager
Community Manager
 
Posts: 858
Joined: 26 Jan 2015, 00:15
Location: United Kingdom
Skill: 3D Modeller
Skill: 2D Artist
Skill: Level Designer
Skill: Great creative

Re: AI Request

Postby StarFire » 28 Jan 2015, 16:55

Wow Thank you for your kind words :D

Ok the idea you have presented is great. What we had planned was to do this in the new visual system as a character module which would show all of the properties you have mentioned but also make it easy for other users to fully customise and save a new module from this. So it would be simple for a new user and customisable for the experienced user. :D
User avatar
StarFire
Skyline Founder
Skyline Founder
 
Posts: 1678
Joined: 03 Jan 2012, 18:50
Location: UK
Skill: Great creative
Skill: Programmer
Skill: 3D Modeller
Skill: 2D Artist
Skill: Level Designer

Re: AI Request

Postby SolarPortal » 28 Jan 2015, 17:03

For a patrolling character, go to to the Tech demos folder
"Asset Library\Scenes\Tech Demos\Lua\SCC - Follow Path.xSkyScene"

this demo has a spline path created using the path editor( next version has an upgraded editor ).
The character in the scene has a basic script to move the character around the path.

Essentially a single line can make a character follow a path:
This is for a character using a "Simple Character Controller" action.
Code: Select all
function onUpdate( td )
    character.followPath(obj, pathName, walkSpeed);
end


For a dynamically spawned character, use:
Code: Select all
function onUpdate( td )
    controller.followPath(dccID, pathName, walkSpeed);
end


lol, just as simple ;)
User avatar
SolarPortal
Skyline Founder
Skyline Founder
 
Posts: 3631
Joined: 29 Jul 2012, 15:56
Location: UK
Skill: 3D Modeller
Skill: 2D Artist
Skill: Programmer
Skill: Level Designer

Re: AI Request

Postby lordalmighty1 » 28 Jan 2015, 17:07

I would welcome this sort of thing for skyline sounds good :)
User avatar
lordalmighty1
Skyline Moderator
Skyline Moderator
 
Posts: 442
Joined: 02 Jan 2014, 12:13
Location: uk
Skill: Great creative
Skill: Level Designer

Re: AI Request

Postby TattieBoJangle » 28 Jan 2015, 17:16

Your welcome :) I have used a good few engines in my time and i can tell you i will be sticking around and spread the word about the engine as i feel that's what is wrong i didn't even know about it until my friend almightyhood told me about it even searching for engines it doesn't come up.

The characters control is important for me at least and i have lua experience but when you come to a different engine its the same language but still very different if you know what i mean ;) also you will get a lot more users if the character support is made so you are only changing values rather than scripting 400-500 lines.

This was my characters code in the last engine i used its got a few bugs but still worked lol

Code: Select all
import "Scripts/AnimationManager.lua"
import "Scripts/Functions/GetEntityNeighbors.lua"

Script.damage= 5.0 --float "Damage"
Script.attackRange=1.5 --float "Attack Range"
Script.lastupdatetime=0
Script.prevtarget=nil
Script.followingtarget=false
Script.maxaccel=10.0--float "Max acceleration"
Script.speed=6.0--float "CharacterSpeed"
Script.lastupdatetargettime=0
Script.attackmode=0
Script.attackbegan=0
Script.health=40--int "Health"
Script.enabled=true--bool "Enabled"
Script.target=nil
Script.sightRange=6--float "Sight Range"
Script.teamid=2--choice "Team" "Neutral,Good,Bad"
Script.updateFrequency = 500 --float "Update frequency"

Script.currentWaypoint = nil --entity "currentWaypoint"
Script.idleToPatrolTime = 3.0 --float "Idle to Patrol Time"
Script.idleToPatrolTimer = 0
Script.isPatrolling = false --bool "Is patrolling"
Script.walkpatrol=false --bool "Walk Patrol"

--Debug
Script.debugSight=false--bool "Debug sight"
Script.debugAttack=false--bool "Debug attack"
Script.debugMode =false --bool "Debug mode"   
Script.debugSightSphere = nil
Script.debugAttackSphere = nil

--Animation
Script.runAnimationSpeed = 0.05 --float "Run speed"
Script.idleAnimationSpeed = 0.04 --float "Idle speed"
Script.deathAnimationSpeed = 0.04 --float "Die speed"
Script.attackAnimationSpeed = 0.04 --float "Attack speed"
Script.walkAnimationSpeed = 0.03 --float "Walk speed"
Script.hurtAnimationSpeed = 0.03 --float "Hurt speed"
Script.randomSpeedMultiplier = Vec2(0.75, 1.25) --Vec2 "Random speed"
Script.idleAnimations = "" --string "Idle animations"
Script.walkAnimations = "" --string "Walk animations"
Script.runAnimations = "" --string "Run animations"
Script.attackAnimations = "" --string "Attack animations"
Script.dieAnimations = "" --string "Die animations"
Script.hurtAnimations = "" --string "Hurt animations"
Script.animation={}
Script.animation.idle = {}
Script.animation.run = {}
Script.animation.attack ={}
Script.animation.walk = {}
Script.animation.die = {}
Script.animation.hurt = {}
Script.removeBodyTimer = 0
Script.removeBodyTime = 500 --int Remove timer

--Sounds
Script.spottedSound1 = "" --path "Spotted1 sound"
Script.spottedSound2 = "" --path "Spotted2 sound"
Script.hurtSound1 = "" --path "Hurt1 sound"
Script.hurtSound2 = "" --path "Hurt2 sound"
Script.attackSound1 = "" --path "Attack1 sound"
Script.attackSound2 = "" --path "Attack2 sound"
Script.idleSound1 = "" --path "Idle1 sound"
Script.idleSound2 = "" --path "Idle2 sound"
Script.dieSound1 = "" --path "Dying1 sound"
Script.dieSound2 = "" --path "Dying2 sound"
Script.sound={}
Script.sound.spotted={}
Script.sound.attack={}
Script.sound.idle={}
Script.sound.hurt={}
Script.sound.die={}

function Script:Start()
   self.entity:SetPickMode(Entity.BoxPick,true)
   self.entity:SetPickMode(0,false)
   --Debug models   
   if self.debugSight then
      local debugSightMaterial = Material:Load("Materials/Developer/DebugSight.mat")
      self.debugSightSphere = Model:Sphere()
      self.debugSightSphere:SetMaterial(debugSightMaterial)
      self.debugSightSphere:SetPosition(self.entity:GetPosition())
      self.debugSightSphere:SetScale((self.sightRange +1) * 2)
      self.debugSightSphere:SetParent(self.entity)   
   end
   if self.debugAttack then
      local debugSenseMaterial = Material:Load("Materials/Developer/DebugSense.mat")
      self.debugAttackSphere = Model:Sphere()
      self.debugAttackSphere:SetMaterial(debugSenseMaterial)
      self.debugAttackSphere:SetPosition(self.entity:GetPosition())
      self.debugAttackSphere:SetScale(self.attackRange * 2)
      self.debugAttackSphere:SetParent(self.entity)   
   end

   --Load sound
   self:LoadSound(self.spottedSound1, self.sound.spotted)
   self:LoadSound(self.spottedSound2, self.sound.spotted)
   self:LoadSound(self.hurtSound1, self.sound.hurt)
   self:LoadSound(self.hurtSound2, self.sound.hurt)
   self:LoadSound(self.attackSound1, self.sound.attack)
   self:LoadSound(self.attackSound2, self.sound.attack)
   self:LoadSound(self.idleSound1, self.sound.idle)
   self:LoadSound(self.idleSound2, self.sound.idle)
   self:LoadSound(self.dieSound1, self.sound.die)
   self:LoadSound(self.dieSound2, self.sound.die)

   --Load animations
   self:LoadAnimation(self.idleAnimations, self.animation.idle )
   self:LoadAnimation(self.walkAnimations, self.animation.walk )
   self:LoadAnimation(self.runAnimations, self.animation.run)
   self:LoadAnimation(self.attackAnimations, self.animation.attack )
   self:LoadAnimation(self.dieAnimations, self.animation.die )
   self:LoadAnimation(self.hurtAnimations, self.animation.hurt )   

   --Animation manager
   self.animationmanager = AnimationManager:Create(self.entity)
   if self.enabled then
      self:SetMode("idle")
   end
   
   --patrolling
   if self.isPatrolling then
      if self.currentWaypoint == nil then
         --error("Patrolling is set to true, but no waypoint is set.")
      else
         self:SetMode("patrolling")
         self.entity:GoToPoint(self.currentWaypoint:GetPosition(true), self.speed, self.maxaccel)
      end
   end
end

function Script:LoadAnimation(animationIndices, animationTable )
   if animationIndicex ~= "" then
      for animationIndex in string.gmatch(animationIndices, "([^,]+)") do
         table.insert(animationTable, animationIndex )
      end
   end
end

function Script:LoadSound(soundPath, soundTable )
   if (soundPath ~= "") then
      local sound = Sound:Load(soundPath)
      table.insert(soundTable, sound)
   end
end

function Script:Enable()--in
   if self.enabled==false then
      if self.health>0 then
         self.enabled=true
         self:SetMode("idle")
      end
   end
end

function Script:LookForTarget()
   local entities = GetEntityNeighbors(self.entity,self.sightRange,true)
   local k,entity
   --Loop over all entity neighbours
   for k,entity in pairs(entities) do
      --The found entity team id can't be nil, 0 or the same as the entity where this script is attached to
      if entity.script.teamid~=nil and entity.script.teamid~=0 and entity.script.teamid~=self.teamid then
         if entity.script.health>0 then
            local d = self.entity:GetDistance(entity)
            local pickinfo=PickInfo()
            --Entity needs to be in line of sight (Most enemies can't look through walls.)
            if self.entity.world:Pick(self.entity:GetPosition()+Vec3(0,1.6,0),entity:GetPosition()+Vec3(0,1.6,0),pickinfo,0,false,Collision.LineOfSight)==false then
               return entity.script
            end
         end
      end
   end
end

function Script:GetDistanceToTarget()
   local pos = self.entity:GetPosition()
   local targetpos = self.target.entity:GetPosition()
   if math.abs(targetpos.y-pos.y)<1.5 then
      return pos:xz():DistanceToPoint(targetpos:xz())
   else
      return 100000
   end
end

function Script:GetDistanceToWaypoint()
   local pos = self.entity:GetPosition()
   local targetpos = self.currentWaypoint:GetPosition(true)
   if math.abs(targetpos.y-pos.y)<1.5 then
      return pos:xz():DistanceToPoint(targetpos:xz())
   else
      return 100000
   end
end

function Script:IsTargetInAttackRange()
   local pos = self.entity:GetPosition()
   local targetpos = self.target.entity:GetPosition()
   if math.abs(targetpos.y-pos.y)<self.attackRange then
      if pos:xz():DistanceToPoint(targetpos:xz())<self.attackRange then
         return true
      end
   end
   return false
end

function Script:Hurt(damage,distributorOfPain)
   if self.health>0 then
      local sound = self.sound.hurt[math.random(1, #self.sound.hurt)]
      if sound ~= nil then
         self.entity:EmitSound(sound,50,1,1,false)
      end
      if self.target==nil then
         self.target=distributorOfPain
         
      end
      self.health = self.health - damage
      if self.health<=0 then
         self.entity:SetMass(0)
         self.entity:SetCollisionType(0)
         self.entity:SetPhysicsMode(Entity.RigidBodyPhysics)
         self:SetMode("dying")
      else
         self:SetMode("hurt")
      end
   end
end

function Script:EndDeath()
   self:SetMode("dead")
   self.removeBodyTimer = 0
end

function Script:UpdateWorld()

   if self.mode == "dead" then
        self.removeBodyTimer = self.removeBodyTimer + (Time:GetSpeed()*1)
        if (self.removeBodyTimer > self.removeBodyTime) then
        self.entity:Release()
        end
   end

end

function Script:SetMode(mode)
   if mode~=self.mode then   
      math.randomseed(Time:GetCurrent())
      if self.debugMode then
         System:Print("AI mode set to: " .. mode)
      end
      local prevmode=self.mode
      self.mode=mode
      if mode=="idle" then
         local sound = self.sound.idle[math.random(1, #self.sound.idle)]
         if sound ~= nil then
            self.entity:EmitSound(sound,50,1,1,false)
         end
         self.target=nil
         local animationIndex = self.animation.idle[math.random(1, #self.animation.idle)]
         self.animationmanager:SetAnimationSequence(animationIndex, self.idleAnimationSpeed * Time:GetSpeed())
         self.entity:Stop()--stop following anything
      elseif mode=="patrolling" then
         
         
         self.entity:GoToPoint(self.currentWaypoint:GetPosition(true), self.speed, self.maxaccel)
         self.isPatrolling = true
         if self.walkpatrol==true then
            local animationIndex = self.animation.walk[math.random(1, #self.animation.walk)]   
            self.animationmanager:SetAnimationSequence(animationIndex, self.walkAnimationSpeed * Time:GetSpeed(),300)
         else
            local animationIndex = self.animation.run[math.random(1, #self.animation.run)]   
            self.animationmanager:SetAnimationSequence(animationIndex, self.runAnimationSpeed * Time:GetSpeed(),300)
         end
      elseif mode=="hurt" then
         self:EndHurt()
      elseif mode=="attack" then
         self:EndAttack()
      elseif mode=="chase" then
         System:Print("Now in chase mode")
         if self.entity:Follow(self.target.entity,self.speed,self.maxaccel) then
            System:Print("follow")
            self.followingtarget=true
            local animationIndex = self.animation.run[math.random(1, #self.animation.run)]   
            self.animationmanager:SetAnimationSequence(animationIndex, self.runAnimationSpeed * Time:GetSpeed(),300)
         else
            System:Print("no path?")
         end   
      elseif mode=="dying" then
         self.entity:Stop()
         local animationIndex = self.animation.die[math.random(1, #self.animation.die)]
         self.animationmanager:SetAnimationSequence(animationIndex, self.attackAnimationSpeed * Time:GetSpeed(),300,1,self,self.EndDeath)         
      elseif mode=="dead" then
         local sound = self.sound.die[math.random(1, #self.sound.die)]
         if sound ~= nil then
            self.entity:EmitSound(sound,50,1,1,false)
         end
         self.entity:SetCollisionType(0)
         self.entity:SetMass(0)
         self.entity:SetShape(nil)
         self.entity:SetPhysicsMode(Entity.RigidBodyPhysics)
         self.enabled=false
      end
   end
end


function Script:EndHurt()
   if self.mode=="hurt" then
      if self.target.health<=0 then
         self:SetMode("idle")
         return
      end

      if self.health<=0 then
         self:EndDeath()
         return
      end
      
      local sound = self.sound.hurt[math.random(1, #self.sound.hurt)]
      if sound ~= nil then
         self.entity:EmitSound(sound,50,1,1,false)
      end
      
      self.entity:Stop()
      local animationSpeed = self.hurtAnimationSpeed * math.random(self.randomSpeedMultiplier.x, self.randomSpeedMultiplier.y)
      --Get a random animation from the hurt animations
      local animationIndex = self.animation.hurt[math.random(1, #self.animation.hurt)]
      self.animationmanager:SetAnimationSequence(animationIndex, animationSpeed * Time:GetSpeed(), 300,1,self,self.SetChaseMode)
      self.attackbegan = Time:GetCurrent()
      if math.random()>0.75 then self.entity:EmitSound(self.sound.hurt[self.attackmode+1]) end
   end
end

function Script:SetChaseMode()
   self:SetMode("chase")
end

function Script:CheckForEnemies()
   self.target = self:LookForTarget()
         
   if self.target then
      --System:Print("The target is within sight range! Going to chase him")
      self:SetMode("chase")
      local sound = self.sound.spotted[math.random(1, #self.sound.spotted)]
      if sound ~= nil then
         self.entity:EmitSound(sound,50,1,1,false)
      end
   end
end

function Script:EndAttack()
   if self.mode=="attack" then
      if self.target.health<=0 then
         self:SetMode("idle")
         return
      end
      local d = self:GetDistanceToTarget()
      if d>self.attackRange*2 then
         self:SetMode("chase")
         return
      end
      
      local sound = self.sound.attack[math.random(1, #self.sound.attack)]
      if sound ~= nil then
         self.entity:EmitSound(sound,50,1,1,false)
      end
      
      self.entity:Stop()
      local animationSpeed = self.attackAnimationSpeed * math.random(self.randomSpeedMultiplier.x, self.randomSpeedMultiplier.y)
      --Get a random animation from the attack animations
      local animationIndex = self.animation.attack[math.random(1, #self.animation.attack)]
      self.animationmanager:SetAnimationSequence(animationIndex, animationSpeed * Time:GetSpeed(), 300,1,self,self.EndAttack)
      self.attackbegan = Time:GetCurrent()
      if math.random()>0.75 then self.entity:EmitSound(self.sound.attack[self.attackmode+1]) end
   end
end

function Script:UpdatePhysics()
   if self.enabled==false then return end

   local t = Time:GetCurrent()
   self.entity:SetInput(self.entity:GetRotation().y,0)

   if self.mode=="dying" or self.mode=="dead" then
      return
   elseif self.mode=="patrolling" then
      if t-self.lastupdatetargettime>self.updateFrequency then
         self.lastupdatetargettime=t

         self:CheckForEnemies()

         --close to waypoint?
         if self:GetDistanceToWaypoint() < 1.5 then
            self.currentWaypoint = self.currentWaypoint.script:GetNextTarget()
            self.entity:GoToPoint(self.currentWaypoint:GetPosition(true), self.speed, self.maxaccel)
         end   
      end
   elseif self.mode=="idle" then
      self.idleToPatrolTimer = self.idleToPatrolTimer + (Time:GetSpeed()/10)
         
      if t-self.lastupdatetargettime>self.updateFrequency then
         self.lastupdatetargettime=t
         
         self:CheckForEnemies()

         --check for going to patrolling
         System:Print("patrolTimer: "..self.idleToPatrolTimer)
         if self.idleToPatrolTimer > self.idleToPatrolTime then
            System:Print("Idled long enough, back to patrolling")
            self.idleToPatrolTimer = 0
            self:SetMode("patrolling")   
         end
      end
   elseif self.mode=="chase" then
      --The target has no health
      if self.target.health<=0 then
         System:Print("The target is no longer alive! back to idle")
         self:SetMode("idle")
         self.idleToPatrolTimer = 0   
         return
      end
      
      --The target is out of range
      if self:GetDistanceToTarget() > (self.sightRange + 1.5) then
         System:Print("The target escaped! out of range. Back to idle")
         self.followingtarget=false
         self:SetMode("idle")
         self.idleToPatrolTimer = 0
         return
      end      

      --Is the target in melee attack range?
      if self:IsTargetInAttackRange() then
         System:Print("The target is within fightingRange!")
         self:SetMode("attack")
         return
      end
   elseif self.mode=="attack" then
      if self.attackbegan~=nil then
         if t-self.attackbegan>300 then
            self.attackbegan=nil
            self.target:Hurt(self.damage)
         end
      end
      local pos = self.entity:GetPosition()
      local targetpos = self.target.entity:GetPosition()
      local dx=targetpos.x-pos.x
      local dz=targetpos.z-pos.z
      self.entity:AlignToVector(-dx,0,-dz)
   end
end

function Script:Draw()
   if self.enabled==false then return end
   self.animationmanager:Update()
   
end

function Script:PostRender(context)
   if self.debugMode then
      context:SetBlendMode(Blend.Alpha)
      context:SetColor(Vec4(1,1,1,1))
      context:DrawText("AI mode: " .. self.mode, 0,0)
      context:SetBlendMode(Blend.Solid)
   end
end
User avatar
TattieBoJangle
Community Manager
Community Manager
 
Posts: 858
Joined: 26 Jan 2015, 00:15
Location: United Kingdom
Skill: 3D Modeller
Skill: 2D Artist
Skill: Level Designer
Skill: Great creative

Re: AI Request

Postby SolarPortal » 28 Jan 2015, 17:30

yes, skyline needs some serious promotion to get it out there. We are trying to find a way of making it go viral.
steam greenlight is definately an option, although it costs £75 for registering.

wow, that lua script looks complicated compared to skyline general script lol :P
pretty cool most of it works though.

the visual mechanics editor will be much easier to use and way more fun! :P
User avatar
SolarPortal
Skyline Founder
Skyline Founder
 
Posts: 3631
Joined: 29 Jul 2012, 15:56
Location: UK
Skill: 3D Modeller
Skill: 2D Artist
Skill: Programmer
Skill: Level Designer

Re: AI Request

Postby TattieBoJangle » 28 Jan 2015, 17:37

the visual mechanics editor will be much easier to use and way more fun!


That's good to know i will await the fun ;) i have posted it on a few pages so hope to see some new users soon.
User avatar
TattieBoJangle
Community Manager
Community Manager
 
Posts: 858
Joined: 26 Jan 2015, 00:15
Location: United Kingdom
Skill: 3D Modeller
Skill: 2D Artist
Skill: Level Designer
Skill: Great creative

Re: AI Request

Postby SolarPortal » 28 Jan 2015, 17:38

cool, thanks :)
User avatar
SolarPortal
Skyline Founder
Skyline Founder
 
Posts: 3631
Joined: 29 Jul 2012, 15:56
Location: UK
Skill: 3D Modeller
Skill: 2D Artist
Skill: Programmer
Skill: Level Designer

Re: AI Request

Postby StarFire » 28 Jan 2015, 17:47

The visual editor is starting to feel like Alice's trip down the rabbit hole !!! :lol:
User avatar
StarFire
Skyline Founder
Skyline Founder
 
Posts: 1678
Joined: 03 Jan 2012, 18:50
Location: UK
Skill: Great creative
Skill: Programmer
Skill: 3D Modeller
Skill: 2D Artist
Skill: Level Designer


Return to Feature Requests

Who is online

Users browsing this forum: No registered users and 5 guests

cron