summaryrefslogtreecommitdiff
path: root/scene/2d
diff options
context:
space:
mode:
Diffstat (limited to 'scene/2d')
-rw-r--r--scene/2d/audio_stream_player_2d.cpp183
-rw-r--r--scene/2d/audio_stream_player_2d.h10
-rw-r--r--scene/2d/listener_2d.cpp112
-rw-r--r--scene/2d/listener_2d.h61
-rw-r--r--scene/2d/physics_body_2d.cpp261
-rw-r--r--scene/2d/physics_body_2d.h55
-rw-r--r--scene/2d/tile_map.cpp66
-rw-r--r--scene/2d/tile_map.h4
8 files changed, 497 insertions, 255 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index ea491e8b0e..8401909384 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -31,6 +31,7 @@
#include "audio_stream_player_2d.h"
#include "scene/2d/area_2d.h"
+#include "scene/2d/listener_2d.h"
#include "scene/main/window.h"
void AudioStreamPlayer2D::_notification(int p_what) {
@@ -59,33 +60,47 @@ void AudioStreamPlayer2D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
//update anything related to position first, if possible of course
+ if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
+ _update_panning();
+ }
- if (!stream_playback.is_valid()) {
- return;
+ if (setplay.get() >= 0 && stream.is_valid()) {
+ active.set();
+ Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+ ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
+ AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get());
+ stream_playbacks.push_back(new_playback);
+ setplay.set(-1);
}
- if (setplay.get() >= 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
- _update_panning();
- if (setplay.get() >= 0) {
- active.set();
- AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
- setplay.set(-1);
+
+ if (!stream_playbacks.is_empty() && active.is_set()) {
+ // Stop playing if no longer active.
+ Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
+ emit_signal(SNAME("finished"));
+ playbacks_to_remove.push_back(playback);
+ }
+ }
+ // Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
+ for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
+ stream_playbacks.erase(playback);
+ }
+ if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
+ // This node is no longer actively playing audio.
+ active.clear();
+ set_physics_process_internal(false);
}
}
- // Stop playing if no longer active.
- if (active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
- active.clear();
- set_physics_process_internal(false);
- emit_signal(SNAME("finished"));
+ while (stream_playbacks.size() > max_polyphony) {
+ AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+ stream_playbacks.remove(0);
}
}
}
StringName AudioStreamPlayer2D::_get_actual_bus() {
- if (!stream_playback.is_valid()) {
- return SNAME("Master");
- }
-
Vector2 global_pos = get_global_position();
//check if any area is diverting sound into a bus
@@ -113,12 +128,10 @@ StringName AudioStreamPlayer2D::_get_actual_bus() {
}
void AudioStreamPlayer2D::_update_panning() {
- if (!stream_playback.is_valid()) {
+ if (!active.is_set() || stream.is_null()) {
return;
}
- last_mix_count = AudioServer::get_singleton()->get_mix_count();
-
Ref<World2D> world_2d = get_world_2d();
ERR_FAIL_COND(world_2d.is_null());
@@ -138,13 +151,22 @@ void AudioStreamPlayer2D::_update_panning() {
continue;
}
//compute matrix to convert to screen
- Transform2D to_screen = vp->get_global_canvas_transform() * vp->get_canvas_transform();
Vector2 screen_size = vp->get_visible_rect().size;
+ Vector2 listener_in_global;
+ Vector2 relative_to_listener;
//screen in global is used for attenuation
- Vector2 screen_in_global = to_screen.affine_inverse().xform(screen_size * 0.5);
+ Listener2D *listener = vp->get_listener_2d();
+ if (listener) {
+ listener_in_global = listener->get_global_position();
+ relative_to_listener = global_pos - listener_in_global;
+ } else {
+ Transform2D to_listener = vp->get_global_canvas_transform() * vp->get_canvas_transform();
+ listener_in_global = to_listener.affine_inverse().xform(screen_size * 0.5);
+ relative_to_listener = to_listener.xform(global_pos) - screen_size * 0.5;
+ }
- float dist = global_pos.distance_to(screen_in_global); //distance to screen center
+ float dist = global_pos.distance_to(listener_in_global); // Distance to listener, or screen if none.
if (dist > max_distance) {
continue; //can't hear this sound in this viewport
@@ -153,10 +175,7 @@ void AudioStreamPlayer2D::_update_panning() {
float multiplier = Math::pow(1.0f - dist / max_distance, attenuation);
multiplier *= Math::db2linear(volume_db); //also apply player volume!
- //point in screen is used for panning
- Vector2 point_in_screen = to_screen.xform(global_pos);
-
- float pan = CLAMP(point_in_screen.x / screen_size.width, 0.0, 1.0);
+ float pan = CLAMP((relative_to_listener.x + screen_size.x * 0.5) / screen_size.x, 0.0, 1.0);
float l = 1.0 - pan;
float r = pan;
@@ -164,28 +183,20 @@ void AudioStreamPlayer2D::_update_panning() {
volume_vector.write[0] = AudioFrame(l, r) * multiplier;
}
- AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, _get_actual_bus(), volume_vector);
-}
-
-void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
- if (stream_playback.is_valid()) {
- stop();
+ for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_bus_exclusive(playback, _get_actual_bus(), volume_vector);
}
- stream_playback.unref();
- stream.unref();
- if (p_stream.is_valid()) {
- stream_playback = p_stream->instance_playback();
- if (stream_playback.is_valid()) {
- stream = p_stream;
- } else {
- stream.unref();
- }
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
}
- if (p_stream.is_valid() && stream_playback.is_null()) {
- stream.unref();
- }
+ last_mix_count = AudioServer::get_singleton()->get_mix_count();
+}
+
+void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
+ stop();
+ stream = p_stream;
}
Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
@@ -203,8 +214,8 @@ float AudioStreamPlayer2D::get_volume_db() const {
void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) {
ERR_FAIL_COND(p_pitch_scale <= 0.0);
pitch_scale = p_pitch_scale;
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, p_pitch_scale);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_pitch_scale(playback, p_pitch_scale);
}
}
@@ -213,44 +224,50 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
}
void AudioStreamPlayer2D::play(float p_from_pos) {
- stop();
- if (stream.is_valid()) {
- stream_playback = stream->instance_playback();
+ if (stream.is_null()) {
+ return;
}
- if (stream_playback.is_valid()) {
- setplay.set(p_from_pos);
- set_physics_process_internal(true);
+ ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
+ if (stream->is_monophonic() && is_playing()) {
+ stop();
}
+
+ setplay.set(p_from_pos);
+ active.set();
+ set_physics_process_internal(true);
}
void AudioStreamPlayer2D::seek(float p_seconds) {
- if (stream_playback.is_valid() && active.is_set()) {
+ if (is_playing()) {
+ stop();
play(p_seconds);
}
}
void AudioStreamPlayer2D::stop() {
- if (stream_playback.is_valid()) {
- active.clear();
- AudioServer::get_singleton()->stop_playback_stream(stream_playback);
- set_physics_process_internal(false);
- setplay.set(-1);
+ setplay.set(-1);
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->stop_playback_stream(playback);
}
+ stream_playbacks.clear();
+ active.clear();
+ set_physics_process_internal(false);
}
bool AudioStreamPlayer2D::is_playing() const {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->is_playback_active(stream_playback);
+ for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ if (AudioServer::get_singleton()->is_playback_active(playback)) {
+ return true;
+ }
}
-
return false;
}
float AudioStreamPlayer2D::get_playback_position() {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->get_playback_position(stream_playback);
+ // Return the playback position of the most recently started playback stream.
+ if (!stream_playbacks.is_empty()) {
+ return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]);
}
-
return 0;
}
@@ -284,11 +301,7 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) {
}
bool AudioStreamPlayer2D::_is_active() const {
- if (stream_playback.is_valid()) {
- // TODO make sure this doesn't change any behavior w.r.t. pauses. Is a paused stream active?
- return AudioServer::get_singleton()->is_playback_active(stream_playback);
- }
- return false;
+ return active.is_set();
}
void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const {
@@ -336,21 +349,35 @@ uint32_t AudioStreamPlayer2D::get_area_mask() const {
}
void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
- // TODO this does not have perfect recall, fix that maybe? If the stream isn't set, we can't persist this bool.
- if (stream_playback.is_valid()) {
- AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
+ // TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
}
}
bool AudioStreamPlayer2D::get_stream_paused() const {
- if (stream_playback.is_valid()) {
- return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+ // There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
+ if (!stream_playbacks.is_empty()) {
+ return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
}
return false;
}
Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
- return stream_playback;
+ if (!stream_playbacks.is_empty()) {
+ return stream_playbacks[stream_playbacks.size() - 1];
+ }
+ return nullptr;
+}
+
+void AudioStreamPlayer2D::set_max_polyphony(int p_max_polyphony) {
+ if (p_max_polyphony > 0) {
+ max_polyphony = p_max_polyphony;
+ }
+}
+
+int AudioStreamPlayer2D::get_max_polyphony() const {
+ return max_polyphony;
}
void AudioStreamPlayer2D::_bind_methods() {
@@ -391,6 +418,9 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused);
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused);
+ ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer2D::set_max_polyphony);
+ ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer2D::get_max_polyphony);
+
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -401,6 +431,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "1,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 6428fbe017..5360fd4934 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -51,10 +51,10 @@ private:
Viewport *viewport = nullptr; //pointer only used for reference to previous mix
};
- Ref<AudioStreamPlayback> stream_playback;
+ Vector<Ref<AudioStreamPlayback>> stream_playbacks;
Ref<AudioStream> stream;
- SafeFlag active;
+ SafeFlag active{ false };
SafeNumeric<float> setplay{ -1.0 };
Vector<AudioFrame> volume_vector;
@@ -64,7 +64,8 @@ private:
float volume_db = 0.0;
float pitch_scale = 1.0;
bool autoplay = false;
- StringName default_bus = "Master";
+ StringName default_bus = SNAME("Master");
+ int max_polyphony = 1;
void _set_playing(bool p_enable);
bool _is_active() const;
@@ -119,6 +120,9 @@ public:
void set_stream_paused(bool p_pause);
bool get_stream_paused() const;
+ void set_max_polyphony(int p_max_polyphony);
+ int get_max_polyphony() const;
+
Ref<AudioStreamPlayback> get_stream_playback();
AudioStreamPlayer2D();
diff --git a/scene/2d/listener_2d.cpp b/scene/2d/listener_2d.cpp
new file mode 100644
index 0000000000..444f05f2b1
--- /dev/null
+++ b/scene/2d/listener_2d.cpp
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* listener_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "listener_2d.h"
+
+bool Listener2D::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "current") {
+ if (p_value.operator bool()) {
+ make_current();
+ } else {
+ clear_current();
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool Listener2D::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "current") {
+ if (is_inside_tree() && get_tree()->is_node_being_edited(this)) {
+ r_ret = current;
+ } else {
+ r_ret = is_current();
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+void Listener2D::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "current"));
+}
+
+void Listener2D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (!get_tree()->is_node_being_edited(this) && current) {
+ make_current();
+ }
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ if (!get_tree()->is_node_being_edited(this)) {
+ if (is_current()) {
+ clear_current();
+ current = true; // Keep it true.
+ } else {
+ current = false;
+ }
+ }
+ } break;
+ }
+}
+
+void Listener2D::make_current() {
+ current = true;
+ if (!is_inside_tree()) {
+ return;
+ }
+ get_viewport()->_listener_2d_set(this);
+}
+
+void Listener2D::clear_current() {
+ current = false;
+ if (!is_inside_tree()) {
+ return;
+ }
+ get_viewport()->_listener_2d_remove(this);
+}
+
+bool Listener2D::is_current() const {
+ if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) {
+ return get_viewport()->get_listener_2d() == this;
+ } else {
+ return current;
+ }
+ return false;
+}
+
+void Listener2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("make_current"), &Listener2D::make_current);
+ ClassDB::bind_method(D_METHOD("clear_current"), &Listener2D::clear_current);
+ ClassDB::bind_method(D_METHOD("is_current"), &Listener2D::is_current);
+}
diff --git a/scene/2d/listener_2d.h b/scene/2d/listener_2d.h
new file mode 100644
index 0000000000..0289a8087d
--- /dev/null
+++ b/scene/2d/listener_2d.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* listener_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef LISTENER_2D_H
+#define LISTENER_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/main/window.h"
+
+class Listener2D : public Node2D {
+ GDCLASS(Listener2D, Node2D);
+
+private:
+ bool current = false;
+
+ friend class Viewport;
+
+protected:
+ void _update_listener();
+
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+public:
+ void make_current();
+ void clear_current();
+ bool is_current() const;
+};
+
+#endif
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 3518d434c3..30f012c7aa 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -165,21 +165,13 @@ void PhysicsBody2D::remove_collision_exception_with(Node *p_node) {
void StaticBody2D::set_constant_linear_velocity(const Vector2 &p_vel) {
constant_linear_velocity = p_vel;
- if (kinematic_motion) {
- _update_kinematic_motion();
- } else {
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity);
- }
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity);
}
void StaticBody2D::set_constant_angular_velocity(real_t p_vel) {
constant_angular_velocity = p_vel;
- if (kinematic_motion) {
- _update_kinematic_motion();
- } else {
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity);
- }
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity);
}
Vector2 StaticBody2D::get_constant_linear_velocity() const {
@@ -209,62 +201,72 @@ Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const {
return physics_material_override;
}
-void StaticBody2D::set_kinematic_motion_enabled(bool p_enabled) {
- if (p_enabled == kinematic_motion) {
- return;
- }
-
- kinematic_motion = p_enabled;
+void StaticBody2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody2D::set_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity);
- if (kinematic_motion) {
- set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC);
- } else {
- set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
- }
+ ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override);
+ ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override);
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
- return;
- }
-#endif
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
+}
- _update_kinematic_motion();
+StaticBody2D::StaticBody2D(PhysicsServer2D::BodyMode p_mode) :
+ PhysicsBody2D(p_mode) {
}
-bool StaticBody2D::is_kinematic_motion_enabled() const {
- return kinematic_motion;
+void StaticBody2D::_reload_physics_characteristics() {
+ if (physics_material_override.is_null()) {
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, 1);
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce());
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, physics_material_override->computed_friction());
+ }
}
-void StaticBody2D::set_sync_to_physics(bool p_enable) {
+void AnimatableBody2D::set_sync_to_physics(bool p_enable) {
if (sync_to_physics == p_enable) {
return;
}
sync_to_physics = p_enable;
+ _update_kinematic_motion();
+}
+
+bool AnimatableBody2D::is_sync_to_physics_enabled() const {
+ return sync_to_physics;
+}
+
+void AnimatableBody2D::_update_kinematic_motion() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- update_configuration_warnings();
return;
}
#endif
- if (kinematic_motion) {
- _update_kinematic_motion();
+ if (sync_to_physics) {
+ PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
+ set_only_update_transform_changes(true);
+ set_notify_local_transform(true);
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr);
+ set_only_update_transform_changes(false);
+ set_notify_local_transform(false);
}
}
-bool StaticBody2D::is_sync_to_physics_enabled() const {
- return sync_to_physics;
-}
-
-void StaticBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) {
- StaticBody2D *body = (StaticBody2D *)p_instance;
+void AnimatableBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) {
+ AnimatableBody2D *body = (AnimatableBody2D *)p_instance;
body->_body_state_changed(p_state);
}
-void StaticBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
+void AnimatableBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
if (!sync_to_physics) {
return;
}
@@ -275,17 +277,7 @@ void StaticBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
set_notify_local_transform(true);
}
-TypedArray<String> StaticBody2D::get_configuration_warnings() const {
- TypedArray<String> warnings = PhysicsBody2D::get_configuration_warnings();
-
- if (sync_to_physics && !kinematic_motion) {
- warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled."));
- }
-
- return warnings;
-}
-
-void StaticBody2D::_notification(int p_what) {
+void AnimatableBody2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
last_valid_transform = get_global_transform();
@@ -295,10 +287,6 @@ void StaticBody2D::_notification(int p_what) {
// Used by sync to physics, send the new transform to the physics...
Transform2D new_transform = get_global_transform();
- double delta_time = get_physics_process_delta_time();
- new_transform.translate(constant_linear_velocity * delta_time);
- new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
-
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
// ... but then revert changes.
@@ -306,98 +294,19 @@ void StaticBody2D::_notification(int p_what) {
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
} break;
-
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
-
- ERR_FAIL_COND(!kinematic_motion);
-
- Transform2D new_transform = get_global_transform();
-
- double delta_time = get_physics_process_delta_time();
- new_transform.translate(constant_linear_velocity * delta_time);
- new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
-
- if (sync_to_physics) {
- // Propagate transform change to node.
- set_global_transform(new_transform);
- } else {
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
-
- // Propagate transform change to node.
- set_block_transform_notify(true);
- set_global_transform(new_transform);
- set_block_transform_notify(false);
- }
- } break;
}
}
-void StaticBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody2D::set_constant_linear_velocity);
- ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity);
- ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity);
- ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity);
-
- ClassDB::bind_method(D_METHOD("set_kinematic_motion_enabled", "enabled"), &StaticBody2D::set_kinematic_motion_enabled);
- ClassDB::bind_method(D_METHOD("is_kinematic_motion_enabled"), &StaticBody2D::is_kinematic_motion_enabled);
-
- ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override);
- ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override);
-
- ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody2D::set_sync_to_physics);
- ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody2D::is_sync_to_physics_enabled);
+void AnimatableBody2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &AnimatableBody2D::set_sync_to_physics);
+ ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &AnimatableBody2D::is_sync_to_physics_enabled);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
}
-StaticBody2D::StaticBody2D() :
- PhysicsBody2D(PhysicsServer2D::BODY_MODE_STATIC) {
-}
-
-void StaticBody2D::_reload_physics_characteristics() {
- if (physics_material_override.is_null()) {
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, 1);
- } else {
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce());
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_FRICTION, physics_material_override->computed_friction());
- }
-}
-
-void StaticBody2D::_update_kinematic_motion() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-#endif
-
- if (kinematic_motion && sync_to_physics) {
- PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
- set_only_update_transform_changes(true);
- set_notify_local_transform(true);
- } else {
- PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr);
- set_only_update_transform_changes(false);
- set_notify_local_transform(false);
- }
-
- bool needs_physics_process = false;
- if (kinematic_motion) {
- if (!Math::is_zero_approx(constant_angular_velocity) || !constant_linear_velocity.is_equal_approx(Vector2())) {
- needs_physics_process = true;
- }
- }
-
- set_physics_process_internal(needs_physics_process);
+AnimatableBody2D::AnimatableBody2D() :
+ StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) {
+ _update_kinematic_motion();
}
void RigidBody2D::_body_enter_tree(ObjectID p_id) {
@@ -651,11 +560,53 @@ real_t RigidBody2D::get_mass() const {
void RigidBody2D::set_inertia(real_t p_inertia) {
ERR_FAIL_COND(p_inertia < 0);
- PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, p_inertia);
+ inertia = p_inertia;
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia);
}
real_t RigidBody2D::get_inertia() const {
- return PhysicsServer2D::get_singleton()->body_get_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA);
+ return inertia;
+}
+
+void RigidBody2D::set_center_of_mass_mode(CenterOfMassMode p_mode) {
+ if (center_of_mass_mode == p_mode) {
+ return;
+ }
+
+ center_of_mass_mode = p_mode;
+
+ switch (center_of_mass_mode) {
+ case CENTER_OF_MASS_MODE_AUTO: {
+ center_of_mass = Vector2();
+ PhysicsServer2D::get_singleton()->body_reset_mass_properties(get_rid());
+ if (inertia != 0.0) {
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_INERTIA, inertia);
+ }
+ } break;
+
+ case CENTER_OF_MASS_MODE_CUSTOM: {
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass);
+ } break;
+ }
+}
+
+RigidBody2D::CenterOfMassMode RigidBody2D::get_center_of_mass_mode() const {
+ return center_of_mass_mode;
+}
+
+void RigidBody2D::set_center_of_mass(const Vector2 &p_center_of_mass) {
+ if (center_of_mass == p_center_of_mass) {
+ return;
+ }
+
+ ERR_FAIL_COND(center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM);
+ center_of_mass = p_center_of_mass;
+
+ PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS, center_of_mass);
+}
+
+const Vector2 &RigidBody2D::get_center_of_mass() const {
+ return center_of_mass;
}
void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
@@ -909,6 +860,12 @@ void RigidBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_inertia"), &RigidBody2D::get_inertia);
ClassDB::bind_method(D_METHOD("set_inertia", "inertia"), &RigidBody2D::set_inertia);
+ ClassDB::bind_method(D_METHOD("set_center_of_mass_mode", "mode"), &RigidBody2D::set_center_of_mass_mode);
+ ClassDB::bind_method(D_METHOD("get_center_of_mass_mode"), &RigidBody2D::get_center_of_mass_mode);
+
+ ClassDB::bind_method(D_METHOD("set_center_of_mass", "center_of_mass"), &RigidBody2D::set_center_of_mass);
+ ClassDB::bind_method(D_METHOD("get_center_of_mass"), &RigidBody2D::get_center_of_mass);
+
ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override);
ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override);
@@ -965,8 +922,11 @@ void RigidBody2D::_bind_methods() {
GDVIRTUAL_BIND(_integrate_forces, "state");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp", PROPERTY_USAGE_NONE), "set_inertia", "get_inertia");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass", PROPERTY_HINT_RANGE, "-10,10,0.01,or_lesser,or_greater"), "set_center_of_mass", "get_center_of_mass");
+ ADD_LINKED_PROPERTY("center_of_mass_mode", "center_of_mass");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator");
@@ -996,11 +956,22 @@ void RigidBody2D::_bind_methods() {
BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED);
BIND_ENUM_CONSTANT(MODE_KINEMATIC);
+ BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO);
+ BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM);
+
BIND_ENUM_CONSTANT(CCD_MODE_DISABLED);
BIND_ENUM_CONSTANT(CCD_MODE_CAST_RAY);
BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE);
}
+void RigidBody2D::_validate_property(PropertyInfo &property) const {
+ if (center_of_mass_mode != CENTER_OF_MASS_MODE_CUSTOM) {
+ if (property.name == "center_of_mass") {
+ property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ }
+ }
+}
+
RigidBody2D::RigidBody2D() :
PhysicsBody2D(PhysicsServer2D::BODY_MODE_DYNAMIC) {
PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 1bf53ea53c..1d6437a3ad 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -63,21 +63,13 @@ public:
class StaticBody2D : public PhysicsBody2D {
GDCLASS(StaticBody2D, PhysicsBody2D);
+private:
Vector2 constant_linear_velocity;
real_t constant_angular_velocity = 0.0;
Ref<PhysicsMaterial> physics_material_override;
- bool kinematic_motion = false;
- bool sync_to_physics = false;
-
- Transform2D last_valid_transform;
-
- static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state);
- void _body_state_changed(PhysicsDirectBodyState2D *p_state);
-
protected:
- void _notification(int p_what);
static void _bind_methods();
public:
@@ -90,17 +82,32 @@ public:
Vector2 get_constant_linear_velocity() const;
real_t get_constant_angular_velocity() const;
- virtual TypedArray<String> get_configuration_warnings() const override;
-
- StaticBody2D();
+ StaticBody2D(PhysicsServer2D::BodyMode p_mode = PhysicsServer2D::BODY_MODE_STATIC);
private:
void _reload_physics_characteristics();
+};
- void _update_kinematic_motion();
+class AnimatableBody2D : public StaticBody2D {
+ GDCLASS(AnimatableBody2D, StaticBody2D);
+
+private:
+ bool sync_to_physics = false;
+
+ Transform2D last_valid_transform;
- void set_kinematic_motion_enabled(bool p_enabled);
- bool is_kinematic_motion_enabled() const;
+ static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state);
+ void _body_state_changed(PhysicsDirectBodyState2D *p_state);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ AnimatableBody2D();
+
+private:
+ void _update_kinematic_motion();
void set_sync_to_physics(bool p_enable);
bool is_sync_to_physics_enabled() const;
@@ -117,6 +124,11 @@ public:
MODE_KINEMATIC,
};
+ enum CenterOfMassMode {
+ CENTER_OF_MASS_MODE_AUTO,
+ CENTER_OF_MASS_MODE_CUSTOM,
+ };
+
enum CCDMode {
CCD_MODE_DISABLED,
CCD_MODE_CAST_RAY,
@@ -128,6 +140,10 @@ private:
Mode mode = MODE_DYNAMIC;
real_t mass = 1.0;
+ real_t inertia = 0.0;
+ CenterOfMassMode center_of_mass_mode = CENTER_OF_MASS_MODE_AUTO;
+ Vector2 center_of_mass;
+
Ref<PhysicsMaterial> physics_material_override;
real_t gravity_scale = 1.0;
real_t linear_damp = -1.0;
@@ -191,6 +207,8 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const override;
+
GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *)
public:
@@ -203,6 +221,12 @@ public:
void set_inertia(real_t p_inertia);
real_t get_inertia() const;
+ void set_center_of_mass_mode(CenterOfMassMode p_mode);
+ CenterOfMassMode get_center_of_mass_mode() const;
+
+ void set_center_of_mass(const Vector2 &p_center_of_mass);
+ const Vector2 &get_center_of_mass() const;
+
void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override);
Ref<PhysicsMaterial> get_physics_material_override() const;
@@ -267,6 +291,7 @@ private:
};
VARIANT_ENUM_CAST(RigidBody2D::Mode);
+VARIANT_ENUM_CAST(RigidBody2D::CenterOfMassMode);
VARIANT_ENUM_CAST(RigidBody2D::CCDMode);
class CharacterBody2D : public PhysicsBody2D {
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 13f1d258a8..0eb424b32c 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -236,6 +236,8 @@ Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffset
}
int TileMap::get_effective_quadrant_size(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), 1);
+
// When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant
if (is_y_sort_enabled() && layers[p_layer].y_sort_enabled) {
return 1;
@@ -314,16 +316,38 @@ int TileMap::get_quadrant_size() const {
return quadrant_size;
}
-void TileMap::set_layers_count(int p_layers_count) {
- ERR_FAIL_COND(p_layers_count < 0);
- _clear_internals();
+int TileMap::get_layers_count() const {
+ return layers.size();
+}
+
+void TileMap::add_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = layers.size();
+ }
+
+ ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
+
+ layers.insert(p_to_pos, TileMapLayer());
+ _recreate_internals();
+ notify_property_list_changed();
+
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
+}
+
+void TileMap::move_layer(int p_layer, int p_to_pos) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
- layers.resize(p_layers_count);
+ TileMapLayer tl = layers[p_layer];
+ layers.insert(p_to_pos, tl);
+ layers.remove(p_to_pos < p_layer ? p_layer + 1 : p_layer);
_recreate_internals();
notify_property_list_changed();
- if (selected_layer >= p_layers_count) {
- selected_layer = -1;
+ if (selected_layer == p_layer) {
+ selected_layer = p_to_pos < p_layer ? p_to_pos - 1 : p_to_pos;
}
emit_signal(SNAME("changed"));
@@ -331,8 +355,20 @@ void TileMap::set_layers_count(int p_layers_count) {
update_configuration_warnings();
}
-int TileMap::get_layers_count() const {
- return layers.size();
+void TileMap::remove_layer(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+
+ layers.remove(p_layer);
+ _recreate_internals();
+ notify_property_list_changed();
+
+ if (selected_layer >= p_layer) {
+ selected_layer -= 1;
+ }
+
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
}
void TileMap::set_layer_name(int p_layer, String p_name) {
@@ -781,7 +817,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
if (atlas_source) {
// Get the tile data.
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
- Ref<ShaderMaterial> mat = tile_data->tile_get_material();
+ Ref<ShaderMaterial> mat = tile_data->get_material();
int z_index = tile_data->get_z_index();
// Quandrant pos.
@@ -1147,7 +1183,7 @@ void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) {
PhysicsServer2D::get_singleton()->body_set_space(body, space);
Transform2D xform;
- xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(layer)));
+ xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)));
xform = global_transform * xform;
PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
}
@@ -2896,8 +2932,10 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size);
ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size);
- ClassDB::bind_method(D_METHOD("set_layers_count", "layers_count"), &TileMap::set_layers_count);
ClassDB::bind_method(D_METHOD("get_layers_count"), &TileMap::get_layers_count);
+ ClassDB::bind_method(D_METHOD("add_layer", "to_position"), &TileMap::add_layer);
+ ClassDB::bind_method(D_METHOD("move_layer", "layer", "to_position"), &TileMap::move_layer);
+ ClassDB::bind_method(D_METHOD("remove_layer", "layer"), &TileMap::remove_layer);
ClassDB::bind_method(D_METHOD("set_layer_name", "layer", "name"), &TileMap::set_layer_name);
ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name);
ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled);
@@ -2907,7 +2945,7 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer_y_sort_origin", "layer", "y_sort_origin"), &TileMap::set_layer_y_sort_origin);
ClassDB::bind_method(D_METHOD("get_layer_y_sort_origin", "layer"), &TileMap::get_layer_y_sort_origin);
ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index);
- ClassDB::bind_method(D_METHOD("get_layer_z_indexd", "layer"), &TileMap::get_layer_z_index);
+ ClassDB::bind_method(D_METHOD("get_layer_z_index", "layer"), &TileMap::get_layer_z_index);
ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "collision_visibility_mode"), &TileMap::set_collision_visibility_mode);
ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode);
@@ -2942,9 +2980,7 @@ void TileMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode");
- ADD_GROUP("Layers", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layers_count"), "set_layers_count", "get_layers_count");
- ADD_PROPERTY_DEFAULT("layers_count", 1);
+ ADD_ARRAY("layers", "layer_");
ADD_PROPERTY_DEFAULT("format", FORMAT_1);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 4e2d76a7b7..3ac50fc7cc 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -308,8 +308,10 @@ public:
static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0));
// Layers management.
- void set_layers_count(int p_layers_count);
int get_layers_count() const;
+ void add_layer(int p_to_pos);
+ void move_layer(int p_layer, int p_to_pos);
+ void remove_layer(int p_layer);
void set_layer_name(int p_layer, String p_name);
String get_layer_name(int p_layer) const;
void set_layer_enabled(int p_layer, bool p_visible);