Help with vectors and limiting speed

Kenny

Back for now.
Reaction score
202
So after attempting to use processing, I decided to come back to wc3 and attempt to implement vectors in a projectile system I am making.

My question is:

Does anyone know how to limit the speed of a projectile that uses vectors to move?

I will try to explain it for now, as I can't post the code until I get on another computer...

Basically, I use a vector for the position of the projectile, a vector for projectile velocity and a vector for gravity.

Projectiles can bounce off the ground and walls etc, and depending on the angle at which they bounce, the velocity vector will get increased / decreased dramatically, resulting in either speeding up the projectile or slowing it down.

What I want is to keep the projectile at a constant speed while it moves, so bounces and angles to not affect the speed.

I will post my projectile system and vector system when I can. :)
 

Anachron

New Member
Reaction score
53
I don't even understand the question. When you calculate the speed, why can't you not just not calculate it?
 

Kenny

Back for now.
Reaction score
202
It is really confusing to explain without the script.

When a projectile is "fired", the system sets the x, y, and z velocity of the projectile.

Something like:

JASS:
set this.vel.x = Sin(this.phi) * Cos(this.theta) * this.speed
set this.vel.y = Sin(this.phi) * Sin(this.theta) * this.speed
set this.vel.z = Sin(this.phi) * this.speed

// this.phi = XYZ angle (includes z angle)
// this.theta = XY angle (normal angle used commonly)


I thought using [ljass]* this.speed[/ljass] would set the speed of the projectile, however, when the velocity is added to the position of the unit after bouncing, it increases or decreases, affecting the overall speed.
 

Kenny

Back for now.
Reaction score
202
Okay, I can show the script now. :)

The projectile system:

JASS:
library ProjSys initializer Init requires AIDS, T32, Event, Vector, AutoFly

    //----------------------------------------------------------
    //
    // To do list:
    //    - Re-write method operators (get rid of useless ones).                  [90% done]
    //    - Attempt to add vertical collision for units.                          [90% done]
    //    - Add method for resetting velocities (for homing projectiles).         [90% done]
    //    - Re-write ProjInterface to work with new operators.                    [90% done]
    //
    //-----------------------------------------------------------

    globals
        private constant integer PROJ_DUMMY_ID       = 'ewsp'
        private constant integer DEFAULT_MAX_BOUNCES = 1
        private constant real    DEFAULT_GRAVITY     = -9.81
        private constant real    DEFAULT_RESTITUTION = 0.90
        private constant real    DEFAULT_UNIT_COLL   = 128.00
        private constant real    DEFAULT_PROJ_COLL   = 64.00
        
        private Event CollisionEvent = 0    
        Projectile    FirstProjData  = 0
        Projectile    SecondProjData = 0
    endglobals
    
    native UnitAlive takes unit id returns boolean
    
    private interface ProjInterface
        method onStart  takes nothing returns nothing defaults nothing
        method onLoop   takes nothing returns nothing defaults nothing
        method onFinish takes nothing returns nothing defaults nothing
        
        method onGroundImpact takes nothing returns nothing defaults nothing
        method onProjImpact   takes nothing returns nothing defaults nothing
        method onUnitImpact   takes unit whichUnit returns nothing defaults nothing
    endinterface
    
    private struct ProjData extends array
        //! runtextmacro AIDS()
        
        Projectile data
        
        private static method AIDS_filter takes unit whichUnit returns boolean
            return GetUnitTypeId(whichUnit) == PROJ_DUMMY_ID
        endmethod
        
        private method AIDS_onCreate takes nothing returns nothing
            set this.data = 0
        endmethod
        
        private method AIDS_onDestroy takes nothing returns nothing
            set this.data = 0
        endmethod
        
    endstruct
    
    struct Projectile extends ProjInterface
    
        // Public members for users.
        unit    caster         = null
        real    timedLife      = -1.00
        real    unitCollision  = DEFAULT_UNIT_COLL
        real    projCollision  = DEFAULT_PROJ_COLL
        real    restitution    = DEFAULT_RESTITUTION
        integer maxBounces     = DEFAULT_MAX_BOUNCES
        boolean pauseProj      = false
        boolean collideable    = false
    
        // Readonly members for users.
        readonly unit   proj   = null
        readonly real   theta  = 0.00
        readonly real   phi    = 0.00
        readonly real   speed  = 0.00
        readonly vector pos    = 0
        readonly vector vel    = 0

        // Private members, not for users. Some have method operators.
        private player  own    = null
        private effect  sfx    = null
        private vector  grav   = 0
        private boolean stop   = false
        private group   dmged
        
        // Static members for grouping units.
        private static vector   tempVect  = 0
        private static thistype tempData  = 0
        private static boolexpr tempFilt  = null
        private static group    tempGroup = null
        
        // Method operators for cleaner interface.
        static method operator[] takes unit whichUnit returns thistype
            return ProjData[whichUnit].data
        endmethod
        
        method operator owner= takes player whichPlayer returns nothing
            set this.own = whichPlayer
            call SetUnitOwner(this.proj,whichPlayer,true)
        endmethod
        
        method operator owner takes nothing returns player
            return this.own
        endmethod
        
        method operator scaleSize= takes real value returns nothing
            call SetUnitScale(this.proj,value,0.00,0.00)
        endmethod
        
        method operator effectPath= takes string path returns nothing
           if this.sfx != null then
               call DestroyEffect(this.sfx)
           endif
           
           if path == "" then
               set this.sfx = null
           else
               set this.sfx = AddSpecialEffectTarget(path,this.proj,"origin")
           endif
        endmethod
        
        method operator gravity= takes real value returns nothing
            if this.grav != 0 then
                call this.grav.destroy()
            endif
            set this.grav = vector.create(0.00,0.00,((value * T32_PERIOD * T32_PERIOD) / 2.00))
        endmethod
        
        method operator isTerminated takes nothing returns boolean
            return this.stop
        endmethod
        
        private method destroy takes nothing returns nothing
            if this.onFinish.exists then
                call this.onFinish()
            endif
            
            set ProjData[this.proj].data = 0
            
            call this.pos.destroy()
            call this.vel.destroy()
            call this.grav.destroy()
            
            if this.sfx != null then
                call DestroyEffect(this.sfx)
            endif
            
            call RemoveUnit(this.proj)
            call GroupClear(this.dmged)
            
            set this.caster = null
            set this.proj = null
            set this.sfx  = null
            
            call this.deallocate()
        endmethod
        
        private static method unitFilter takes nothing returns boolean
            local thistype this = thistype.tempData
            local unit     filt = GetFilterUnit()
            
            if UnitAlive(filt) and not IsUnitInGroup(filt,this.dmged) then
                if this.onUnitImpact.exists then
                    call this.onUnitImpact(filt)
                endif
                call GroupAddUnit(this.dmged,filt)
            endif
            
            set filt = null
            
            return false
        endmethod
        
        private method projCollisions takes nothing returns nothing
            local thistype that = thistype(0).next
            local vector   v    = 0
            
            loop
                exitwhen that == 0
                if this != that and that.collideable then
                    if that.pos.isInSphere(this.pos,this.projCollision) and this.pos.isInSphere(that.pos,that.projCollision) then
                        if this.onProjImpact.exists then
                            call this.onProjImpact()
                        endif
                        if that.onProjImpact.exists then
                            call that.onProjImpact()
                        endif
                        set FirstProjData  = this
                        set SecondProjData = that
                        call CollisionEvent.fire()
                    endif
                endif
                set that = that.next
            endloop
        endmethod
        
        private method periodic takes nothing returns nothing
            local vector v = 0
            
            if this.stop then
                call this.stopPeriodic()
                call this.destroy()
            endif
            
            if this.collideable then
                call this.projCollisions()
            endif
            
            if this.onLoop.exists then
                call this.onLoop()
            endif
            
            if not this.pauseProj then
                if this.timedLife > -1.0 then
                    set this.timedLife = this.timedLife - T32_PERIOD
                    if this.timedLife <= 0.00 then
                        set this.stop = true
                    endif
                endif
                
                call this.vel.add(this.grav)
                call this.pos.add(this.vel)
                call this.vel.add(this.grav)
                
                call thistype.tempVect.getTerrainPoint(this.pos.x,this.pos.y)
                
                call SetUnitX(this.proj,this.pos.x)
                call SetUnitY(this.proj,this.pos.y)
                call SetUnitFlyHeight(this.proj,this.pos.z - thistype.tempVect.z,0.00)
                
                set thistype.tempData = this
                call GroupEnumUnitsInRange(thistype.tempGroup,this.pos.x,this.pos.y,this.unitCollision,thistype.tempFilt)
                
                if thistype.tempVect.z >= this.pos.z then
                    call thistype.tempVect.getTerrainNormal(thistype.tempVect.x,thistype.tempVect.y,16.00)
                    if vector.dotProduct(this.vel,thistype.tempVect) <= 0.00 then
                        set v = vector.projectionVector(this.vel,thistype.tempVect)
                        call v.mult(-this.restitution)
                        call this.vel.add(v)
                        call v.destroy()
                        
                        if this.maxBounces == 1 then
                            set this.stop = true
                        else
                            set this.maxBounces = this.maxBounces - 1
                        endif

                        if this.onGroundImpact.exists then
                            call this.onGroundImpact()
                        endif
                    endif
                endif
            endif
        endmethod
        
        implement T32x
        
        method terminate takes nothing returns nothing
            set this.effectPath = ""
            set this.stop = true
        endmethod
        
        method isUnitInHeightRange takes unit whichUnit returns boolean
            return SquareRoot((this.pos.z - GetUnitFlyHeight(whichUnit)) * (this.pos.z - GetUnitFlyHeight(whichUnit))) <= this.unitCollision
        endmethod
        
        method adjustTargetCoords takes real xPos, real yPos, real zPos returns nothing
            set this.theta = Atan2((yPos - this.pos.y),(xPos - this.pos.x))
            set this.phi   = Atan2(SquareRoot((xPos - this.pos.x) * (xPos - this.pos.x) + (yPos - this.pos.y) * (yPos - this.pos.y)),(zPos - this.pos.z))
        
            set this.vel.x = Sin(this.phi) * Cos(this.theta) * this.speed
            set this.vel.y = Sin(this.phi) * Sin(this.theta) * this.speed
            set this.vel.z = Cos(this.phi) * this.speed
        endmethod
        
        method scaleVelocity takes real value returns nothing
            local vector v = vector.sum(this.pos,this.vel)
            
            set this.theta = Atan2((v.y - this.pos.y),(v.x - this.pos.x))
            set this.phi   = Atan2(SquareRoot((v.x - this.pos.x) * (v.x - this.pos.x) + (v.y - this.pos.y) * (v.y - this.pos.y)),(v.z - this.pos.z))
            set this.speed   = this.speed * value
            
            set this.vel.x = Sin(this.phi) * Cos(this.theta) * this.speed
            set this.vel.y = Sin(this.phi) * Sin(this.theta) * this.speed
            set this.vel.z = Cos(this.phi) * this.speed
            
            call v.destroy()
        endmethod
        
        method start takes real xPos, real yPos, real zPos, real speed returns nothing
            set this.theta = Atan2((yPos - this.pos.y),(xPos - this.pos.x))
            set this.phi   = Atan2(SquareRoot((xPos - this.pos.x) * (xPos - this.pos.x) + (yPos - this.pos.y) * (yPos - this.pos.y)),(zPos - this.pos.z))
            set this.speed = speed * T32_PERIOD
            set this.vel   = vector.create(Sin(this.phi) * Cos(this.theta) * this.speed,Sin(this.phi) * Sin(this.theta) * this.speed,Cos(this.phi) * this.speed)
            
            if this.grav == 0 then
                set this.grav = vector.create(0.00,0.00,(DEFAULT_GRAVITY * 4.00) * T32_PERIOD)
            endif
            
            if this.onStart.exists then
                call this.onStart()
            endif
            
            call this.startPeriodic()
        endmethod
        
        static method create takes real xPos, real yPos, real zPos, real facing returns thistype
            local thistype this = thistype.allocate()
            
            set this.proj = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),PROJ_DUMMY_ID,xPos,yPos,facing * bj_RADTODEG)
            set this.pos  = vector.create(xPos,yPos,zPos)
            
            call SetUnitX(this.proj,xPos)
            call SetUnitY(this.proj,yPos)
            call thistype.tempVect.getTerrainPoint(xPos,yPos)
            call SetUnitFlyHeight(this.proj,zPos - thistype.tempVect.z,0.00)
            
            if this.dmged == null then
                set this.dmged = CreateGroup()
            endif
            
            set ProjData[this.proj].data = this
            
            return this
        endmethod 
        
        private static method onInit takes nothing returns nothing
            set thistype.tempVect  = vector.create(0.00,0.00,0.00)
            set thistype.tempFilt  = Filter(function thistype.unitFilter)
            set thistype.tempGroup = CreateGroup()
        endmethod
        
    endstruct
    
    function GroupEnumProjectilesInRange takes group whichGroup, real x, real y, real z, real radius returns nothing
        local Projectile p = Projectile(0).next
        
        loop
            exitwhen p == 0
            if p.pos.isInSphereEx(x,y,z,radius) then
                call GroupAddUnit(whichGroup,p.proj)
            endif
            set p = p.next
        endloop
    endfunction
    
    function IsUnitInHeightRange takes real projHeight, real unitHeight, real unitCollision returns boolean
        return SquareRoot((projHeight - unitHeight) * (projHeight - unitHeight)) <= unitCollision
    endfunction
    
    function TriggerRegisterProjectileCollisionEvent takes trigger whichTrigger returns nothing
        call CollisionEvent.register(whichTrigger)
    endfunction
    
    private function Init takes nothing returns nothing
        set CollisionEvent = Event.create()
    endfunction
    
endlibrary


The vector system:

JASS:
library Vector

    struct vector
    
        real x = 0.00
        real y = 0.00
        real z = 0.00
        
        private static location loc = Location(0.00,0.00)
        
        //---------------------------------------------------------------------\\
        // Method to create a vector.
        static method create takes real xPos, real yPos, real zPos returns thistype
            local thistype this = thistype.allocate()
            
            set this.x = xPos
            set this.y = yPos
            set this.z = zPos
            
            return this
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods for adding two vectors together.
        static method sum takes vector a, vector b returns thistype
            local thistype this = thistype.allocate()
            
            set this.x = a.x + b.x
            set this.y = a.y + b.y
            set this.z = a.z + b.z
            
            return this
        endmethod
        
        method add takes vector a returns nothing
            set this.x = this.x + a.x
            set this.y = this.y + a.y
            set this.z = this.z + a.z
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods for subtracting two vectors from eachother.
        static method diff takes vector a, vector b returns thistype
            local thistype this = thistype.allocate()
            
            set this.x = a.x - b.x
            set this.y = a.y - b.y
            set this.z = a.z - b.z
            
            return this
        endmethod
        
        method sub takes vector a returns nothing
            set this.x = this.x - a.x
            set this.y = this.y - a.y
            set this.z = this.z - a.z
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods for getting the cross and dot products of vectors.
        static method crossProduct takes vector a, vector b returns thistype
            local thistype this = thistype.allocate()
            
            set this.x = a.y * b.z - a.z * b.y
            set this.y = a.z * b.x - a.x * b.z
            set this.z = a.x * b.y - a.y * b.x
            
            return this
        endmethod
        
        static method dotProduct takes vector a, vector b returns real
            return a.x * b.x + a.y * b.y + a.z * b.z
        endmethod
        
        //---------------------------------------------------------------------\\
        // Multiplies a vector depending on a given value.
        method mult takes real value returns nothing
            if value < 0.00 then
                set value = value - 1.00 // Only temporary.
            elseif value > 0.00 then
                set value = value + 1.00
            endif
            
            set this.x = this.x * value
            set this.y = this.y * value
            set this.z = this.z * value
        endmethod
        
        method div takes real value returns nothing
            set this.x = this.x / value
            set this.y = this.y / value
            set this.z = this.z / value
        endmethod
        
        //---------------------------------------------------------------------\\  
        // Checks if a vector is within a certain radius of another vector or xyz location.    
        method isInSphereEx takes real x, real y, real z, real radius returns boolean
            if radius * radius < ((this.x - x) * (this.x - x) + (this.y - y) * (this.y - y) + (this.z - z) * (this.z - z)) then
                return false
            endif
            return true
        endmethod
        
        method isInSphere takes vector a, real radius returns boolean
            return this.isInSphereEx(a.x,a.y,a.z,radius)
        endmethod
        
        //---------------------------------------------------------------------\\ 
        // Methods to project vectors onto other vectors.
        static method projectionVector takes vector a, vector b returns thistype
            local thistype this   = thistype.allocate()
            local real     length = b.x * b.x + b.y * b.y + b.z * b.z
            
            if length == 0.00 then
                call this.destroy()
                return 0
            endif
            
            set length = (a.x * b.x + a.y * b.y + a.z * b.z) / length
            set this.x = b.x * length
            set this.y = b.y * length
            set this.z = b.z * length
            
            return this
        endmethod
        
        method projectVector takes vector a returns nothing
            local real length = a.x * a.x + a.y * a.y + a.z * a.z
            
            if length == 0.00 then
                return
            endif
            
            set length = (this.x * a.x + this.y * a.y + this.z * a.z) / length
            set this.x = a.x * length
            set this.y = a.y * length
            set this.z = a.z * length
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods to get the angle between two vectors (includes z height).
        static method angleBetween takes vector a, vector b returns real
            local real length = SquareRoot(a.x * a.x + a.y * a.y + a.z * a.z) * SquareRoot(b.x * b.x + b.y * b.y + b.z * b.z)
            
            if length == 0.00 then
                return 0.00
            endif
            
            return Acos((a.x * b.x + a.y * b.y + a.z * b.z) / length)
        endmethod
        
        method angleTo takes vector a returns real
            local real length = SquareRoot(this.x * this.x + this.y * this.y + this.z * this.z) * SquareRoot(a.x * a.x + a.y * a.y + a.z * a.z)
            
            if length == 0.00 then
                return 0.00
            endif
            
            return Acos((this.x * a.x + this.y * a.y + this.z * a.z) / length)
        endmethod
        
        //---------------------------------------------------------------------\\
        // Methods to set and get the magnitude (length) of a vector.
        method setMagnitude takes real value returns nothing
            local real length = SquareRoot(this.x * this.x + this.y * this.y + this.z * this.z)
            
            if length == 0.00 then
                return
            endif
            
            set length = length / length
            set this.x = this.x * length
            set this.y = this.y * length
            set this.z = this.z * length
        endmethod
        
        method getMagnitude takes nothing returns real
            return SquareRoot(this.x * this.x + this.y * this.y + this.z * this.z)
        endmethod
        
        //---------------------------------------------------------------------\\ 
        // Methods to check for terrain heights and terrain averages.        
        method getTerrainPoint takes real x, real y returns nothing
            call MoveLocation(vector.loc,x,y)
            set this.x = x
            set this.y = y
            set this.z = GetLocationZ(vector.loc)
        endmethod
        
        method getTerrainNormal takes real x, real y, real sampleRadius returns nothing
            local real zx = 0.00
            local real zy = 0.00
            
            call MoveLocation(vector.loc,x - sampleRadius,y)
            set zx = GetLocationZ(vector.loc)
            call MoveLocation(vector.loc,x + sampleRadius,y)
            set zx = zx - GetLocationZ(vector.loc)
            call MoveLocation(vector.loc,x,y - sampleRadius)
            set zy = GetLocationZ(vector.loc)
            call MoveLocation(vector.loc,x,y + sampleRadius)
            set zy = zy - GetLocationZ(vector.loc)
            
            set sampleRadius = 2.00 * sampleRadius
            
            set this.x = zx * sampleRadius
            set this.y = zy * sampleRadius
            set this.z = sampleRadius * sampleRadius
        endmethod
        
    endstruct
    
endlibrary


The problem occurs here:

JASS:
...
                if thistype.tempVect.z >= this.pos.z then
                    call thistype.tempVect.getTerrainNormal(thistype.tempVect.x,thistype.tempVect.y,16.00)
                    if vector.dotProduct(this.vel,thistype.tempVect) <= 0.00 then
                        set v = vector.projectionVector(this.vel,thistype.tempVect)
                        call v.mult(-this.restitution)
                        call this.vel.add(v)
                        call v.destroy()
                        
                        if this.maxBounces == 1 then
                            set this.stop = true
                        else
                            set this.maxBounces = this.maxBounces - 1
                        endif

                        if this.onGroundImpact.exists then
                            call this.onGroundImpact()
                        endif
                    endif
                endif
...


The above works correctly (for the most part), I am just wondering if anyone has a way to constrain the velocity of the projectile to [ljass]this.speed[/ljass].
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      The Helper Discord

      Staff online

      • Ghan
        Administrator - Servers are fun

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top