rotation_degrees by 90 not always whole number

78 views Asked by At

I decided to learn godot and started with a tetris clone to learn on. Everything is going fine except that every once in a while when I rotate a Node2d object, it doesn't set as a whole number. Then when I check each box that makes up a tetromino, I get an intersection because it is just barely...just slightly askew.

Looking for any ideas on how to stop this behavior. I tried forcing by only allowing ints to be added/subtracted but that didn't work

the code is very simple. The variables: boundary => passes in a Rect2 defining the play area playedblocks => an array of individual blocks that have been set from previous pieces get_boundary() => returns the piece's Rect2 based on the individual block positions that make up the tetromino piece

func rotate_piece(boundary,playedblocks):
if(!movable):return
color_blocks(Color.RED)
self.rotation_degrees += int(90)
var isenclosed = boundary.encloses(get_boundary())
var doesintersect = IntersectsPlayedPieces(playedblocks)
if(!isenclosed || doesintersect):
    self.rotation_degrees -= int(90)

func IntersectsPlayedPieces(playedblocks)-> bool:
var intersects = false
for setblock in playedblocks:       
    if(!setblock.is_deleted()):
        var setrect = setblock.get_rect()
        for currblock in blocks:
            var currrect = currblock.get_rect()
            if(setrect.intersects(currrect,false)):
                intersects = true
return intersects

*edited examples and expounding on findings:

I set the rotation exclusively to be one of 0,90,180,270 and I am now no longer seeing the weird rotation values on the piece. I am seeing weird positional values of the child blocks if the piece is rotated. I added labels on to the blocks so I can see the positional values of each block.

I cleared 12 lines without turning a block to see if it was the movement down function of the blocks that was creating an error. It was not.

However on the 3rd piece when I started rotating pieces, the block highlighted landed with a slightly off x positional value. There is something going on behind the scenes that slightly alters whole number values of positional data after a rotation.

I do have a rotation correction for each block to keep it upright when the parent node is being rotated.

rotation correction code:

func _process(delta):  
var parent_rotation = get_parent().rotation 
set_rotation(-parent_rotation)
self.rotation_degrees = int(floor(self.rotation_degrees))
var text = str("x: ",self.position.x,"\ny: ", self.position.y)
label.text = text

pic of rotated piece position values after it lands vs. non-rotated pieces: enter image description here

2

There are 2 answers

0
Bugfish On

Because it helped with the problem, I add my idea as an answer, without knowing the exact problem with the underlying math of godot.

It seems like there is a rounding problem, even if you substract an integer from the current rotation.

One solution can be:

  1. Add a array with all the possible rotations (for example [0,90,180,270] ) and an index for each element.
  2. On rotation add or substract 1 from the index and use modulo to stay in between the range of this array and then set the rotation to a fixed value instead of substracting from the current rotation directly.

This could look like this:

var possible_rotations = [0,90,180,270]
var current_rotation = 0
func rotate_piece(boundary,playedblocks):
   if(!movable):return
   var last_rotation = current_rotation
   color_blocks(Color.RED)
   current_rotation = (current_rotation + 1) % possible_rotations.size()
   self.rotation_degrees = possible_rotations[current_rotation]
   var isenclosed = boundary.encloses(get_boundary())
   var doesintersect = IntersectsPlayedPieces(playedblocks)
   if(!isenclosed || doesintersect):
       self.rotation_degrees = possible_rotations[last_rotation]
1
Bulsatar On

Tested a bunch of possible fixes and each one brought the stability further along. The solution ended up being a mix of Bugfish's suggestion of only allowing a certain set of values for rotation and also adding snapped(,10) to all position transformation and all rotation transformations.

Here are a couple of examples of how I utilized both instances. I hope this helps someone else not chase their tail for a couple of days because of a really weird bug somewhere deep in godot...

might make it prettier later. Have to rotate the piece first to know the space it will occupy. Then we check for collisions. Then roll back the rotation if collisions would happen.

func rotate_piece(boundary,playedblocks):
if(!movable):return
color_blocks(Color.RED)
if(self.rotation_degrees == 0): self.rotation_degrees = 90
elif(self.rotation_degrees == 90): self.rotation_degrees = 180
elif(self.rotation_degrees == 180): self.rotation_degrees = 270
elif(self.rotation_degrees == 270): self.rotation_degrees = 0

var isenclosed = boundary.encloses(get_boundary())
var doesintersect = IntersectsPlayedPieces(playedblocks)
if(!isenclosed || doesintersect):
    if(self.rotation_degrees == 0): self.rotation_degrees = 270
    elif(self.rotation_degrees == 90): self.rotation_degrees = 0
    elif(self.rotation_degrees == 180): self.rotation_degrees = 90
    elif(self.rotation_degrees == 270): self.rotation_degrees = 180

counter rotating the blocks that make up the tetromino piece:

func _process(delta):  
var parent_rotation = get_parent().rotation 
set_rotation(-parent_rotation)
self.rotation_degrees = snapped(self.rotation_degrees,10)

self moving the block after a line(s) has been deleted:

func move_down():
for row in moverows:
    var y = self.global_position.y + blocksize
    var x = self.global_position.x 
    self.position = Vector2(snapped(x,10), snapped(y,10))
moverows = 0