Help with moving sprites together

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By sugarbrace901

Hey all,

I’m fairly new to Godot, but I’ve been programming games for a bit. I understand GDScript, but using it fully still escapes me. So i’m still learning/struggling. I moved over from python/pygame for obvious reasons.

Anyway, I’m working on a sokoban style puzzle game (grid based movement) and I want blocks that have moved next each other to ‘combine’ and move together. Kinda like legos. I’m currently using raycast2D to detect collision between sprites, and it has been successful, but I receive an error when the sprite being moved collides with a wall (object in tilemap).

This is the error: Invalid get index ‘position’ (on base: ‘Node’).

To track the position i’m essentially grabbing colliding sprite and saving it once in a variable. Then I match the collided sprites position to that of the one that is in ‘control’ or allowed to move, but again I receive the error when the control sprite reaches a collision.

The idea behind control is that i’ll have multiple instances of a sprite and have the ability to shuffle control between the various sprites in a scene.

Here is the sprite script:

extends Sprite

# if the sprite is currently in control, can be rotated with space
export var Control = false

# make the raycast available to use in the player script
onready var ray = $Area2D/RayCast2D
onready var rayUP = $Area2D/rayUP
onready var rayDOWN = $Area2D/rayDOWN
onready var rayLEFT = $Area2D/rayLEFT
onready var rayRIGHT = $Area2D/rayRIGHT

# detect collision only once with these flags
var colUP = false
var colDOWN = false
var colLEFT = false
var colRIGHT = false

var UColSprite
var DColSprite
var LColSprite
var RColSprite

var speed = 600 # large speed value to determine how fast the sprite will move
var tile_size = 64 # size in pixels of the tiles on the grid

var last_position = position # last idle position to keep track of
var target_position = position # new position to be moved to
var movedir = Vector2(0, 0) # the move direction 

# init function
func _ready():
	# snap the player to a grid position if misplaced
	# and update its position trackers to the snapped vector2
	position = position.snapped(Vector2(tile_size, tile_size))
	last_position = position
	target_position = position
# set move direction based on player input
func get_movedir():
	# collect the input and store it
	var LEFT = Input.is_action_pressed("ui_left")
	var RIGHT = Input.is_action_pressed("ui_right")
	var UP = Input.is_action_pressed("ui_up")
	var DOWN = Input.is_action_pressed("ui_down")
	# process the movement based on each vector
	movedir.x = -int(LEFT) + int(RIGHT) # is negative one if moving left and vice versa, if both then zero
	movedir.y = -int(UP) + int(DOWN) # same up with y
	# prevent diagonal movement
	if movedir.x !=0 && movedir.y != 0:
		movedir = Vector2.ZERO # no value
	# set the end of the ray cast to the edge of the player sprite
	if movedir != Vector2.ZERO:
		ray.cast_to = movedir * tile_size / 2
# process movement every frame
func _process(delta):
	if Control == true:
		# idle
		if position == target_position:
			last_position = position # save the players current position
			target_position += movedir * tile_size # if movement, then calculate new target position
		# moving
			# make sure not to move the player if they will collide with a wall
			if ray.is_colliding():
				target_position = last_position # set the player back to there original location
				# update the position
				position += speed * movedir * delta
				# prevent the player from moving past the target
				var distance = (position - last_position).abs().length() # how far they moved frem the idle position
				# track the distance
				if distance > tile_size - speed * delta:
					# subtract speed * delta so there's no break/pause in movement
					position = target_position
		# if the sprite is not being controlled, collect a collision if the controlled sprite moved next to it6
		if rayUP.is_colliding():
			if colUP == false:
				colUP = true
				UColSprite = rayUP.get_collider().get_parent()
			position = UColSprite.position - Vector2(0, 64)
		if rayDOWN.is_colliding():
			if colDOWN == false:
				colDOWN = true
				DColSprite = rayDOWN.get_collider().get_parent()
			position = DColSprite.position + Vector2(0, 64)
		if rayLEFT.is_colliding():
			if colLEFT == false:
				colLEFT = true
				LColSprite = rayLEFT.get_collider().get_parent()
			position = LColSprite.position + Vector2(64, 0)
		if rayRIGHT.is_colliding():
			if colRIGHT == false:
				colRIGHT = true
				RColSprite = rayRIGHT.get_collider().get_parent()
			position = RColSprite.position - Vector2(64, 0)
:bust_in_silhouette: Reply From: flurick

You can avoid this specific error by converting the node that contains your walls to Node2D. The class “node” is too general to have a variable for “position” its only at the level of Node2D and after that the concept of space exists.
(Sprite < Node2D < CanvasItem < Node < Object)

However assuming you have walls placed in the scene to test, that parent (with the type Node) will just move the player to the same position which ever wall it collides with? Project files needed for further details.


I figured it out based on your response.

The Tilemap within my level scene tree was reporting collision to the root of the scene, which like you said has no “position”.

I just had to turn collision with bodies off within the raycast2D nodes that I have attached to my sprite to stop the error from occurring!

sugarbrace901 | 2019-03-31 16:53