Example Advanced Camera

From HEWIKI
Jump to: navigation, search

Contents

Tutorials
Art | Scripting | World Design | Game Mechanics | All

NOTE:this page currently contains timer functions and system variables that are part of an upcoming HeroEngine Update. SYSTEM.TIME.LOCAL and <timer>.setTimeSource("LOCAL") will be released in a future update
Remove the calls to setTimeSource and change SYSTEM.TIME.LOCAL to SYSTEM.TIME.NOW to get the current scripts to work.

What this page covers

This page serves as an example of how you might want to implement a linear interpolated camera and smooth mouse wheel zoom. Instead of snapping the cameras rotation instantly to the desired position/rotation it will break down the desired position/rotation in to small sections and transition through them over a small time interval. The end result is that it makes the camera's movement look much smoother for the user. This page is setup for HeroCloud users that do not already have a linear interpolated camera. This code is written such that it can be copied and pasted directly in to a default HeroCloud world and have the linear interpolated camera work right away. If you already have overridden some of the scripts used in this example you will need to change some of these steps to a game specific implementation. Note: Newer HeroCloud worlds may already have this type of camera implemented.

What you will need to know

This page is intended for users who know how to use the DOM editor and the HSL Editor to create custom DOM definitions and Scripts. While this page is setup so a new user could copy paste the majority of the code with out any issues if your are no longer using the default scripts for your game you will to change some of these steps to a game specific implementation for your world.


What this page does not cover

This page does not cover the exact details of how and why the the camera is implemented this way. It is here to serve as a working example of how one might implement a linear interpolated camera and may not be the right way to implement the camera depending on the needs of you game.


Created New Client DOM definitions

Note:All DOM definitions must be uniquely named in HeroEngine. The names given are specific enough to have a very low chance of already existing in your world. If you wish to use different names or if these names already exist in your world be sure to replace those names as needed in the sample scripts

Create These CLIENT DOM definitions.

New Class

New Fields

Once all of these fields are created add them to the ADV_LerpCamera Class

Edit Scripts

Make a new empty script for the ADV_LerpCamera class and copy paste the code below in to it. After that is done compile and submit the code.

method ADV_Lerp( end as Vector3, change as Float) as Vector3
  total_distance as Vector3 = end
  total_distance *= change
  me.ADV_DragRotateCurrentLERPPosition = total_distance
  total_distance += me.ADV_DragRotateStartingCamRot
  return total_distance
.
 
method ADV_DragRotateTimer_tick()
    total_time as TimeInterval = 00:00:00.1
    total_elapsed as TimeInterval = SYSTEM.TIME.LOCAL - me.ADV_DragRotateTimer.startTime
    if total_elapsed < total_time
      total_milli as Float = total_time.millisecondsTotal 
      total_elapsed_milli as Float = total_elapsed.millisecondsTotal
      change as Float = total_elapsed_milli / total_milli
      new_rot as Vector3 = me.ADV_Lerp(me.ADV_DragRotateDesiredEndPos, change)
      me.ADV_ClampCameraYRotation( new_rot )
      SetNodeRotation( me, new_rot )
      return
    .
    me.ADV_DragRotateCurrentLERPPosition = (0,0,0)
    me.ADV_DragRotateTimer.stop()
.
 
method ADV_DragRotateCamera( cam as NodeRef , fy as Float , fx as Float , fz as Float )
  if me.ADV_DragRotateTimer.timerState == OFF
    me.ADV_DragRotateCurrentLERPPosition = (0,0,0)
    me.ADV_DragRotateDesiredEndPos = (fy,fx,fz)
    me.ADV_DragRotateStartingCamRot = GetNodeRotation( cam )
    me.ADV_DragRotateTimer.fireRate = 00:00:00.001
    me.ADV_DragRotateTimer.setTimeSource("LOCAL")
    me.ADV_DragRotateTimer.start()
  else
    //timer was running stop it and change values
    me.ADV_DragRotateTimer.stop()
    me.ADV_DragRotateStartingCamRot = GetNodeRotation( cam )
    me.ADV_DragRotateDesiredEndPos = (fy,fx,fz) + (me.ADV_DragRotateDesiredEndPos - me.ADV_DragRotateCurrentLERPPosition)
    me.ADV_DragRotateCurrentLERPPosition = (0,0,0)
    me.ADV_DragRotateTimer.start()
  .
.
 
method ADV_ClampCameraYRotation( rotation references Vector3 )
  if rotation.x > 85
    rotation.x = 85
  else if rotation.x < -85
    rotation.x = -85
  .
.
 
method ADV_WheelLERP( start as Float ,end as Float, change as Float ) as Float
    total_distance as Float = end - start
    total_distance *= change
    return total_distance
.
 
 
method ADV_ClampCameraOffset( value references Float) 
  if value > 1.333
    value = 1.333
  else if value <.06
    value = .06
  .
.
 
method ADV_MouseWheel( desired_change as Float )
  if desired_change > 0
    //moving out
    me.ADV_MouseScrollCheckReverse("out")
  else
    me.ADV_MouseScrollCheckReverse("in")
  .
  if me.ADV_MouseWheelTimer.timerState == OFF
    me.ADV_MouseWheelCurrentOffset = 0
    me.ADV_MouseWheelStartPosition = GetCameraOffset("*") 
    me.ADV_MouseWheelDesiredEnd = desired_change   
    me.ADV_MouseWheelTimer.fireRate = 00:00:00.001
    me.ADV_MouseWheelTimer.setTimeSource("LOCAL")
    me.ADV_MouseWheelTimer.start()
  else
    me.ADV_MouseWheelTimer.stop()
    me.ADV_MouseWheelStartPosition = GetCameraOffset("*")
    me.ADV_MouseWheelDesiredEnd = desired_change + (me.ADV_MouseWheelDesiredEnd - me.ADV_MouseWheelCurrentOffset)
    me.ADV_MouseWheelCurrentOffset = 0
    me.ADV_MouseWheelTimer.start()
  .
.
 
method ADV_MouseScrollCheckReverse( direction as String )
  if direction != me.ADV_MouseWheelDirection
    me.ADV_MouseWheelTimer.stop()
    me.ADV_MouseWheelDirection = direction
  .
.
 
method ADV_MouseWheelTimer_tick()
  total_time as TimeInterval = 00:00:00.5
  total_elapsed as TimeInterval = SYSTEM.TIME.LOCAL - me.ADV_MouseWheelTimer.startTime
      if total_elapsed < total_time
      total_milli as Float = total_time.millisecondsTotal 
      total_elapsed_milli as Float = total_elapsed.millisecondsTotal
      change as Float = total_elapsed_milli / total_milli
      new_offset as Float = me.ADV_WheelLERP( 0, me.ADV_MouseWheelDesiredEnd, change)
 
      me.ADV_MouseWheelCurrentOffset = new_offset
//      println(new_offset)
      new_offset += me.ADV_MouseWheelStartPosition
      me.ADV_ClampCameraOffset( new_offset )
      SetCameraOffset("*",new_offset)
      return
    .
    me.ADV_MouseWheelCurrentOffset = 0
    me.ADV_MouseWheelTimer.stop()
.
 
method ADV_StopDragRotate()
  me.ADV_DragRotateTimer.stop()
  me.ADV_DragRotateDesiredEndPos = (0,0,0)
.

Next what you will need to do is make sure your "game" camera is now of type ADV_LerpCamera. This should done when the camera is created. In HeroCloud worlds this happens in the client side E_ACCCOntrollerClassMethods. The method _HE_ACCC_NavigateLoaclline line 1383 cam as NodeRef of Class HBNode = AddCamera("GAME") makes the Default game camera NOTE: the line number may be different depending on if you've edited this file or if we have updated it in a recent update. Immediately after that we want to glom our ADV_LerpCamera on to the camera node so insert the follow code below the line that makes the camera.

if not (cam is kindof ADV_LerpCamera)
  GlomClass( "ADV_LerpCamera", cam )
.

Lastly you will need to edit the script that handles camera input. In HeroCloud worlds this is the script Input_Camera by default. If you have edited the Input_Camera script or if you are using a different input script and command layer the code below may not work for your game. What you will need to do in that case is find where your camera is being rotated and instead of using the external functions to rotate the camera node use the ADV_LerpCamera method ADV_DragRotateCamera(cameraNode as noderef,fy as float ,fx as float ,fz as float)

Below is the full implementation of Input_Camera just copy paste it in to Input_Camera, then compile and submit it.

function OnMouseMove(mx as Integer, my as Integer, dx copies Integer, dy copies Integer) as Boolean
  if (GetCmdState("Camera","DragRotate"))
    cam as NodeRef = GetActiveCamera()
    if (GetTweakableBoolean("GameInvertMouseX")) or $INPUT._InputGameInvertMouseX
      dx = dx * -1
    .
    if (not GetTweakableBoolean("GameInvertMouseY")) and ( not $INPUT._InputGameInvertMouseY )
      dy = dy * -1
    .
 
    fx as Float = dx
    fy as Float = dy
    if $INPUT._InputGameMouseSensitivity > 0
//      dx = round( dx * $INPUT._InputGameMouseSensitivity )
//      dy = round( dy * $INPUT._InputGameMouseSensitivity )
      fx = fx * $INPUT._InputGameMouseSensitivity
      fy = fy * $INPUT._InputGameMouseSensitivity
    .
 
    if cam is kindof ADV_LerpCamera
      cam.ADV_DragRotateCamera(cam,fy,fx,0)
    else
      RotateNode(cam,fy,fx,0)
    .
 
    return true
  else if (GetCmdState("Camera","DragRotateCamera"))
    SetCmdState("Camera","DragRotateCameraEngaged",true)
    cam as NodeRef = GetActiveCamera()
    if (GetTweakableBoolean("GameInvertMouseX")) or $INPUT._InputGameInvertMouseX
      dx = dx * -1
    .
    if (not GetTweakableBoolean("GameInvertMouseY")) and ( not $INPUT._InputGameInvertMouseY )
      dy = dy * -1
    .
 
    fx as Float = dx
    fy as Float = dy
 
    if $INPUT._InputGameMouseSensitivity > 0
//      dx = round( dx * $INPUT._InputGameMouseSensitivity )
//      dy = round( dy * $INPUT._InputGameMouseSensitivity )
      fx = fx * $INPUT._InputGameMouseSensitivity
      fy = fy * $INPUT._InputGameMouseSensitivity
    .
 
    playerCharacter as NodeRef of Class HBNode = GetPlayerCharacterNode()
    if playerCharacter <> None
      ACCController as NodeRef of Class _ACCController
      ro as Vector3
 
      where playerCharacter is kindof _ACCControllerOwner
        ACCController = playercharacter._getACCController()
 
        if ACCController <> None
          ro.y = ACCController._getGameCameraRotationalOffset().y + dx
          ACCController._setGameCameraRotationalOffset(ro)
        .
      .      
    .
 
    if cam is kindof ADV_LerpCamera
     cam.ADV_DragRotateCamera(cam,fy,fx,0)
    else
      RotateNode(cam,fy,fx,0)
    .
    return true  
  .
 
  return false
.
 
function OnMouseWheel(delta as Integer) as Boolean
// Used to determine the offset to use for the camera
//
  w as Float = delta * .0008
  gco as Float = GetCameraOffset("*")
  gco = gco + w
  if (gco > 1.3333)
    gco = 1.3333
  else if (gco < 0.06)
    gco = 0.06
  .
  cam as NodeRef = GetActiveCamera()
  if cam is kindof ADV_LerpCamera
    w *= -1
    cam.ADV_MouseWheel( w )
  else
    SetCameraOffset("*",gco)
  .
  return true
.
 
function onCmdStart(cmd as String) as Boolean
  if (cmd=="DragRotate" or cmd=="DragRotateCamera")
    if GetCmdState("Camera", "DragRotateCameraEngaged")
      SetCmdState("Camera","DragRotateCameraEngaged",false)
      playerCharacter as NodeRef of Class HBNode = GetPlayerCharacterNode()
      if playerCharacter <> None
        ACCController as NodeRef of Class _ACCController
        ro as Vector3
 
        where playerCharacter is kindof _ACCControllerOwner
          ACCController = playercharacter._getACCController()
 
          if ACCController <> None
            ro.y = ACCController._getGameCameraRotationalOffset().y
            ACCController._clearGameCameraRotationalOffset()
          .
        .
 
        cam as NodeRef = GetActiveCamera()
        RotateNode(cam,0,-ro.y,0)
      .
    .
    if $PLAYMODE._getPlayModeState() == ON
      $Cursor._setCustomCursorVisibility(false)
    else
      SetCursorVisibility(false)
    .
 
//    SetIgnoreCursor(true)
    return true
  .
  if (cmd=="ToggleFullscreen")
    SYSTEM.EXEC.CPULIMIT = 0:00:30
    NotifyStateChange("TOGGLE_FULLSCREEN")
    return true
  .
  if (cmd=="ClearRotateCamera")
    playerCharacter as NodeRef of Class HBNode = GetPlayerCharacterNode()
    if playerCharacter <> None
      ACCController as NodeRef of Class _ACCController
      ro as Vector3
 
      where playerCharacter is kindof _ACCControllerOwner
        ACCController = playercharacter._getACCController()
 
        if ACCController <> None
          ro.y = ACCController._getGameCameraRotationalOffset().y
          ACCController._clearGameCameraRotationalOffset()
        .
      .
 
      cam as NodeRef = GetActiveCamera()
      RotateNode(cam,0,-ro.y,0)
    .
    return true
  .
  if (cmd=="ToggleAspectRatio")
    NotifyStateChange("TOGGLE_ASPECTRATIO")
    return true
  .
  if (cmd=="RotateCameraUp" or cmd=="RotateCameraDown" or cmd=="ZoomIn" or cmd=="ZoomOut")
    return true
  .
  if (cmd=="Click")
    return false    
  .
  return false
.
 
 
function onCmdStop(cmd as String) as Boolean
  if (cmd=="DragRotate" or cmd=="DragRotateCamera")
    cam as NodeRef = GetActiveCamera()
    if cam is kindof ADV_LerpCamera
      cam.ADV_StopDragRotate()
    .
    if $PLAYMODE._getPlayModeState() == ON
      $Cursor._setCustomCursorVisibility(true)
    else
      SetCursorVisibility(true)
    .
    SetIgnoreCursor(false)
    return true
  .
 
  if (cmd=="ToggleFullscreen" or cmd=="ClearRotateCamera" or cmd=="ToggleAspectRatio")
    return true
  .
  if (cmd=="RotateCameraUp" or cmd=="RotateCameraDown" or cmd=="ZoomIn" or cmd=="ZoomOut")
    return true
  .
  if (cmd=="Click")
    return false
  .
  return false
.
 
function onFrameUpdate(elap as TimeInterval)  
  if (GetCmdState("Camera","RotateCameraUp"))
    dX as Float = .12 * elap.millisecondsTotal
    cam as NodeRef = GetActiveCamera()
    RotateNode(cam,dX,0,0)
  else if (GetCmdState("Camera","RotateCameraDown"))
    dX as Float = -.12 * elap.millisecondsTotal
    cam as NodeRef = GetActiveCamera()
    RotateNode(cam,dX,0,0)
  else if (GetCmdState("Camera","ZoomIn"))
    w as Float = elap.millisecondsTotal * .0007
    gco as Float = GetCameraOffset("*")
    gco = gco - w
    if (gco > 2.0)
      gco = 2.0
    else if (gco < 0.06)
      gco = 0.06
    .
    SetCameraOffset("*",gco)
  else if (GetCmdState("Camera","ZoomOut"))
    w as Float = elap.millisecondsTotal * .0007
    gco as Float = GetCameraOffset("*")
    gco = gco + w
    if (gco > 2.0)
      gco = 2.0
    else if (gco < 0.06)
      gco = 0.06
    .
    SetCameraOffset("*",gco)
  .
.

Other Considerations

With this setup the external function setIgnoreCursor( On as boolean ) can cause odd behavior with very high DPI mice and tablets. You will want to make sure not to turn on setIgnoreCursor with out modifying the implementation if this affects you. In default HeroCloud worlds this is turned on when you drag rotate the mouse on so you will want to turn it off.

Final Result

Now you should have a fully functional linear interpolated camera. When you go in to character mode your camera should rotate smoothly as well as when you use the mouse wheel to zoom in and out.

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox