#Hollow knight Camera system.
159 messages · Page 1 of 1 (latest)
Hello, sorry that i can't help you, but I am trying to recreate the same camera and I am quite interested in this "set grid" you are talking about. What does it do? Right now i use the "position soothing" for my camera and nothing else, and i would love to upgrade it. (And if you don't mind rewriting your second sentence, i could try to help you with that, but right now i dont understand what you mean....)
Sry for the unclear statement I was in a bit of a rush. The set grid is something I found online becuase currently I am starting the engine and am not very experienced. https://youtu.be/vsssq7ZxEKg?si=CzTCP0JF1wlvQrCi
A short tutorial on how to setup a camera2D that transitions from room to room.
The code that controls the camera is available here:
https://gist.github.com/securas/2400b3fa1a31650a270618d1c8851ae6
I'll sometimes post stuff online here: https://twitter.com/Securas2010
@dry shuttle
I found a video on the system I was going for if you want to see it. https://youtu.be/lPJMj-ov7ys?si=tuv0eVKl4W0qKhrO
Thanks for watching
Leave any issues or fixes you find in the comments
Link to Verrazano's Room-Based Camera Tutorial:
https://www.youtube.com/watch?v=DBgIES-CIUI
Link to Nesi's Smoothed Pixel Art Camera:
https://www.youtube.com/watch?v=AsrmXTUB6tI&t=2s
Link to Scripts:
Player Script: https://pastebin.com/YkyqzQFn
Camera Script: https://pas...
Omg this is actually what i was looking for, thank you!
Since you just started it, i assume you never worked with a TileMap before? I could help you with that. https://youtu.be/tQSL2scuqeU
In this tutorial, we show you how to setup a tilemap in Godot 4 Alpha. We cover tilemap layers, terrains, collisions and much more. The new tilemap implementation is a huge upgrade from the one we have in Godot 3.4.
Download Godot 4.0 Alpha 2: https://godotengine.org/article/dev-snapshot-godot-4-0-alpha-2
Download the tileset: https://jamiebr...
I am also just a beginner, but I feel pretty confident with the tileMaps, so feel free to ask me about it (I also used TileMapLayers that are only in the newest Versions of Godot)
I followed brackeys game tutorial and have been messing around so I know how to use a tile map except for changing the tile cut size.
I watched the video and it told me except for the layers. Now it’s a new tile set for another layer right?
Well, if you use a TileMap, you simply add a new Layer and you can use the same tiles you added earlier
Like this
but if you use TileMapLayer (a node that is only found in Godot 4.3 i think) you have to add a new tileset for each layer yes
@dry shuttle I can’t figure out how to implement the camera system.
See if this works? I just put it into a global script, and in the scene I called activate with the parameters
extends Node2D
## Size/Distance between screens, in world coordinates
const screen_size: Vector2 = Vector2(576, 324)
## Main camera that moves between screens
var camera : Camera2D
## Reference to the player the camera follows
var player: Node2D
## Grid representation of the current screen
var current_screen: Vector2 = Vector2.ZERO
## Location of the starting screen, which will be the middle of the screen grid, 0,0
var screen_offset: Vector2 = Vector2.ZERO
func _ready():
# Turn off process so it doesn't have errors if there is no camera/player
process_mode = PROCESS_MODE_DISABLED
## Call this function to start the screen camera, or you could
## set camera, player, and screen_offset in _ready if this node can access them directly
func activate_screen_camera(use_camera, use_player, starting_screen_node):
# Turn on process when we start the camera
process_mode = PROCESS_MODE_INHERIT
camera = use_camera
player = use_player
screen_offset = -starting_screen_node.global_position
# Set the starting screen
current_screen = calculate_current_screen()
move_camera(current_screen)
func _process(delta: float) -> void:
var next_screen = calculate_current_screen()
if next_screen != current_screen:
move_camera(next_screen)
current_screen = next_screen
## Returns the current place in the screen grid
func calculate_current_screen():
return floor((player.global_position + screen_offset)/screen_size + Vector2(0.5, 0.5))
func move_camera(next_screen):
var next_position = next_screen * screen_size - screen_offset
camera.global_position = next_position
and then turned on camera smoothing
ah oh wait you want to have different room sizes too
do you have a picture of what your rooms look like? You'll basically need to define when the player should move the camera, so you'll need to define the rectangles somewhere
Im a beginner so I don’t really understand @haughty chasm
just like a picture of your scene with your tilemap, and where you'd like rooms
Currently it’s a room with a prototype tile set,
Not a map yet.
i think doing it like this would be easiest, and just have a set of "rooms" that you put in a completely separate node
Ok. I don’t really know how to set it up. The tutorial barely explains it.
Could you help me know how to set it up?
@haughty chasm
yeah sure
np i'll see what i can do
Thank you.
I have to admit, that i tried it myself a few minutes ago and it didn't work either T-T
I have to pass on this one
Same. But I’m a beginner so I don’t know much,
👍
Any luck! @haughty chasm
yeah actually pretty easy
kinda
so basically you can just make area2d's where you want your rooms to be
on your area2d for the rooms, set the collision layer to its own layer, i just used 8 but you would want to name it as well
With the previous script? @haughty chasm
no just a sec i'm adding comments to my new code lol
Ok Ty.
ok my message was too long
Ok this is in a global script named ScreenCameraController
extends Node2D
var camera_smoothing = 5.0
var screen_camera : Camera2D
var player: Node2D
var current_screen_rect: Rect2
func _ready():
process_mode = PROCESS_MODE_DISABLED
func activate_screen_camera(use_camera, use_player):
screen_camera = use_camera
player = use_player
process_mode = PROCESS_MODE_INHERIT
func _process(delta: float) -> void:
limit_camera_to_screen(delta)
func change_screen(screen_area: Area2D):
var collision_rect = screen_area.get_child(0)
current_screen_rect = Rect2(collision_rect.global_position - collision_rect.shape.size/2, collision_rect.shape.size)
print("current_screen: ", current_screen_rect)
## Sets the camera's position to be inside the current screen's rectangle
func limit_camera_to_screen(delta):
# camera_rect is the size and position of the camera
# I actually just found this answer in a random reddit post
var camera_rect = screen_camera.get_canvas_transform().affine_inverse() * get_viewport_rect()
# check if the current screen is actually too small, and if so, center the camera instead
if current_screen_rect.size.x <= camera_rect.size.x or current_screen_rect.size.y <= camera_rect.size.y:
var screen_center = current_screen_rect.position + current_screen_rect.size/2
set_camera_position(screen_center, delta)
else:
# otherwise, let the camera follow the player but not past the screen's border
var top_left_bound = current_screen_rect.position + camera_rect.size/2
var bottom_right_bound = current_screen_rect.end - camera_rect.size/2
var new_position = player.global_position.clamp(top_left_bound, bottom_right_bound)
set_camera_position(new_position, delta)
## Changes the camera's position with interpolation
func set_camera_position(new_position, delta):
screen_camera.global_position = screen_camera.global_position.lerp(new_position, camera_smoothing * delta)
func deactive_screen_camera():
process_mode = PROCESS_MODE_DISABLED
func reactivate_screen_camera():
process_mode = PROCESS_MODE_INHERIT
last 2 functions are optional, I just added them in case you want to show a cutscene
I put that in camera and setup the area 2D stuff?
or if you want to switch scenes you maybe deactivate it before scene transition and then call activate_screen_camera again with new stuff
wait no not in camera
in your main scene, you want to call ScreenCameraController.activate_screen_camera(your camera, your player scene)
@onready var camera: Camera2D = $Camera2D
@onready var character: CharacterBody2D = $Character
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
ScreenCameraController.activate_screen_camera(camera, character)
like so
So I attach a script to my main scene. With that code u just sent?
also theres one more piece, which is adding a matching Area2D to the player
you could do that but you'd need a way for the player to tell the script when to change screens
the script on this area has this:
extends Area2D
func _on_area_entered(area: Area2D) -> void:
ScreenCameraController.change_screen(area)
Where do I put this?
I put it in a global,
however you could put it in your scene too, on the camera actually
I added the big chunk of code to the Game scene. Is that correct?
uhh actually let me see if I can clean it up so it just goes on the camera
Ok.
I added Area 2d for rooms, with collision shapes on layer 8. and the big code u sent I put in the game scene
@haughty chasm
ok for the player's area's CollisionShape2D, use a SegmentShape2D with 0,0 for A and B
this makes it a single point so it will never be overlapping 2 screens at once
ok so on your camera2d you want to follow the player and change screens
put this code:
extends Camera2D
@export var camera_smoothing = 5.0
@export var player: Node2D
var current_screen_rect: Rect2
func _ready():
# Don't start processing if there isn't a player set
if player == null:
process_mode = PROCESS_MODE_DISABLED
else:
connect_area_signal()
func activate_on_player(use_player):
player = use_player
connect_area_signal()
process_mode = PROCESS_MODE_INHERIT
func connect_area_signal():
var screen_change_area: Area2D = player.get_node("ScreenChangeArea")
screen_change_area.area_entered.connect(change_screen)
func _process(delta: float) -> void:
limit_camera_to_screen(delta)
func change_screen(screen_area: Area2D):
var collision_rect = screen_area.get_child(0)
current_screen_rect = Rect2(collision_rect.global_position - collision_rect.shape.size/2, collision_rect.shape.size)
## Sets the camera's position to be inside the current screen's rectangle
func limit_camera_to_screen(delta):
# camera_rect is the size and position of the camera
# I actually just found this answer in a random reddit post
var camera_rect = get_canvas_transform().affine_inverse() * get_viewport_rect()
var top_left_bound = current_screen_rect.position + camera_rect.size/2
var bottom_right_bound = current_screen_rect.end - camera_rect.size/2
var new_position = player.global_position.clamp(top_left_bound, bottom_right_bound)
# check if the current screen is actually too small, and if so, center the camera instead
if current_screen_rect.size.x <= camera_rect.size.x:
new_position.x = current_screen_rect.position.x + current_screen_rect.size.x/2
if current_screen_rect.size.y <= camera_rect.size.y:
new_position.y = current_screen_rect.position.y + current_screen_rect.size.y/2
set_camera_position(new_position, delta)
## Changes the camera's position with interpolation
func set_camera_position(new_position, delta):
global_position = global_position.lerp(new_position, camera_smoothing * delta)
func deactive_screen_camera():
process_mode = PROCESS_MODE_DISABLED
func reactivate_screen_camera():
process_mode = PROCESS_MODE_INHERIT
and then in your main scene
in _ready(), call yourcamera.activate_on_player(your player's node)
oops I also forgot, in the area2d on your player, you want to set the MASK to 8, to match the screen
This is in the Game scene? Right?
in my first one, no, its a global which is added to every scene
Also my guy doesn’t have gravity anymore?
uh what lol
wait can you take a picture of your scene's node structure on the left?
It works now.
Also it’s one camera and it doesn’t change anymore
ah ok
also i made a change to limit_camera_to_screen(), to fix weird centering in small rooms:
## Sets the camera's position to be inside the current screen's rectangle
func limit_camera_to_screen(delta):
# camera_rect is the size and position of the camera
# I actually just found this answer in a random reddit post
var camera_rect = get_canvas_transform().affine_inverse() * get_viewport_rect()
var top_left_bound = current_screen_rect.position + camera_rect.size/2
var bottom_right_bound = current_screen_rect.end - camera_rect.size/2
var new_position = player.global_position.clamp(top_left_bound, bottom_right_bound)
# check if the current screen is actually too small, and if so, center the camera instead
if current_screen_rect.size.x <= camera_rect.size.x:
new_position.x = current_screen_rect.position.x + current_screen_rect.size.x/2
if current_screen_rect.size.y <= camera_rect.size.y:
new_position.y = current_screen_rect.position.y + current_screen_rect.size.y/2
set_camera_position(new_position, delta)
Ok. Question. Where do I connect the change room in player to the script?
This fine? Sry took from my phone
is camera2d a direct child of your screen or is it in another node?
Node of player
ah ok i guess that techically works since it just changes its own position each frame
Wait the giant code you sent me is for global right? The first one.
This.
the first one is but I made a change so you could just put it on the camera
Oh. So no global?
but if you put it in global then you can call activate_screen_camera(camera, player) in your main scene script in _ready()
you can do global if you want, just need to give it the player when you load the scene
This used to be global right?
thats the non global version, global was up a little bit
this one
oh wait no
this one
Btw I have the global script and camera script both still on if that makes a difference
yeah you just want one of them
but either way you need to activate it by handing it the player
ok create a new script in your Game node, and make it be:
extends Node2D
@onready var camera: Camera2D = $Camera2D
@onready var player: CharacterBody2D = $Player
func _ready() -> void:
camera.activate_on_player(player)
Removing global
Adding…
It’s still in the corner 🥲
try turning off position_smoothing in the normal camera settings
same still.
Oops. I sent vid meant to send screenshot
do you have mask layer 8 on ScreenChangeArea?
ah ok thats the issue, change it to be
player should be like this
mask means what layer it looks for other stuff on, and layer is what layer its on when other stuff is looking for it
so player gets mask 8, and rooms get layer 8
and then make sure they don't have anything set for the other, since it might trigger random stuff in your game
OH MY GOD IT WORKS. Thank you so much!
And there’s one last bug…
Tall room don’t work
dont work as in doesn't follow at all or it does it in a wierd way?
did you make this change to the function in the camera script
Shoves camera tiny bit into corner and doesn’t follow u up.
weird, can you take a video
that sounds like the problem that lead to me changing the function lol
if its the bottom left corner
actually it would be top left maybe idk
Sending rn
One sec
her @haughty chasm sry for being late
hm thats weird, whats the collision rectangle look like for that room?
hmmmm not sure why its going to the middle other than the code i had being wrong before
Ok. Was the camera coded to detach in bigger rooms for scrolling?
kinda
it follows the player when it has room
but if it doesn't have room it goes to the center on that axis, so if it has no room in both x and y, it will go to exactly the cetner
but the fact that you can't see the bottom of the level means it should have room
can you try deleting all the text in the camera's script and re-copy-pasting the code, i changed it again
Ok
Works now Thank you!!
@dry shuttle there’s working code here for it. Works perfectly!