• Hey! Guest! The 39th GMC Jam will take place between November 26th, 12:00 UTC and November 30th, 12:00 UTC. Why not join in! Click here to find out more!

SOLVED Rooms within Rooms and Camera View Constraints

Hello,

Basically I'm trying to accomplish something like how Metroid or Super Metroid handles its "camera" and room transitions. What I want to do is draw various rooms using tiles within one GM Room asset, and the reason I want to do this is that more objects than just the player will be moving freely in the world (like an AI enemy searching for the player). So the camera view follows the player until the view hits the edge of one of these "rooms", when it would then freeze on that axis.

I'd prefer not to type in these boundaries manually because it's slow and annoying to change, especially if I would play test and need to move "rooms" around. Ideally I'd like this to work with tiles, perhaps even just putting a single tile in each corner of a "room", and then code would just handle the rest. Room transitions could be easy this way too, because I could just turn off the view collision when I want to transition between "rooms."

I know about tilemap_get_at_pixel and camera_set_view_pos, but I'm just having trouble wrapping my head around how this might work.

Thanks a bunch
 
This may or may not help, but here's what I've been trying, and so far it's doing nothing:

GML:
//Create Event
camera = camera_create_view(0, 0, 64, 64, 0, oPlayer, -1, -1, 32, 32);
//there's some other camera and view stuff, but I don't think it's that relevant
boundaries = layer_tilemap_get_id("Boundaries");

//Step Event
camX = camera_get_view_x(camera);
camY = camera_get_view_y(camera);

xa1 = tilemap_get_at_pixel(boundaries, camX - 1, camY + 32);//left side of view border
xa2 = tilemap_get_at_pixel(boundaries, camX + 65, camY + 32);//right side of view border
ya1 = tilemap_get_at_pixel(boundaries, camX + 32, camY - 1);//top of view border
ya2 = tilemap_get_at_pixel(boundaries, camX + 32, camY + 65);//bottom of view border

if (xa1 != 0)x1 = camX;//if my left side is about to collide with boundary tiles
if (xa2 != 0)x2 = camX + 64;
if (ya1 != 0)y1 = camY;
if (ya2 != 0)y2 = camY + 64;

camera_set_view_pos(camera, clamp(camX, x1, x2), clamp(camY, y1, y2));
I did a little show_message test to see if the xa1 (xa2, etc) variables are working, and they are, but then the view is not freezing once it hits that boundary/barrier. Also, I placed my boundary tiles around the perimeter of my "room" within the room.
 
Last edited:

NeoShade

Member
Just spitballing here because as soon as I read this an idea popped into my head. I've never tried this before, and I don't know if there might be better ways to do what you're asking, but I thought it was worth mentioning.

What if you created a new object obj_room and give it a sprite the size of your tiles. Then, place an instance of this object in the room editor everywhere that you want a sub-room to be, and stretch it so that it covers the size of the sub-room.

During play, when the player comes into contact with obj_room, clamp your camera position: camX = clamp(camX, obj_room.bbox_left, obj_room.bbox_right - view_width)
 

Yal

šŸ§ *penguin noises*
GMC Elder
It might be easier to handle this with (invisible) rectangular instances instead of tiles (since you can stretch them to cover an arbitrary area in the room editor, and also add other variables... for instance, boss rooms could zoom the camera out to fill the entire room rather than set scroll boundaries). Have four global variables for the left / top / right / bottom boundary of the current room, which are set whenever you transition into a new room (using a collision check in the direction you're transitioning to get the id of the next room's instance). Clamp the view to these coordinates, except when transitioning through a door. (A state machine for the camera controller which alternates between "door view" and "normal view" would be the easiest way to implement this, and if you go for the boss zoom idea, you'd add a third "zoom out to fill the entire available space" state)

If you use instance deactivation, the additional 1 instance that's active won't have any performance impact. (Put the room objects on a separate layer inst_maprooms and have the object itself be invisible so the player can't see them, set the layer's grid size to your view size and you'll be able to easily create rooms aligned to a screen-sized grid)

If you have other room properties like separate backgrounds, you could offload the handling of that stuff to the room objects as well. You could get up-to-date map data by just running a with loop over all the room objects present.
 
Thanks for the replies. I worked out a sort of hybrid of what you both were saying, and I'll be using stretched room objects instead of tiles. I think I also learned something that is not mentioned in the manual, which is that you cannot restrict a view's movement via camera_set_view_pos when that camera view has a target (at least I couldn't get it to work). So the next best thing is to just change the view speed:

GML:
camX = camera_get_view_x(camera);
camY = camera_get_view_y(camera);

currentRoom = instance_position(x, y, oRoom);//this was originally instance_nearest, but this is far more accurate
left = currentRoom.bbox_left;
right = currentRoom.bbox_right;
top = currentRoom.bbox_top;
bottom = currentRoom.bbox_bottom;

viewWhole = 64;
viewHalf = 32;

//Check to see if view will collide with room boundary
if (!point_in_rectangle(camX - 1, y, left, top, right, bottom)){//left
    xa1 = 1;
}else{xa1 = 0;}
if (!point_in_rectangle(camX + viewWhole, y, left, top, right, bottom)){//right
    xa2 = 1;
}else{xa2 = 0;}
if (!point_in_rectangle(x, camY - 1, left, top, right, bottom)){//top
    ya1 = 1;
}else{ya1 = 0;}
if (!point_in_rectangle(x, camY + viewWhole, left, top, right, bottom)){//bottom
    ya2 = 1;
}else{ya2 = 0;}

//This resets the view speed when you walk away from the edge of a room
//If your player is moving faster than a speed of 1, you may want to adjust the value of 1 below to something higher
if (point_distance(x, 0, camX + viewHalf, 0) <= 1){
    camSpdX = -1;
}
if (point_distance(0, y, 0, camY + viewHalf) <= 1){
    camSpdY = -1;
}

if (xa1 != 0 && (x <= camX + viewHalf)){camSpdX = 0;}
if (xa2 != 0 && (x >= camX + viewHalf)){camSpdX = 0;}
if (ya1 != 0 && (y <= camY + viewHalf)){camSpdY = 0;}
if (ya2 != 0 && (y >= camY + viewHalf)){camSpdY = 0;}

camera_set_view_speed(camera, camSpdX, camSpdY);
So, some important things to note if you'd want to implement something similar:
-My camera view is controlled by my player, so when you see variables like x, y, instance_nearest, those are in relation to the player
-My view is a square, hence why I can use viewWhole and viewHalf for both the width and height of my view
-All your "rooms" basically have to be rectangles, or at least any doors you have will have to be along the edges of your room object

Some other cool things are that you might actually want to make these room objects visible when you are not inside them or when you are transitioning between rooms, because that way you won't be able to see into the next room when transitioning. And as you mentioned, the nice thing about using objects is that you can assign other variables to them, like drawing only instances that are in a room, or turning on only certain AI when you are in a room. The best part though is that I don't have to type anything, I just go in the room editor and stretch a room object over each "room."
 
Last edited:

Rob

Member
I used rectangles to drag areas out in one single room yeah - and colliding the player with instances of those rectangles would change 4 variables that would dictate how far the camera would move in a particular direction, if at all.

It seemed the most straight forward way
 
Top