• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Flock/Swarm System

Status
Not open for further replies.
Don't know what I came into my mind but I'm currently developing this system:
JASS:
library Boid requires MapBounds, Vector, Alloc

    globals
        private constant integer BOID_ID = 'boid'
        
        private constant real ALIGNMENT_WEIGHT = 1.
        private constant real COHESION_WEIGHT = 1.
        private constant real SEPERATION_WEIGHT = 2.
        
        private constant real SEPERATION_RANGE = 32
        
        private constant real NEIGHBOR_RANGE = 64
        
        private constant real TIMEOUT = 0.031250000
    endglobals
    
    private function GetAngleRad takes real x, real y, real x2, real y2 returns real
        return Atan2(y2 - y, x2 - x)
    endfunction
    
    struct Boid extends array
        implement Alloc
        
        private unit u
        
        private vector velocity
        private real spd
        private real x
        private real y
        private effect sfx
        
        private thistype n
        private thistype p
        
        private static integer c = 0
        private static constant timer t = CreateTimer()
        
        method destroy takes nothing returns nothing
            call deallocate()
            set c = c - 1
            if c == 0 then
                call PauseTimer(t)
            endif
            set p.n = n
            set n.p = p
            
            call DestroyEffect(sfx)
            call velocity.destroy()
            set x = 0
            set y = 0
            set spd = 0
            set u = null
            set sfx = null
        endmethod
        
        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).n
            local real a
            local real tx
            local real ty
            local real ax
            local real ay
            local real cx
            local real cy
            local real sx
            local real sy
            local real nc
            local thistype id
            loop
                exitwhen this == 0
                set nc = 0
                set ax = 0
                set ay = 0
                set cx = 0
                set cy = 0
                set sx = 0
                set sy = 0
                set id = thistype(0).n
                loop
                    exitwhen id == 0
                    if id != this then
                        if IsUnitInRange(id.u, u, NEIGHBOR_RANGE) then
                            set ax = ax + id.velocity.x
                            set ay = ay + id.velocity.y
                            set cx = cx + id.x
                            set cy = cy + id.y
                            if IsUnitInRange(id.u, u, SEPERATION_RANGE) then
                                set sx = sx + id.x - x
                                set sy = sy + id.y - y
                            endif
                            set nc = nc + 1
                        endif
                    endif
                    set id = id.n
                endloop
                if nc > 0 then
                    set ax = ay/nc
                    set ax = ay/nc
                    
                    set cx = cx/nc - x
                    set cy = cy/nc - y
                    
                    set sx = -sx/nc
                    set sy = -sy/nc
                endif
                    
                set velocity.x = velocity.x + ax*ALIGNMENT_WEIGHT + cx*COHESION_WEIGHT + sx*SEPERATION_WEIGHT
                set velocity.y = velocity.y + ay*ALIGNMENT_WEIGHT + cy*COHESION_WEIGHT + sy*SEPERATION_WEIGHT
                call velocity.setLength(spd)
                
                set a = GetAngleRad(0, 0, velocity.x, velocity.y) 
                set tx = 0
                set ty = 0
                if not (MapContainsX(x + velocity.x/TIMEOUT) and MapContainsY(y + velocity.y/TIMEOUT)) then
                    set a = a - bj_PI/2
                    set tx = spd*Cos(a)
                    set ty = spd*Sin(a)
                    if not (MapContainsX(x + tx/TIMEOUT) and MapContainsY(y + ty/TIMEOUT)) then
                        set a = a+bj_PI
                        set tx = spd*Cos(a)
                        set ty = spd*Sin(a)
                        if not (MapContainsX(x + tx/TIMEOUT) and MapContainsY(y + ty/TIMEOUT)) then
                            set a = a + bj_PI/2
                            set tx = spd*Cos(a)
                            set ty = spd*Sin(a)
                        endif
                    endif
                endif
                
                set velocity.x = velocity.x + tx
                set velocity.y = velocity.y + ty
                call velocity.setLength(spd)
                
                set x = x + velocity.x
                set y = y + velocity.y
                call SetUnitX(u, x)
                call SetUnitY(u, y)
                
                call SetUnitFacing(u, GetAngleRad(0, 0, velocity.x, velocity.y)*bj_RADTODEG)
                set this = n
            endloop
        endmethod
        
        
        static method create takes string model, real tx, real ty, real z, real scale, real speed returns thistype
            local thistype this = allocate()
            local real face = GetRandomReal(-bj_PI, bj_PI)
            set u = CreateUnit(Player(15), BOID_ID, tx, ty, face*bj_RADTODEG)
            set spd = speed*TIMEOUT
            set velocity = vector.create(spd*Cos(face), spd*Sin(face), 0)
            set x = tx
            set y = ty
            
            set n = 0
            set p = thistype(0).p
            set thistype(0).p.n = this
            set thistype(0).p = this
            
            if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
            endif
            
            call SetUnitFlyHeight(u, z, 0)
            call SetUnitScale(u, scale, 0, 0)
            
            set sfx = AddSpecialEffectTarget(model, u, "chest")
            
            set c = c + 1
            if c == 1 then
                call TimerStart(t, TIMEOUT, true, function thistype.periodic)
            endif
            return this
        endmethod
    endstruct
endlibrary

Current problems:
- Needs better performance(can process 40-50 boids in a map on average)
- Better Deflection method
 

Attachments

  • NewFlock 1.3.w3x
    35 KB · Views: 73
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
JASS:
    function IsUnitAlive takes unit u returns boolean
        return GetWidgetLife(u) >= 0.405
    endfunction

whats the problem with native or doing IsUnitType(UNIT_TYPE_ALIVE)? Because this method is malfunctioning(dead units can have more than 0.405 life)
 
JASS:
    function IsUnitAlive takes unit u returns boolean
        return GetWidgetLife(u) >= 0.405
    endfunction

whats the problem with native or doing IsUnitType(UNIT_TYPE_ALIVE)? Because this method is malfunctioning(dead units can have more than 0.405 life)

The result is I can't test the map. IT seems that I don't know the right term for the IsUnitAlive native :D
 
Cool, will they deal damage and follow a target? :p

I think you will need 2-3 variations of movement pattern for it not to look the same all the time?

What if they lift up the target in the air?

lol. This is just a Swarming System (for terraining or something).

I'm still improving the movement pattern of the system.
 
Update: WIP

JASS:
library Boid requires WorldBounds, MapBounds, Alloc
    globals
        private constant integer BOID_ID = 'boid'
        
        private constant real MAX_STEERING_ANGLE = 36*bj_DEGTORAD
        
        private constant real MIN_SEPERATION_RANGE = 32
        
        private constant real MAX_ATTRACTION_RANGE = 1024
        
        private constant real TIMEOUT = 0.031250000
    endglobals
    
    private function IsInRadius takes real x, real y, real x2, real y2, real radius returns boolean
        return (x2 - x)*(x2 - x) + (y2 - y)*(y2 - y) <= radius*radius
    endfunction
    
    private function GetAngleRad takes real x, real y, real x2, real y2 returns real
        return Atan2(y2 - y, x2 - x)
    endfunction
    
    private function GetSteeringDirection takes real current, real target returns real
        set target = target - current
        if target > 0 and target <= bj_PI then
            return 1.
        elseif target == 0 then
            return 0.
        endif
        return -1.
    endfunction
    
    struct Boid extends array
        implement Alloc
        
        private unit u
        
        private real velocity
        private real face
        private effect model
        
        private thistype n
        private thistype p
        
        private static integer c = 0
        private static constant timer t = CreateTimer()
        private static constant group g = CreateGroup()
        private static unit temp_u
        
        method destroy takes nothing returns nothing
        endmethod
        
        private static method getId takes unit t returns thistype
            local thistype this = thistype(0).n
            loop
                exitwhen this == 0
                if u == t then
                    return this
                endif
                set this = n
            endloop
            return -1
        endmethod
        
        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).n
            local unit boid
            local real x
            local real y
            local real x2
            local real y2
            local real a
            local real a2
            local real ave_vel
            local real ave_fac
            local unit temp
            local thistype id
            local integer nc
            loop
                exitwhen this == 0
                set x = GetUnitX(u)
                set y = GetUnitY(u)
                set a = GetUnitFacing(u)*bj_DEGTORAD
                set a = face
                set ave_vel = 0
                set ave_fac = 0
                set nc = 0
                set boid = null
                call GroupEnumUnitsInRange(g, x, y, MAX_ATTRACTION_RANGE, null)
                loop
                    set temp = FirstOfGroup(g)
                    exitwhen temp == null
                    if temp != u then
                        set id = getId(temp)
                        //if id != -1 then
                            if boid == null then
                                set boid = temp
                            endif
                            set ave_vel = ave_vel + id.velocity
                            set ave_fac = ave_fac + id.face//GetUnitFacing(temp)*bj_DEGTORAD
                            set nc = nc + 1
                        //endif
                    endif
                    call GroupRemoveUnit(g, temp)
                endloop
                
                /*if nc > 0 then
                    
                    set ave_vel = ave_vel/nc
                    set ave_fac = ave_fac/nc
                
                    set velocity = ave_vel
                    set a = ave_fac
                endif*/
                
                if boid != null then
                    set x2 = GetUnitX(boid)
                    set y2 = GetUnitY(boid)
                    set a2 = GetAngleRad(x, y, x2, y2)
                    if IsInRadius(x, y, x2, y2, MIN_SEPERATION_RANGE) then
                        set a = a - GetSteeringDirection(a, a2)*MAX_STEERING_ANGLE
                    elseif a2 <= a + MAX_STEERING_ANGLE and a2 >= a - MAX_STEERING_ANGLE then
                        set a = a2
                    else
                        set a = a + GetSteeringDirection(a, a2)*MAX_STEERING_ANGLE
                    endif
                else
                    set x2 = x + velocity*Cos(a)
                    set y2 = y + velocity*Sin(a)
                endif
                
                                
                set x2 = x + (velocity/TIMEOUT)*Cos(a)
                set y2 = y + (velocity/TIMEOUT)*Sin(a)
                if not( MapContainsX(x2) and MapContainsY(y2)) then
                    set a = a - GetSteeringDirection(a, GetAngleRad(x, y, x2, y2))*MAX_STEERING_ANGLE
                endif
                call SetUnitX(u, GetBoundedX(x + velocity*Cos(a)))
                call SetUnitY(u, GetBoundedY(y + velocity*Sin(a)))
                
                call SetUnitFacing(u, a*bj_RADTODEG)
                set face = a
                set this = n
            endloop
            set temp = null
            set boid = null
        endmethod
        
        
        static method create takes string model, real x, real y, real z, real scale, real speed returns thistype
            local thistype this = allocate()
            set face = GetRandomReal(-bj_PI, bj_PI)
            set u = CreateUnit(Player(15), BOID_ID, x, y, face*bj_RADTODEG)//GetRandomReal(0, 360))
            set velocity = speed*TIMEOUT
            set n = 0
            set p = thistype(0).p
            set thistype(0).p.n = this
            set thistype(0).p = this
            
            set c = c + 1
            if c == 1 then
                call TimerStart(t, TIMEOUT, true, function thistype.periodic)
            endif
            return this
        endmethod
    endstruct
endlibrary
 
Update:
JASS:
library Boid requires WorldBounds, MapBounds, Alloc
    globals
        private constant integer BOID_ID = 'boid'
        
        private constant real MAX_STEERING_ANGLE = 45*bj_DEGTORAD
        
        private constant real DEFLECTION_ANGLE = 45*bj_DEGTORAD
        
        private constant real MIN_SEPERATION_RANGE = 32
        
        private constant real NEIGHBOR_RANGE = 128
        
        private constant real SIGHT_SECTOR = 137.5*bj_DEGTORAD
        
        private constant real TIMEOUT = 0.031250000
    endglobals
    
    private function GetDist takes real x, real y, real x2, real y2 returns real
        return (x2 - x)*(x2 - x) + (y2 - y)*(y2 - y)
    endfunction
    
    private function IsInRadius takes real x, real y, real x2, real y2, real radius returns boolean
        return (x2 - x)*(x2 - x) + (y2 - y)*(y2 - y) <= radius*radius
    endfunction
    
    private function GetAngleRad takes real x, real y, real x2, real y2 returns real
        return Atan2(y2 - y, x2 - x)
    endfunction
    
    private function GetSteeringDirection takes real current, real target returns real
        set target = target - current
        if target > 0 and target <= bj_PI then
            return 1.
        endif
        return -1.
    endfunction
    
    struct Boid extends array
        implement Alloc
        
        private unit u
        
        private real velocity
        private real face
        private effect model
        
        private thistype n
        private thistype p
        
        private static integer c = 0
        private static constant timer t = CreateTimer()
        private static constant group g = CreateGroup()
        
        private static real ave_vel
        private static real ave_fac
        private static real ax
        private static real ay
        private static integer nc
        private static unit m
        method destroy takes nothing returns nothing
        endmethod
        
        private static method getId takes unit t returns thistype
            local thistype this = thistype(0).n
            loop
                exitwhen this == 0
                if u == t then
                    return this
                endif
                set this = n
            endloop
            return -1
        endmethod
        
        private static method enumUnits takes nothing returns nothing
            local unit tu = GetEnumUnit()
            local real tx = GetUnitX(tu)
            local real ty = GetUnitY(tu)
            local real mx = GetUnitX(m)
            local real my = GetUnitY(m)
            local thistype id = getId(tu)
            local real a = GetAngleRad(mx, my, tx, ty)
            local real a2 = getId(m).face
            if IsInRadius(mx, my, tx, ty, NEIGHBOR_RANGE) and a >= a2 - SIGHT_SECTOR and a <= a2 + SIGHT_SECTOR then
                set ave_vel = ave_vel + id.velocity
                set ave_fac = ave_fac + id.face*bj_DEGTORAD
                set ax = ax + tx
                set ay = ay + ty
                set nc = nc + 1
            endif
            set tu = null
        endmethod
       
        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).n
            local unit boid
            local real x
            local real y
            local real x2
            local real y2
            local real tx
            local real ty
            local real a
            local real a2
            local unit temp
            local thistype id
            loop
                exitwhen this == 0
                set x = GetUnitX(u)
                set y = GetUnitY(u)
                set a = GetUnitFacing(u)*bj_DEGTORAD
                set a = face
                call GroupRemoveUnit(g, u)
                set ave_vel = 0
                set ave_fac = 0
                set ax = 0
                set ay = 0
                set nc = 0
                set m = u
                call ForGroup(g, function thistype.enumUnits)
                
                set nc = nc + 1
                set velocity = (ave_vel+velocity)/nc
                set a = (ave_fac+face)/nc
                /*if nc > 1 then
                    set ax = (ax+x)/nc
                    set ay = (ay+y)/nc
                    
                    set a2 = GetAngleRad(x, y, ax, ay)
                    
                    if a2 >= a - MAX_STEERING_ANGLE and a2 <= a + MAX_STEERING_ANGLE then
                        set a = a2
                    else
                        set a = a + GetSteeringDirection(a, a2)*MAX_STEERING_ANGLE
                    endif
                endif*/
                
                set boid = GetClosestUnitInGroup(x, y, g)
                call GroupAddUnit(g, u)
                if boid != null then
                    set ax = GetUnitX(boid)
                    set ay = GetUnitY(boid)
                    if IsInRadius(x, y, ax, ay, MIN_SEPERATION_RANGE) then
                        set a = a - GetSteeringDirection(a, GetAngleRad(x, y, ax ,ay))*MAX_STEERING_ANGLE
                    else
                    set a2 = GetAngleRad(x, y, ax, ay)
                    
                    if a2 >= a - MAX_STEERING_ANGLE and a2 <= a + MAX_STEERING_ANGLE then
                        set a = a2
                    else
                        set a = a + GetSteeringDirection(a, a2)*MAX_STEERING_ANGLE
                    endif
                    
                    endif
                endif
                
                set ax = x + (velocity/TIMEOUT)*Cos(a)
                set ay = y + (velocity/TIMEOUT)*Sin(a)
                
                if not (MapContainsX(ax) and MapContainsY(ay)) then
                    set a = a - GetSteeringDirection(a, GetAngleRad(x, y, ax, ay))*DEFLECTION_ANGLE
                endif
                
                call SetUnitX(u, GetBoundedX(x + velocity*Cos(a)))
                call SetUnitY(u, GetBoundedY(y + velocity*Sin(a)))
                
                call SetUnitFacing(u, a*bj_RADTODEG)
                set face = a
                set this = n
            endloop
            set boid = null
        endmethod
        
        
        static method create takes string model, real x, real y, real z, real scale, real speed returns thistype
            local thistype this = allocate()
            set face = GetRandomReal(-bj_PI, bj_PI)
            set u = CreateUnit(Player(15), BOID_ID, x, y, face*bj_RADTODEG)
            set velocity = speed*TIMEOUT
            set n = 0
            set p = thistype(0).p
            set thistype(0).p.n = this
            set thistype(0).p = this
            
            call GroupAddUnit(g, u)
            set c = c + 1
            if c == 1 then
                call TimerStart(t, TIMEOUT, true, function thistype.periodic)
            endif
            return this
        endmethod
    endstruct
endlibrary

Current problems :
- Can't process 15+ boids(fps drop)
- some boids gets stuck on the map bounds
 

Attachments

  • NewFlock.w3x
    21.2 KB · Views: 58
Good News!

I succesfully made the grouping algorithm, which was nice. The current problem now is how Individual behaves(which I shall test now)

JASS:
library Boid requires WorldBounds, MapBounds, Alloc
    globals
        private constant integer BOID_ID = 'boid'
        
        private constant real MAX_STEERING_ANGLE = 72*bj_DEGTORAD
        
        private constant real DEFLECTION_ANGLE = 90*bj_DEGTORAD
        
        private constant real SEPERATION_RANGE = 128
        
        private constant real NEIGHBOR_RANGE = 512
        
        private constant real SIGHT_SECTOR = 135*bj_DEGTORAD
        
        private constant real TIMEOUT = 0.031250000
    endglobals
    
    private function GetDist takes real x, real y, real x2, real y2 returns real
        return (x2 - x)*(x2 - x) + (y2 - y)*(y2 - y)
    endfunction
    
    private function IsInRadius takes real x, real y, real x2, real y2, real radius returns boolean
        return (x2 - x)*(x2 - x) + (y2 - y)*(y2 - y) <= radius*radius
    endfunction
    
    private function GetAngleRad takes real x, real y, real x2, real y2 returns real
        return Atan2(y2 - y, x2 - x)
    endfunction
    
    private function GetSteeringDirection takes real current, real target returns real
        set target = target - current
        if target > 0 and target <= bj_PI then
            return 1.
        endif
        return -1.
    endfunction
    
    private function AngleBetweenAngles takes real a, real b returns real
        return a + ((b-a)/2)
    endfunction
    
    struct Boid extends array
        implement Alloc
        
        private unit u
        
        private real velocity
        private real face
        private effect model
        
        private thistype n
        private thistype p
        
        private static integer c = 0
        private static constant timer t = CreateTimer()
        private static constant group g = CreateGroup()
        
        private static real ave_vel = 0
        private static real ave_fac = 0
        private static real ax = 0
        private static real ay = 0
        private static integer nc = 0
        private static unit m
        private static real mx = 0
        private static real my = 0
        private static real ac = 0
        private static real as = 0
        method destroy takes nothing returns nothing
        endmethod
        
        private static method getId takes unit t returns thistype
            local thistype this = thistype(0).n
            loop
                exitwhen this == 0
                if u == t then
                    return this
                endif
                set this = n
            endloop
            return -1
        endmethod
        
        private static method enumNeighbors takes nothing returns nothing
            local unit tu = GetEnumUnit()
            local real tx = GetUnitX(tu)
            local real ty = GetUnitY(tu)
            local thistype id = getId(tu)
            local real a = GetAngleRad(mx, my, tx, ty)
            local real a2 = getId(m).face
            if IsInRadius(mx, my, tx, ty, NEIGHBOR_RANGE) and a >= a2 - SIGHT_SECTOR and a <= a2 + SIGHT_SECTOR then
                set ave_vel = ave_vel + id.velocity
                set ave_fac = ave_fac + id.face
                set ax = ax + tx
                set ay = ay + ty
                if IsInRadius(mx, my, tx, ty, SEPERATION_RANGE) then
                    set a = GetAngleRad(tx, ty, mx, my)
                    set ac = ac + Cos(a)
                    set as = as + Sin(a)
                endif
                set nc = nc + 1
            endif
            set tu = null
        endmethod
        
        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).n
            local unit boid
            local real x2
            local real y2
            local real tx
            local real ty
            local real a
            local real a2
            local thistype id
            loop
                exitwhen this == 0
                set mx = GetUnitX(u)
                set my = GetUnitY(u)
                set a = face
                set ac = Cos(a)
                set as = Sin(a)
                call GroupRemoveUnit(g, u)
                set ave_fac = 0
                set ax = 0
                set ay = 0
                set nc = 0
                set m = u
                
                call ForGroup(g, function thistype.enumNeighbors)
                if nc > 0 then
                    set nc = nc + 1
                    set a = (ave_fac+a)/nc
                    set ac = ac + Cos(a)
                    set as = as + Sin(a)
                    set a = GetAngleRad(mx, my, (ax+mx)/nc, (ay+my)/nc)
                    set ac = ac + Cos(a)
                    set as = as + Sin(a)
                endif
                call GroupAddUnit(g, u)
                
                set tx = mx + velocity*ac
                set ty = my + velocity*as
                
                set a = GetAngleRad(mx, my, tx, ty)
                
                set ax = mx + (velocity/TIMEOUT)*Cos(a)
                set ay = my + (velocity/TIMEOUT)*Sin(a)
                
                if not (MapContainsX(ax) and MapContainsY(ay)) then
                    set a = 2*GetAngleRad(mx, my, ax, ay) + bj_PI - a
                    set ac = ac + Cos(a)
                    set as = as + Sin(a)
                endif
                
                set tx = mx + velocity*ac
                set ty = my + velocity*as
                
                set a = GetAngleRad(mx, my, tx, ty)
                
                
                call SetUnitX(u, GetBoundedX(mx + velocity*Cos(a)))
                call SetUnitY(u, GetBoundedY(my + velocity*Sin(a)))
                
                call SetUnitFacing(u, a*bj_RADTODEG)
                set face = a
                set this = n
            endloop
            set m = null
            set boid = null
        endmethod
        
        
        static method create takes string model, real x, real y, real z, real scale, real speed returns thistype
            local thistype this = allocate()
            set face = GetRandomReal(-bj_PI, bj_PI)
            set u = CreateUnit(Player(15), BOID_ID, x, y, face*bj_RADTODEG)
            set velocity = speed*TIMEOUT
            set n = 0
            set p = thistype(0).p
            set thistype(0).p.n = this
            set thistype(0).p = this
            
            call GroupAddUnit(g, u)
            set c = c + 1
            if c == 1 then
                call TimerStart(t, TIMEOUT, true, function thistype.periodic)
            endif
            return this
        endmethod
    endstruct
endlibrary

I will now get rid of some useless variables.

[edit]

I forgot that this, now, have a very terrible performance issue :(. It can only process less than 15 boids which i am very unhappy about.
 

Attachments

  • NewFlock.w3x
    21.1 KB · Views: 53
Update:
JASS:
library Boid requires WorldBounds, MapBounds, Alloc, Vector
    globals
        private constant integer BOID_ID = 'boid'
        
        private constant real ALIGNMENT_WEIGHT = 0.5
        private constant real COHESION_WEIGHT = 0.5
        private constant real SEPERATION_WEIGHT = 0.5
        
        private constant real SEPERATION_RANGE = 256
        
        private constant real NEIGHBOR_RANGE = 512
        
        private constant real TIMEOUT = 0.031250000
    endglobals
    
    private function GetAngleRad takes real x, real y, real x2, real y2 returns real
        return Atan2(y2 - y, x2 - x)
    endfunction
    
    struct Boid extends array
        implement Alloc
        
        private unit u
        
        private vector velocity
        private real spd
        private real x
        private real y
        private effect sfx
        
        private thistype n
        private thistype p
        
        private static integer c = 0
        private static constant timer t = CreateTimer()
        
        method destroy takes nothing returns nothing
        endmethod
        
        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).n
            local real a
            local real x2
            local real y2
            local real ax
            local real ay
            local real cx
            local real cy
            local real sx
            local real sy
            local real nc
            local thistype id
            loop
                exitwhen this == 0
                set nc = 0
                set ax = 0
                set ay = 0
                set cx = 0
                set cy = 0
                set sx = 0
                set sy = 0
                set id = thistype(0).n
                loop
                    exitwhen id == 0
                    if id != this then
                        if IsUnitInRangeXY(id.u, x, y, NEIGHBOR_RANGE) then
                            set ax = ax + id.velocity.x
                            set ay = ay + id.velocity.y
                            set cx = cx + id.x
                            set cy = cy + id.y
                            if IsUnitInRangeXY(id.u, x, y, SEPERATION_RANGE) then
                                set sx = sx + id.x - x
                                set sy = sy + id.y - y
                            endif
                            set nc = nc + 1
                        endif
                    endif
                    set id = id.n
                endloop
                if nc > 0 then
                    set ax = ay/nc
                    set ax = ay/nc
                    
                    set cx = cx/nc - x
                    set cy = cy/nc - y
                    
                    set sx = -sx/nc
                    set sy = -sy/nc
                endif
                    
                set velocity.x = velocity.x + ax*ALIGNMENT_WEIGHT + cx*COHESION_WEIGHT + sx*SEPERATION_WEIGHT
                set velocity.y = velocity.y + ay*ALIGNMENT_WEIGHT + cy*COHESION_WEIGHT + sy*SEPERATION_WEIGHT
                call velocity.setLength(spd)
                
                set a = GetAngleRad(0, 0, velocity.x, velocity.y) 
                if not (MapContainsX(x + velocity.x) and MapContainsY(y + velocity.y)) then
                    set a = a - bj_PI/2
                    set velocity.x = spd*Cos(a)
                    set velocity.y = spd*Sin(a)
                    if not (MapContainsX(x + velocity.x) and MapContainsY(y + velocity.y)) then
                        set a = a+bj_PI
                        set velocity.x = spd*Cos(a)
                        set velocity.y = spd*Sin(a)
                        if not (MapContainsX(x + velocity.x) and MapContainsY(y + velocity.y)) then
                            set a = a + bj_PI/2
                            set velocity.x = spd*Cos(a)
                            set velocity.y = spd*Sin(a)
                        endif
                    endif
                endif
                
                set x = x + velocity.x
                set y = y + velocity.y
                call SetUnitX(u, x )
                call SetUnitY(u, y)
                
                call SetUnitFacing(u, a*bj_RADTODEG)
                set this = n
            endloop
        endmethod
        
        
        static method create takes string model, real tx, real ty, real z, real scale, real speed returns thistype
            local thistype this = allocate()
            local real face = bj_PI/4//GetRandomReal(-bj_PI, bj_PI)
            set u = CreateUnit(Player(15), BOID_ID, tx, ty, face*bj_RADTODEG)
            set spd = speed*TIMEOUT
            set velocity = vector.create(spd*Cos(face), spd*Sin(face), 0)
            set x = tx
            set y = ty
            
            set n = 0
            set p = thistype(0).p
            set thistype(0).p.n = this
            set thistype(0).p = this
            
            if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
            endif
            
            call SetUnitFlyHeight(u, z, 0)
            call SetUnitScale(u, scale, 0, 0)
            
            set sfx = AddSpecialEffectTarget(model, u, "chest")
            
            set c = c + 1
            if c == 1 then
                call TimerStart(t, TIMEOUT, true, function thistype.periodic)
            endif
            return this
        endmethod
    endstruct
endlibrary

System now uses Vector and now can handle 30-40 units (which is a big difference compared to our first tests)

Now the current problem still is we need a better performance and the boids act weirdly when they bounce of walls(which is a worse than the second version, where we achieved the best bouncing method)

I should study Vectors well :v
 

Attachments

  • NewFlock 1.2.w3x
    32.1 KB · Views: 75
Update:
Bouncing now uses Reflection. I have now fixed the weird bug I discovered lately where 2 boids are just flying on a certain location(w/o going out of that location)

Will add Flying pathability soon.

JASS:
library Boid requires MapBounds, Vector, Alloc

    globals
        private constant integer BOID_ID = 'boid'
        
        private constant real ALIGNMENT_WEIGHT = 0.1
        private constant real COHESION_WEIGHT = 0.5
        private constant real SEPERATION_WEIGHT = 0.5
        
        private constant real SEPERATION_RANGE = 32
        
        private constant real NEIGHBOR_RANGE = 64
        
        private constant real TIMEOUT = 0.031250000
    endglobals
    
    private function GetAngleRad takes real x, real y, real x2, real y2 returns real
        return Atan2(y2 - y, x2 - x)
    endfunction
    
    struct Boid extends array
        implement Alloc
        
        private unit u
        
        private vector velocity
        private real spd
        private real x
        private real y
        private effect sfx
        
        private thistype n
        private thistype p
        
        private static integer c = 0
        private static constant timer t = CreateTimer()
        
        method destroy takes nothing returns nothing
            call deallocate()
            set c = c - 1
            if c == 0 then
                call PauseTimer(t)
            endif
            set p.n = n
            set n.p = p
            
            call DestroyEffect(sfx)
            call velocity.destroy()
            set x = 0
            set y = 0
            set spd = 0
            set u = null
            set sfx = null
        endmethod
        
        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).n
            local real a
            local real tx
            local real ty
            local real ax
            local real ay
            local real cx
            local real cy
            local real sx
            local real sy
            local real nc
            local thistype id
            
            loop
                exitwhen this == 0
                set nc = 0
                set ax = 0
                set ay = 0
                set cx = 0
                set cy = 0
                set sx = 0
                set sy = 0
                set id = thistype(0).n
                loop
                    exitwhen id == 0
                    if id != this then
                        if IsUnitInRange(id.u, u, NEIGHBOR_RANGE) then
                            set ax = ax + id.velocity.x
                            set ay = ay + id.velocity.y
                            set cx = cx + id.x
                            set cy = cy + id.y
                            if IsUnitInRange(id.u, u, SEPERATION_RANGE) then
                                set sx = sx + id.x - x
                                set sy = sy + id.y - y
                            endif
                            set nc = nc + 1
                        endif
                    endif
                    set id = id.n
                endloop
                
               /* set ax = (ax+x)/(nc+1)
                set ax = (ay+y)/(nc+1)
                    
                set cx = (cx+x)/(nc+1) - x
                set cy = (cy+y)/(nc+1) - y
                */
                if nc > 0 then
                    set sx = -sx/nc
                    set sy = -sy/nc
                    
                    set ax = ax/nc
                    set ax = ay/nc
                    
                    set cx = cx/nc- x
                    set cy = cy/nc - y
                endif
                    
                set velocity.x = velocity.x + ax*ALIGNMENT_WEIGHT + cx*COHESION_WEIGHT + sx*SEPERATION_WEIGHT
                set velocity.y = velocity.y + ay*ALIGNMENT_WEIGHT + cy*COHESION_WEIGHT + sy*SEPERATION_WEIGHT
                call velocity.setLength(spd)
                
                if not MapContainsX(x + velocity.x) then
                    set velocity.x = -velocity.x
                endif
                if not MapContainsY(y + velocity.y ) then
                    set velocity.y = -velocity.y
                endif
                
                set x = x + velocity.x
                set y = y + velocity.y
                call SetUnitX(u, x)
                call SetUnitY(u, y)
                
                call SetUnitFacing(u, GetAngleRad(0, 0, velocity.x, velocity.y)*bj_RADTODEG)
                set this = n
            endloop
        endmethod
        
        
        static method create takes string model, real tx, real ty, real z, real scale, real speed returns thistype
            local thistype this = allocate()
            local real face = bj_PI/3 //GetRandomReal(-bj_PI, bj_PI)
            set u = CreateUnit(Player(15), BOID_ID, tx, ty, face*bj_RADTODEG)
            set spd = speed*TIMEOUT
            set velocity = vector.create(spd*Cos(face), spd*Sin(face), 0)
            set x = tx
            set y = ty
            
            set n = 0
            set p = thistype(0).p
            set thistype(0).p.n = this
            set thistype(0).p = this
            
            if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
            endif
            
            call SetUnitFlyHeight(u, z, 0)
            call SetUnitScale(u, scale, 0, 0)
            
            set sfx = AddSpecialEffectTarget(model, u, "chest")
            
            set c = c + 1
            if c == 1 then
                call TimerStart(t, TIMEOUT, true, function thistype.periodic)
            endif
            return this
        endmethod
    endstruct
endlibrary
 

Attachments

  • NewFlock 1.4.w3x
    32.8 KB · Views: 62
Status
Not open for further replies.
Top