Player controls relative to camera

Blitz3D Forums/Blitz3D Beginners Area/Player controls relative to camera

Cubed Inc.(Posted 2010) [#1]
The other day, I was playing The Legend of Zelda Majora's Mask on an emulator (it's legal, so don't worry) and i'm trying to "imitate" the controls in that game and recreate them in Blitz3d.

I know it's impractical to perfectly recreate them, but I'm hoping to get the core mechanics right.

I'm not expecting anyone to do it for me, but what would be the steps for creating something like this.

Any help is highly appreciated, and with all seriousness, i'd personally be extremely grateful for assistance.

please reply. thanks.

Last edited 2010


BLaBZ(Posted 2010) [#2]
Just consider some of the factors that are occurring and apply them,

Point the camera at the player, if player is a certain distance from camera then move the camera forward and down a bit,

You can think of the camera as being "dragged" by the player


jhocking(Posted 2010) [#3]
Vector commands like TFormVector and AlignToVector are highly useful for this sort of stuff. You can use vectors to do things like transform the camera's left direction into a direction that the player should face.


Rob the Great(Posted 2010) [#4]
It's funny that you mention this, because I studied the 3D Zelda games closely for years to understand how their camera works.

I'm a hobbyist programmer, and I've got a fairly large demo going right now of my take on Zelda. It's in a horribly glitchy state currently, so I'm not feeling bold enough to post some sample code for you to take a look at. But what I can offer is some insight on how I mimicked the camera style.

Zelda's camera work is broken into some simple categories, and for this example, I'll refer to Wind Waker and how the camera is used. I'm going to be using some pseudo instead of actual code, because I find the logic to pseudo easier to follow. Let's make some notes on their system first, and we can move towards code later.

First, an important note about the camera system is that the camera tends to match Link's movements in perfect speed. If he runs forward, the camera will run forward with him in the same direction and at the same pace, and it usually doesn't matter which way the camera is facing.

Next, there tends to be a "bubble" around Link, and if the camera hits the edge of this bubble, it will adjust it's speed to always stay within the bubble.

If the player pushes the "Z" button and no enemies are close by, the camera will immediately jump directly behind Link. This is especially useful if the camera is oriented in an odd way, and you wish to reset it back to a normal position.

The last major note to the camera is that if Link is climbing up a ladder, the camera will purposely drop to a lower angle, allowing the player to see up the ladder. Consequently, if Link is climbing down the ladder or falling, the camera will purposely drop to a higher angle to let the player see downward.

That's the basic principles of the camera, and once you understand those, the other principles, such as locking on an enemy, will be easier to make.

Let's start with the first concept of the camera matching Link's speed and direction. When I'm starting a new section of code, I always like to take the time to clearly identify what the current problems are and find solutions. In this case, the problem is telling Blitz to match the camera to link. The solution would be to come up with a system where you can orient the camera to Link, match the movement he made, and after moving the camera, re-orient the camera back to it's original rotation. Please note that the examples were pieced together from memory, so there may be tiny things that I am missing in between.

;This is a spot of code that will UpdateCameraMovement()

   ;First, check for Input. If we push the Up Key...
      ;Move Link 0 units left, 0 units up, and 1 unit forward
      ;Next, let's grab some temporary variables to store the camera's current rotation
      ;For now, we will call them:
      ;TempCamPitch = EntityPitch(camera)
      ;TempCamYaw = EntityYaw(camera)
      ;TempCamRoll = EntityRoll(camera)
      ;Now that we remembered which way our camera was facing, let's rotate the camera
      ;to match the same rotation of link. This can be done with:
      ;RotateEntity camera,EntityPitch(Link),EntityYaw(Link),EntityRoll(Link)
      ;Now that the camera is facing the same direction as link, we can now move the
      ;camera by the same amount as above, and the camera will match link's forward
      ;movement. The pseudo, as above, would be:
      ;Move the Camera 0 units left, 0 units up, and 1 unit forward
      ;At this point, we have now moved Link forward 1, rotated the camera to match
      ;Link's current rotation, and moved the camera forward 1. The only thing left to do is
      ;rotate the camera back to it's original rotation. This is where we will call the
      ;temporary variables we made earlier and set the camera back to pointing where it
      ;was. This can be done with:
      ;RotateEntity camera,TempCamPitch,TempCamYaw,TempCamRoll
   ;That's the end of this 'If' section.

;That's the end of this function that will UpdateCameraMovement()

This can look a little ugly, but if you fully understand the pseudo above, then the following code will make sense:
Function UpdateCameraMovement()

   If KeyDown(UP_KEY)
      MoveEntity Link,0,0,1
      TempCamPitch = EntityPitch(camera)
      TempCamYaw = EntityYaw(camera)
      TempCamRoll = EntityRoll(camera)
      RotateEntity camera,EntityPitch(Link),EntityYaw(Link),EntityRoll(Link)
      MoveEntity camera,0,0,1
      RotateEntity camera,TempCamPitch,TempCamYaw,TempCamRoll
   EndIf

End Function

The cool thing about this function is that we can make relative movements according to Link's orientation, and we can also do so in a way that the player will never know the difference that we were secretly rotating the camera, moving it, and then rotating it back. Blitz will not reflect any changes made to the camera until RenderWorld is called, in which the camera's last orientation before the call will be displayed on the screen.

Let's look at the next concept, which is the camera staying within a bubble of link. This is actually easier than it sounds. First, the pseudo:
;This is a section of code to KeepCameraInBubble(radius = 50)

   ;What I've done above is set a variable that can be passed into this function. There are
   ;going to be times later on down the road where you may want the camera to be
   ;further away from Link than normal, or closer than normal. You don't want to have to
   ;write a function for every different bubble size, so we're going to make it easy to 
   ;change the bubble size and keep it all in one, clean function. By default, I've told Blitz
   ;that to assume it to be 50 if we haven't given it a different value when we call
   ;the function.

   ;First, check if the camera is outside of the bubble
      ;If we are, we want to move the camera towards Link. To do this, let's first save our
      ;current rotation.
      ;Next, just point the camera at link. After that, we will want to move the camera
      ;forward based on how far the camera is outside of the bubble.
      ;When we've moved the camera forward, we'll want to re-orient the camera back to
      ;however it was facing.
   ;That's the end of this 'If' section

;That's the end of the KeepCameraInBubble() function.

Now, the code:
Function KeepCameraInBubble(radius = 50)

   If EntityDistance(Link,camera) > radius
   ;if the distance between Link and the camera is greater than the radius of the bubble...
      TempCamPitch = EntityPitch(camera)   ;Get Temporary Camera Rotations
      TempCamYaw = EntityYaw(camera)
      TempCamRoll = EntityRoll(camera)
      PointEntity camera,link
      MoveEntity camera,0,0,(EntityDistance(Link,camera) - radius)/1.8
      ;Don't let the above line scare you. Basically, it will move the camera faster if it is
      ;further away from our bubble. This makes for a smooth camera movement and will
      ;not allow the camera to get too far away from the bubble.
      RotateEntity camera,TempCamPitch,TempCamYaw,TempCamRoll
   EndIf

End Function.

That's very basic camera motion, but Zelda has always been known for its Z-Targeting and Z-Resetting of the camera. This is very useful, and such a process can be described below.
;This is a section of code to ZResetCamera()

   ;First, get input. If we push the 'Z' button...
      ;All that you have to do is position the camera wherever Link is, rotate the camera
      ;to face the same direction he is facing, and move the camera backwards to where
      ;it needs to be.
   ;That's the end of this 'If' section.

;That's the end of the ZResetCamera() function.

Sounds pretty simple? For the most part, it is.
Function ZResetCamera()

   If KeyDown(Z_BUTTON)
      PositionEntity camera,EntityX(Link),EntityY(Link),EntityZ(Link)
      RotateEntity camera,EntityPitch(Link),EntityYaw(Link),EntityRoll(Link)
      MoveEntity camera,0,0,-25
      PointEntity camera,Link
   EndIf

End Function

The next section is a little more complex, but it should make sense if you study it closely. We're now going to alter our pitch to the camera to tilt it up or down, based upon what Link is doing.
;This is a section of code that will UpdateCameraPitch()

   ;First, we have to know what our good friend Link is up to.
   ;Let's start with checking if he's on the ladder.
      ;If he is indeed on the ladder, let's also see if we're pushing the Up key.
         ;When both of those are true, let's move the camera down and point it up at Link
      ;That's the end of the Up Key section while he's on the ladder.
      ;But if he's on the ladder and we're instead pushing the Down key...
         ;Then we're going to want to move the camera up and point it down at Link
      ;That's the end of the Down Key section while he's on the ladder.
   ;And that's the end of the section of Link being on the ladder.

   ;But what if he's not on the ladder...
      ;and is not touching the ground (i.e. falling)?
         ;Move the camera up and point it down on Link.
      ;That's the end of the section dealing with Link falling.
   ;That's the end of the section dealing with Link being on the ladder.

   ;Finally, if all the above is false, we still need a default action for the camera to take.
   ;Otherwise, it will get stuck in whatever position it was last in until something above is
   ;true. Therefore...
   ;If Link is not on the ladder...
      ;And if link is touching the ground...
         ;Center the camera on Link so it point neither up nor down.
      ;That's the end of the Link touching the ground 'If'
   ;And it's also the end of the Link being on the ladder 'If'

;That's the end of the UpdateCameraPitch() function.

Ok, I'm starting to get a little worn down by all of this, but here's how it would look in code:
Function UpdateCameraPitch()

   If EntityCollided(Link,TYPE_LADDER)
      If Keydown(UP_KEY)
         PositionEntity camera,EntityX(camera),EntityY(Link),EntityZ(camera)
         TranslateEntity camera,0,-25,0
         PointEntity camera,Link
      EndIf
      If KeyDown(DOWN_KEY)
         PositionEntity camera,EntityX(camera),EntityY(Link),EntityZ(camera)
         TranslateEntity camera,0,25,0
         PointEntity camera,Link
      EndIf
   EndIf

   If Not EntityCollided(Link,TYPE_LADDER)
      If Not EntityCollided(Link,TYPE_GROUND)
         PositionEntity camera,EntityX(camera),EntityY(Link),EntityZ(camera)
         TranslateEntity camera,0,25,0
         PointEntity camera,Link
      EndIf
   EndIf

   If Not EntityCollided(Link,TYPE_LADDER)
      If EntityCollided(Link,TYPE_GROUND)
         PositionEntity camera,EntityX(camera),EntityY(Link),EntityZ(camera)
         PointEntity camera,Link
      EndIf
   EndIf

End Function.

As you can see, I value functions deeply. In my eyes, functions are the most important thing in my life, followed by God, and then my girlfriend. I don't want to have to copy and paste anything I don't have to, and I certainly don't want to have to re-code anything I've already written, which is probably why I'm drawn to them so much.

Now, with 1 line, I can repeat any of those steps above. For example, if I wanted to reset the camera, all that I have to do it type in ZResetCamera(), and Blitz will take care of the rest.

These are just some example of how I was able to mimic the style of the Zelda camera movements. Obviously, this just barely scrapes the surface, and you'll for sure have to debug it and add more to it once you have the basic principles nailed. I normally start from the ground up when I'm coding. In other words, I will do one concept at a time, test the game, and see what the results are. If I like the results, I will move on to harder and more complex concepts, and before I know it, I've got a fully developed system on my hands.

I can give some more examples if you need them, and I'm sure there are plenty of programmers here who have more efficient ways to do the same techniques I'm demonstrating. I've never liked the Vector commands much, but to my understanding, they can completely replace three or four lines of my code, all in one step.

In either case, I would recommend using the pseudo coding process like I demonstrated. English is my native tongue, and Blitz is one that I had to teach myself. It's easier for me to talk through the whole problem in pure English first, and then gradually translate that in Blitz.


jhocking(Posted 2010) [#5]
Another tip that can be useful for third person cameras is having a pivot that the camera is parented to. The pivot is at the player's position, while the camera is farther away and pointed at the pivot. Then when you rotate that pivot the camera orbits around rather than turning in place.

Depending on what exactly you want the camera to do this may not be flexible enough for you, but for many situations this is an easy way to make the camera orbit, as opposed to doing all the funky math to figure out where to position the camera.


I've never liked the Vector commands much, but to my understanding, they can completely replace three or four lines of my code, all in one step.


Specifically, using vectors can replace a lot of the TempCamPitch/TempCamRoll stuff. I'm not sure it's much more efficient though, just reads differently, so do whatever makes more sense to you.

Last edited 2010


Rob the Great(Posted 2010) [#6]
@jhocking

That's a great idea. It also sounds easier to manage than my system.

If I wasn't so far into my camera controls in my game already, I would totally go with that system over mine, as it's easier to code.