Physics Ability Example - Exorcism

edited February 2015 in Tutorials

Here's in the breakdown of an ability that spawns units and moves them with rotation, making use of the Physics library

The end result while there is no enemies to go to would be like this:

I include a Debug boolean that can be enabled to show the path and acquisition of different states:

The complete codes for the ability can be found in the following SpellLibrary links:

The entire lua file has comments for every decision. I hope it helps understand and adapt this skill to different behaviors.


I'll just leave the lines related to physics here, special thanks to BMD for helping me through the entire process of rewriting this ability.

First step is to make each spawned unit a physics units and apply the properties.
Physics Readme to know what these do.

-- Make the spirit a physics unit
Physics:Unit(unit)

-- General properties
unit:PreventDI(true)
unit:SetAutoUnstuck(false)
unit:SetNavCollisionType(PHYSICS_NAV_NOTHING)
unit:FollowNavMesh(false)
unit:SetPhysicsVelocityMax(spirit_speed)
unit:SetPhysicsVelocity(spirit_speed * RandomVector(1))
unit:SetPhysicsFriction(0)
unit:Hibernate(false)
unit:SetGroundBehavior(PHYSICS_GROUND_LOCK)

After this, we want to control the units behavior on each frame, making use of the OnPhysicsFrame function.

-- This is set to repeat on each frame
unit:OnPhysicsFrame(function(unit)

    -- Move the unit orientation to adjust the particle
    Unit:SetForwardVector( ( unit:GetPhysicsVelocity() ):Normalized() )

    -- Movement and Collision detection are state independent

    -- MOVEMENT 
    -- Get the direction
    local diff = point - unit:GetAbsOrigin()
    diff.z = 0
    local direction = diff:Normalized()

    -- Calculate the angle difference
    local angle_difference = RotationDelta(VectorToAngles(unit:GetPhysicsVelocity():Normalized()), VectorToAngles(direction)).y
        
    -- Set the new velocity
    if math.abs(angle_difference) < 5 then
    -- CLAMP
    local newVel = unit:GetPhysicsVelocity():Length() * direction
    unit:SetPhysicsVelocity(newVel)
    elseif angle_difference > 0 then
    local newVel = RotatePosition(Vector(0,0,0), QAngle(0,10,0), unit:GetPhysicsVelocity())
    unit:SetPhysicsVelocity(newVel)
    else        
    local newVel = RotatePosition(Vector(0,0,0), QAngle(0,-10,0), unit:GetPhysicsVelocity())
    unit:SetPhysicsVelocity(newVel)
    end

    -- COLLISION CHECK
    local distance = (point - current_position):Length()
    local collision = distance < 50

    -- STATE DEPENDENT LOGIC
    -- Damage, Healing and Targeting are state dependent.
    -- Check the full script on SpellLibrary

Last is to stop the units, very simple with this:

unit:SetPhysicsVelocity(Vector(0,0,0))
unit:OnPhysicsFrame(nil)

For a different logic, check Locust Swarm from DotaCraft repository, it uses the same movement physics but has different acquire and return logic, to fit Warcraft 3 behavior.

The concept of Modding Community doesn't go well together with Competitive Business
My Project Page || My GitHub Profile ||

Comments

  • Posts: 17

    Incredible! Thanks for this :)

  • edited February 2015 Posts: 1,670

    My pleasure

    Here is a modification of the first behavior which I posted yesterday on reddit:

    To make this I made the following changes:

    • When the dummy unit spawns in lua, SetModel to a golden greevil
    • Change the particleDamage in lua to hand_of_midas.vpcf, with the hit on the casters hitloc.
    • On the return logic, add the same type of message particle as in the alchemist_goblins_greed.lua file
    • Remove or change the datadriven particles that go attached to the spirits, in the example I used courier_golden_doomling_ambient.vpcf

    The concept of Modding Community doesn't go well together with Competitive Business
    My Project Page || My GitHub Profile ||