Help with Zelda Style puzzle blocks...

Le_Beholder

Member
Hello mercenary person(s); I have money* and a problem.
(*32 monopoly bucks)

I am trying to create puzzle blocks in my game that can be pushed by the player, and only move 16 blocks smoothly at a time.

so far I've managed how to get it to move, and control what direction, what I don't know is how to get it to stop after 16 pixels and make it stop being pushable anymore.
here's my code:
Code:
if (pushright = true && place_meeting(x-2,y,obj_player)&& obj_player.sprite_index = spr_playerR && keyboard_check(vk_right))
{
move_towards_point(x+16,y,2);
pushright = false;
}
if (pushleft = true &&  place_meeting(x+2,y,obj_player)&& obj_player.sprite_index = spr_playerL && keyboard_check(vk_left))
{
move_towards_point(x-16,y,2);
pushleft = false;
}
if (pushup = true &&  place_meeting(x,y+2,obj_player)&& obj_player.sprite_index = spr_playerU && keyboard_check(vk_up))
{
move_towards_point(x,y-16,2);
pushup = false;
}
if (pushdown = true &&  place_meeting(x,y-2,obj_player)&& obj_player.sprite_index = spr_playerD && keyboard_check(vk_down))
{
move_towards_point(x,y+16,2);
pushdown = false;
}
the variables pushleft, pushright, pushup and pushdown are set in the creation code of the object inside the room, 3 are set to false and only one is set to true, given on what direction I want the player to push the block only towards.
the problem is I dont have clue how to make the object stop after 16 pixels...
Any ideas on how to limit it's movement?
 
Last edited:

HayManMarc

Member
You just need to keep track of where you are. Record your position before the push, then stop when 16 pixels away from that position.

But there might be a better way to do it using some kind of snap.
 

Le_Beholder

Member
alright, ive figured out something that works ish. it removes the need to have blocker objects to make it stop anyway..
Code:
border_x = 16;
border_y = 16;

move_snap(16, 16);
x += border_x;
y += border_y;
(actually, move_snap is redundant here..)
but the problem still remains; it makes the block teleport, and not transition smoothly like i want it too....
so how do i do that part??
 

Roa

Member
Dont use move_snap() That does exactly what it sounds like, it moves and snaps to the nearest divisor of the arguments provided along each axis.
You want to use place_snapped to check if you are snapped to the grid.
 

Le_Beholder

Member
ok. lets both forget about snap -anything, and just start over, because I am now frustrated with this and keep missing something.

I am trying to figure out how to do something seemingly real simple here:
move the block. have it slide gracefully at a speed of 2. then have it stop after 16 pixels.


EDIT; ive cooled down abit now, got lunch, still cant think of how to stop it from moving beyond 16 pixels, or slide smoothly instead of snapping..
also crucify is too strong a word, really i'd just give up and play TF2 for the rest of the week.
 
Last edited:
ok. lets both forget about snap -anything, and just start over, because I am now frustrated with this and keep missing something.

I am trying to figure out how to do something seemingly real simple here:
move the block. have it slide gracefully at a speed of 2. then have it stop after 16 pixels.


EDIT; ive cooled down abit now, got lunch, still cant think of how to stop it from moving beyond 16 pixels, or slide smoothly instead of snapping..
also crucify is too strong a word, really i'd just give up and play TF2 for the rest of the week.
You should be checking the position of the block as it's moving every step using place_snapped(). This is a simplified solution, but try this:
Code:
if (place_snapped(16, 16)) {
    speed = 0;
    move_snap(16, 16);
}
 

TheouAegis

Member
Or use an alarm. Not always viable, but since you are moving 2 pixels at a time in your original code, set an alarm to 8 (16/2). In the alarm event set the speed to 0.

Also you can get rid of 3 of those push variables. Just use one and set it to the direction the block can be pushed. Should also be horizontal/vertical. Zelda blocks typically go 2 directions, not 1. But i you want 1-way blocks AND 2 or 3 or 4-way blocks, you could use just one variable and work with bits.
 

curato

Member
I am doing some similar types of things in my game. what you are missing is you need to instead of moving in the if statement you need set a target for x and y and then check against them in the step even and use move toward them if they are not reached yet something like this:
Code:
if point_distance(x, y, target.x, target.y) > 5
    {
    move_towards_point(target.x, target.y, 5);
    }
else speed = 0;
 

Le_Beholder

Member
Thanks to everyone for their replies, I'm also sorry I almost spazzed out there.. really cant skip breakfast just to work on this...
Or use an alarm. Not always viable, but since you are moving 2 pixels at a time in your original code, set an alarm to 8 (16/2). In the alarm event set the speed to 0.

Also you can get rid of 3 of those push variables. Just use one and set it to the direction the block can be pushed. Should also be horizontal/vertical. Zelda blocks typically go 2 directions, not 1. But i you want 1-way blocks AND 2 or 3 or 4-way blocks, you could use just one variable and work with bits.
(Hey! long time no see!) yes i could do that actually, at first I wanted these staunchly as a control/lock feature, in case i wanted blocks to only go in a certain direction.. idk perhaps ill make a similar object and keep this iteration around for a real challenge to some puzzles.
using one variable though does makes sense because I do also want to make the objects "pullable" by the player like in of some Zelda games, having 3 less lines to make room for the pull variable/mechanic would make things alot easier to track in the code.
but that just leaves one question.. since i have 4 seperate variables to control those 4 directions, directly.. how would i then code the same ability using one general push variable?

actually as ive typed that question out the other half of my brain already got an answer: I could set the direction based off the sprite of the player im already detecting for.
I will try that and see how it goes :3
 

TheouAegis

Member
For push-pull, just set your variable to 0 (horizontal) or 1 (vertical). If Link pushes up on a block with the variable set to 0 or tries to pull it down, don't move it. If the variable is set to 1, let Link move it.
 

Elkrom

Member
I’m new to this but couldn’t you just make a separate wall object and check collisions with that somehow??
 
I’m new to this but couldn’t you just make a separate wall object and check collisions with that somehow??
Yes, but that shouldn't really be involved with the meat of the pushing code. Ideally, in a Zelda-like game where blocks are locked onto a grid, you wouldn't do collision checks while moving. You do them before moving and if there's an object/wall in the way, the block will not move.

I have no idea why anyone is even talking about alarms. Why would you use alarms when moving blocks? It seems like such a kludgy way of solving this problem and artificially increasing its complexity. You don't need several variables for direction or pulling direction or whatnot. Here is a simplified version of how I have approached this problem previously:

Code:
//Setting a global variable for grid size to prevent "magic numbers." Not necessary, but very helpful.
global.grid_size = 16;

//obj_block Create event
speed = 0;
direction = 0;
isMoving = false;

//obj_block Step event
if (isMoving && place_snapped(global.grid_size, global.grid_size)) {
  move_snap(global.grid_size, global.grid_size);
  speed = 0;
  isMoving = false;
}

//obj_player push interaction with obj_block
//This might be "Collision with x" event, but it just depends on how you approach the interaction between player and block
if (playerIsPushing && !other.isMoving) {
  var wallBlocking, blockRight, blockUp, blockLeft, blockDown;
  wallBlocking = false;
  blockRight   = 0;
  blockUp      = 90;
  blockLeft    = 180;
  blockDown    = 270;
  //The following isn't exact code. IDK how you're dealing with player movement, so alter this accordingly
  switch(playerMovementDirection) {
    case right:
      other.direction = blockRight;
      //Check for walls
      wallBlocking = instance_position(other.x + global.grid_size, other.y, obj_wall);
      break;
    case up:
      other.direction = blockUp;
      wallBlocking = instance_position(other.x, other.y - global.grid_size, obj_wall);
      break;
    case left:
      other.direction = blockLeft;
      wallBlocking = instance_position(other.x - global.grid_size, other.y, obj_wall);
      break;
    case down:
      other.direction = blockDown;
      wallBlocking = instance_position(other.x, other.y + global.grid_size, obj_wall);
      break;
  }
 
  if (!wallBlocking) {
    other.speed = 2;
    other.isMoving = true;
  }
}
This is by no means comprehensive code, but it's a start-off point.
 

TheouAegis

Member
Yes, but that shouldn't really be involved with the meat of the pushing code. Ideally, in a Zelda-like game where blocks are locked onto a grid, you wouldn't do collision checks while moving. You do them before moving and if there's an object/wall in the way, the block will not move.

I have no idea why anyone is even talking about alarms. Why would you use alarms when moving blocks? It seems like such a kludgy way of solving this problem and artificially increasing its complexity. You don't need several variables for direction or pulling direction or whatnot. Here is a simplified version of how I have approached this problem previously:
How many Legend Of Zelda games have you played? Pushable blocks come in 3 types: One-Way, Push-Pull, Four-Way. And in all three cases, the block can only be moved a specific distance one time. Which means:
  1. A variable needs to be set to denote what direction it can be pushed (which as I told the OP, you only need 1).
  2. The block needs a way to be "locked" after being pushed. As the OP craftily deduced, that can be handled by clearing the variable specifying the direction. Once he narrows it down to one variable, then he's about as concise as he's going to get there.
  3. Once the block is moving, there's no point checking for collisions, since it's going to be guaranteed the allowed 16 pixels. If the distance traveled is easily divisible by the speed it will move at, then an alarm is just as simple, if not faster than what you have going there.
  4. The alarm method can allow for unusual movement speeds which the distance is not easily divisible by; place_snapped() is prone to errors, alarms aren't.
Regardless, I will point out to the OP the most important thing that was missing from his code that your code did address:
//Check for walls
wallBlocking = instance_position(other.x + global.grid_size, other.y, obj_wall);
I didn't notice until you posted your code that the OP never checked if the block was impeded. He would have been pushing blocks into other blocks, or accidentally locking a block before a path is cleared for it.
 
How many Legend Of Zelda games have you played?
Literally all of the 2D ones. Unnecessary diatribe.

  1. A variable needs to be set to denote what direction it can be pushed (which as I told the OP, you only need 1).
  2. The block needs a way to be "locked" after being pushed. As the OP craftily deduced, that can be handled by clearing the variable specifying the direction. Once he narrows it down to one variable, then he's about as concise as he's going to get there.
  3. Once the block is moving, there's no point checking for collisions, since it's going to be guaranteed the allowed 16 pixels. If the distance traveled is easily divisible by the speed it will move at, then an alarm is just as simple, if not faster than what you have going there.
  4. The alarm method can allow for unusual movement speeds which the distance is not easily divisible by; place_snapped() is prone to errors, alarms aren't.
I agree with almost all your points here, but considering OP is only moving the block at a speed divisible by 16 and is still working on fully understanding coding logic, introducing another point of complexity in alarms seems premature. I brought up place_snapped() because it exists for this express purpose. OP is already thinking in terms of grids (move_snap), and it works perfectly for his use case.
 

Le_Beholder

Member
as far as blocks being pushed into other blocks, that actually doesn't happen so much as ive already set collision codes with a general wall object, and set they're parents to that, but sometimes the player will sink into it after its stuck against the wall...
which would explain why im noticing the limitations with move_snap now..

EDIT: ok i think i solved it now; albeit the character does get stuck into it abit sometimes, by like two pixels. easy to get out by moving in another direction though.
Code:
if (place_snapped(16, 16)) {
    speed = 0;
    move_snap(16, 16);
}
if (push = true && place_meeting(x-2,y,obj_player)&& obj_player.sprite_index = spr_playerR && keyboard_check(vk_right))
{
move_towards_point(x+16,y,2);

}
if (push = true &&  place_meeting(x+2,y,obj_player)&& obj_player.sprite_index = spr_playerL && keyboard_check(vk_left))
{
move_towards_point(x-2,y,2);

}
if (push = true &&  place_meeting(x,y+2,obj_player)&& obj_player.sprite_index = spr_playerU && keyboard_check(vk_up))
{
move_towards_point(x,y-16,2);

}
if (push = true &&  place_meeting(x,y-2,obj_player)&& obj_player.sprite_index = spr_playerD && keyboard_check(vk_down))
{
move_towards_point(x,y+16,2);

}
I've tried making only one if push = true statement that overlapped all the check movement codes there, and that's when the quirks started to happen... so i referred back to my original objects format.

EDIT AGAIN:
nope, calling the variable once does work, i just wasnt using move_towards_point, and instead tried doing x +=2 etc, which made things weird.
NOW it all works the way I want it too
Code:
if (place_snapped(16, 16)) {
    speed = 0;
    move_snap(16, 16);
}
if push = true
{
if (place_meeting(x-2,y,obj_player)&& obj_player.sprite_index = spr_playerR && keyboard_check(vk_right))
{
move_towards_point(x+16,y,2);

}
if (place_meeting(x+2,y,obj_player)&& obj_player.sprite_index = spr_playerL && keyboard_check(vk_left))
{
move_towards_point(x-2,y,2);

}
if (place_meeting(x,y+2,obj_player)&& obj_player.sprite_index = spr_playerU && keyboard_check(vk_up))
{
move_towards_point(x,y-16,2);

}
if (place_meeting(x,y-2,obj_player)&& obj_player.sprite_index = spr_playerD && keyboard_check(vk_down))
{
move_towards_point(x,y+16,2);

}
}
thanks everyone!
 
Last edited:

Le_Beholder

Member
Ok, Last tiny issue before I close this thread as completely solved:
How do I get the player to stop bouncing against the moveable block object every time I push it?
He kinda twitched against it by 2 pixels.
 
Top