System Projectile

Kenny

Back for now.
Reaction score
202
Version 0.2.5 (Beta) is now out. Read the change log in the first post for details.

I also updated the first, second and third posts to include the optional modules and neater documentation and notes.

I am pretty happy with where this is at now. Unless there are some serious changes that are needed, I will probably take this out of beta with the next update.

@ Tom_Kazansky:

Oops, I actually meant to write 30-40%. And after testing your test map I would say it isn't even that (possibly 25% or so when done correctly).

But to be honest, I found it less pleasing visually than my current version. In that test map, when projectiles needed to update their facing angle it is jittery, while in my test map it is fluid and smooth (because of the dummy unit it uses which allows very fast direction changing). Honestly the only time it will be needed is for immediate 180 degree turns or something.

I just can't see myself adding it to a public resource, however what you did in that test map is pretty cool (the CustomAttack library is very extensive, probably even too extensive) and every works quite well.

Thanks for the suggestion though. If you really would like to use the "two dummies" method, by all means do so. I don't mind.
 

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
yeah, I will use "two dummies" method for my own need :)
---
I must say this again, awesome system!

edit: with this system, I don't have to use DDS anymore :D
 

Switch33

New Member
Reaction score
12
Your 2 dummy method has a problem. Try with units that are really close to each other a bit. There happens to be a glitch where missiles fly almost straight up in the air for no reason. Also to be honest you can barely tell the difference between the 2 dummy method and how it is now. Anyways, it's been a long time since I last posted in this thread. The projectile system looks a lot better now with all the updates. :thup:

As far as I tested the newest version of the projectile system 0.2.5 (one dummy) can handle something like 400-450 attacking projectiles on my computer before it starts to lag a bit. And i'm guessing a lot of that lag is from models.

However, I do get lag at around 200 or so if i'm using the slow time. Since it clumps all of the attack projectiles.
So i'd suggest not modifying missile speeds in-game too much as you can reduce a huge amount of lag this way and especially reduce the number of missiles slowed since the missile tends to stay in-game then.
 

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
Also to be honest you can barely tell the difference between the 2 dummy method and how it is now.

is the Recycle ON ?
but yeah, the differences is not much :)

----
here is a test map with bouncing attack ;)

just for bump! :D
 

Attachments

  • [Test Map] Projectile.w3x
    298.5 KB · Views: 380

Kenny

Back for now.
Reaction score
202
I've been doing a lot of re-thinking about how this system functions internally and if some of the benefits from its unique mechanisms out-weighed the drawbacks from them..

This is of course in regards to how unit and projectile collision function (using dynamic triggers and stuff).

In the current internal build I have removed projectile on projectile collision detection completely (I have been constanty trying to get it to a stable stage in the background of all other developments, but it hasn't met my standards).

I have also reverted to using [ljass]GroupEnumUnitsInRange()[/ljass] instead of the unit enters range event in the current public build. I have done this for a few reasons, including:

  • It seems to be a bit more accurate for high-speed projectiles.
  • It removes the need for dynamic triggers for each projectile.
  • It will not bug when a unit is already inside the specified range on more than one occasion.
  • I have added complete (and proper) 3D movement to projectiles, so that projectiles will not be instantaneous when firing from 500.00 height to 0.00 at the same coordinates.

I believe the last point in that list is a huge advantage that the next version of this system will bring. It will also bring a more accurate animation algorythm that finds the correct animation for almost all circumstances.

I really want some feedback on whether or not people think this is the right direction for the system and if they would still use it.

Some questions I would like answered:

  • Is the removal of projectile on projectile collision detection a big deal to you?
  • There is possibly a small efficiency loss in using [ljass]GroupEnumUnitsInRange()[/ljass] over the unit enters range event, would this be significant for how you wish to use the system?
  • Does anyone really care? :p

Edit:

Also, I should note that projectile on projectile is still possible if some one is in desperate need of it. All it takes is about 11 extra lines using the [ljass].onLoop[/ljass] function interface and a projgroup (with the new [ljass].forGroupEx()[/ljass] method).
 

BlackRose

Forum User
Reaction score
239
I really want some feedback on whether or not people think this is the right direction for the system and if they would still use it.

Yes =)

Is the removal of projectile on projectile collision detection a big deal to you?

No =) If they want, they can destroy themselves on the onCollision method =)

There is possibly a small efficiency loss in using GroupEnumUnitsInRange() over the unit enters range event, would this be significant for how you wish to use the system?

Hm. It is more accurate it is better, but having high speed missile is absurd =)
 

Kenny

Back for now.
Reaction score
202
Thanks for the feedback. :)

No =) If they want, they can destroy themselves on the onCollision method =)

What are you referring to when you say the onCollision method?

Hm. It is more accurate it is better, but having high speed missile is absurd =)

By high-speed projectiles, I mean anything over ~1200 units per second. Which is common enough.

For projectile on projectile collision, one could simply do something like this:

JASS:
private function ProjCollisionActions takes nothing returns nothing
    // Typecasting from an integer allows different data structures to be used, rather than just projectiles.
    if EnumProjectile.sourceUnit != projectile(ForProjGroupData).sourceUnit then // Needs integer to projectile typecasting.
        call EnumProjectile.terminate()
        call projectile(ForProjGroupData).terminate() // More typecasting. Could be saved to a variable.
    endif
endfunction

private function OnLoopActions takes projectile p returns nothing
    call TEMP_PROJGROUP.enumNearby(p.posX,p.posY,p.posZ,64.00)
    call TEMP_PROJGROUP.forGroupEx(ProjCollisionActions,p) // A new forGroup method. Allows access to temporary data and parent projgroups.
    // The second parameter (p) in the above method refers to any temporary data you may want to transfer to the for group function.
endfunction

// Somewhere in the initial projectile set up:
set p.onLoop = OnLoopActions


And there you have projectile on projectile collision for a single projectile instance.

Although, this is no where near as efficient as the previous method, but it is far more accurate.

I would suggest not using that for many projectiles.
 

BlackRose

Forum User
Reaction score
239
Idk. LOL. I thought you'd have some onCollision interface thingy-ma-bob so your onLand, onStart thing would be consistent. *SHRUGS* =)

Oh. High speed is 1200+? I thought highspeed was 3000 o_O.

I would suggest not using that for many projectiles.

30-40 :D?
 

Kenny

Back for now.
Reaction score
202
It use to have onProj for projectile on projectile collisions, but I am saying that I have removed that.

Currently the system has events for:

onStart -> When a projectile is lauched (not created, but lauched via the .project methods)
onLoop -> Executed every iteration of the timer for a projectile.
onFinish -> When a projectile is destroyed.
onExpire -> When a projectiles timed life expires (defaults to distance / speed).
onLand -> When a projectile hits the ground.
onUnit -> When a projectile hits a unit.
onDest -> When a projectile hits a destructable.

High speed in regards to the unit enters range event was around 1250 or so (that was when it started to get less accurate), but with [ljass]GroupEnumUnitsInRange()[/ljass] high speeds could be around 4000-5000 or so.

And yeah, with a good computer it could possibly deal with 30-40 projectiles with that type of collision detection.
 

Sevion

The DIY Ninja
Reaction score
424
JASS:
private function ProjCollisionActions takes nothing returns nothing
    // Typecasting from an integer allows different data structures to be used, rather than just projectiles.
    if EnumProjectile.sourceUnit != projectile(ForProjGroupData).sourceUnit and SOME_GLOBAL_CONSTANT_DESTROY_ON_COLLIDE then // Needs integer to projectile typecasting.
        call EnumProjectile.terminate()
        call projectile(ForProjGroupData).terminate() // More typecasting. Could be saved to a variable.
    endif
endfunction


Give players the option to have that. It's a simple constant boolean check. Or even you could have a boolean PER projectile. Though, this is fine too.
 

Laiev

Hey Listen!!
Reaction score
188
Well, testing it today, I notice a bug...

if height of unit is less then height of missile, it will make a giant arc inverse and hit unit from the sky (LOL)

EDIT: just happen when use an arc with 0.10/0.15 + (din't tested with 0.10, but with 0.15 it happen)
 

Tamisrah

Active Member
Reaction score
16
I think I might have spotted another bug: What happens to the projectile if the target unit dies before contact?
I have changed the code for the homing part like this:
JASS:
if this.target != null and this.allowTargetHoming then
    if IsUnitAlive(this.target) then
        set TempX = GetUnitX(this.target)
        set TempY = GetUnitY(this.target)
        call MoveLocation(TempL,TempX,TempY)
        set TempZ = GetUnitFlyHeight(this.target) + GetLocationZ(TempL) + this.zOffset
        if this.tarX != TempX or this.tarY != TempY or this.tarZ != TempZ then
            set this.update = true
            set this.tarX  = TempX
            set this.tarY  = TempY
            set this.tarZ  = TempZ
        endif
    else
        set this.stop = true
    endif
endif

Which seems to have solved my problems. But on the other hand it might be totally my fault in doing something wrong with your system. If you think my spellcode will help I will attach it. (I'm not doing so right now because its rather lengthy)
 

BlackRose

Forum User
Reaction score
239
I don't know if others will find it useful, but I certainly cannot remember all the function names! .. I don't know what to do about struct members aside from have a easy reference list open :3

ProjShadows and ProjBounces don't need highlighting, I think.

JASS:
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//           _____           _           _   _ _      
//          |  __ \         (_)         | | (_) |     
//          | |__) | __ ___  _  ___  ___| |_ _| | ___ 
//          |  ___/ '__/ _ \| |/ _ \/ __| __| | |/ _ \
//          | |   | | | (_) | |  __/ (__| |_| | |  __/
//          |_|   |_|  \___/| |\___|\___|\__|_|_|\___|
//                         _/ |                       
//                        |__/
//                             By Kenny v0.2.5 (Beta)
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//  	What is this?
// 	¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Implementing this allows the TESH syntax highlighter in Jass NewGen
//          to highlight the syntax for the Proejctile system (and autocomplete the functions)
//
//      How to import?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Copy this into a blank text file, and save it at:
//          <Your Jass NewGen folder>\tesh\includes\Projectile.j
//
//
//------------------------------------------------------------------------------------------------------------------------\\

function projectile.create takes real x, real y, real z, real angle returns projectile
//        - Above is the only method for creating projectiles. It takes four simple parameters: The starting x, y and z
//          coordinates of the projectile you want to create, and the angle you want it to face. The angle for the
//          facing direction of the projectile is taken in RADIANS. This method will return the created projectile for you
//          to save into a variable.
endfunction

function projectNormal takes real x, real y, real z, real speed returns boolean
//        - Above is one of the two different methods that can be chosen for launching a projectile. This method is the
//          simpler of the two, which doesn't allow for an arcing movement. It takes the target x, y and z coordinates
//          of the projectile and the speed at which you want the projectile to move. This method will return true if the
//          projectile successfully launched and false if it didn't (possibly because it had already been launched).
endfunction

function projectArcing takes real x, real y, real z, real speed, real arcSize returns boolean
//        - Similar to the above method, this method is also used for launching a projectile, however this method allows
//          for the projectile to arc along the z axis. It takes the target x, y and z coordinates of the projectile, the
//          speed at which the projectile will move and the size of the arc you want. Values for the arcSize should range
//          from 0.00 to 1.00 (0.10-0.40 recommended). This method returns the same boolean as this.projectNormal()
endfunction

function setTargPoint takes real x, real y, real z, boolean isInstant returns nothing
//        - The above method can be used to change the target coordinates of a projectile, effectively changing where the
//          projectile will move to. The method takes the x, y and z coordinates of the new target destination, which will
//          update the direction of the projectile in the next iteration of the timer. The boolean parameter at the end
//          determines whether or not the projectile should face the new direction instantly (this does not work if 
//          projectile recycling is on).
endfunction

function setProjPoint takes real x, real y, real z returns nothing
//        - The above method can be used to change the current position of a projectile, without changing its target
//          coordinates or any other aspect of the projectile. The method takes the x, y and z coordinates of the new
//          position of the projectile. This method does not return anthing (same with the above method).
endfunction

function disjoint takes nothing returns nothing
//        - This method is basically an easy way for the user to remove the target unit of a projectile and effectively
//          make it target the ground and discontinue its homing capabilities. The method has no parameters and can simply
//          be used whenever the user wants. This works excellently if you want a projectile to disjoint when a unit blinks.
endfunction

function refresh takes boolean fireOnStart returns nothing
//        - This method will stop a projectile from moving and enable it to be launched again via the .projectNormal and
//          .projectArcing methods. It allows users to quickly stop and start a projectile with a different arc height as
//          as well as target coordinates and speed. The boolean parameter is used to determine whether or not the 
//          .onStart function interface member should be executed again, or removed from the projectile. Using true as the
//          parameter will allow the .onStart function to be executed again, while false will remove it completely.
endfunction

function terminate takes nothing returns nothing
//        - The above method is used to terminate (AKA destroy) a projectile. This will in turn remove the projectile from
//          the game as well as any data associated with it. Projectiles will be removed from any projgoups they are in.
//          This method takes no parameters and can be used whenever the user wants.
endfunction

function addUnit takes unit u returns nothing
//        - The above method was added for completeness. It will add a unit to the 'already passed through' group of a 
//          projectile without firing the unit impact event. This can be used to quickly add units to the group that you
//          know won't be a target of a projectile. It takes a unit parameter, which is the unit that will be added.
endfunction

function isUnitAdded takes unit u returns boolean
//        - The above method was also added for completeness. It will check to see if a given unit is already in the
//          'already passed through' group of a projectile. This can be used in conjunction with this.addUnit() and
//          this.removeUnit() to do some interesting things. It takes a unit parameter.
endfunction

function removeUnit takes unit u returns nothing
//        - The above method is the last of the functions added for completeness. This method will remove a given unit
//          from a projectile's 'already passed through' group so that it may fire the unit impact event again (or maybe
//          for the first time if this.addUnit() was used on it). It takes a unit parameter.
endfunction

function removeAll takes nothing returns nothing
//        - This method can be used to completely clear all units that have been hit by a projectile. Doing so will make
//          all previously hit units available to be hit once more. Can be used with projectiles that travel to a target
//          point and return, so that damage can be dealt twice.
endfunction

function attachData takes integer whichData returns nothing
//        - Users can attach extra data to a projectile via the above method. This enables extra data to be transfered via
//          structs so that projectiles can be more inclusive in regards to user needs. The method takes one parameter,
//          which can be any struct that the user wants to attach the the projectile.
endfunction

function getData takes nothing returns integer
//        - The above method is used to retrieve data attached to a projectile. It has no parameters and can be used in a
//          similar fashion to how TimerUtils and KeyTimers 2 works. The data can then be manipulated in the function
//          interface members of a projectile.
endfunction

function hasData takes nothing returns boolean
//        - This method will return true if a given projectile currently has some form of data attached to it and false
//          if there is no data attached.
endfunction

function detatchData takes nothing returns nothing
//        - The above method will remove any currently attached data from a projectile. Users do not have to manually
//          remove data as it will be automatically removed upon projectile destruction. Users can also just override the
//          currently attached data by using this.attachData() again. This is just here for API completion.
endfunction

//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//           _____           _  _____                       
//          |  __ \         (_)/ ____|                      
//          | |__) | __ ___  _| |  __ _ __ ___  _   _ _ __  
//          |  ___/ '__/ _ \| | | |_ | '__/ _ \| | | | '_ \ 
//          | |   | | | (_) | | |__| | | | (_) | |_| | |_) |
//          |_|   |_|  \___/| |\_____|_|  \___/ \__,_| .__/ 
//                         _/ |                      | |    
//                        |__/                       |_|
//                             By Kenny v0.1.2 (Beta)
//
//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\

function CreateProjGroup takes nothing returns projgroup
//        - The above method enables users to create a projgroup, which is basically a group for projectiles (similar to
//          WC3 native groups for units). This method will returns a projgroup to be saved into a variable.
//        - The alternative WC3 naming for this requires no parameters as well.
endfunction

function ProjGroupAddProjectile takes projgroup g, projectile p returns boolean
//        - The above method adds a projectile to a projgroup (similar to GroupAddUnit()). This method will return true
//          if the projectile was successfully added and false if it wasn't.
//        - The alternative WC3 naming for this requires a projgroup parameter before the projectile parameter.
endfunction

function ProjGroupRemoveProjectile takes projgroup g, projectile p returns nothing
//        - This method removes a projectile from a projgroup (similar to GroupRemoveUnit()). This method will return true
//          if the projectile was successfully removed and false if it wasn't.
//        - The alternative WC3 naming for this requires a projgroup parameter before the projectile parameter
endfunction

function IsProjectileInProjGroup takes projgroup g, projectile p returns boolean
//        - This method checks to see if a specified projectile is in a projgroup (similar to IsUnitInGroup()). This method
//          will return true if the projectile is in the group and false if it isn't.
//        - The alternative WC3 naming for this requires a projgroup parameter before the projectile parameter.
endfunction

function CountProjectilesInProjGroup takes projgroup g returns integer
//        - This method will return an integer that represents the number of projectiles currently in a group (similar to
//          CountUnitsInGroup()).
//        - The alternative WC3 naming for this requires a projgroup parameter.
endfunction

function FirstOfProjGroup takes projgroup g returns projectile
//        - This method attempts to simulate the WC3 native FirstOfGroup(). It will return a projectile that is at the
//          start of the internal projectile array of a projgroup. Useful for first-of-group-loops.
//        - The alternative WC3 naming for this requires a projgroup parameter.
endfunction

function ProjGroupClear takes projgroup g returns nothing
//        - This method will clear all projectiles from a projgroup, emptying it of all data completely (similar to
//          GroupClear()). It returns nothing.
//        - The alternative WC3 naming for this requires a projgroup parameter.
endfunction

function GroupEnumProjectilesInRange takes projgroup g, real x, real y, real z, real radius returns nothing
//        - This method will group all projectiles in a specified radius around given coordinates and add them to a
//          projgroup. Before adding projectiles to the projgroup it will clear to projgroup of all existing projectiles,
//          to better simulate the WC3 GroupEnumUnitsInRange(). It takes four parameters, the first three being the x, y and
//          z coordinates around which you want projectiles to be added, and the fourth being the radius in which
//          projectiles need to be to be added to the projgroup.
//        - The alternative WC3 naming for this requires a projgroup parameter before other parameters.
endfunction

function ForProjGroup takes projgroup g, boolexpr e returns nothing
//        - This method will perform a function for every projectile in a projgroup. It has one parameter, which is the
//          function that will be executed for all projectiles, it is similar to the function interface members for the
//          projectile struct. The function that is to be passed through this method as an parameter must take nothing and
//          return nothing. For example:
//
//            private function EnumFunctionExample takes nothing and returns nothing
//                call EnumProjectile.terminate()
//            endfunction
//
//        - As you can see above the function takes nothing and returns nothing, and inside this function users can use
//          the EnumProjectile global to access the current projectile the function is being executed for.
//        - The alternative WC3 naming for this requires a projgroup parameter before the enumFunc parameter.
endfunction

function DestroyProjGroup takes projgroup g returns nothing
//        - This method destroys a projgroup, clearing it of all data (similar to DestroyGroup()). This method returns
//          nothing.
//        - The alternative WC3 naming for this requires a projgroup parameter.
endfunction

function GetEnumProjectile takes nothing returns projectile
//        - This global returns the most recent projectile a function from .forGroup() has been called for. It is to be
//          used like GetEnumUnit() in a normal ForGroup() call.
//        - The alternative WC3 naming for this is a function not a global.
endfunction

function GetTempProjGroup takes nothing returns projgroup
//        - This global is to be used like the temporary group from GroupUtils. It saves the user from creating and 
//          destroying a projgroup if they need to use one .enumNearby() and .forGroup() call.
//        - The alternative WC3 naming for this is a function not a global.
endfunction

//------------------------------------------------------------------------------------------------------------------------\\
//
//------------------------------------------------------------------------------------------------------------------------\\

function RegisterProjectileCollisionEvent takes trigger t returns EventReg
//        - This function registers a trigger to be executed whenever there is a projectile on projectile collision. It
//          has one parameter, which is the trigger the user wants to register. It returns EventReg.
endfunction

function GetFirstCollisionProj takes nothing returns projectile
//        - This function retrieves the one of the two projectiles that were in the most recent collision. Be careful when
//          using this, as a projectile may have already been terminated beforehand (via this.onProj).
endfunction

function GetSecondCollisionProj takes nothing returns projectile
//        - This function retrieves the second of the two projectiles that were in the most recent collision. Be careful
//          when using this, as a projectile may have already been terminated beforehand (via this.onProj).
endfunction

function GetTotalProjectiles takes nothing returns integer
//        - This function returns the total amount of projectiles that are currently active in the map. This can be useful
//          for debugging purposes.
endfunction

function IsUnitProjectile takes unit u returns boolean
//        - This function takes a unit parameter (referring to which unit the user wants to check) and checks whether or not
//          it is a projectile.
endfunction

function GetUnitProjectileData takes unit u returns projectile
//        - This function retrieves the projectile data that is attached to the unit passed through the function. Be sure
//          to check if the unit is a projectile beforehand.
endfunction
 

Laiev

Hey Listen!!
Reaction score
188
BlackRose, their level of organization is as high as mine, and his way of tesh struct members is great.
 

FarAwaY

TH.net Regular
Reaction score
9
1 question.

- What if I want to use it but I am not experienced in JASS or vJASS?

Nice job on the system btw :thup:
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top