Godot Version
4.5.2
Question
Hello guys ! I m currently trying to develop a game which one the hero can throw and fight with a spear and i m on throwing mechanic now but i don’t know why but when the spear touch the ground or a wall it doesn’t be freeze correctly. The problem is that the rotation change before freeze i think but i don’t know how to solve this
Note : Spear is a RigidBody2D. I set the contact_monitor on true and the max_contacts_reported on 1. In addition i create a raycast to follow mouse position (mouse_dir) on the player and add a node that is the point where spear is launched (muzzle) as a child of the raycast
There is my code for the spear below :
extends RigidBody2D
@export var SPEED : float = 200
signal spear_impact(body:Node2D)
@onready var vector: Vector2 = Vector2(SPEED,0).rotated(rotation)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
if not body_entered.is_connected(_on_body_entered):
body_entered.connect(_on_body_entered)
apply_central_impulse(vector)
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(_delta: float) -> void:
global_rotation = linear_velocity.angle()
pass
func _on_body_entered(body:Node2D):
if body.is_in_group("tilemap"):
_freeze_object.call_deferred(body)
pass
func _freeze_object(body:Node2D):
set_physics_process(false)
linear_velocity = Vector2.ZERO
angular_velocity = 0
freeze = true
spear_impact.emit(body)
pass
func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
print("instance free")
queue_free.call_deferred()
pass # Replace with function body.
And there is the code for instanciate the spear by the player :
func throw_spear():
if mouse_dir.is_colliding() :
pass
else:
print("try throw")
var thrown_weapon = weapon.instantiate()
thrown_weapon.position = muzzle.global_position
thrown_weapon.look_at(get_global_mouse_position())
get_parent().add_child(thrown_weapon)
pass
A few general notes:
pass literally means "do nothing and don’t throw an error bout it. It is a placeholder. Delete all of them.
queue_free() is already a deferred call. Adding .call_deferred() on the end of it does nothing. Delete it.
- The fact that you’re checking if
body_entered is connected before connecting it is weird. If indicates to me that you are doing something with the spear that is causing a bug and this is your fix. It could be a key to your actual bug that you are masking by doing this.
- You can simplify the second function:
func throw_spear():
if not mouse_dir.is_colliding() :
print("try throw")
var thrown_weapon = weapon.instantiate()
thrown_weapon.position = muzzle.global_position
thrown_weapon.look_at(get_global_mouse_position())
add_sibling(thrown_weapon)
As for your issue, I think the problem is that physics_process() is not stopping before the RigidBody2D collides and rebounds. My recommendation would be to try using an Area2D instead.
1 Like
thanks for your advise ! i think i found a solution for my problem by using _integrate_forces instead of _physics_process wrote in the documentation:
extends RigidBody2D
@export var SPEED : float = 200
var _last_rotation:float = 0.0
signal spear_impact(body:Node2D,orthogonal_impact:Vector2)
@onready var vector: Vector2 = Vector2(SPEED,0).rotated(rotation)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
if not body_entered.is_connected(_on_body_entered):
body_entered.connect(_on_body_entered)
apply_central_impulse(vector)
func _integrate_forces(_state: PhysicsDirectBodyState2D) -> void:
_last_rotation = global_rotation
global_rotation = _state.linear_velocity.angle()
func _on_body_entered(body:Node2D):
if body.is_in_group("tilemap"):
_freeze_object.call_deferred(body)
func _freeze_object(body:Node2D):
set_physics_process(false)
global_rotation = _last_rotation
linear_velocity = Vector2.ZERO
angular_velocity = 0
freeze = true
handle_impact(body,Vector2.from_angle(_last_rotation).orthogonal().floor())
func handle_impact(_body:Node2D,_orthogonal_impact:Vector2):
pass
func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
queue_free()
do you still think the area2D is a better idea ,or i can keep mine ?
If it works for you, go for it.
Ok thanks for your help
just for share my latest changes as the solution
extends RigidBody2D
@export var SPEED : float = 200
var _last_rotation:float = 0.0
@onready var wind_zone: Area2D = $wind_zone
@onready var wind_zone_shape: CollisionShape2D = $wind_zone/wind_zone_shape
var is_floor = false
var is_wall = false
@onready var vector: Vector2 = Vector2(SPEED,0).rotated(rotation)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
#body_entered.connect(_on_body_entered)
wind_zone.body_entered.connect(_on_wind_zone_entered)
wind_zone.body_exited.connect(_on_wind_zone_exited)
apply_central_impulse(vector)
func _integrate_forces(_state: PhysicsDirectBodyState2D) -> void:
_last_rotation = global_rotation
global_rotation = _state.linear_velocity.angle()
if _state.get_contact_count() > 0:
handle_impact(_state)
func handle_impact(_state:PhysicsDirectBodyState2D):
if _state.get_contact_collider(0).is_valid():
var normal = _state.get_contact_local_normal(0)
var body = _state.get_contact_collider_object(0)
is_floor = normal.dot(Vector2.UP) > 0.5
is_wall = abs(normal.dot(Vector2.RIGHT)) > 0.5
_freeze_object.call_deferred(body)
func _freeze_object(_body:Node2D):
set_physics_process(false)
global_rotation = _last_rotation
linear_velocity = Vector2.ZERO
angular_velocity = 0
freeze = true
func _on_visible_on_screen_notifier_2d_screen_exited() -> void:
queue_free()
1 Like