# Unity Smash Bros Style Camera

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

## Recommended Posts

Hello ladies and gentlemen! Thanks for taking the time to look at my topic. I massively appreciate it :)

I'm experimenting with different camera modes in my game at the moment, and I've come across a bit of a problem

regarding multiplayer. I want to create a camera which can fit all of the players in the screen at all times. I've actually

already worked out half of the problem. The camera essentially averages the positions of all players in the GameSpace

and then centers between all of them, meaning they all have equal coverage of the camera.

This does not, however, take zoom into account! If the players disperse too far from one another, they all leave the camera's

view, and it stays right in the center of the battle, no matter how spaced apart it may be.

I've spent a very long and painful few hours looking for ANYTHING explaining how to do this, but the only two tutorials which

seem to come up are a Unity one (XNA/MonoGame) do not support the functions available, and a tutorial which refers to

the indie game "Demons with Shotguns" which seems to have come up about 10 times!!

Could somebody possibly explain in the most unconvoluted way possible how I can take the positions of the players and

somehow convert that into a zoom factor which always keeps the players in view? I think I heard something about using

a cross product calculation, but there was no further explanation.

Any help would be super appreciated! Thank you :D

##### Share on other sites

Do you have zoom functionality available? For 2D this is actually quite simple to calculate.

Once you're centered you should have an x/y world coordinate that you're placing the camera at. You need to do a little (very little) vector math on the players to find out which one is farthest from that point in the x dimension and which one is farthest in the y dimension.

To find the relative coordinate of a player from the center-point, just:

relative_pos = player.pos - centerpoint

using 2D vectors. Create max_abs_x and max_abs_y variables and set them both to zero (or some minimum value that you decide). Iterate through the players and determine their relative positions, then abs the x and y and if either of them is larger than the value in your max variables then replace that value with the new larger one. You'll end up with a pair of values representing the half-width of the minimum area you need to represent. You can add a buffer at this point if you want so that the players aren't at the absolute screen edge.

Once you have that you just need to consider the size of the viewport compared to the result.

Say you have a viewport that's 800x600. The half-width dimensions of the viewport are 400 and 300. If your max_abs_x is 500 and your max_abs_y is 500 then you can get the ratios:

x_ratio = 500 / 400 = 1.25

y_ratio = 500 / 300 = 1.66~

So you can see that the larger of the two is the y, so you need to zoom to a factor of 1.66~.

Depending on how your zoom is implemented you may be able to just plug that in directly.

You'd only need to get a little more complicated if you're working with a 3D'ish camera and need to move the camera back and forth or play with the field of view.

##### Share on other sites

Thank you so much for such a detailed response

I'll actually post up a snippet of the code so you can see what's going on once I have the problem completed, and hopefully

people might be able to learn something from it! The positioning section seems to be working fine, but I can't get the zoom to

work properly. It seems to almost be doing the inverse, by zooming out as players get nearer, and vice versa. I also noticed the

the camera just completely snaps out of correct zoom sometimes if players cross the middle of the screen. This is really

confusing me now.

The top section of this code works fine - it's the second half (which I separated with a comment) that is causing all the issues!

switch (camera.cameraType)
{
case (Camera.CameraType.SmashBros):

//This section sorts the camera positioning and works fine:
List<float> positionsX = new List<float>();
List<float> positionsY = new List<float>();

foreach (Player P in playerList)
{
}

float posXAverage = 0;
float posYAverage = 0;

foreach (float F in positionsX)
{
posXAverage += F;
}
posXAverage /= playerList.Count();

foreach (float F in positionsY)
{
posYAverage += F;
}
posYAverage /= playerList.Count();

camera.Pos = new Vector2(posXAverage, posYAverage);

//DOWN HERE
//DOWN HERE
//DOWN HERE
//HERE is where the problems start
//This section here sorts the zooming aspect:

List<Vector2> relativeList = new List<Vector2>();

foreach (Player P in playerList)
{
Vector2 tempVector = P.position - camera.Pos;
}

float maxX = 0, maxY = 0;

foreach (Vector2 v in relativeList)
{
if (v.X > Math.Abs(maxX))
{
maxX = Math.Abs(v.X);
}

if (v.Y > Math.Abs(maxY))
{
maxY = Math.Abs(v.Y);
}
}

float xRatio, yRatio;

xRatio = maxX / (Game1.screenWidth / 2);
yRatio = maxY / (Game1.screenHeight / 2);

camera.Zoom = xRatio;

if (maxY > maxX)
{
camera.Zoom = yRatio;
}

break;
}


Is there possibly something just out of place here? Look forward to hearing back from you!

Edited by zuhane

##### Share on other sites

First, avoid putting long statements in switches as it quickly gets too messy. Break the subroutine out into a new function and just call it from the switch. It will make life easier for you.

Abs the vector components instead of the max value. If the zoom is inverted then it means the fractions for the ratios are inverted, so just flip them over (invert the comparison as well).

There's a cleaner way of doing this though, since you're already iterating the characters to find the camera position. Instead of finding the position and then the zoom, just find the bounding box which contains everything you want and then derive what you need from that:

Rectangle rect = new Rectangle(playerList[0].Position.X, playerList[0].Position.Y, 0, 0);

foreach (Player P in playerList)
{
int x = (int)P.position.X;
int y = (int)P.position.Y;
if (x < rect.Left  ) { rect.X      = x; }
if (x > rect.Right ) { rect.Width  = x - rect.X; }
if (y < rect.Top   ) { rect.Y      = y; }
if (y > rect.Bottom) { rect.Height = y - rect.Y; }
}

//You may want to increase the rect size at this point by subtracting
//some from the x and y and adding to the width and height. Alternatively
//you could just find some factor very close to 1.0f to multiply the 'zoom'
//by before applying it.

Vector2 cameraPos = new Vector2((float)rect.X, (float)rect.Y);
cameraPos.X += (float)rect.Width  / 2;
cameraPos.Y += (float)rect.Height / 2;

//this part may be inverted??? if so flip the divisions and use max instead of min
float xZoomReq = (float)Game1.screenWidth  / rect.Width;
float yZoomReq = (float)Game1.screenHeight / rect.Height;
float zoom = Math.Min(xZoomReq, yZoomReq);

//At this point you can compare the result against min/max zoom values.
//These are numbers you should determine yourself through experimentation,
//as would be the buffer size mentioned above.


For reasons unknown to any sane person, there's no float rectangle struct, so the math is a bit rough and I may have missed some casting. You don't need super-high precision here though, since you're probably going to monkey with the final result a little anyway.

##### Share on other sites

I'm trying to just get the X component working first, then I'll throw Y into the equation as it'll

be incredibly simple from there. The zoom functionality still doesn't seem to be working though. I tried

a copy-paste of your code, and that didn't work, and also tried tweaking it in all different ways, as

well as modifying my own to accommodate for changes.

It seems to work as long as player 1 (playerList[0], used above) is further to the left than all the

other players (lower X value). I tested a map with 2 players, with player 1 on the far left

and player 2 on the far right, and the zoom worked perfectly. As the players

get closer, the zoom increases, then is capped at max zoom level. However, as player 1 begins to

run past player 2, or player 2 past player 1, the zoom just stays at maximum and no longer fits both

players within the screen.

Also, with more than 2 players the complexity seems to become insane and throws all kinds of weird

bugs. I'm guessing it's something to do with an absolute value?

Current attempt:

int margin = 200;
Rectangle rect = new Rectangle((int)playerList[0].Position.X - margin, (int)playerList[0].Position.Y - margin, margin, margin);

foreach (Player P in playerList)
{
int x = (int)P.position.X;
int y = (int)P.position.Y;
if (x < rect.Left) { rect.X = x; }
if (x > rect.Right) { rect.Width = x - rect.X; }
if (y < rect.Top) { rect.Y = y; }
if (y > rect.Bottom) { rect.Height = y - rect.Y; }
}

float xZoomReq = (float)Game1.screenWidth / rect.Width;
//float yZoomReq = (float)Game1.screenHeight / rect.Height;
//camera.Zoom = Math.Min(xZoomReq, yZoomReq);

camera.Zoom = xZoomReq;



Also, I'd just like to ask, why is the original rectangle based on player 1 (playerList[0])? It seems like the camera is mostly influenced

by his position rather than every player's position with equal importance.

Edited by zuhane

##### Share on other sites

The algorithm only expands the rect. It can't shrink it. I don't know what your coordinate system is based on, but if 0,0 is the upper-left world coord then starting the rect there would make the rect stick to that corner, since it can't shrink to fit the active play area. By setting the initial x/y to the coordinates of a character (any of them) it ensures that the initial coords are within the bounds of the final rect, so it can expand to fit everyone. It's only an initial value. It doesn't give player 1 any more influence than the other players. The loop expands the rect to fit all the players precisely. It doesn't matter what the starting coordinate is as long as it's inside the area that will be covered by the final rect. Assuming that the players can be anywhere in the play area, that means that the only points we know for sure will be inside the final rect are the player coordinates, and the only player that I know will always be present is player 1.

Adding the margin to the initial rect is incorrect. Add it after the bounds are determined by the loop. Again, the starting values are intended to be overwritten by the loop.

The reason it fails when player 1 passes player 2 is that I'm a moron and forgot to expand the width and height when moving the left and top.

if (x < rect.Left)
{
rect.Width += rect.X - x; //keep the right side in place while moving the left side
rect.X = x;
}


And similarly for Y.

Are you using the cameraPos derived from this? It's not in what you posted. Your original algo used an average of all the player positions. That's not going to work well. If you have three players on the left and one player at the far right then it's going to pull the centerpoint heavily leftward and the lone player will likely be offscreen.

##### Share on other sites

is that I'm a moron and forgot to expand the width and height when moving the left and top.

If anyone's the moron, it's me haha! This shouldn't be a day-long problem, but it has been for me. I'm definitely more acquainted to the

art/animation side of game development and less inclined towards the grueling maths/thinking part. It's almost 3:00am over here, so I'll have to

retire for the night and I'll post up what I've done tomorrow.

##### Share on other sites

Okay, I'm back on it again! Going back to what you said, I think you're right about using the player averages. I've finally got the

camera to a point where to can track 1-2 players perfectly, or 4 players if the players are in pairs. But what you said is true; If most

of the players move to one side, and a player moves to the other, the influence causes the camera to move way to busiest part,

which causes a player to be cut out of the view.

However, your code seems to make it behave quite strange when it comes to positioning the camera. I'm not too sure how to

fix this problem, to be honest. The zoom works fine, but the positioning code of yours caused all sorts of things to pay up! If it's any help,

the camera essentially has lots of getters and setters available, and the camera.Pos refers to the top-left corner of the rectangle.

It's possible to define the center, too, if that helps make it easier.

Anyway, I've got a couple of videos here that kind of show what's going on. Like I say, the margins and zooming work fine with 1-2 players

only!

If you could recommend a better method for positioning the camera correctly, please let me know!

##### Share on other sites

If the position refers to the top left rather than the center then just set it to the x,y of the rect after adding your margin. However, if you need to take the zoom into account then it gets a bit more complicated. In that case you'll need to find the centerpoint and the halfwidth (as a 2D vector), multiply the halfwidth by the zoom value (or possibly its inverse: 1/zoom), then subtract it from the centerpoint.

##### Share on other sites

I'll be completely honest and say that I'm stuck and don't really have a clue what to do, or what the reasoning behind the

camera logic is. I just don't really understand it. I've been playing with the same few lines of code for the past few days, but

I just can't get it to work :(

1. 1
2. 2
Rutin
16
3. 3
4. 4
5. 5

• 26
• 11
• 9
• 9
• 11
• ### Forum Statistics

• Total Topics
633703
• Total Posts
3013455
×