- Joined
- Jan 21, 2006
- Messages
- 2,552
I put this together for Midnighters initially, but since it worked so well I wanted to share it with the rest of the community. It is a 3rd-person camera library, which by itself really isn't anything special. The only thing that makes this camera library a preferable choice is that it contours with terrain level changes. I know for a fact many people have had the problem of the camera stooping too low or too high when the terrain nearby them is shifty, but this camera attempts to solve those problems with a couple of simple calculations.
First, put this code in your custom script (doesn't really matter if it is done first or not). It doesn't make a difference where it is added, since it is a scope and will be placed beneath the library. What it does is wait until the initialization thread is finished and then it applies the camera to a newly created Footman (for player 1).
Tell me what you guys think, as I wanted to add a couple more things before submitting it. There are still quite a few problems, and things that could be done better, but with a reasonable (if it's too close, the camera tends to go beneath the terrain - I'm trying to fix this) set of data, it should work pretty well.
First, put this code in your custom script (doesn't really matter if it is done first or not). It doesn't make a difference where it is added, since it is a scope and will be placed beneath the library. What it does is wait until the initialization thread is finished and then it applies the camera to a newly created Footman (for player 1).
JASS:
scope Setup initializer init
public function initDelayed takes nothing returns nothing
set camera.localCamera.controller = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
call camera.localCamera.start(true)
call DestroyTimer(GetExpiredTimer())
endfunction
public function init takes nothing returns nothing
call TimerStart(CreateTimer(),0,false,function initDelayed)
endfunction
endscope
JASS:
library Camera
//############################################################################################
globals
//****************************************************************************************
// Configuration Constants
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Default constant values used for initializing each static camera instance.
//
public constant real DEFAULT_PAN_DURATION = 0.7
//
// The following configuration constants are preferred defaults. The camera is set
// to perform best under these circumstances, though adjustments *can* be made if it
// is necessary.
//
public constant real DEFAULT_TARGET_DISTANCE = 1250
public constant real DEFAULT_ZOFFSET = 30
public constant real DEFAULT_ANGLE_OF_ATTACK = 340
//
//
//****************************************************************************************
endglobals
globals
//****************************************************************************************
// Dynamic Storage Utility
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// These are private storage variables that allow easy-access of utilities, such as
// the GetLocationZ function which allows terrain-detection.
private constant location core__tempLoc = Location(0, 0)
private real core__tempLocZ
//
//
//****************************************************************************************
endglobals
//############################################################################################
private struct cameradata
public real targetDistance
public real angleOfAttack
public real zOffset
static method create takes nothing returns thistype
local thistype data=allocate()
set data.targetDistance=DEFAULT_TARGET_DISTANCE
set data.angleOfAttack=DEFAULT_ANGLE_OF_ATTACK
set data.zOffset=DEFAULT_ZOFFSET
return data
endmethod
endstruct
//############################################################################################
struct camera extends array
private unit cam__controller
private cameradata cam__specData
readonly player controllerPlayer
private boolean cam__enabled
public boolean inheritRotation
public real panDuration
readonly boolean isLocal
readonly static thistype localCamera
private static timer core__looptmr = CreateTimer()
private static constant real core__looptmrRef = 0.04
//############################################################################################
method operator controller takes nothing returns unit
return cam__controller
endmethod
method operator controller= takes unit u returns nothing
set cam__controller=u
if(isLocal) then
call SetCameraTargetController(cam__controller, 0, 0, true)
endif
endmethod
//############################################################################################
method start takes boolean rotation returns boolean
set inheritRotation = rotation
//If there is an available camera-target then .start() will return true. If no target is
//available at all then "inheritRotation" will be queued for when .controller= is used
return cam__controller != null
endmethod
method stop takes nothing returns nothing
set inheritRotation = false
if (isLocal) then
call StopCamera()
endif
endmethod
//############################################################################################
static if(DEBUG_MODE) then
//The following code will only be implemented if DEBUG_MODE is enabled.
private static method debug takes nothing returns nothing
endmethod
endif
//############################################################################################
private static method loopFunc takes nothing returns nothing
local real r0=SquareRoot(Pow(GetCameraEyePositionX(), 2)+Pow(GetCameraEyePositionY(), 2))
local real r1=SquareRoot(Pow(GetCameraTargetPositionX(), 2)+Pow(GetCameraTargetPositionY(), 2))
local real d =GetCameraTargetPositionZ()-GetCameraEyePositionZ()
set d=SquareRoot((r1-r0)*(r1-r0)+(d*d))
call MoveLocation(core__tempLoc, GetUnitX(localCamera.controller), GetUnitY(localCamera.controller))
set core__tempLocZ=GetLocationZ(core__tempLoc)
if(localCamera.isLocal) then
if(localCamera.inheritRotation) then
call SetCameraField(CAMERA_FIELD_ROTATION, GetUnitFacing(localCamera.controller),/*
*/localCamera.panDuration)
endif
//In order to counter-weight the smoothing that the camera uses we must find out the
//offset and correct it using ZOFFSET. On flat-ground, diffz will be 0.00.
call SetCameraField(CAMERA_FIELD_ZOFFSET, core__tempLocZ-GetCameraTargetPositionZ()/*
*/+localCamera.cam__specData.zOffset+(GetCameraEyePositionZ()/*
*/-core__tempLocZ)/5, localCamera.panDuration)
//Now we must adjust the target-distance depending on how much the result has varied from it's original
set core__tempLocZ=localCamera.cam__specData.targetDistance-d
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE,/*
*/localCamera.cam__specData.targetDistance+(core__tempLocZ/3),/*
*/localCamera.panDuration)
call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK,/*
*/localCamera.cam__specData.angleOfAttack-(GetCameraField(CAMERA_FIELD_ZOFFSET)/6),/*
*/localCamera.panDuration)
endif
debug call debug()
endmethod
//############################################################################################
private static method onInit takes nothing returns nothing
local integer i = 0
loop
exitwhen(i == 12)
//Initialize camera components:
set camera[i].controllerPlayer =Player(i)
set camera[i].cam__controller =null
set camera[i].cam__specData =cameradata.create()
set camera[i].panDuration =DEFAULT_PAN_DURATION
//Setup local camera data:
set camera[i].isLocal =false
set i = i + 1
endloop
//Finalize local data:
set localCamera = camera[GetPlayerId(GetLocalPlayer())]
set localCamera.isLocal = true
//Initialize update/refresh loop timer:
call TimerStart(core__looptmr, core__looptmrRef, true, function thistype.loopFunc)
endmethod
endstruct
endlibrary
Tell me what you guys think, as I wanted to add a couple more things before submitting it. There are still quite a few problems, and things that could be done better, but with a reasonable (if it's too close, the camera tends to go beneath the terrain - I'm trying to fix this) set of data, it should work pretty well.
|
Last edited: