This tutorial is well paced, explains a lot of the decisions, shows the math, but doesn't show too much as to make less experienced devs just copy paste the chunks into their own game, and instead makes you think about how you would implement this system within your already existing features. Truly a great tutorial, thank you for this.
Holy shit, someone watched it after a month :D. Improving production by small steps is starting to bear fruits I suppose. Yep, I'm trying to make architecture talks the core theme of the channel, even if it'll potentially loose me some views, at least, the Godoverse will become a bit better place. Glad you enjoyed and thank you for your support, it means a lot for me).
Привет. Спасибо за туториалы. Лайк, подписка и вот это вот все 👍 Два вопроса: 1. Можно ли пощупать исходники по камере? Без них как-то грустно. 2. Ролики на русском не планируются?
1) Пока низя). Но у меня есть серия про контроллер на гитхабе, там буквально есть в конечном автомате персонажа состояния Run и Sprint. Меняешь их код на буквально скрины, и уже почти работает, осталось только саму камеру приделать, структура нод и буквально все, что нужно в видосе есть. Он специально так сделан, мооожет быть, выдам код, если прям миллиард вопросов будет и структурно в будущих сериях будет подходить. 2) Не думаю, мб сабами запарюсь, если пойму, что выхлоп будет соотносим с затратами сил.
At 5:31, would you mind explaining how you get this orbiting value and orbiting curved? My understanding is that the orbiting value is simply a component of the input vector, but I'm not sure how you got the curved value. Looking at the diagram, how do you make sure the orbiting value isn't larger than the radius of the large circle below? If it is, would you be able to come up with the orbiting curved value? Thanks :D
5:52 is an explanation for orbiting vector. Base orbiting component is an input component. Orbiting curved then is strictly defined. And why is orbiting is lower then the circle - is "an implementation sense" detail. Orbiting component is the orbiting velocity of the character *per frame*. This is about 0.05 meters for a realistic running human speed, because a second has 60 phys-frames usually in 2024. And the camera arm length is just the camera arm length of several meters. Surely, the geometry problem solution will break on hight speeds, but you need to have 400+ km/h for it to start, and if that's the case I doubt you need a soulslike camera. The diagram has these proportions just for illustration purposes.
I am a begginer. 😅 Currently following the tutorial at 6:47. What is input internal? I wrote it and it could not find "input internal" in the current scope.
Ah, nevermind the name, it's just an input package. A data structure that holds my two imput vectors and some other things. I use a similar approach and talk about it more in the controller series. And a name InputInternal is for the fact that the project on screen is also a multiplayer, so I have InputInternal for local inputs transfering and InputSynchronized for sending to the server side. I will be remastering this something something Autumn, right now, it's a project with no source attached. Consider this just a number of geometry problems solved and don't copy code from screenshot line by line.
@@PointDown sorry for disturbing u again, currently, i not sure why but my visual(node that i turn into mesh ) is rotating toward the character. whileas I am unable to move the character. extends CharacterBody3D const RUN_SPEED := 10 @export var visuals: Node3D # Reference to the Visual node @onready var local_camera: SoulsCamera = $LocalCamera # LocalCamera is a child of the CharacterBody3D @onready var player: CharacterBody3D = $"." @onready var movement_velocity = Vector3.ZERO class InputInternal: var direction : Vector2 var target : Vector3 func _init(direction : Vector2 = Vector2(), target : Vector3 = Vector3()): self.direction = direction self.target = target func get_forward() -> float: return direction.y func get_orbiting() -> float: return direction.x # Placeholder logic, adjust based on your specific orbit control func _ready(): if not visuals: print("Visual node not found! Check the path to the visual node.") if not local_camera: print("LocalCamera node not found! Check the path to the LocalCamera node.") func _process(_delta): var input_internal = InputInternal.new() var move_right = Input.get_action_strength("Move_Right") var move_left = Input.get_action_strength("Move_Left") var move_back = Input.get_action_strength("Move_Back") var move_forward = Input.get_action_strength("Move_Forward")
input_internal.direction = Vector2( move_right - move_left, move_back - move_forward ) input_internal.target = global_position # Target is the position of the CharacterBody3D itself print("InputInternal direction: ", input_internal.direction) # Update character movement based on input and player state update(input_internal, self) func update(input_internal : InputInternal, player : CharacterBody3D): player.movement_velocity = velocity_by_input(input_internal, player) player.visuals.look_at(player.global_position - player.movement_velocity) player.move_and_slide() func velocity_by_input(input_internal : InputInternal, player : CharacterBody3D) -> Vector3: var from_center_speed : float = input_internal.get_forward() var orbit_speed : float = input_internal.get_orbiting() #print("From Center Speed: ", from_center_speed) #print("Orbit Speed: ", orbit_speed) if player.local_camera.is_target_locked: from_center_speed *= 1 orbit_speed *= 1 #print("Target Locked: Adjusted From Center Speed and Orbit Speed") if from_center_speed != 0: var grounded_target = input_internal.target grounded_target.y = 0 movement_velocity -= player.global_position.direction_to(grounded_target) * from_center_speed * RUN_SPEED movement_velocity.y = 0 print("Calculated Movement Velocity (From Center): ", movement_velocity)
if orbit_speed != 0: var d: float = orbit_speed * RUN_SPEED / 60 var grounded_target = input_internal.target grounded_target.y = 0 var target_direction = grounded_target - player.global_position var distance_to_target = target_direction.length() var alpha = 2 * asin(d / (2 * distance_to_target)) var d_vector = grounded_target - target_direction.rotated(Vector3.UP, alpha) - player.global_position movement_velocity += d_vector * 60 print("Calculated Movement Velocity (Orbit): ", movement_velocity)
@@rj_enoz Ooof, thats hard to do from a youtube comment. What I found is your target for some reason is your own character. That's incorrect, in unlocked mode, the target is gamera's position. Currently when you *var grounded_target = input_internal.target* grounded_target.y = 0 movement_velocity -= *player.global_position.direction_to(grounded_target)* * from_center_speed * RUN_SPEED the resulting vector of direction is zero. Also for some reason, when "adjusting" to target locking, you multiplaying your values on 1 instead of -1. But that's just what I saw, I can't guarantee it will work if corrected, but at least you'll get a different bug, woo-hoo.)
@@PointDown Hey, Just wanted to let u know. That I have succeeded in fixing it. Thank you for replying to the comment. I was able to find that my input = was set to global_position instead of visual.global_position. Onto the next part of this tutorial (mouse movements)
Можно пытаться маскировать это и пытаться в британское произношение из школы, а можно записаться в 7 раз быстрее). Мб если продолжу, со временем подвыправится.
This tutorial is well paced, explains a lot of the decisions, shows the math, but doesn't show too much as to make less experienced devs just copy paste the chunks into their own game, and instead makes you think about how you would implement this system within your already existing features. Truly a great tutorial, thank you for this.
Holy shit, someone watched it after a month :D. Improving production by small steps is starting to bear fruits I suppose. Yep, I'm trying to make architecture talks the core theme of the channel, even if it'll potentially loose me some views, at least, the Godoverse will become a bit better place. Glad you enjoyed and thank you for your support, it means a lot for me).
I'm definitely gonna use this guide, this kind of thing would make my game feel so much better!
legend starts here, mark my words!
when calculating the camera rotation/offset (8:18) I keep getting the error "The axis Vector3 (0, 0, 0) must be normalized."
Vector (0,0,0) has no length and can't be used as an axis to drscribe rotation.
Привет. Спасибо за туториалы. Лайк, подписка и вот это вот все 👍
Два вопроса:
1. Можно ли пощупать исходники по камере? Без них как-то грустно.
2. Ролики на русском не планируются?
1) Пока низя). Но у меня есть серия про контроллер на гитхабе, там буквально есть в конечном автомате персонажа состояния Run и Sprint. Меняешь их код на буквально скрины, и уже почти работает, осталось только саму камеру приделать, структура нод и буквально все, что нужно в видосе есть. Он специально так сделан, мооожет быть, выдам код, если прям миллиард вопросов будет и структурно в будущих сериях будет подходить.
2) Не думаю, мб сабами запарюсь, если пойму, что выхлоп будет соотносим с затратами сил.
At 5:31, would you mind explaining how you get this orbiting value and orbiting curved?
My understanding is that the orbiting value is simply a component of the input vector, but I'm not sure how you got the curved value.
Looking at the diagram, how do you make sure the orbiting value isn't larger than the radius of the large circle below? If it is, would you be able to come up with the orbiting curved value?
Thanks :D
5:52 is an explanation for orbiting vector. Base orbiting component is an input component. Orbiting curved then is strictly defined. And why is orbiting is lower then the circle - is "an implementation sense" detail. Orbiting component is the orbiting velocity of the character *per frame*. This is about 0.05 meters for a realistic running human speed, because a second has 60 phys-frames usually in 2024. And the camera arm length is just the camera arm length of several meters. Surely, the geometry problem solution will break on hight speeds, but you need to have 400+ km/h for it to start, and if that's the case I doubt you need a soulslike camera. The diagram has these proportions just for illustration purposes.
I am a begginer. 😅 Currently following the tutorial at 6:47.
What is input internal?
I wrote it and it could not find "input internal" in the current scope.
Ah, nevermind the name, it's just an input package. A data structure that holds my two imput vectors and some other things. I use a similar approach and talk about it more in the controller series. And a name InputInternal is for the fact that the project on screen is also a multiplayer, so I have InputInternal for local inputs transfering and InputSynchronized for sending to the server side. I will be remastering this something something Autumn, right now, it's a project with no source attached. Consider this just a number of geometry problems solved and don't copy code from screenshot line by line.
@@PointDown sorry for disturbing u again,
currently, i not sure why but my visual(node that i turn into mesh ) is rotating toward the character.
whileas I am unable to move the character.
extends CharacterBody3D
const RUN_SPEED := 10
@export var visuals: Node3D # Reference to the Visual node
@onready var local_camera: SoulsCamera = $LocalCamera # LocalCamera is a child of the CharacterBody3D
@onready var player: CharacterBody3D = $"."
@onready var movement_velocity = Vector3.ZERO
class InputInternal:
var direction : Vector2
var target : Vector3
func _init(direction : Vector2 = Vector2(), target : Vector3 = Vector3()):
self.direction = direction
self.target = target
func get_forward() -> float:
return direction.y
func get_orbiting() -> float:
return direction.x # Placeholder logic, adjust based on your specific orbit control
func _ready():
if not visuals:
print("Visual node not found! Check the path to the visual node.")
if not local_camera:
print("LocalCamera node not found! Check the path to the LocalCamera node.")
func _process(_delta):
var input_internal = InputInternal.new()
var move_right = Input.get_action_strength("Move_Right")
var move_left = Input.get_action_strength("Move_Left")
var move_back = Input.get_action_strength("Move_Back")
var move_forward = Input.get_action_strength("Move_Forward")
input_internal.direction = Vector2(
move_right - move_left,
move_back - move_forward
)
input_internal.target = global_position # Target is the position of the CharacterBody3D itself
print("InputInternal direction: ", input_internal.direction)
# Update character movement based on input and player state
update(input_internal, self)
func update(input_internal : InputInternal, player : CharacterBody3D):
player.movement_velocity = velocity_by_input(input_internal, player)
player.visuals.look_at(player.global_position - player.movement_velocity)
player.move_and_slide()
func velocity_by_input(input_internal : InputInternal, player : CharacterBody3D) -> Vector3:
var from_center_speed : float = input_internal.get_forward()
var orbit_speed : float = input_internal.get_orbiting()
#print("From Center Speed: ", from_center_speed)
#print("Orbit Speed: ", orbit_speed)
if player.local_camera.is_target_locked:
from_center_speed *= 1
orbit_speed *= 1
#print("Target Locked: Adjusted From Center Speed and Orbit Speed")
if from_center_speed != 0:
var grounded_target = input_internal.target
grounded_target.y = 0
movement_velocity -= player.global_position.direction_to(grounded_target) * from_center_speed * RUN_SPEED
movement_velocity.y = 0
print("Calculated Movement Velocity (From Center): ", movement_velocity)
if orbit_speed != 0:
var d: float = orbit_speed * RUN_SPEED / 60
var grounded_target = input_internal.target
grounded_target.y = 0
var target_direction = grounded_target - player.global_position
var distance_to_target = target_direction.length()
var alpha = 2 * asin(d / (2 * distance_to_target))
var d_vector = grounded_target - target_direction.rotated(Vector3.UP, alpha) - player.global_position
movement_velocity += d_vector * 60
print("Calculated Movement Velocity (Orbit): ", movement_velocity)
movement_velocity = movement_velocity.limit_length(RUN_SPEED)
return movement_velocity
@@rj_enoz Ooof, thats hard to do from a youtube comment. What I found is your target for some reason is your own character. That's incorrect, in unlocked mode, the target is gamera's position. Currently when you
*var grounded_target = input_internal.target*
grounded_target.y = 0
movement_velocity -= *player.global_position.direction_to(grounded_target)* * from_center_speed * RUN_SPEED
the resulting vector of direction is zero.
Also for some reason, when "adjusting" to target locking, you multiplaying your values on 1 instead of -1.
But that's just what I saw, I can't guarantee it will work if corrected, but at least you'll get a different bug, woo-hoo.)
@@PointDown Hey,
Just wanted to let u know.
That I have succeeded in fixing it.
Thank you for replying to the comment.
I was able to find that my input = was set to global_position instead of visual.global_position.
Onto the next part of this tutorial (mouse movements)
Очень чётко слышно русский акцент
Можно пытаться маскировать это и пытаться в британское произношение из школы, а можно записаться в 7 раз быстрее). Мб если продолжу, со временем подвыправится.