• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Issues with projectile not bouncing off of walls properly in GUI

Status
Not open for further replies.

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,517
EDIT 2: Alright, so this system is tailored to my very specific needs and designed around my map. There's a good chance that this long wall of text won't be helpful to you, but maybe you can pull something out of it. Here's how I'm detecting Wall Bounces for Horizontal and Vertical facing walls.

  • Untitled Trigger 004
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • -------- Do whatever you want here. Set the ball's flying height, Parabola effects, etc... --------
      • -------- - --------
      • Set tempPoint[0] = (Position of RG_BlazebombsDummy[Spell_Array])
      • -------- Then check if our projectile has already ricocheted recently. If it has reduce the counter by 1. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • RG_BlazebombsRichochetCount[Spell_Array] Greater than 0
        • Then - Actions
          • Set RG_BlazebombsRichochetCount[Spell_Array] = (RG_BlazebombsRichochetCount[Spell_Array] - 1)
        • Else - Actions
      • -------- - --------
      • -------- If it hasn't ricocheted recently (RicochetCounter is 0) then we can check for Walls to ricochet off of. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • RG_BlazebombsRichochetCount[Spell_Array] Equal to 0
        • Then - Actions
          • -------- tempPoint[0] is the position of our projectile --------
          • -------- Create an Item a small distance ahead of our projectile --------
          • Set tempPoint[1] = (tempPoint[0] offset by RG_BlazebombsSpeed[Spell_Array] towards RG_BlazebombsAngle[Spell_Array] degrees)
          • Item - Create Pathing Item at tempPoint[1]
          • Set tempItem[0] = (Last created item)
          • Set tempPoint[2] = (Position of tempItem[0])
          • Item - Remove tempItem[0]
          • -------- - --------
          • -------- Check if the item has moved far enough from it's initial point (due to something blocking it's pathing) --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Distance between tempPoint[1] and tempPoint[2]) Greater than or equal to 60.00
            • Then - Actions
              • Custom script: call RemoveLocation(udg_tempPoint[2])
              • -------- - --------
              • -------- Set Ricochet count to 2. This prevents the projectile from bouncing again for 0.03 * RicochetCount seconds --------
              • Set RG_BlazebombsRichochetCount[Spell_Array] = 2
              • -------- - --------
              • -------- Find the closest Wall to the projectile --------
              • Set RG_BlazebombsDestructible[Spell_Array] = No destructible
              • Set tempReal[0] = 110.00
              • Destructible - Pick every destructible within 110.00 of tempPoint[1] and do (Actions)
                • Loop - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Or - Any (Conditions) are true
                        • Conditions
                          • (Destructible-type of (Picked destructible)) Equal to Pathing Blocker Wall
                          • (Destructible-type of (Picked destructible)) Equal to Pathing Blocker Corner
                    • Then - Actions
                      • Set tempPoint[2] = (Position of (Picked destructible))
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Distance between tempPoint[1] and tempPoint[2]) Less than tempReal[0]
                        • Then - Actions
                          • Set tempReal[0] = (Distance between tempPoint[1] and tempPoint[2])
                          • Set RG_BlazebombsDestructible[Spell_Array] = (Picked destructible)
                        • Else - Actions
                      • Custom script: call RemoveLocation(udg_tempPoint[2])
                    • Else - Actions
              • Custom script: call RemoveLocation(udg_tempPoint[1])
              • -------- - --------
              • -------- Find the closest Point on that chosen Wall --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • RG_BlazebombsDestructible[Spell_Array] Not equal to No destructible
                • Then - Actions
                  • Set RG_BlazebombsHasRicocheted[Spell_Array] = True
                  • -------- - --------
                  • -------- Find the type of Wall and load it's points --------
                  • Set tempInteger[0] = (Integer((Max life of RG_BlazebombsDestructible[Spell_Array])))
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Destructible-type of RG_BlazebombsDestructible[Spell_Array]) Equal to Pathing Blocker Wall
                    • Then - Actions
                      • Set Wall_DetectPointCount = 3
                      • Set Wall_DetectPoints[1] = (Point((Load 0 of tempInteger[0] from WallCoordinateHashtable), (Load 1 of tempInteger[0] from WallCoordinateHashtable)))
                      • Set Wall_DetectPoints[2] = (Point((Load 2 of tempInteger[0] from WallCoordinateHashtable), (Load 3 of tempInteger[0] from WallCoordinateHashtable)))
                      • Set Wall_DetectPoints[3] = (Point((Load 4 of tempInteger[0] from WallCoordinateHashtable), (Load 5 of tempInteger[0] from WallCoordinateHashtable)))
                    • Else - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Destructible-type of RG_BlazebombsDestructible[Spell_Array]) Equal to Pathing Blocker Corner
                        • Then - Actions
                          • Set Wall_DetectPointCount = 2
                          • Set Wall_DetectPoints[1] = (Point((Load 0 of tempInteger[0] from WallCoordinateHashtable), (Load 1 of tempInteger[0] from WallCoordinateHashtable)))
                          • Set Wall_DetectPoints[2] = (Point((Load 2 of tempInteger[0] from WallCoordinateHashtable), (Load 3 of tempInteger[0] from WallCoordinateHashtable)))
                        • Else - Actions
                  • -------- - --------
                  • -------- Find closest Wall Point --------
                  • Set tempReal[0] = 99999.00
                  • For each (Integer A) from 1 to Wall_DetectPointCount, do (Actions)
                    • Loop - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Distance between tempPoint[0] and Wall_DetectPoints[(Integer A)]) Less than tempReal[0]
                        • Then - Actions
                          • Set tempReal[0] = (Distance between tempPoint[0] and Wall_DetectPoints[(Integer A)])
                          • Set tempInteger[1] = (Integer A)
                          • Set X = (X of Wall_DetectPoints[(Integer A)])
                          • Set Y = (Y of Wall_DetectPoints[(Integer A)])
                        • Else - Actions
                  • For each (Integer A) from 1 to Wall_DetectPointCount, do (Actions)
                    • Loop - Actions
                      • Custom script: call RemoveLocation(udg_Wall_DetectPoints[bj_forLoopAIndex])
                  • -------- - --------
                  • -------- Choose the appropriate formula for the type of wall (horiz or vert angle) --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Destructible-type of GenerateDestructible[tempInteger[0]]) Equal to Desert Wall (Horizontal)
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • tempInteger[1] Less than Wall_DetectPointCount
                        • Then - Actions
                          • Set RG_BlazebombsAngle[Spell_Array] = (360.00 - RG_BlazebombsAngle[Spell_Array])
                          • Game - Display to (All players) for 5.00 seconds the text: Horiz Angle
                        • Else - Actions
                          • Set RG_BlazebombsAngle[Spell_Array] = (180.00 - RG_BlazebombsAngle[Spell_Array])
                          • Game - Display to (All players) for 5.00 seconds the text: Vertical Angle
                    • Else - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Destructible-type of GenerateDestructible[tempInteger[0]]) Equal to Desert Wall (Vertical)
                        • Then - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • tempInteger[1] Less than Wall_DetectPointCount
                            • Then - Actions
                              • Set RG_BlazebombsAngle[Spell_Array] = (180.00 - RG_BlazebombsAngle[Spell_Array])
                              • Game - Display to (All players) for 5.00 seconds the text: Vertical Angle
                            • Else - Actions
                              • Set RG_BlazebombsAngle[Spell_Array] = (360.00 - RG_BlazebombsAngle[Spell_Array])
                              • Game - Display to (All players) for 5.00 seconds the text: Horiz Angle
                        • Else - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Destructible-type of RG_BlazebombsDestructible[Spell_Array]) Equal to Pathing Blocker Corner
                            • Then - Actions
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • tempInteger[1] Less than Wall_DetectPointCount
                                • Then - Actions
                                  • Set RG_BlazebombsAngle[Spell_Array] = (360.00 - RG_BlazebombsAngle[Spell_Array])
                                  • Game - Display to (All players) for 5.00 seconds the text: Horiz Angle
                                • Else - Actions
                                  • Set RG_BlazebombsAngle[Spell_Array] = (180.00 - RG_BlazebombsAngle[Spell_Array])
                                  • Game - Display to (All players) for 5.00 seconds the text: Vertical Angle
                            • Else - Actions
                  • Game - Display to (All players) for 30.00 seconds the text: (Bounce Angle: + (String(RG_BlazebombsAngle[Spell_Array])))
                • Else - Actions
            • Else - Actions
              • Custom script: call RemoveLocation(udg_tempPoint[1])
              • Custom script: call RemoveLocation(udg_tempPoint[2])
        • Else - Actions
      • -------- - --------
      • -------- Move the projectile regardless if it has ricocheted or not. --------
      • Unit - Move RG_BlazebombsDummy[Spell_Array] instantly to (tempPoint[0] offset by RG_BlazebombsSpeed[Spell_Array] towards RG_BlazebombsAngle[Spell_Array] degrees)

If you've ever made a projectile that moves every 0.03 seconds this is all pretty standard stuff. It only gets confusing when we start loading Points from a Hashtable.

So, all of the "Walls" in my game are destructibles that are either facing Vertical or Horizontal angles. This keeps the Bounce Formula simple. Unfortunately, due to the specfics of this system I doubt you can take advantage of it unless you do the same exact thing as me.

Anyway, if you're still interested, here's how I create a cornerless Horizontal Wall. Look at the Wall Points picture to get a better idea, although in the picture there are some extra Corner pieces and stuff that I left out of this example trigger. In the trigger, the Pathing Blocker Walls are the red Watch Towers. We create these Pathing Blockers inside of our Desert Wall to give it Pathing (The walls pathing is turned off).
  • Create Horizontal Wall
    • Events
    • Conditions
    • Actions
      • Set GenerateCount = (GenerateCount + 1)
      • Destructible - Create a Desert Wall (Horizontal) at NextPosition facing (Random angle) with scale 1.00 and variation 0
      • Destructible - Set max life of (Last created destructible) to (Real(GenerateCount))
      • Set GenerateDestructible[GenerateCount] = (Last created destructible)
      • -------- - --------
      • Destructible - Create a Pathing Blocker Wall at NextPosition facing (Random angle) with scale 1.00 and variation 0
      • Destructible - Set max life of (Last created destructible) to (Real(GenerateCount))
      • -------- - --------
      • Destructible - Create a Pathing Blocker Wall at (NextPosition offset by (128.00, 0.00)) facing (Random angle) with scale 1.00 and variation 0
      • Destructible - Set max life of (Last created destructible) to (Real(GenerateCount))
      • -------- - --------
      • -------- WALL POINTS: --------
      • -------- Top--------
      • Set tempPoint[0] = (NextPosition offset by (128.00, 64.00))
      • Hashtable - Save (X of tempPoint[0]) as 0 of GenerateCount in WallCoordinateHashtable
      • Hashtable - Save (Y of tempPoint[0]) as 1 of GenerateCount in WallCoordinateHashtable
      • Special Effect - Create a special effect at tempPoint[0] using Abilities\Spells\Other\TalkToMe\TalkToMe.mdl
      • Special Effect - Set Height of (Last created special effect) to: 200.00
      • Custom script: call RemoveLocation( udg_tempPoint[0] )
      • -------- Bottom --------
      • Set tempPoint[0] = (NextPosition offset by (128.00, -64.00))
      • Hashtable - Save (X of tempPoint[0]) as 2 of GenerateCount in WallCoordinateHashtable
      • Hashtable - Save (Y of tempPoint[0]) as 3 of GenerateCount in WallCoordinateHashtable
      • Special Effect - Create a special effect at tempPoint[0] using Abilities\Spells\Other\TalkToMe\TalkToMe.mdl
      • Special Effect - Set Height of (Last created special effect) to: 200.00
      • Custom script: call RemoveLocation( udg_tempPoint[0] )
      • -------- Side --------
      • Set tempPoint[0] = (NextPosition offset by (192.00, 0.00))
      • Hashtable - Save (X of tempPoint[0]) as 4 of GenerateCount in WallCoordinateHashtable
      • Hashtable - Save (Y of tempPoint[0]) as 5 of GenerateCount in WallCoordinateHashtable
      • Special Effect - Create a special effect at tempPoint[0] using Abilities\Spells\Other\TalkToMe\TalkToMe.mdl
      • Special Effect - Set Height of (Last created special effect) to: 200.00
      • Custom script: call RemoveLocation( udg_tempPoint[0] )

So, I store the top, bottom, and one side of the wall into X and Y coordinates inside our Hashtable. They're stored inside GenerateCount which is just an Index I use to track all of my destructibles. In order to index Destructibles, I use their maximum life as their "custom value". Since my destructibles are untargetable, I don't care what their maximum life is so this method works for me.

You can see that all of my walls/pathing blockers have the same maximum life (Index), this is how I "link" them together which allows them to behave as one single entity.

  • -------- Find the type of Wall and load it's points --------
    • Set tempInteger[0] = (Integer((Max life of RG_BlazebombsDestructible[Spell_Array])))
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • (Destructible-type of RG_BlazebombsDestructible[Spell_Array]) Equal to Pathing Blocker Wall
      • Then - Actions
        • Set Wall_DetectPointCount = 3
        • Set Wall_DetectPoints[1] = (Point((Load 0 of tempInteger[0] from WallCoordinateHashtable), (Load 1 of tempInteger[0] from WallCoordinateHashtable)))
        • Set Wall_DetectPoints[2] = (Point((Load 2 of tempInteger[0] from WallCoordinateHashtable), (Load 3 of tempInteger[0] from WallCoordinateHashtable)))
        • Set Wall_DetectPoints[3] = (Point((Load 4 of tempInteger[0] from WallCoordinateHashtable), (Load 5 of tempInteger[0] from WallCoordinateHashtable)))
      • Else - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Destructible-type of RG_BlazebombsDestructible[Spell_Array]) Equal to Pathing Blocker Corner
          • Then - Actions
            • Set Wall_DetectPointCount = 2
            • Set Wall_DetectPoints[1] = (Point((Load 0 of tempInteger[0] from WallCoordinateHashtable), (Load 1 of tempInteger[0] from WallCoordinateHashtable)))
            • Set Wall_DetectPoints[2] = (Point((Load 2 of tempInteger[0] from WallCoordinateHashtable), (Load 3 of tempInteger[0] from WallCoordinateHashtable)))
          • Else - Actions
    • -------- - --------
    • -------- Find closest Wall Point --------
    • Set tempReal[0] = 99999.00
    • For each (Integer A) from 1 to Wall_DetectPointCount, do (Actions)
      • Loop - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Distance between tempPoint[0] and Wall_DetectPoints[(Integer A)]) Less than tempReal[0]
          • Then - Actions
            • Set tempReal[0] = (Distance between tempPoint[0] and Wall_DetectPoints[(Integer A)])
            • Set tempInteger[1] = (Integer A)
            • Set X = (X of Wall_DetectPoints[(Integer A)])
            • Set Y = (Y of Wall_DetectPoints[(Integer A)])
          • Else - Actions
    • For each (Integer A) from 1 to Wall_DetectPointCount, do (Actions)
      • Loop - Actions
        • Custom script: call RemoveLocation(udg_Wall_DetectPoints[bj_forLoopAIndex])
    • -------- - --------

So breaking down exactly what happened:

1. First we create a Wall destructible that consists of Pathing Blockers and save it's top, bottom, and side coordinates inside a Hashtable.
2. Then inside our projectile trigger we check for nearby Walls around our projectile.
3. If the closest destructible is a "Pathing Blocker Wall" then we know we hit a wall.
4. We then load the Wall Points from that "Pathing Blocker Wall" and find the closest Wall Point to our projectile.
5. We now have the top, bottom, or side Point, and we can use this information to figure out which formula we should use to bounce our projectile off of the wall.

Formulas: 360 - Projectile Angle = Horizontal. 180 - Projectile Angle = Vertical

This is convoluted, I know, but it's actually working surprisingly great for my map.
 

Attachments

  • Wall Points.png
    Wall Points.png
    4.6 MB · Views: 62
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,517
I can wait for the map ill gladly help look

I edited the original post. Unfortunately, this is way too much I think. A tl;dr: I create my "Walls" via triggers, store their sides as points into a Hashtable using X and Y reals, and then when my projectile hits a "Wall" I load these points, and find the closest one to my projectile. Once we have that point we know which side of the Wall our projectile hit so we can bounce it off accordingly.

Formulas: 360 - Projectile Angle = Horizontal. 180 - Projectile Angle = Vertical
 
Status
Not open for further replies.
Top