0 votes

I have an Area2D as a child node of a Camera2D, so the area follows the camera. This works, until the camera reaches a limit. Then, the area just moves past the limit. How can I prevent this?
I have tried using a CanvasLayer and having the area follow that, but this seems to disable the area's collision.

Godot version 3.3.stable
in Engine by (22 points)

Maybe you can set the position property of the Area2D in the process function? Or if the Camera2D is following another node you can make the Area2D a sibling of the Camera?

I already tried both of those, but but neither of them worked sadly.

1 Answer

0 votes

You want your structure to be:

Area2D
>Camera2D
>CanvasLayer

By limit do you mean the limit properties of the camera e.g. left_limit? If that's the case, you need to bound your level area with some kind of collision object so the Area2D can't go any further once it hits the bounds.

by (3,545 points)

Yeah, I mean those limits. What would be the best way to prevent the area from going somewhere? By code?

If you're randomly generating your levels you're going to need to instance them using code. If you're designing them in the editor I would either use Area2Ds or StaticBody2Ds.

I've tried connecting a KinematicBody2D to my player character to serve as the camera's hitbox, and as a parent for all the camera stuff. Problem is, the kinematicbody just moves through StaticBodies because it wants to stay with its parent, the player character, no matter what.Scene Tree
I'm probably supposed to have the Area2D collide with the StaticBodies, but I have no clue how...

Your structure should be:

Player (KinematicBody2D)
>CollisionShape2D
>Camera2D
>CanvasLayer2D

The camera's extents should be limited by the limit properties. If you're using a TileMap for a background this code will set those for you, just pass it a TileMap:

Camera2D.gd
...
func set_extents(tile_map:TileMap) -> void:
    var tile_rect = tile_map.get_used_rect()
    var cell_size = tile_map.cell_size
    limit_left = tile_rect.position.x * cell_size.x
    limit_top = tile_rect.position.y * cell_size.y
    limit_right = tile_rect.end.x * cell_size.x
    limit_bottom = tile_rect.end.y * cell_size.y

Otherwise, you can hard-code the values into that function that suit your level - or set them via the Editor.

The Player collision_layer is set to like Layer 1 and collision_mask is set to like Layer 2, and the opposite for your StaticBody2Ds. You want the StaticBody2Ds to have their collision_layer as Layer 2 and their collision_mask as Layer 1. This allows them to "see" each other in order to collide with each other.

Think of the collision_layer as the "I am a Layer 1" and the collision_mask as "I am looking to collide with a Layer 2"

I understand collision layers and masks, I just don't understand how to prevent an Area2D from going somewhere. I'll try to explain my problem with a little more detail:

There is a Camera2D connected to my player. There's also an Area2D, connected to the Camera2D. This Area needs to always be where the camera is, even when hitting the camera's limits. Currently, when the Camera2D hits a limit, the Area2D just keeps moving, but I want it to stop at the camera's limit.

I'm sorry if I'm just being really dumb and confusing right now, heh.

Umm it really depends on your game... Like does this Area2D actually collide with anything? What's the purpose of it? And how does it move?

You could make the Area2D only collide with the boundaries of your level then check get_overlapping_bodies().empty() in the _physics_process() before moving the Area2D. You could also make a RayCast2D as a child of the Area2D that performs the same task I laid out above if you need to check collisions of other things with that Area2d. Outside of that, I would need more information about the Area2D to offer you a better solution.

I am using an Area2D so I can accurately know when certain stuff is on screen / is in range of my player (I am making a 2D platformer). But currently, when an object is to the right of the screen and the camera hits a left limit, the object thinks it's off screen. I suppose both the raycast and getoverlappingbodies().empty() may work, but I don't know how I'd prevent the Area from moving then.
I might just be overcomplicating stuff though.
Also, I'm sorry for the late reply.

You are over-complicating things, that's why I asked for clarification. It's perfectly fine though! :)

There's a VisibilityNotifier2D node that's built exactly for that purpose. They emit the screen_exited, screen_entered, signals when they show on/exit from screen. Attach those nodes as children of your objects that you need to be notified of when they enter screen, connect the signals and you now know when those objects are on screen. Here's a quick demo on how to use it: https://youtu.be/WEt2JHEe-do?t=1714

The Area2D should just be a child of the Player - it will move along side the player and act as another collision shape for the player. Disable it from colliding with terrain and it can act as a pickup radius.

Oh, I probably should have mentioned this before, the visibility notifiers weren’t always accurate. I tried looking online for solutions but the only thing I could find was on the documentation site (https://docs.godotengine.org/en/stable/classes/class_visibilitynotifier2d.html):

Note: For performance reasons, VisibilityNotifier2D uses an approximate heuristic with precision determined by ProjectSettings.world/2d/cell_size. If you need precise visibility checking, use another method such as adding an Area2D node as a child of a Camera2D node.

And now I’m having this issue.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.