Path: blob/master/scene/2d/physics/character_body_2d.cpp
9906 views
/**************************************************************************/1/* character_body_2d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "character_body_2d.h"3132#ifndef DISABLE_DEPRECATED33#include "servers/extensions/physics_server_2d_extension.h"34#endif3536// So, if you pass 45 as limit, avoid numerical precision errors when angle is 45.37#define FLOOR_ANGLE_THRESHOLD 0.013839bool CharacterBody2D::move_and_slide() {40// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.41double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();4243Vector2 current_platform_velocity = platform_velocity;44Transform2D gt = get_global_transform();45previous_position = gt.columns[2];4647if ((on_floor || on_wall) && platform_rid.is_valid()) {48bool excluded = false;49if (on_floor) {50excluded = (platform_floor_layers & platform_layer) == 0;51} else if (on_wall) {52excluded = (platform_wall_layers & platform_layer) == 0;53}54if (!excluded) {55//this approach makes sure there is less delay between the actual body velocity and the one we saved56PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);57if (bs) {58Vector2 local_position = gt.columns[2] - bs->get_transform().columns[2];59current_platform_velocity = bs->get_velocity_at_local_position(local_position);60} else {61// Body is removed or destroyed, invalidate floor.62current_platform_velocity = Vector2();63platform_rid = RID();64}65} else {66current_platform_velocity = Vector2();67}68}6970motion_results.clear();71last_motion = Vector2();7273bool was_on_floor = on_floor;74on_floor = false;75on_ceiling = false;76on_wall = false;7778if (!current_platform_velocity.is_zero_approx()) {79PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin);80parameters.recovery_as_collision = true; // Also report collisions generated only from recovery.81parameters.exclude_bodies.insert(platform_rid);82if (platform_object_id.is_valid()) {83parameters.exclude_objects.insert(platform_object_id);84}8586PhysicsServer2D::MotionResult floor_result;87if (move_and_collide(parameters, floor_result, false, false)) {88motion_results.push_back(floor_result);89_set_collision_direction(floor_result);90}91}9293if (motion_mode == MOTION_MODE_GROUNDED) {94_move_and_slide_grounded(delta, was_on_floor);95} else {96_move_and_slide_floating(delta);97}9899// Compute real velocity.100real_velocity = get_position_delta() / delta;101102if (platform_on_leave != PLATFORM_ON_LEAVE_DO_NOTHING) {103// Add last platform velocity when just left a moving platform.104if (!on_floor && !on_wall) {105if (platform_on_leave == PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY && current_platform_velocity.dot(up_direction) < 0) {106current_platform_velocity = current_platform_velocity.slide(up_direction);107}108velocity += current_platform_velocity;109}110}111112return motion_results.size() > 0;113}114115void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) {116Vector2 motion = velocity * p_delta;117Vector2 motion_slide_up = motion.slide(up_direction);118119Vector2 prev_floor_normal = floor_normal;120121platform_rid = RID();122platform_object_id = ObjectID();123floor_normal = Vector2();124platform_velocity = Vector2();125126// No sliding on first attempt to keep floor motion stable when possible,127// When stop on slope is enabled or when there is no up direction.128bool sliding_enabled = !floor_stop_on_slope;129// Constant speed can be applied only the first time sliding is enabled.130bool can_apply_constant_speed = sliding_enabled;131// If the platform's ceiling push down the body.132bool apply_ceiling_velocity = false;133bool first_slide = true;134bool vel_dir_facing_up = velocity.dot(up_direction) > 0;135Vector2 last_travel;136137for (int iteration = 0; iteration < max_slides; ++iteration) {138PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin);139parameters.recovery_as_collision = true; // Also report collisions generated only from recovery.140141Vector2 prev_position = parameters.from.columns[2];142143PhysicsServer2D::MotionResult result;144bool collided = move_and_collide(parameters, result, false, !sliding_enabled);145146last_motion = result.travel;147148if (collided) {149motion_results.push_back(result);150_set_collision_direction(result);151152// If we hit a ceiling platform, we set the vertical velocity to at least the platform one.153if (on_ceiling && result.collider_velocity != Vector2() && result.collider_velocity.dot(up_direction) < 0) {154// If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward.155if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (result.collision_normal + up_direction).length() < 0.01) {156apply_ceiling_velocity = true;157Vector2 ceiling_vertical_velocity = up_direction * up_direction.dot(result.collider_velocity);158Vector2 motion_vertical_velocity = up_direction * up_direction.dot(velocity);159if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) {160velocity = ceiling_vertical_velocity + velocity.slide(up_direction);161}162}163}164165if (on_floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) {166Transform2D gt = get_global_transform();167if (result.travel.length() <= margin + CMP_EPSILON) {168gt.columns[2] -= result.travel;169}170set_global_transform(gt);171velocity = Vector2();172last_motion = Vector2();173motion = Vector2();174break;175}176177if (result.remainder.is_zero_approx()) {178motion = Vector2();179break;180}181182// Move on floor only checks.183if (floor_block_on_wall && on_wall && motion_slide_up.dot(result.collision_normal) <= 0) {184// Avoid to move forward on a wall if floor_block_on_wall is true.185if (p_was_on_floor && !on_floor && !vel_dir_facing_up) {186// If the movement is large the body can be prevented from reaching the walls.187if (result.travel.length() <= margin + CMP_EPSILON) {188// Cancels the motion.189Transform2D gt = get_global_transform();190gt.columns[2] -= result.travel;191set_global_transform(gt);192}193// Determines if you are on the ground.194_snap_on_floor(true, false, true);195velocity = Vector2();196last_motion = Vector2();197motion = Vector2();198break;199}200// Prevents the body from being able to climb a slope when it moves forward against the wall.201else if (!on_floor) {202motion = up_direction * up_direction.dot(result.remainder);203motion = motion.slide(result.collision_normal);204} else {205motion = result.remainder;206}207}208// Constant Speed when the slope is upward.209else if (floor_constant_speed && is_on_floor_only() && can_apply_constant_speed && p_was_on_floor && motion.dot(result.collision_normal) < 0) {210can_apply_constant_speed = false;211Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized();212motion = motion_slide_norm * (motion_slide_up.length() - result.travel.slide(up_direction).length() - last_travel.slide(up_direction).length());213}214// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.215else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) {216Vector2 slide_motion = result.remainder.slide(result.collision_normal);217if (slide_motion.dot(velocity) > 0.0) {218motion = slide_motion;219} else {220motion = Vector2();221}222if (slide_on_ceiling && on_ceiling) {223// Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall.224if (vel_dir_facing_up) {225velocity = velocity.slide(result.collision_normal);226} else {227// Avoid acceleration in slope when falling.228velocity = up_direction * up_direction.dot(velocity);229}230}231}232// No sliding on first attempt to keep floor motion stable when possible.233else {234motion = result.remainder;235if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) {236velocity = velocity.slide(up_direction);237motion = motion.slide(up_direction);238}239}240241last_travel = result.travel;242}243// When you move forward in a downward slope you don’t collide because you will be in the air.244// This test ensures that constant speed is applied, only if the player is still on the ground after the snap is applied.245else if (floor_constant_speed && first_slide && _on_floor_if_snapped(p_was_on_floor, vel_dir_facing_up)) {246can_apply_constant_speed = false;247sliding_enabled = true;248Transform2D gt = get_global_transform();249gt.columns[2] = prev_position;250set_global_transform(gt);251252Vector2 motion_slide_norm = motion.slide(prev_floor_normal).normalized();253motion = motion_slide_norm * (motion_slide_up.length());254collided = true;255}256257can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled;258sliding_enabled = true;259first_slide = false;260261if (!collided || motion.is_zero_approx()) {262break;263}264}265266_snap_on_floor(p_was_on_floor, vel_dir_facing_up);267268// Scales the horizontal velocity according to the wall slope.269if (is_on_wall_only() && motion_slide_up.dot(motion_results.get(0).collision_normal) < 0) {270Vector2 slide_motion = velocity.slide(motion_results.get(0).collision_normal);271if (motion_slide_up.dot(slide_motion) < 0) {272velocity = up_direction * up_direction.dot(velocity);273} else {274// Keeps the vertical motion from velocity and add the horizontal motion of the projection.275velocity = up_direction * up_direction.dot(velocity) + slide_motion.slide(up_direction);276}277}278279// Reset the gravity accumulation when touching the ground.280if (on_floor && !vel_dir_facing_up) {281velocity = velocity.slide(up_direction);282}283}284285void CharacterBody2D::_move_and_slide_floating(double p_delta) {286Vector2 motion = velocity * p_delta;287288platform_rid = RID();289platform_object_id = ObjectID();290floor_normal = Vector2();291platform_velocity = Vector2();292293bool first_slide = true;294for (int iteration = 0; iteration < max_slides; ++iteration) {295PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin);296parameters.recovery_as_collision = true; // Also report collisions generated only from recovery.297298PhysicsServer2D::MotionResult result;299bool collided = move_and_collide(parameters, result, false, false);300301last_motion = result.travel;302303if (collided) {304motion_results.push_back(result);305_set_collision_direction(result);306307if (result.remainder.is_zero_approx()) {308motion = Vector2();309break;310}311312if (wall_min_slide_angle != 0 && result.get_angle(-velocity.normalized()) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {313motion = Vector2();314} else if (first_slide) {315Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized();316motion = motion_slide_norm * (motion.length() - result.travel.length());317} else {318motion = result.remainder.slide(result.collision_normal);319}320321if (motion.dot(velocity) <= 0.0) {322motion = Vector2();323}324}325326if (!collided || motion.is_zero_approx()) {327break;328}329330first_slide = false;331}332}333void CharacterBody2D::apply_floor_snap() {334_apply_floor_snap();335}336337// Method that avoids the p_wall_as_floor parameter for the public method.338void CharacterBody2D::_apply_floor_snap(bool p_wall_as_floor) {339if (on_floor) {340return;341}342343// Snap by at least collision margin to keep floor state consistent.344real_t length = MAX(floor_snap_length, margin);345346PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);347parameters.recovery_as_collision = true; // Also report collisions generated only from recovery.348parameters.collide_separation_ray = true;349350PhysicsServer2D::MotionResult result;351if (move_and_collide(parameters, result, true, false)) {352if ((result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) ||353(p_wall_as_floor && result.get_angle(-up_direction) > floor_max_angle + FLOOR_ANGLE_THRESHOLD)) {354on_floor = true;355floor_normal = result.collision_normal;356_set_platform_data(result);357358// Ensure that we only move the body along the up axis, because359// move_and_collide may stray the object a bit when getting it unstuck.360// Canceling this motion should not affect move_and_slide, as previous361// calls to move_and_collide already took care of freeing the body.362if (result.travel.length() > margin) {363result.travel = up_direction * up_direction.dot(result.travel);364} else {365result.travel = Vector2();366}367368parameters.from.columns[2] += result.travel;369set_global_transform(parameters.from);370}371}372}373374void CharacterBody2D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up, bool p_wall_as_floor) {375if (on_floor || !p_was_on_floor || p_vel_dir_facing_up) {376return;377}378379_apply_floor_snap(p_wall_as_floor);380}381382bool CharacterBody2D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up) {383if (up_direction == Vector2() || on_floor || !p_was_on_floor || p_vel_dir_facing_up) {384return false;385}386387// Snap by at least collision margin to keep floor state consistent.388real_t length = MAX(floor_snap_length, margin);389390PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);391parameters.recovery_as_collision = true; // Also report collisions generated only from recovery.392parameters.collide_separation_ray = true;393394PhysicsServer2D::MotionResult result;395if (move_and_collide(parameters, result, true, false)) {396if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {397return true;398}399}400401return false;402}403404void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResult &p_result) {405if (motion_mode == MOTION_MODE_GROUNDED && p_result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor406on_floor = true;407floor_normal = p_result.collision_normal;408_set_platform_data(p_result);409} else if (motion_mode == MOTION_MODE_GROUNDED && p_result.get_angle(-up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling410on_ceiling = true;411} else {412on_wall = true;413wall_normal = p_result.collision_normal;414// Don't apply wall velocity when the collider is a CharacterBody2D.415if (ObjectDB::get_instance<CharacterBody2D>(p_result.collider_id) == nullptr) {416_set_platform_data(p_result);417}418}419}420421void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) {422PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(p_result.collider);423if (bs == nullptr) {424return;425}426427platform_rid = p_result.collider;428platform_object_id = p_result.collider_id;429platform_velocity = p_result.collider_velocity;430431#ifndef DISABLE_DEPRECATED432// Try to accommodate for any physics extensions that have yet to implement `PhysicsDirectBodyState2D::get_collision_layer`.433PhysicsDirectBodyState2DExtension *bs_ext = Object::cast_to<PhysicsDirectBodyState2DExtension>(bs);434if (bs_ext != nullptr && !GDVIRTUAL_IS_OVERRIDDEN_PTR(bs_ext, _get_collision_layer)) {435platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(p_result.collider);436} else437#endif438{439platform_layer = bs->get_collision_layer();440}441}442443const Vector2 &CharacterBody2D::get_velocity() const {444return velocity;445}446447void CharacterBody2D::set_velocity(const Vector2 &p_velocity) {448velocity = p_velocity;449}450451bool CharacterBody2D::is_on_floor() const {452return on_floor;453}454455bool CharacterBody2D::is_on_floor_only() const {456return on_floor && !on_wall && !on_ceiling;457}458459bool CharacterBody2D::is_on_wall() const {460return on_wall;461}462463bool CharacterBody2D::is_on_wall_only() const {464return on_wall && !on_floor && !on_ceiling;465}466467bool CharacterBody2D::is_on_ceiling() const {468return on_ceiling;469}470471bool CharacterBody2D::is_on_ceiling_only() const {472return on_ceiling && !on_floor && !on_wall;473}474475const Vector2 &CharacterBody2D::get_floor_normal() const {476return floor_normal;477}478479const Vector2 &CharacterBody2D::get_wall_normal() const {480return wall_normal;481}482483const Vector2 &CharacterBody2D::get_last_motion() const {484return last_motion;485}486487Vector2 CharacterBody2D::get_position_delta() const {488return get_global_transform().columns[2] - previous_position;489}490491const Vector2 &CharacterBody2D::get_real_velocity() const {492return real_velocity;493}494495real_t CharacterBody2D::get_floor_angle(const Vector2 &p_up_direction) const {496ERR_FAIL_COND_V(p_up_direction == Vector2(), 0);497return Math::acos(floor_normal.dot(p_up_direction));498}499500const Vector2 &CharacterBody2D::get_platform_velocity() const {501return platform_velocity;502}503504int CharacterBody2D::get_slide_collision_count() const {505return motion_results.size();506}507508PhysicsServer2D::MotionResult CharacterBody2D::get_slide_collision(int p_bounce) const {509ERR_FAIL_INDEX_V(p_bounce, motion_results.size(), PhysicsServer2D::MotionResult());510return motion_results[p_bounce];511}512513Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) {514ERR_FAIL_INDEX_V(p_bounce, motion_results.size(), Ref<KinematicCollision2D>());515if (p_bounce >= slide_colliders.size()) {516slide_colliders.resize(p_bounce + 1);517}518519// Create a new instance when the cached reference is invalid or still in use in script.520if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->get_reference_count() > 1) {521slide_colliders.write[p_bounce].instantiate();522slide_colliders.write[p_bounce]->owner_id = get_instance_id();523}524525slide_colliders.write[p_bounce]->result = motion_results[p_bounce];526return slide_colliders[p_bounce];527}528529Ref<KinematicCollision2D> CharacterBody2D::_get_last_slide_collision() {530if (motion_results.is_empty()) {531return Ref<KinematicCollision2D>();532}533return _get_slide_collision(motion_results.size() - 1);534}535536void CharacterBody2D::set_safe_margin(real_t p_margin) {537margin = p_margin;538}539540real_t CharacterBody2D::get_safe_margin() const {541return margin;542}543544bool CharacterBody2D::is_floor_stop_on_slope_enabled() const {545return floor_stop_on_slope;546}547548void CharacterBody2D::set_floor_stop_on_slope_enabled(bool p_enabled) {549floor_stop_on_slope = p_enabled;550}551552bool CharacterBody2D::is_floor_constant_speed_enabled() const {553return floor_constant_speed;554}555556void CharacterBody2D::set_floor_constant_speed_enabled(bool p_enabled) {557floor_constant_speed = p_enabled;558}559560bool CharacterBody2D::is_floor_block_on_wall_enabled() const {561return floor_block_on_wall;562}563564void CharacterBody2D::set_floor_block_on_wall_enabled(bool p_enabled) {565floor_block_on_wall = p_enabled;566}567568bool CharacterBody2D::is_slide_on_ceiling_enabled() const {569return slide_on_ceiling;570}571572void CharacterBody2D::set_slide_on_ceiling_enabled(bool p_enabled) {573slide_on_ceiling = p_enabled;574}575576uint32_t CharacterBody2D::get_platform_floor_layers() const {577return platform_floor_layers;578}579580void CharacterBody2D::set_platform_floor_layers(uint32_t p_exclude_layers) {581platform_floor_layers = p_exclude_layers;582}583584uint32_t CharacterBody2D::get_platform_wall_layers() const {585return platform_wall_layers;586}587588void CharacterBody2D::set_platform_wall_layers(uint32_t p_exclude_layers) {589platform_wall_layers = p_exclude_layers;590}591592void CharacterBody2D::set_motion_mode(MotionMode p_mode) {593motion_mode = p_mode;594}595596CharacterBody2D::MotionMode CharacterBody2D::get_motion_mode() const {597return motion_mode;598}599600void CharacterBody2D::set_platform_on_leave(PlatformOnLeave p_on_leave_apply_velocity) {601platform_on_leave = p_on_leave_apply_velocity;602}603604CharacterBody2D::PlatformOnLeave CharacterBody2D::get_platform_on_leave() const {605return platform_on_leave;606}607608int CharacterBody2D::get_max_slides() const {609return max_slides;610}611612void CharacterBody2D::set_max_slides(int p_max_slides) {613ERR_FAIL_COND(p_max_slides < 1);614max_slides = p_max_slides;615}616617real_t CharacterBody2D::get_floor_max_angle() const {618return floor_max_angle;619}620621void CharacterBody2D::set_floor_max_angle(real_t p_radians) {622floor_max_angle = p_radians;623}624625real_t CharacterBody2D::get_floor_snap_length() {626return floor_snap_length;627}628629void CharacterBody2D::set_floor_snap_length(real_t p_floor_snap_length) {630ERR_FAIL_COND(p_floor_snap_length < 0);631floor_snap_length = p_floor_snap_length;632}633634real_t CharacterBody2D::get_wall_min_slide_angle() const {635return wall_min_slide_angle;636}637638void CharacterBody2D::set_wall_min_slide_angle(real_t p_radians) {639wall_min_slide_angle = p_radians;640}641642const Vector2 &CharacterBody2D::get_up_direction() const {643return up_direction;644}645646void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) {647ERR_FAIL_COND_MSG(p_up_direction == Vector2(), "up_direction can't be equal to Vector2.ZERO, consider using Floating motion mode instead.");648up_direction = p_up_direction.normalized();649}650651void CharacterBody2D::_notification(int p_what) {652switch (p_what) {653case NOTIFICATION_ENTER_TREE: {654// Reset move_and_slide() data.655on_floor = false;656platform_rid = RID();657platform_object_id = ObjectID();658on_ceiling = false;659on_wall = false;660motion_results.clear();661platform_velocity = Vector2();662} break;663}664}665666void CharacterBody2D::_validate_property(PropertyInfo &p_property) const {667if (!Engine::get_singleton()->is_editor_hint()) {668return;669}670if (motion_mode == MOTION_MODE_FLOATING) {671if (p_property.name.begins_with("floor_") || p_property.name == "up_direction" || p_property.name == "slide_on_ceiling") {672p_property.usage = PROPERTY_USAGE_NO_EDITOR;673}674} else {675if (p_property.name == "wall_min_slide_angle") {676p_property.usage = PROPERTY_USAGE_NO_EDITOR;677}678}679}680681void CharacterBody2D::_bind_methods() {682ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody2D::move_and_slide);683ClassDB::bind_method(D_METHOD("apply_floor_snap"), &CharacterBody2D::apply_floor_snap);684685ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &CharacterBody2D::set_velocity);686ClassDB::bind_method(D_METHOD("get_velocity"), &CharacterBody2D::get_velocity);687688ClassDB::bind_method(D_METHOD("set_safe_margin", "margin"), &CharacterBody2D::set_safe_margin);689ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin);690ClassDB::bind_method(D_METHOD("is_floor_stop_on_slope_enabled"), &CharacterBody2D::is_floor_stop_on_slope_enabled);691ClassDB::bind_method(D_METHOD("set_floor_stop_on_slope_enabled", "enabled"), &CharacterBody2D::set_floor_stop_on_slope_enabled);692ClassDB::bind_method(D_METHOD("set_floor_constant_speed_enabled", "enabled"), &CharacterBody2D::set_floor_constant_speed_enabled);693ClassDB::bind_method(D_METHOD("is_floor_constant_speed_enabled"), &CharacterBody2D::is_floor_constant_speed_enabled);694ClassDB::bind_method(D_METHOD("set_floor_block_on_wall_enabled", "enabled"), &CharacterBody2D::set_floor_block_on_wall_enabled);695ClassDB::bind_method(D_METHOD("is_floor_block_on_wall_enabled"), &CharacterBody2D::is_floor_block_on_wall_enabled);696ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody2D::set_slide_on_ceiling_enabled);697ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody2D::is_slide_on_ceiling_enabled);698699ClassDB::bind_method(D_METHOD("set_platform_floor_layers", "exclude_layer"), &CharacterBody2D::set_platform_floor_layers);700ClassDB::bind_method(D_METHOD("get_platform_floor_layers"), &CharacterBody2D::get_platform_floor_layers);701ClassDB::bind_method(D_METHOD("set_platform_wall_layers", "exclude_layer"), &CharacterBody2D::set_platform_wall_layers);702ClassDB::bind_method(D_METHOD("get_platform_wall_layers"), &CharacterBody2D::get_platform_wall_layers);703704ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody2D::get_max_slides);705ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides);706ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody2D::get_floor_max_angle);707ClassDB::bind_method(D_METHOD("set_floor_max_angle", "radians"), &CharacterBody2D::set_floor_max_angle);708ClassDB::bind_method(D_METHOD("get_floor_snap_length"), &CharacterBody2D::get_floor_snap_length);709ClassDB::bind_method(D_METHOD("set_floor_snap_length", "floor_snap_length"), &CharacterBody2D::set_floor_snap_length);710ClassDB::bind_method(D_METHOD("get_wall_min_slide_angle"), &CharacterBody2D::get_wall_min_slide_angle);711ClassDB::bind_method(D_METHOD("set_wall_min_slide_angle", "radians"), &CharacterBody2D::set_wall_min_slide_angle);712ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody2D::get_up_direction);713ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction);714ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody2D::set_motion_mode);715ClassDB::bind_method(D_METHOD("get_motion_mode"), &CharacterBody2D::get_motion_mode);716ClassDB::bind_method(D_METHOD("set_platform_on_leave", "on_leave_apply_velocity"), &CharacterBody2D::set_platform_on_leave);717ClassDB::bind_method(D_METHOD("get_platform_on_leave"), &CharacterBody2D::get_platform_on_leave);718719ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody2D::is_on_floor);720ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody2D::is_on_floor_only);721ClassDB::bind_method(D_METHOD("is_on_ceiling"), &CharacterBody2D::is_on_ceiling);722ClassDB::bind_method(D_METHOD("is_on_ceiling_only"), &CharacterBody2D::is_on_ceiling_only);723ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody2D::is_on_wall);724ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody2D::is_on_wall_only);725ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody2D::get_floor_normal);726ClassDB::bind_method(D_METHOD("get_wall_normal"), &CharacterBody2D::get_wall_normal);727ClassDB::bind_method(D_METHOD("get_last_motion"), &CharacterBody2D::get_last_motion);728ClassDB::bind_method(D_METHOD("get_position_delta"), &CharacterBody2D::get_position_delta);729ClassDB::bind_method(D_METHOD("get_real_velocity"), &CharacterBody2D::get_real_velocity);730ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody2D::get_floor_angle, DEFVAL(Vector2(0.0, -1.0)));731ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody2D::get_platform_velocity);732ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody2D::get_slide_collision_count);733ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision);734ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody2D::_get_last_slide_collision);735736ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Floating", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");737ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");738ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "suffix:px/s", PROPERTY_USAGE_NO_EDITOR), "set_velocity", "get_velocity");739ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");740ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides");741ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians_as_degrees", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle");742743ADD_GROUP("Floor", "floor_");744ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled");745ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled");746ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled");747ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians_as_degrees"), "set_floor_max_angle", "get_floor_max_angle");748ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater,suffix:px"), "set_floor_snap_length", "get_floor_snap_length");749750ADD_GROUP("Moving Platform", "platform_");751ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_on_leave", PROPERTY_HINT_ENUM, "Add Velocity,Add Upward Velocity,Do Nothing", PROPERTY_USAGE_DEFAULT), "set_platform_on_leave", "get_platform_on_leave");752ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_floor_layers", "get_platform_floor_layers");753ADD_PROPERTY(PropertyInfo(Variant::INT, "platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_platform_wall_layers", "get_platform_wall_layers");754755ADD_GROUP("Collision", "");756ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001,suffix:px"), "set_safe_margin", "get_safe_margin");757758BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED);759BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING);760761BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_ADD_VELOCITY);762BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_ADD_UPWARD_VELOCITY);763BIND_ENUM_CONSTANT(PLATFORM_ON_LEAVE_DO_NOTHING);764}765766CharacterBody2D::CharacterBody2D() :767PhysicsBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) {768}769770771