(info) This AI is still in early stages of development.

Camera ponder

JS API - updates required

Objects:

Will require a new object type (and associated constant):

  • POINT object – like POSITION, but with addition of a 'z' (height) property.

Duration / easing:

Many of the features mentioned below have duration and easing params – Both are optional:

  • duration = amount of time a transition takes in milliseconds. Unless specified otherwise, defaults to 0 (ie. instant transition, no easing).
  • easing = name of function to call (see below) to determine the value at a specific time since the start of the transition. Default: Linear transition.

The easing equation functions would be called with fixed params as shown below:

function whatever(elapsedTime, originalVal, delta, duration) {
	// elapsedTime = number of ms since tween started
	// originalVal = value at start of twean
	// delta = targetValue-originalValue
	//         ...where targetValue is what you want at the end of the tween
	//         The C++ code only needs to calculate the delta once at start of tween
	// duration = total duration of the tween in ms
 
	var valAtCurrentTime = // some math
	return valAtCurrentTime;
}

(This would also mean other things could use those easing functions - such as setSunIntensity() and setSunPosition(). You could even have an AI that tweens it's aggression from A to B during the first 10 mins of the game.)

Demos of the various algorithms can be seen here, and BSD-licensed source code (in actionscript) is available here – very easy to port to JS. Functions requiring additional params can be curried.

I don't know how the C++ code will be implemented, but if one aspect of the camera is already being animated and then something else happens that affects the same properties, the previous animation should be cancelled.

For example:

  • cameraWalk() is used to start the camera moving forward
  • cameraCrab() is used to start it moving sideways - this is fine, you can walk forwards and move to the side at the same time
  • centreView() is called – this sets x,y, thus cancelling both the cameraWalk() and cameraCrab() animations (the camera can't be in two places at the same time)

Another example:

  • cameraWalk() and cameraFloat() animations are in progress
  • cameraWatch() is called with allowMove = true: only if that causes a camera movement will the cameraWalk() and cameraFloat() animations be cancelled (can't be in two places at once), otherwise those other animations continue (because the lens can pan/tilt/zoom independently while the camera is moving).

Hope that makes sense! (smile)

Camera position:

  • getCameraPosition() – returns current position of camera (POINT object)
  • cameraPosition([walk[, crab[, float[, duration[, easing]]]]]) – a combination of the 3 functions below, specify null for any of walk/crab/float if you don't want to alter. Call function without any params to stop all position transitions.
    • cameraWalk(distance[, duration[, easing]]) – like ↑ and ↓ keyboard shortcuts in-game, +ve distance = forward, -ve = backwards, 0 = stop
    • cameraCrab(distance[, duration[, easing]]) – like ← and → keyboard shortcuts in-game, -ve distance = left, +ve distance = right, 0 = stop
    • cameraFloat(distance[, duration[, easing]]) – lift or drop the height of the camera, +ve distance = move up, -ve distance = move down, 0 = stop
  • centreView(x, y) – the camera instantly moves to look at an x,y location (ie. no transition or easing) [does this also change lens pan/tilt?]
  • cameraSlide(x, y[, duration[, easing]]) – the camera slides over terrain to specified position – can we add 'duration' and 'easing' params, with duration defaulting to whatever current duration is?
  • eventCameraPosition(beforeChange) – triggered just before a position change, or just after a position transition ends

Note: cameraSlide(x, y, 0) == centreView(x, y)

Is it worth ditching the individual functions for walk, crab and float to reduce number of functions?

(The events are important in the case of "recording" manual camera moves in a cut-scene editor.)

Camera lens:

Note: As it's a virtual camera, I've treated roll as if it's an attribute of the lens (whereas in real-life you actually roll the entire camera). Roll just seemed to fit better here, as it doesn't change the POINT position of the camera.

  • getCameraLens() – return object with pan, tilt, roll, zoom properties
  • cameraLens([pan[, tilt[, roll[, zoom[, duration[, easing]]]]]]) – a combination of the 4 functions below, specify null for any of pan/tilt/roll/zoom if you don't want to alter. call function without any params to stop all lens transitions.
    • cameraPan(angle[, duration[, easing]]) – look left (-ve angle) or right (+ve angle), in relation to north, like Ctrl + Left/Right mouse-drag in-game
    • cameraTilt(angle[, duration[, easing]]) – look up (+ve angle) or down (-ve angle), in relation to horizon, like Ctrl + Up/Down mouse-drag in-game
    • cameraRoll(angle[, duration[, easing]]) – like tilting your head to the side, +ve angle to the right, -ve angle to the left, 0º for no tilt (horizontal camera).
    • cameraZoom() – add optional 'easing' parameter, could 'time' param be renamed to 'duration'?
  • eventCameraLens(beforeChange) – triggered just before a lens change occurs, or just after a lens transition ends

Would azimuth and altitude make more sense than angles for pan and tilt?

Is it worth ditching the individual functions for pan, tilt, roll and zoom to reduce number of functions?

Watching objects:

Automates the camera pan/tilt and (optionally) zoom to make camera watch an object...

  • getCameraWatch() – returns object with target,mode,allowMove properties, or null if not watching anything
  • cameraWatch([target[, mode[, allowMove[, duration[, easing]]]]]) – see notes below
  • eventCameraWatch([target]) – triggered when target changes, passing in a target if just started watching, or null if just finished watching

Does not trigger eventCameraLens() or eventCameraPosition().

Camera will stop watching target if:

  • cameraWatch() is called with no parameters
  • The function is called again but with a different target
  • The target gets destroyed
  • Any other lens transition occurs

Parameters:

  • target – a POSITION, POINT or Game object. If duration specified, camera will transition so that the target is in view, optionally moving (allowMove) to get a better view
  • mode can be one of the following:
    • LOOK_AT (default) – camera transitions to look at target (auto-pan/tilt and optionally move) then stops (clears target)
    • MONITOR – camera continues to watch the target (auto-pan/tilt and optionally move), but the zoom won't be altered automatically (default)
    • TRACK_ZOOM – same as MONITOR, but zoom will automatically be adjusted whenever distance between camera and target changes (ie. if either of them move) so that the target appears to remain the same size
    • DOLLY_ZOOM – same as TRACK_ZOOM but also alters field of view (formulas)
  • allowMove – if true, the camera can be moved to get a better view of the target, same duration/easing applied to the move. Default: false

Following objects:

The camera position can be automated by making it follow an object (usually a droid object)...

  • getCameraAnchor() – returns the object containing target (usually a droid object) and offset (POINT object)
  • cameraAnchor([target[, offset[, duration[, easing]]]]) – see notes below
  • eventCameraAnchor([target]) – triggered when anchor changes, passing in a target if just started following, or null if just stopped following

Following non-droid objects is useful if you just want to anchor the camera to some static object but with an offset, and then be able to call the function again with a different object (basically moving camera in relation to static object).

Camera will stop following target if:

  • cameraAnchor() is called with no parameters
  • The function is called again but with a different target
  • The target gets destroyed
  • Any other position transition occurs

Parameters:

  • target – the object to anchor to. default: un-anchor the camera
  • offset – a POINT object defining the camera position's offset from the centre of the object so, for example, you can put camera behind / in front / at the side / under or over the droid and at any distance from it – default: behind the object like in drive mode

The camera transitions to the offset position (unless duration is 0 or null). The offset can be changed while the droid is in motion by calling cameraAnchorTo() again for the same target, but with a different offset.

Once the camera reaches the object, or offset from that object, the camera's position and roll settings get linked to the object – if the object changes position, the camera follows by the same amount. However, there needs to be some 'damping' applied so the camera position/roll updates lag behind the a little. This is to avoid "camera whiplash" (drive mode a circling VTOL and you'll see what I mean) and also avoid a visual effect that makes it look like it's the world, and not the droid, that's moving. For example, if the camera is behind the droid and watching the droid, then the droid suddenly stops, the camera should almost crash in to the back of it!

Strafing:

The camera can be moved in a circular motion on the horizontal plane around an object ("strafing")...

  • getCameraStrafe() – returns the current subject being strafed, or null of not strafing
  • cameraStrafe([target[, speed[, radius[, duration[, easing]]]]]) – like in-game when you win/lose and the camera circles around the HQ
  • eventCameraStrafe(target) – triggered when anchor changes, passing in a target if just started strafing, or null if just finished strafing

Once started, strafing can be stopped by:

  • calling the function with no parameters
  • object gets destroyed
  • camera anchored to some other object
  • any function that sets x,y of camera

Parameters:

  • taret – the object to strafe, can be POSITION, POINT or Game object. Default: stop strafing
  • speed – speed at which camera moves around circumference in tiles per second (-ve = anti-clockwise, +ve = clockwise). Default: whatever speed camera moves at when scores are shown at end of game.
  • radius – distance between target (centre of circle) and circumference, in tiles. Default: same as VTOL circle radius

The duration and easing, if set, cause the camera to transition from it's current location to the circumference before it starts to strafe.

Watching & Following a droid:

Anchoring the camera to a droid doesn't make the camera watch at the droid. In fact, the camera could be watching something completely different, or not watching anything (static pan/tilt/zoom values). For example, the camera could be watching the HQ, then I tell it to anchor to a VTOL – it's like I'm looking at the HQ from the window of the VTOL. If allowMove is set on cameraWatch(), and the camera decides to move to get a better look at the watch target, the camera will stop following its anchor.

Sometimes you just want a "drive mode" effect which is what cameraTrack() function is for - it's effectively the same as doing:

cameraWatch(someDroid); // camera will keep looking at someDroid
cameraAnchorTo(someDroid); // camera snaps to a short distance behind droid

The cameraTrack() function is still useful, but would ideally be altered to do the C++ equivalent of:

function cameraTrack(droid, duration, easing) {
	cameraWatch(droid);
	cameraAnchorTo(droid, null, duration, easing);
}

This way, the watch and follow events get called, and I can stop one or the other independently.