• Advertisement
Sign in to follow this  

One Camera With Multiple Targets

This topic is 2165 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm currently working on a 3D multiplayer game with a singular camera that tracks multiple targets. Right now it translates its position relative to the centroid. This causes issues when, for example, 3 players are in one corner and one player is in the other. Anyone have a relatively simple algorithm that properly weights how these targets contribute to the lookat/position of the camera and ensures no target ever goes off screen?

Share this post


Link to post
Share on other sites
Advertisement
My first thought is to define a function that, given the coordinates of the targets (after projection), computes a utility (how happy we are with the situation). You can then compute the gradient of this function with respect to the position parameters of the camera, and move the camera in the direction that maximizes the utility.

Does that make sense?

Share this post


Link to post
Share on other sites

My first thought is to define a function that, given the coordinates of the targets (after projection), computes a utility (how happy we are with the situation). You can then compute the gradient of this function with respect to the position parameters of the camera, and move the camera in the direction that maximizes the utility.

Does that make sense?

That makes sense, but what might the inputs/outputs be for a typical utility function? I get this is the general idea, but finding a way to process that utility function is the tricky part.

Share this post


Link to post
Share on other sites
You should also zoom in and out to match view size to target position; you are effectively trying to keep inside the view a composite target that, in addition to moving, becomes larger if the tracked characters move apart and smaller, up to the size of a single character, if they move closer to each other. In your example, you have to zoom out to include all "corners" over the course of many frames, as characters spread around.

Given a camera position, I'd compute a satisfactory frustum to frame every tracked target and merge them (leftmost left side, rightmost right side, etc.) to obtain a frustum that includes all targets, which translates easily to a camera transform matrix. Meanwhile, you can move the camera to a better place.

Share this post


Link to post
Share on other sites
Your utility function can be a sum of several terms. Make a soft function that rewards being near the center of the screen, and add that for each target. Then add something that rewards having the targets be separated on the screen, perhaps by pairs, or perhaps by the distance between the two most distant targets (in screen space).

You'll have to play around with it until you are happy with the results. If you are not happy with the results, think of what feature of the configuration doesn't make you happy, and tweak the utility function to represent that. Rinse. Repeat.

Share this post


Link to post
Share on other sites
If you want it simple, you could just average the coordinate positions of all targets that your camera needs to look at, then have your camera focus on the averaged position.

Share this post


Link to post
Share on other sites

If you want it simple, you could just average the coordinate positions of all targets that your camera needs to look at, then have your camera focus on the averaged position.

We've been doing that but, for example, when 3 are in one corner and 1 is in the other, the isolated target is not in the camera view at all.

Share this post


Link to post
Share on other sites
We've been doing that but, for example, when 3 are in one corner and 1 is in the other, the isolated target is not in the camera view at all.[/quote]
In some cases, it may be impossible to have every target on screen, unless your camera is reaaaally far away (in which cases the targets might be much too small to be seen).

Share this post


Link to post
Share on other sites

We've been doing that but, for example, when 3 are in one corner and 1 is in the other, the isolated target is not in the camera view at all.

In some cases, it may be impossible to have every target on screen, unless your camera is reaaaally far away (in which cases the targets might be much too small to be seen).
[/quote]

Perhaps you could transition to two cameras with a split screen in that case. It might work for some games, and it would be really cool if done well.

Share this post


Link to post
Share on other sites
Have you thought of something like one of the Lego games? I don't recall which one specifically, but Pirates may be what I am thinking of. If you had a second player, and the camera could not zoom out enough, instead of 'locking' the camera in the level to view the 2 players, and having one player 'push' the other one when they move further out of bounds, the screen would split itself diagonally, and if you moved further away from each other, the split would progressively align itself vertically.

I do not know how well it could translate to more than 4 players, maybe some dynamic viewport magic :)

Share this post


Link to post
Share on other sites
TADA! This should probably work, explanation in the comments, basically the camtrack::track takes a null terminated list of positions you want to be visible within the camera

I haven't tested this, but the math should work. This is for a camera that translates with the action, it has a fixed direction that it looks down on the scene with. If you want the fixed position non-fixed direction, ask and if I see the post I'll try and figure it out. Basically read the whole lot of comments in there to get the fixed direction solution .



#include<stdio.h>
#include<math.h>
#include <limits>
#include <algorithm>
using namespace std;

// Normalised direction vertor
class Direction {
public:
float x, y, z;
Direction() { x = 1; y = 0; z = 0; }
Direction( const float&amp; _x, const float &amp;_y, const float &amp;_z ) {
x = _x;
y = _y;
z = _z;
float len = sqrt( x * x + y * y + z * z );
if( len != 0.0f )
{
x /= len;
y /= len;
z /= len;
}
}
};

// Position class to do some math
class Position {
public:
float x, y, z;
Position() { x = 0; y = 0; z = 0; }
Position( const float&amp; _x, const float &amp;_y, const float &amp;_z ) {
x = _x; y = _y; z = _z;
}

Position&amp; MaxValues( const Position&amp; other ) {
return Position( max(x,other.x), max(y,other.y), max(z,other.z) );
}
Position&amp; MinValues( const Position&amp; other ) {
return Position( min(x,other.x), min(y,other.y), min(z,other.z) );
}

};

// Camera tracking class that moves the camera into the correct position to show all suppiled elements as being on screen
class CamTrack {
public:
CamTrack( const float &amp;_fov, const Direction &amp;_view );
void Track( const Position **_items );

protected:
float fov;
Direction view;
Position eye;
Position target;
};

CamTrack::CamTrack( const float &amp;_fov, const Direction &amp;_view ) {
fov = _fov;
view = _view;
}

void CamTrack::Track( const Position **_items ) {
const Position **_end = _items;
while( *_end ) _end++;

Position pmin = Position( numeric_limits<float>::max(), numeric_limits<float>::max(), numeric_limits<float>::max() );
Position pmax = Position( numeric_limits<float>::min(), numeric_limits<float>::min(), numeric_limits<float>::min() );

// Get the min / max points
for( const Position **iter = _items; iter < _end; iter++ )
{
pmin = pmin.MinValues( **iter );
pmax = pmax.MaxValues( **iter );
}
// Get the mid point
this->target = Position( pmin.x + (pmax.x - pmin.x) * 0.5f, pmin.y + (pmax.y - pmin.y) * 0.5f, pmin.z + (pmax.z - pmin.z) * 0.5f );
// Get the maximum distance from the midpoint to the furthest outlying element to render visibly
float maxdist = 0.0f;
for( const Position **iter = _items; iter < _end; iter++ )
{
float newdist = sqrt( ( target.x - (*iter)->x ) * ( target.x - (*iter)->x ) + ( target.y - (*iter)->y ) * ( target.y - (*iter)->y ) + ( target.z - (*iter)->z ) * ( target.z - (*iter)->z ) );
maxdist = max(newdist, maxdist);
}

// This is the important part, Now that we have the distance from the midpoint to the outlier, we need to figure out how far to pull the camera back so all elements are visible
// This is determined by the field of view of the camera, and some scale values we need to tweak.
//
// Imaginge an isosceles triangle /\, the width of the triangle and the height, are linked by the angle, So imagine the camera as this, and the field of view as the angle.
// The isosceles triangle is 2 opposed right angle triangles, now we can use trigonometry
// we have the "base" of the triangle from the code above, now we want to find the height, and then we want to move the camera that distance away from the mid (target) point
// We are using the "base" and the "height" of the isosceles triangle in our right angle triangle, so we use tan(angle) = opposite / adjacent. We have the opposite value which
// is the distance from the midpoint to the outlier, so we need to find the adjacent
// tan(a) x adjacent = opposite
// adjacent = opposite / tan(a)
// Either tan(fov) for radians, or tan(fov * PI / 180) if you're using degrees
float zdist = maxdist / tan(fov);
// Now set the eye into the position dictated by the direction we want the camera to be looking down on the scene
eye = Position( target.x - view.x * zdist, target.y - view.y * zdist, target.z - view.z * zdist );
}
</float></float></float></float></float></float></algorithm></limits></math.h></stdio.h>

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement