#Hollow knight Camera system.

159 messages · Page 1 of 1 (latest)

neon rose
#

Any way to make a hollow knight camera system? I found a way to make one with a set grid but it’s not what I’m looking for. Also I don’t want it to be diffrent scenes or each room a node just a tile map if possible.

dry shuttle
#

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....)

neon rose
# dry shuttle Hello, sorry that i can't help you, but I am trying to recreate the same camera ...

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

▶ Play video
#

@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...

▶ Play video
dry shuttle
#

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...

▶ Play video
#

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)

neon rose
#

I watched the video and it told me except for the layers. Now it’s a new tile set for another layer right?

dry shuttle
#

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

neon rose
#

@dry shuttle I can’t figure out how to implement the camera system.

haughty chasm
#

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

haughty chasm
neon rose
haughty chasm
#

just like a picture of your scene with your tilemap, and where you'd like rooms

neon rose
#

Not a map yet.

haughty chasm
neon rose
#

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

haughty chasm
#

yeah sure

neon rose
#

Ok thank you.

#

I have to go for around 30 minutes. But will be back then.

haughty chasm
#

np i'll see what i can do

neon rose
dry shuttle
#

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

neon rose
dry shuttle
#

👍

neon rose
#

Any luck! @haughty chasm

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

neon rose
#

With the previous script? @haughty chasm

haughty chasm
#

no just a sec i'm adding comments to my new code lol

haughty chasm
#

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

neon rose
#

I put that in camera and setup the area 2D stuff?

haughty chasm
#

or if you want to switch scenes you maybe deactivate it before scene transition and then call activate_screen_camera again with new stuff

haughty chasm
#

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

neon rose
#

So I attach a script to my main scene. With that code u just sent?

haughty chasm
#

also theres one more piece, which is adding a matching Area2D to the player

neon rose
#

So I add that script above to the Game scene

#

Set up the area 2D on player and rooms.

haughty chasm
#

the script on this area has this:

extends Area2D

func _on_area_entered(area: Area2D) -> void:
    ScreenCameraController.change_screen(area)
haughty chasm
#

however you could put it in your scene too, on the camera actually

neon rose
#

I added the big chunk of code to the Game scene. Is that correct?

haughty chasm
#

uhh actually let me see if I can clean it up so it just goes on the camera

neon rose
#

Ok.

neon rose
#

@haughty chasm

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
haughty chasm
#

in _ready(), call yourcamera.activate_on_player(your player's node)

haughty chasm
# haughty chasm

oops I also forgot, in the area2d on your player, you want to set the MASK to 8, to match the screen

neon rose
haughty chasm
neon rose
#

Also my guy doesn’t have gravity anymore?

haughty chasm
#

uh what lol

#

wait can you take a picture of your scene's node structure on the left?

neon rose
#

Also it’s one camera and it doesn’t change anymore

haughty chasm
#

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)
neon rose
#

Ok. Question. Where do I connect the change room in player to the script?

haughty chasm
#

made this to condense everything since i sorta explained stuff all over the place

neon rose
#

This fine? Sry took from my phone

haughty chasm
#

is camera2d a direct child of your screen or is it in another node?

neon rose
#

Node of player

haughty chasm
#

ah ok i guess that techically works since it just changes its own position each frame

neon rose
#

Wait the giant code you sent me is for global right? The first one.

haughty chasm
neon rose
#

Oh. So no global?

haughty chasm
#

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

haughty chasm
#

thats the non global version, global was up a little bit

neon rose
#

Btw I have the global script and camera script both still on if that makes a difference

haughty chasm
#

but either way you need to activate it by handing it the player

haughty chasm
# neon rose

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)
neon rose
haughty chasm
#

that should work then

#

(i think)

neon rose
haughty chasm
#

try turning off position_smoothing in the normal camera settings

neon rose
haughty chasm
#

what does your player scene look like

#

oh nvm its in the video

neon rose
#

Oops. I sent vid meant to send screenshot

haughty chasm
#

do you have mask layer 8 on ScreenChangeArea?

neon rose
haughty chasm
#

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

neon rose
#

OH MY GOD IT WORKS. Thank you so much!

#

And there’s one last bug…

#

Tall room don’t work

haughty chasm
haughty chasm
neon rose
haughty chasm
#

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

neon rose
#

One sec

#

her @haughty chasm sry for being late

haughty chasm
#

hm thats weird, whats the collision rectangle look like for that room?

neon rose
haughty chasm
#

hmmmm not sure why its going to the middle other than the code i had being wrong before

neon rose
haughty chasm
#

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

neon rose
#

Works now Thank you!!

#

@dry shuttle there’s working code here for it. Works perfectly!

dry shuttle
#

nice

#

thx for your help peepo