0
\$\begingroup\$

So I'm making a falling-block puzzle game. It's not a Tetris clone, but the block clusters move in a similar fashion. I want to make it so that if you rotate against a wall, or against another piece, it pushes you back and rotates the cluster if it's possible, or does nothing if not. This is called wall-kicking in Tetris, I think. Problem is, I can't seem to get it to work. The simplest algorithm I've heard of is to check if it's possible to move left or right. If you can do either, then rotate the block. But then I can't rotate at the edge of the grid.

I have the rotation itself working (both on-screen and the grid position representation), and I can stop a cluster from moving if it's at the edge of the grid. It's rotating at the edges that gets me.

These are the assumptions my game makes;

  • There are many types of individual blocks, so hard-coding anything probably isn't an option.
  • Each block cluster has exactly three connected blocks, with two possible configurations (assuming rotations are considered equal); I-like and J-like.
  • For each block cluster, there is one block in the center that the other two rotate around. Assuming no wall-kicking, this one never moves when the cluster is rotated.

  • The playing field is eight blocks wide and thirteen blocks tall.

  • After the cluster lands, the blocks will fall down Puyo-Puyo style, i.e. you won't have to navigate around hanging pieces like in Tetris.

This is my code, written in Haxe;

class BlockMovementSystem extends System {
    private var cluster    : BlockCluster;
    private var delay      : TimeDelay;
    private var grid       : EntityGrid;
    private var nextAction : NextAction;

    public function new(grid:EntityGrid, cluster:Entity) {
        super();
        this.cluster    = cluster.get(BlockCluster);
        this.delay      = cluster.get(TimeDelay);
        this.grid       = grid;
        this.nextAction = NextAction.None;
    }

    public override function update(time:Float) : Void {
        var center    : Entity     = this.cluster.blocks[1];
        var blockSize : GridMotion = center.get(GridMotion);

        switch (this.nextAction) {
            case MoveLeft:
                if (this.canMoveLeft()) {
                    for (i in this.cluster.blocks) {
                        var gpos : GridPosition = i.get(GridPosition);
                        var block_left : Entity = {
                            if (gpos.x > 0)
                                this.grid.entities.get(gpos.x - 1, gpos.y)
                            else
                                null;
                        };

                        if (gpos.x > 0 || block_left != null) {
                            var pos : Positionable = i.get(Positionable);
                            pos.position.x -= blockSize.cell_size.x;
                            gpos.x -= 1;
                        }
                    }
                }
            case MoveRight:
                if (this.canMoveRight()) {
                    for (i in this.cluster.blocks) {

                        var gpos : GridPosition = i.get(GridPosition);
                        var block_right : Entity = {
                            if (gpos.x < Constants.GRID_WIDTH - 1)
                                this.grid.entities.get(gpos.x + 1, gpos.y)
                            else
                                null;
                        };

                        if (gpos.x < Constants.GRID_WIDTH - 1 && block_right == null) {
                            var pos : Positionable = i.get(Positionable);
                            pos.position.x += blockSize.cell_size.x;
                            gpos.x += 1;
                        }
                    }
                }
            case Rotate:
                if (this.canMoveRight() || this.canMoveLeft()) {
                    var origin : Vector = center.get(Positionable).position;
                    var origincell : GridPosition = center.get(GridPosition);
                    for (i in this.cluster.blocks) {
                        if (i != center) {
                            var p : Vector = i.get(Positionable).position;
                            var x : Float = origin.x - (origin.y - p.y);
                            var y : Float = origin.y - (p.x - origin.x);
                            p.x = x;
                            p.y = y;

                            var g : GridPosition = i.get(GridPosition);
                            var gx : Int = origincell.x - (origincell.y - g.y);
                            var gy : Int = origincell.y - (g.x - origincell.x);
                            g.x = gx;
                            g.y = gy;
                        }
                    }
                }
            }
    }

    private function canMoveLeft() : Bool {
        for (i in this.cluster.blocks) {
            var gpos : GridPosition = i.get(GridPosition);
            var block_left: Entity = {
                if (gpos.x > 0)
                    this.grid.entities.get(gpos.x - 1, gpos.y)
                else
                    null;
            };

            if (gpos.x <= 0 || block_left != null) {
            //If the edge of the grid or another block is to the left of this block...
                return false;
            }
        }

        return true;
    }

    private function canMoveRight() : Bool {
        for (i in this.cluster.blocks) {
            var gpos : GridPosition = i.get(GridPosition);
            var block_right : Entity = {
                if (gpos.x < Constants.GRID_WIDTH - 1)
                    this.grid.entities.get(gpos.x + 1, gpos.y)
                else
                    null;
            };

            if (gpos.x >= Constants.GRID_WIDTH - 1 || block_right != null) {
            //If the edge of the grid or another block is to the right of this block...
                return false;
            }
        }

        return true;
    }
}

Any tips?

\$\endgroup\$
  • \$\begingroup\$ That's actually one of the answers I read first before posting this question. Didn't help me. \$\endgroup\$ – JesseTG Aug 23 '13 at 21:17
  • 1
    \$\begingroup\$ Your simplest algorithm does not match with your description of what wall-kicking is. It seems that you should always rotate, detect out-of-bounds-ness, and move your piece away if it is out of bounds. What have you tried? Some code posting would help. \$\endgroup\$ – Seth Battin Aug 23 '13 at 21:35
  • \$\begingroup\$ @SethBattin Added. \$\endgroup\$ – JesseTG Aug 23 '13 at 21:40
  • \$\begingroup\$ Probably the easiest thing to do will be check each of the pieces edges and make sure that they are between the two walls. If they are not just snap them over the distance between the edge and the wall. You could also do the same check on vertices if you wanted to make a smoother rotation and have the piece push off the wall. \$\endgroup\$ – Benjamin Danger Johnson Aug 23 '13 at 23:28
  • \$\begingroup\$ What happens if I try to rotate a cluster, but there's no space because of other blocks? \$\endgroup\$ – JesseTG Aug 24 '13 at 1:48