How to drag a physics body with touch?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By HahBikes
:warning: Old Version Published before Godot 3 was released.

I want to be able to touch a physics body and drag it around the screen.

How do I do it?

Please Help … I m new to this engine.

:bust_in_silhouette: Reply From: Nichefect

You want to move the body around in the scene editor, or while running the game?

I want to move the body around while running the game…like this :

https://www.youtube.com/watch?v=1kcbjcAdEQM

HahBikes | 2016-04-23 09:21

:bust_in_silhouette: Reply From: CowThing

The way I’ve done it is using a RigidBody2D, I have a script on the RigidBody2D.

In the Inspector tab for the RigidBody2D set CollisionObject2D -> Input -> Pickable as true.
Then make a connection for the signal input_event on the RigidBody2D
If you made the connection through the Scene tab in the editor it should make a function in the script with something like this:

func _on_RigidBody2D_input_event( viewport, event, shape_idx ):
	pass

With this you’ll be able to get input events only when they are clicked on the RigidBody2D.
What I do is have a var that checks if the object is being held or not.

var is_held = false
func _on_RigidBody2D_input_event( viewport, event, shape_idx ):
	if event.type == InputEvent.MOUSE_BUTTON and event.pressed and event.button_index == BUTTON_LEFT:
		is_held = true

And to release the RigidBody2D we will need to use the normal input event.

var is_held = false
func _ready():
	set_process_input(true)

func _input(event):
	if event.type == InputEvent.MOUSE_BUTTON and not event.pressed and event.button_index == BUTTON_LEFT:
		is_held = false

func _on_RigidBody2D_input_event( viewport, event, shape_idx ):
	if event.type == InputEvent.MOUSE_BUTTON and event.pressed and event.button_index == BUTTON_LEFT:
		is_held = true

Now to make the object actually move. Since it’s a RigidBody2D we can use the _integrate_forces() function.

var is_held = false
func _ready():
	set_process_input(true)

func _integrate_forces(state):
	var lv = state.get_linear_velocity()
	
	if is_held:
		lv = (get_viewport().get_mouse_pos() - get_pos()) * 16
	
	state.set_linear_velocity(lv)

func _input(event):
	if event.type == InputEvent.MOUSE_BUTTON and not event.pressed and event.button_index == BUTTON_LEFT:
		is_held = false

func _on_RigidBody2D_input_event( viewport, event, shape_idx ):
	if event.type == InputEvent.MOUSE_BUTTON and event.pressed and event.button_index == BUTTON_LEFT:
		is_held = true

And that should do it. The Object will be picked up when you click and hold on to it. And it will follow your mouse around until you let go.

You can see an example usage of this in a game I made for a jam, here: Pet Rock

I tried to do exactly the same thing but I can not access to

func_on_RigidBody2D_input_event( viewport, event, shape_idx ):  

any help ?

huseng | 2016-07-28 10:00

doesnt make any sense to me,
event.pressed ?
event.button_index
.
it is not inside ImputEvent
InputEvent — Godot Engine (stable) documentation in English
.
is this another version of gdscript ? what i did wrong ?

marcus vinicius | 2018-01-25 00:28

Hey Marcus,
I have posted an updated answer here for Godot 3.
Check it out if you still have any problems!

Dlean Jeans | 2018-01-30 16:24

:bust_in_silhouette: Reply From: Dlean Jeans

UPDATED: This should work in 3.1.1

Since CowThing’s answer seems to be from the days of old Godot 2 and no longer compatible with Godot 3, I have rewritten it:

  1. Create a RigidBody2D
  2. In the Inspector tab: Enable CollisionObject2D > Pickable
  3. Add this to your script:
    extends RigidBody2D

    export var mouse_drag_scale = 20
    var _is_picked = false

    func _input_event(viewport, event, shape_idx):
    	if _event_is_left_click(event): # check if left click is pressed on the body
    		_is_picked = event.pressed

    func _input(event): # check if left click is released even outside of the body
        if _event_is_left_click(event) and not event.pressed:
            _is_picked = false

    func _event_is_left_click(event):
        return event is InputEventMouseButton and event.button_index == BUTTON_LEFT

    func _integrate_forces(state):
    	if _is_picked:
    		state.linear_velocity = get_global_mouse_position() - global_position
	    		state.linear_velocity *= mouse_drag_scale

Thanks!

For anyone else reading, I also had to uncheck “can sleep” before it worked for me.

AlwaysDan | 2021-01-21 19:49