• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

RPG camera that pans with character over high terrain

Status
Not open for further replies.
Level 5
Joined
Dec 29, 2008
Messages
61
I'm trying to implement an RPG hero camera that pans the camera over high terrain. I did a few searches and it looks like everyone has given up.

Here's the camera:

  • Update Camera
    • Events
      • Time - Every 0.03 seconds of game time
  • Actions
    • Camera - Lock camera target for mPlayer[PlayerCount] to mHero[PlayerCount], offset by (0.00, 0.00) using Default rotation
    • Camera - Set mPlayer[PlayerCount]'s camera Rotation to (Facing of mHero[PlayerCount]) over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Distance to target to 300.00 over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Angle of attack to (330.00 + ((((Position - Z of mHero[PlayerCount]) - (Position - Z of Aiden Hait 0108 <gen>)) / (Target Z of current camera view)) x 1.00)) over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Height Offset to (80.00 - (Abs(((Position - Z of mHero[PlayerCount]) - (Position - Z of Aiden Hait 0108 <gen>))))) over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Far Z to 5000.00 over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Field of view to 1000.00 over 0.00 seconds
The above code clips on elevated terrain, causing the camera to go under the character.

Notes:

  • I suspect that (Position - Z of mHero[PlayerCount]) is not the true value of the terrain height at character position.
  • Aiden Hait 0108 <gen> is a dummy unit set at the 0 point of the terrain.
  • ANGLEDELTA is a constant value that represents the maximum change in angle for a given change in Z offset. You can adjust this value to control the rate at which the camera angle changes as the Z offset changes.

This equation sets the camera height offset to the terrain height at the character's position, plus Z units, while also adjusting the camera's angle of attack based on the difference between the terrain height at the character's position and the lowest point on the terrain. This will ensure that the camera view is always constant relative to the character, even as the character moves up and down hills.

Edit: EUREKA! After some fiddling around this works like a charm.

  • Events
    • Time - Every 0.03 seconds of game time
  • Actions
    • Camera - Lock camera target for mPlayer[PlayerCount] to mHero[PlayerCount], offset by (0.00, 0.00) using Default rotation
    • Camera - Set mPlayer[PlayerCount]'s camera Rotation to (Facing of mHero[PlayerCount]) over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Distance to target to 300.00 over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Angle of attack to (330.00 + ((((Position - Z of mHero[PlayerCount]) - (Position - Z of Aiden Hait 0108 <gen>)) / (Target Z of current camera view)) x 1.00)) over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Height Offset to (((Position - Z of mHero[PlayerCount]) + 200.00) - (Abs(((Position - Z of mHero[PlayerCount]) - (Position - Z of Aiden Hait 0108 <gen>))))) over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Far Z to 5000.00 over 0.00 seconds
    • Camera - Set mPlayer[PlayerCount]'s camera Field of view to 1000.00 over 0.00 seconds
If anyone has a more efficient way to do this then feel free to add to the post.
 

Attachments

  • elevated_height_2.png
    elevated_height_2.png
    2.8 MB · Views: 13
  • elevated_height_1.png
    elevated_height_1.png
    2.9 MB · Views: 11
  • base_height.png
    base_height.png
    2.9 MB · Views: 12
Last edited:

Uncle

Warcraft Moderator
Level 66
Joined
Aug 10, 2018
Messages
6,748
I don't think you need to Lock the camera constantly. The camera will remain Locked until you Reset it, so doing it once should suffice. I think the same should be true for the Far Z.

Also, it looks like you're misusing PlayerCount again. That's not going to be multiplayer friendly.

Does the Z Position of Aiden Hait change? I'm a little confused by this. Also, why are you multiplying the Angle of attack by 1.00?
 
Last edited:
Level 5
Joined
Dec 29, 2008
Messages
61
The x 1.00 is a scalar that modifies the rate at which the angle of attack changes. I don't think it's necessary but it adds some customization there.

Z Position of Aiden Hait never changes. However, this would probably stop working if the player followed terrain that was BELOW this unit. I think some additional math using absolute values or something could fix that.

As for PlayerCount I'm just using a bad variable name but it shouldn't affect functionality. It should just be PlayerName or PN, but I didn't want the trigger to break by changing the variable. Very lazy of me!

This is what it looks like now, and big thanks to pyrogasm for working through it with me.

CameraInitialization2 (Initially On):
  • Custom script: set bj_wantDestroyGroup = true
  • Unit Group - Pick every unit in (Units in START <gen> matching (((Matching unit) is A Hero) Equal to True)) and do (Actions)
    • Loop - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ((Owner of (Picked unit)) slot status) Equal to Is playing
        • Then - Actions
          • Set VariableSet PlayerCount = (Player number of (Owner of (Picked unit)))
          • Set VariableSet mHero[PlayerCount] = (Picked unit)
          • Set VariableSet mPlayer[PlayerCount] = (Owner of mHero[PlayerCount])
          • Set VariableSet mPlaying[PlayerCount] = True
          • -------- Set Visibility --------
          • Visibility - Create an initially Enabled visibility modifier for mPlayer[PlayerCount] emitting Visibility across (Playable map area)
          • -------- Give Heroes Initial Items --------
          • Hero - Create Lesser Clarity Potion and give it to mHero[PlayerCount]
          • Item - Set charges remaining in (Last created item) to 10
          • Hero - Create Healing Salve and give it to mHero[PlayerCount]
          • Item - Set charges remaining in (Last created item) to 10
          • -------- Initial Camera Setup --------
          • Camera - Lock camera target for mPlayer[PlayerCount] to mHero[PlayerCount], offset by (0.00, 0.00) using Default rotation
          • Camera - Set mPlayer[PlayerCount]'s camera Rotation to (Facing of mHero[PlayerCount]) over 0.00 seconds
          • Camera - Set mPlayer[PlayerCount]'s camera Distance to target to 300.00 over 0.00 seconds
          • Camera - Set mPlayer[PlayerCount]'s camera Angle of attack to (330.00 + ((((Position - Z of mHero[PlayerCount]) - (Position - Z of Aiden Hait 0108 <gen>)) / (Target Z of current camera view)) x 1.00)) over 0.00 seconds
          • Camera - Set mPlayer[PlayerCount]'s camera Height Offset to (((Position - Z of mHero[PlayerCount]) + 200.00) - (Abs(((Position - Z of mHero[PlayerCount]) - (Position - Z of Aiden Hait 0108 <gen>))))) over 0.00 seconds
          • Camera - Set mPlayer[PlayerCount]'s camera Far Z to 5000.00 over 0.00 seconds
          • Camera - Set mPlayer[PlayerCount]'s camera Field of view to 1000.00 over 0.00 seconds
          • -------- Clean Up --------
          • Trigger - Add to LeaveTrigger <gen> the event (Player - mPlayer[PlayerCount] leaves the game)
        • Else - Actions
  • Trigger - Turn on UpdateCamera2 <gen>
UpdateCamera2 (Initially Off):
  • Events
    • Time - Every 0.03 seconds of game time
  • For each (Integer A) from 1 to 12, do (Actions)
    • Loop - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • mPlaying[(Integer A)] Equal to True
        • Then - Actions
          • Camera - Set mPlayer[(Integer A)]'s camera Rotation to (Facing of mHero[(Integer A)]) over 0.00 seconds
          • Camera - Set mPlayer[(Integer A)]'s camera Distance to target to 300.00 over 0.00 seconds
          • Camera - Set mPlayer[(Integer A)]'s camera Angle of attack to (330.00 + ((((Position - Z of mHero[(Integer A)]) - (Position - Z of Aiden Hait 0108 <gen>)) / (Target Z of current camera view)) x 1.00)) over 0.00 seconds
          • Camera - Set mPlayer[(Integer A)]'s camera Height Offset to (((Position - Z of mHero[(Integer A)]) + 200.00) - (Abs(((Position - Z of mHero[(Integer A)]) - (Position - Z of Aiden Hait 0108 <gen>))))) over 0.00 seconds
          • Camera - Set mPlayer[(Integer A)]'s camera Far Z to 5000.00 over 0.00 seconds
          • Camera - Set mPlayer[(Integer A)]'s camera Field of view to 1000.00 over 0.00 seconds
        • Else - Actions
I've tested this on multiplayer and I've seen no issues so far.
 

Uncle

Warcraft Moderator
Level 66
Joined
Aug 10, 2018
Messages
6,748
There's a few issues here, although the triggers will indeed work. Forgive me for being nitpicky but these fixes can improve your map a lot, especially if you apply this logic to all of your future triggers.

1) If the Z position of Aiden Hait never changes then there's no reason to use that function, just type in the exact value that you want it to be. You could type 0.00 as the Value or use a Real variable if you wanted to change it dynamically.

2) You're leaking a lot of Points. Memory leaks may be too much to worry about as someone still learning, but in this case I can't help but mention them since your trigger is running 30+ times per second for up to 12 people. That's potentially 360+ leaks per second. Edit: Misread, there's no leaks here.

3) Some of those Camera actions shouldn't need to be set multiple times. They're one and done unless you Reset the camera which probably shouldn't be happening and if it does happen you can just reapply those settings again.

4) You're using a For Loop to reference multiple Players at once, but that's what Player Groups are for.

If I were you I would create some Player Group variables and a trigger like this:
  • Setup Teams
    • Events
      • Time - Elapsed game time is 0.10 seconds
    • Conditions
    • Actions
      • Player Group - Pick every player in (All players) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked player) controller) Equal to User
              • ((Picked player) slot status) Equal to Is playing
            • Then - Actions
              • Player Group - Add (Picked player) to PG_Users
              • Player Group - Add (Picked player) to PG_UsersActive
            • Else - Actions
PG_Users contains all of the Users that were playing the game from the start.

PG_UsersActive contains all of the Users in the game that are still actively playing. That means we need to Remove leavers from PG_UsersActive:
  • Events
    • Player - Player 1 (red) leaves the game
    • Player - Player 2 (blue) leaves the game
    • Player - Player 3 (teal) leaves the game
    • etc...
  • Conditions
  • Actions
    • Player Group - Remove (Triggering player) from PG_UsersActive
With this setup, your future triggers can take advantage of these Player Groups when you want to interact with multiple Players at once.

Here's your camera's periodic trigger setup to only ever run for Players that are actively playing the game:
  • Events
    • Time - Every 0.03 seconds of game time
  • Actions
    • Player Group - Pick every Player in PG_UsersActive and do Actions
      • Loop - Actions
        • Set Variable PlayerNumber = (Player number of (Picked player))
        • Set Variable ZHeight = (Position - Z of mHero[PlayerNumber])
        • Camera - Set mPlayer[PlayerNumber]'s camera Rotation to (Facing of mHero[PlayerNumber]) over 0.00 seconds
        • Camera - Set mPlayer[PlayerNumber]'s camera Distance to target to 300.00 over 0.00 seconds
        • Camera - Set mPlayer[PlayerNumber]'s camera Angle of attack to (330.00 + (((ZHeight) - ZValue) / (Target Z of current camera view)) x 1.00)) over 0.00 seconds
        • Camera - Set mPlayer[PlayerNumber]'s camera Height Offset to (((ZHeight) + 200.00) - (Abs((ZHeight) - (ZValue)))) over 0.00 seconds
        • Camera - Set mPlayer[PlayerNumber]'s camera Field of view to 1000.00 over 0.00 seconds
I also cleaned up the trigger to remove Actions that didn't need to be repeated and replaced the Aiden Hait Z Position thing with a variable that makes more sense here (again, it doesn't need to be a variable, you could type the exact value that you want).

The question "Is this user playing the game" or your Boolean equivalent mPlaying become unnecessary because the player group PG_UsersActive will only ever contain Users that are actively playing the game. I would also rename PlayerCount to PlayerNumber or PN, since it's a useful variable to reuse throughout your triggers when dealing with Player related arrays and the current name doesn't really reflect what it's for.

I know some of this seems nitpicky but it can really help keep your map running smoothly.
 
Last edited:

Uncle

Warcraft Moderator
Level 66
Joined
Aug 10, 2018
Messages
6,748
Actually, I misread your trigger, there are no Point leaks, I thought the Position Z thing said "Position of unit" which is what would cause a leak.

I'll edit my posts in a second to reflect this, but everything else I said still holds true.
 
Level 5
Joined
Dec 29, 2008
Messages
61
Thanks for all this. I'll review it thoroughly.

One question that comes to mind is whether player numbers change when players leave the game? It sounds like a dumb question with an obvious answer but I hope that they don't.
 
Status
Not open for further replies.
Top