how to make a box slide system?
Asked Answered
L

5

0

I'm making a game for a game jam and I'm trying to do those box sliding puzzle games, the problem is, because of godot's safe margin, the body of the box will get stuck in pixel perfect holes, so i decided to make the collision of the box 1 pixel smaller and use a ray cast so it can stop when it collides with a wall

and then everything fell appart

the ray cast is innacurate, if it moves for long enough (around 1-2 seconds) it will move 1-2 pixels extra and misalign with the tile set, since it's a sliding box game, it shouldn't really misalign (and it makes the game look kinda ugly)

after that, I went to the discord of the jam and asked for some help, they said for me to use the available physics systems, so I decided to try and change from a raycast to just an area2D, but because of the fact that the function for the Area2D only goes once, now it'll get stuck in walls eternally moving if you try to move twice to the same direction, into a wall. I tried to stop the monitoring when the area2D touches the wall, but it caused the box to either: 1: only move once and never again or 2: move like a normal topdown game

I have no idea how to fix this and there's 5 days left on the jam, I really need this bug fixed, I'll leave the code for both the raycast box and the area2d box

#raycast body:

const SPEED = 300
var schmooving:bool = false
var selected:bool = false
@onready var ray:RayCast2D = get_node("ray")

func _physics_process(delta):
	if not schmooving and selected:
		ray.enabled = true
		var frontdir = Input.get_axis("left","right")
		var sidedir = Input.get_axis("up","down")
		if sidedir:
			ray.target_position = Vector2(0,9)*sidedir
			velocity.y = SPEED*sidedir
			schmooving = true
		elif frontdir:
			ray.target_position = Vector2(9,0)*frontdir
			velocity.x = SPEED*frontdir
			schmooving = true

	if ray.is_colliding():
		velocity = Vector2.ZERO
		schmooving = false
	move_and_slide()

#area2d body:

extends CharacterBody2D


const SPEED = 300

var schmooving:bool = false


@onready var vision:Area2D = get_node("Vision")
@onready var visionCol: CollisionShape2D = get_node("Vision/CollisionShape2D")

func _physics_process(delta):
	if not schmooving and selected:
		vision.monitoring = true
		var frontdir = Input.get_axis("left","right")
		var sidedir = Input.get_axis("up","down")
		if sidedir:
			visionCol.shape.b = Vector2(0,9)*sidedir
			velocity.y += SPEED*sidedir
			schmooving = true
		elif frontdir:
			visionCol.shape.b = Vector2(9,0)*frontdir
			velocity.x += SPEED*frontdir
			schmooving = true
	move_and_slide()

func _on_vision_area_entered(area):
	velocity = Vector2(0,0)
	schmooving = false` 

one thing to note is that, when I change the speed of the raycast box, the collisions work differently, on 100 speed, it works just like I want it to, on 50 or 150 it works, but the sprite flickers and distorts when standing still, on 200 it already stops working again, please help

edit: forgot to say it, but the box needs to keep moving untill it hits a wall

Lyingin answered 10/1 at 0:40 Comment(0)
L
0

Lyingin I'm trying to format it to be on code blocks, it just won't work, sorry

Place ~~~ on lines before and after the code.

Since you're short of time, you could try using a non-physics method. Detect the cursor position and reposition the box to a preset position, without using collisions or physics.

Limbourg answered 10/1 at 1:15 Comment(0)
L
0

Limbourg I'm trying to make the box slide, so teleporting it to the point wouldn't really work

also thanks for the formatting help, now it looks a lot better

also also I changed the physics tics per second rate to 120 and now the y axis works perfectly, but not the x, which is misaligned by one pixel to the left, and the image of the box changing stopped, I was using an tilemap and setting it's coords in the sprite2d, but changed to just an image and now it works fine

Lyingin answered 10/1 at 1:17 Comment(0)
C
0

This seems like an ideal use case for tweening from its position to the desired end position. No _physics_process-ing required. e.g.

var tween = create_tween()
tween.tween_property(box, "position", <your_end_position_here>, 1)

Will move box to <your_end_position> over 1 second. Trigger the tween when desired, calculate the end position (i.e. where it should hit the wall), let the tween do all the work. I feel using physics is overkill!

Chura answered 10/1 at 14:40 Comment(0)
L
0

Chura well that's what I'm doing now! after a little while i noticed that using physics for grid based movement is kinda not ideal at all, so I'm now calculating the destination using the tilemap, but, when I use the tween to move instead of simply teleporting to the location, the tween makes the box move 1 square over the intended distance, I'm trying to fix this now

I'll leave the code here, if you can help me with this I'll be really gratefull

extends CharacterBody2D

enum dir {NULL,X,Y}


var schmooving:bool = false
var selected:bool = false
var curdir:int
var dire = dir.NULL
@onready var ray:RayCast2D = get_node("ray")
@onready var tilemap:TileMap = get_parent().get_node("objects")

func _physics_process(delta):
	#move
	var tween:Tween = create_tween()
	if schmooving:
		match dire:
			dir.Y:
				ray.target_position = Vector2(0,9)*curdir
			dir.X:
				ray.target_position = Vector2(9,0)*curdir
		ray.force_raycast_update()
		if ray.is_colliding():
			schmooving = false
			ray.target_position = Vector2(0,0)
			dire = dir.NULL
			tween.kill()
		elif !ray.is_colliding() and dire == dir.Y:
			var current_tile: Vector2i = tilemap.local_to_map(position)
			var next_tile: Vector2i = current_tile + Vector2i.DOWN*Vector2i(curdir,curdir)
			var new_position: Vector2 = tilemap.map_to_local(next_tile)
			tween.tween_property(self,"position",new_position,1)
		elif !ray.is_colliding() and dire == dir.X:
			var current_tile: Vector2i = tilemap.local_to_map(position)
			var next_tile: Vector2i = current_tile + Vector2i.RIGHT*Vector2i(curdir,curdir)
			var new_position: Vector2 = tilemap.map_to_local(next_tile)
			tween.tween_property(self,"position",new_position,1)


func _on_area_2d_input_event(viewport, event, shape_idx):
	#select (click the box)
	if (event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT):
		if not selected:
			selected = true

func _input(event):
	#deselect (click nywhere BUT the box)
	if (event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT):
		var evLocal = make_input_local(event)
		if !Rect2($FilColS/CollisionShape2D.global_position,$FilColS/CollisionShape2D.shape.size).has_point(evLocal.position) and selected:
				selected = false
	
	#get current direction based on key inputs
	if event is InputEventKey and not schmooving:
		var frontdir = Input.get_axis("left","right")
		var sidedir = Input.get_axis("up","down")
		if frontdir and selected:
			dire = dir.X
			curdir = frontdir
			schmooving = true
		elif sidedir and selected:
			dire = dir.Y
			curdir = sidedir
			schmooving = true

update: did some tests and it seems that for that last movement the part of the process function that calculates the movement is not running, so therefore the tween shouldn't even be animating, since it hasn't been called to do that, any ideas why that happens?

update 2: and for some reason if I tween the box the raycast can't see the objective, but if I simply change its position, it does...
why are tween so annoying to use? they just break stuff and cause a million errors, good lord

update 3: actually, it does see the objective, but it happens before the objective checks to see if it is touching, so it considers itself not touching anything? I swear to god tweens are ruining me

Lyingin answered 10/1 at 16:53 Comment(0)
A
1

Lyingin If this is just sliding boxes, you shouldn't burden the system with any of physics and collision stuff, including raycasts. Simply calculate target positions and tween to them.

Adherent answered 10/1 at 19:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.