I'm trying to create a windy area within which the player would be pushed continuously to the left <-
So far this is what I've come up with for the WindyArea
:
extends Area2D
var bodies_within=[] # saves bodies within the area which have "direction" property
func _physics_process(delta): # the "wind" process
for body in bodies_within:
body.direction.x-=50 # constantly pushing x back by 50
body.direction=body.move_and_slide(body.direction)
func _body_entered(body):
if("direction" in body): # if body has direction then append it to the array
bodies_within.append(body)
set_physics_process(true) # turn on the "wind"
func _body_exited(body):
if(body in bodies_within):
bodies_within.erase(body)
if!(len(bodies_within)): # if no bodies are left within the area then turn off the "wind"
set_physics_process(false)
func _ready():
set_physics_process(false)
self.connect("body_entered",self,"_body_entered")
self.connect("body_exited",self,"_body_exited")
and for my Player body (just for reference, I'm trying not to change this script and having to create a new windy function in it):
extends KinematicBody2D
var direction:Vector2
var gravity_speed=4500
var jump_speed=-1500
var descend_speed=50
var horizontal_speed=600
func _physics_process(delta):
direction.x=Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
direction.x*=horizontal_speed
if(is_on_floor()):
if(Input.is_action_just_pressed("ui_up")):
direction.y=jump_speed
## Movement ##
direction.y += gravity_speed * delta + descend_speed * Input.get_action_strength("ui_down")
direction=move_and_slide(direction,Vector2.UP)
but it only works in pushing the player back when the player is not given any user input and the body is static, but when the player is given user input it does not seem to work, instead it starts moving faster (I'm guessing since move_and_slide
is being called twice?)
So is there any fix or alternate solution which involves the same approach of not having to change the player script (much) and instead hands the movement alteration to the Area2D?
I'm going to offer an entirely different approach: gravity overrides.
Most tutorials out there have you hard code the gravity in the player character. In fact, you are doing that. Well, As Dr. Evil would put it: How about "No"?
There is a global gravity that you can get from project settings. You can get like this:
var g_scale:float = ProjectSettings.get("physics/2d/default_gravity")
var g:Vector2 = ProjectSettings.get("physics/2d/default_gravity_vector") * g_scale
Which, perhaps you don't want. However, you might find useful to express the gravity you want as a multiple of this gravity.
var g_scale:float = ProjectSettings.get("physics/2d/default_gravity")
var g:Vector2 = ProjectSettings.get("physics/2d/default_gravity_vector") * g_scale
gravity = g * g_factor
Why? Because we are going to use the Area2D
to modify the gravity, and then you can apply the same multiplier to the gravity you get.
So, as you might be aware, the Area2D
has "physics override" properties which allow you to modify the gravity.
Which begs the question? How do you query the gravity under which your kinematic body is? Well, like this:
var gravity = Physics2DServer.body_get_direct_state(get_rid()).total_gravity
And, yes, you could apply a factor:
gravity = Physics2DServer.body_get_direct_state(get_rid()).total_gravity * g_factor
Now you use this gravity for your motion. And you make an Area2D
that overrides the default gravity with an horizontal component. The player character falls according to the modified gravity, which also means it moves side ways. Ta-da! "wind".
Be aware that this implies to drop the assumption that gravity is along the y
axis. You could do it like this: direction += gravity * delta
.
Ok, ok, ok, let us say you don't want to do that. What you could do is expose either a velocity or acceleration property that the Area2D
can set to the bodies that enter.
For example:
var area_effect_velocity:Vector2
Then the Area2D
does this:
func _body_entered(body):
if("area_effect_velocity" in body):
bodies_within.append(body)
body.area_effect_velocity = Vector2(-50.0, 0.0)
func _body_exited(body):
if(body in bodies_within):
bodies_within.erase(body)
body.area_effect_velocity = Vector2.ZERO
Then you can add that in your motion. For example:
direction += area_effect_velocity
Addendum on overlapping Area2D
:
With the gravity override approach, you can set the space override to "Combine", and Godot solves that for you.
With the other approach, you can handle it by offsets:
var effect_velocity := Vector2(-50.0, 0.0)
func _body_entered(body):
if("area_effect_velocity" in body):
bodies_within.append(body)
body.area_effect_velocity += effect_velocity
func _body_exited(body):
if(body in bodies_within):
bodies_within.erase(body)
body.area_effect_velocity -= effect_velocity
And that would avoid extra checking.