Sign in to follow this  
Mybowlcut

[C++] Where to draw player relative to camera?

Recommended Posts

Hey. I have a Level_Camera class that stores information about a camera:
class Level_Camera
{
public:
	Level_Camera(const SDL_Rect& camera, const Point& offset);

	void Centre_On(const Point& on, const SDL_Rect& boundary);

	const SDL_Rect& Camera() const;
	const Point& Offset() const;
private:
	SDL_Rect camera; // in tiles.
	Point offset; // in pixels.
};


Level_Camera::Level_Camera(const SDL_Rect& camera, const Point& offset)
: camera(camera)
, offset(offset)
{
}

void Level_Camera::Centre_On(const Point& on, const SDL_Rect& boundary)
{
	// Centre the camera over point.
	camera.x = on.x - camera.w / 2;
	camera.y = on.y - camera.h / 2;
	// Clamp x, y values if out of bounds.
	camera.x = std::min<int>(boundary.w, std::max<int>(0, camera.x));
	camera.y = std::min<int>(boundary.h, std::max<int>(0, camera.y));
	// Account for the width and height of camera by subtracting difference.
	// (if went over edge of boundary adjust it to fit).
	int x_diff = camera.x + camera.w - boundary.w;
	int y_diff = camera.y + camera.h - boundary.h;
	camera.x -= x_diff > 0 ? x_diff : 0;
	camera.y -= y_diff > 0 ? y_diff : 0;
}

const SDL_Rect& Level_Camera::Camera() const
{
	return camera;
}

const Point& Level_Camera::Offset() const
{
	return offset;
}


I draw the Level according to the position of the camera:
void Level_View::Render_View(const Level& level, Renderer& renderer, const Level_Camera& camera)
{
	const std::vector<Tile>& background_tiles = level.Background_Tiles();
	const std::vector<Tile>& foreground_tiles = level.Foreground_Tiles();
	const std::vector<Object_Tile>& object_tiles = level.Object_Tiles();
	typedef unsigned int uint;

	uint ax = 0, ay = 0, index = 0;
	const SDL_Rect& cam = camera.Camera();
	// c = Camera (position of camera) , r = Render (position tile will be rendered on screen)
	// a = Adjusted (position tile will be rendered on screen after camera_offset and tile size adjustment.
	// All tiles inside camera view are drawn. Saves testing if tile is inside camera.
	const Object_Tile* it = NULL;
	for(int cy = cam.y, ry = 0; cy < cam.y + cam.h; ++cy, ++ry)
	{
		for(int cx = cam.x, rx = 0; cx < cam.x + cam.w; ++cx, ++rx)
		{
			ax = rx * Tile::TILE_WIDTH + camera.Offset().x;
			ay = ry * Tile::TILE_HEIGHT + camera.Offset().y;
			index = cy * level.Width() + cx;

			Render_Tile(renderer, Renderer::D1, ax, ay,
				background_tiles[index].File_Name()); 

			it = &object_tiles[index]; // could be level width instead?
			Render_Tile(renderer, Depth_From_Passability(it->Passability()),
				ax, ay, it->File_Name()); 

			Render_Tile(renderer, Renderer::D4, ax, ay,
				foreground_tiles[index].File_Name()); 
		}
	}
}


That all works fine. My problem is that my character can walk outside the bounds of the camera... and because of this the camera doesn't centre on them properly.
void Player_View::Render_View(Renderer& renderer, const Player& player,
const Point& camera_offset)
{
	const SDL_Surface* surface = renderer.Surface_At(player.Image_Fn());
	SDL_Rect clip = SDL_Tools::calculate_clip(surface->w, surface->h,
		player.Frame(), player.Facing());

	renderer.Render(Renderer::D2, player.Position() + camera_offset,
		player.Image_Fn(), clip);
}


camera_offset holds the value of the offset member in the Level_Camera class. The offset is the distance from the top left corner of the screen to where the camera is drawn. The addition of player.position and camera_offset looks like this:
const Point operator+(const Point& lhs, const Point& rhs)
{
	return Point(lhs.x + rhs.x, lhs.y + rhs.y);
}


So I'm wondering what kinda equation I'd need to get the character properly drawn within the camera. Cheers!

Share this post


Link to post
Share on other sites
I haven't read all of your code, but I think that the following line is wrong:

renderer.Render(Renderer::D2, player.Position() + camera_offset,
player.Image_Fn(), clip);


If the second parameter of Render is supposed be the position to render to (pixels, screen coordinates), and camera_offset is the top-left corner of the camera view (pixels, world coordinates) and player.Position is the top-left corner of the player (pixels, world-coordinates) then it should be player.Position() - camera_offset.

Share this post


Link to post
Share on other sites
Quote:
Original post by Barius
I haven't read all of your code, but I think that the following line is wrong:

renderer.Render(Renderer::D2, player.Position() + camera_offset,
player.Image_Fn(), clip);


If the second parameter of Render is supposed be the position to render to (pixels, screen coordinates), and camera_offset is the top-left corner of the camera view (pixels, world coordinates) and player.Position is the top-left corner of the player (pixels, world-coordinates) then it should be player.Position() - camera_offset.
Yeah, camera_offset is the top-left corner of the camera on the screen (usually 20,20). What if the player was at (0,0), though? (0,0) - (20,20) = (-20, -20)..

Share this post


Link to post
Share on other sites
No, not "yeah". I thought camera_offset was the offset of the camera in the world, not the offset of the view window on the screen.

The basic way to do this is:

- Calculate a view rectangle "viewrect" (world coordinates, pixels) based on the player position (world coordinates, pixels), and adjust it for special cases like the edges of the map.

- Draw each object or tile that intersects the view rectangle at camera_offset + object.topleft - viewrect.topleft.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this