[Intermediate] How to make pushable and pullable objects

Galenmereth

B___ _E__
Veteran
Joined
May 15, 2013
Messages
2,220
Reaction score
2,008
First Language
English
Primarily Uses
RMMV
Pushable and pull-able objects

This short tutorial will explain how to make an object both pushable and pullable, all using the editor's eventing system. There are a few script fields used within these events, but these are all explained. No new scripts are required.
 
Before we begin, you can download the demo project to check it out. If you don't quite get what's going on, then come back here and read up :)
 
Demo project (requires VX Ace RTP): https://dl.dropboxusercontent.com/u/73268/rmvxace/PushAndPull.zip

Updates:

  • June 18th 2014: Corrected some errors in the tutorial text; rewrote the functionality to fix possible incompatibilities with scripts, based on feedback from Shaz and NPC Thad. The demo file has been updated.
  • May 26th 2014: Fixed splelling errlols and added a description for the @passable variable. The demo project was updated with a Common Event example of the event.
 
What you get
This solution will give you an event object that can be pushed and pulled, but only onto areas where the player can walk. It can be pushed through events that are set to "Through" (i.e passable), behind trees and other tiles that are marked with a star passability, and so on. The event can be copied as-is, and placed anywhere. You can also put the whole event page into a Common Event; the demo project demonstrates this, too.
 
The event page


  • Options: I've set "Walking Anim." to on, since we want the boulder to animate when moves.
  • Trigger: Is set to "Action Button", so that the player must face the object and hit the action button to start interacting with it.
  • Priority: Set to "Same as Character" so that the object stops the player from moving through it. This is not required, but having it "passable" by the player might make it hard to interact with, and is normally not what you want with these kinds of objects.
The first level of the event section is a loop; this ensures that once the action button is pressed in front of the event, it keeps doing what we want it to. We then put a "Wait: 1 frame(s)" event at the bottom of the loop; this is necessary to not create an "endless loop" scenario. This might sound strange, but without that wait event, the engine will perform the loops forever, because it will never get to wait 1 frame for anything to change. And this will crash the engine.
 
The next level of the event section is a conditional branch:
 

 
Here we check if the action button (called "C" internally) is pressed. It is important that we check "Set handling when conditions do not apply" here, because we want the whole event to stop looping through the loop we created when the player lets go of the action button. We do this by placing a "Break Loop" event after the "Else" clause. You can see this in the overview of the event page.
 
Once we know that the player is pressing (holding down) the action button, we start by locking the player in the direction she's facing using a set move route and Direction Fix ON; this is so that pulling the object doesn't turn her around.

We then set up some temporary variables for this event object. The first script part does the following things:

  • We set local variables (local to this event).
  • @_x: Is set to the player's current X position on the map
  • @_y: Is set to the player's current Y position on the map
  • @_d: Shorthand for "direction"; set to the player's direction
  • @_left, @_right, @_up and @_down: I set these explicitly to the corresponding engine values for clarity. If you like, you can remove this line, and replace these variables in later script calls with the corresponding numbers.
  • @_passable_bit: This is the passability bit value the engine uses to check if a character can move onto the given x and y coordinates.
We also define some methods that check the passability of given @_x and @_y coords:

# Checks if object is movable in the set xy coordsdef _is_movable? $game_map.check_passage(@_x,@_y,@_passable_bit) && $game_map.events_xy_nt(@_x,@_y).empty?end# These two methods simply check if the player is facing the correct direction# for moving the object, so that you can't grab an object from the bottom# and move it sideways. If you want that functionality, replace these two# methods in the conditional checks further down with _is_movable?def _is_movable_x? _is_movable? && (@_d == @_left || @_d == @_right)enddef _is_movable_y? _is_movable? && (@_d == @_up || @_d == @_down)endAfter that, we create 4 blocks of conditional branches; one for each direction (left, right, up and down). I will explain the first one in details (moving left), and hopefully that will give you an understanding of the other four directions.
 
We start by making a Conditional Branch like this:
 

 
Within that Conditional Branch, we enter a script snippet:
 


Here we check what direction the player is facing; based on that, we modify the @_x and @_y coordinates, to make sure that we check for passability—i.e if the object should be able to be moved in the given direction.

We then use the methods that we defined earlier to make sure the given @_x and @_y coordinates are valid.

 

 
If we can move the event object and the player, then we do it with two move routes; one for the player, and one for the event object itself:


 
Notes: 

This is why we have to do all the elaborate checking of passability earlier in the script entry window; if we didn't, we would need to tell the player to move first, wait, and then move the boulder, when pulling the object. We would also need to make a whole separate conditional branch for pushing. Overall it would be a lot more work, and it wouldn't look nice.
 
We repeat the above Conditional Branch three more times for the three remaining directions. There are some small variations, but please take a look at the first picture—or the demo project—to see these in their entirety. I think they should be relatively easy to understand once you know how the first one works.
Finally, if the Action button is no longer pressed, we turn off the direction fix on the player using another set move route command, and break the loop.

 
Credits and acknowledgements

  • Thanks to ksjp17 for asking about a way to pull objects; it got me thinking about the whole thing in the first place.
  • Thanks to GrandmaDeb for mentioning that we lack a tutorial showing how to do this without any new scripts.
It was a fun challenge to solve :3
 
Last edited by a moderator:

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
39,413
Reaction score
12,947
First Language
English
Primarily Uses
RMMV
So ... this happened, and I suspect it would happen with the demo here too.


Button C is the action button, isn't it? Space/Enter? What determines if you're pushing or pulling? I'm trying to get my head around why you have the loop and the conditional branch to check for button C being pressed (because Action Button trigger means Button C has been pressed), and why you're handling all the directions individually. There's a lot of discussion on HOW you're doing things, but not WHY (at least, at that level).


Also, why the switch from $game_map.passable? to $game_map.check_passage?
 
Last edited by a moderator:

Galenmereth

B___ _E__
Veteran
Joined
May 15, 2013
Messages
2,220
Reaction score
2,008
First Language
English
Primarily Uses
RMMV
I read through that post, but couldn't reproduce the problem. However, I did notice some issues with my tutorial. First off: the long format picture displaying all of the event code was wrong, and a layering error in photoshop led to the first passability check to display the old, faulty passable? usage instead of check_passage (like you noticed). But I've also been setting local vars in a dangerous way; setting @x, @y and so on as local vars can conflict with the actual class that the event runs at. I found this out the hard way in my current game. I thought that the interpreter for event code kept local vars separate in some magical way, but obviously not ;)

So I've decided to rewrite the code, and I've updated the first post. Here's answers to the questions that are still relevant:

The loop

We have to use the loop because the event is fired by an Action Button trigger type. Even if we encapsulate all the logic in a conditional branch check of the C button (Action button) being pressed, it will be impossible to also trigger the directional keys straight after, because the check is over in 1/60th of a second if the game runs at peak fps. Thus we trigger a loop with a 1 frame interval to check if the C button has been pressed after the event is triggered. This ensures that we get to check for the player holding down the C button while also holding down a directional button.

$game_map.passable? vs $game_map.check_passage

It is necessary to use check_passage because I ran into scenarios where passable? didn't check the corret bit passability. passable? only checks terrain passability, which means you can push two events into each other. Instead, I check if the area is passable by the player, so that one can only shove objects into spots where the player may walk.

Hope this clears things up, and I apologize for causing people some undue amounts of headaches. I might flesh out the tutorial with deeper explanations soon-ish, but right now, the IGMC has me pretty squeezed for time. But I'll pop in to answer any questions when I get the chance :)
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,230
Reaction score
3,082
First Language
English
how to do this without any new scripts.
I think if you're going to start defining methods directly in the script calls, it looks like you're trying to force it to not involve opening the script editor.


It would be more maintainable and less error-prone to provide a script that handles all the push/pull logic and it would just be a matter of indicating which events can be pushed/pulled.
 
Last edited by a moderator:

Galenmereth

B___ _E__
Veteran
Joined
May 15, 2013
Messages
2,220
Reaction score
2,008
First Language
English
Primarily Uses
RMMV
While I do agree with that, you could say the same of any slightly-involved script call. The reason I define methods is to keep it DRY (don't repeat yourself) instead of copy-pasting the same logic twice. You'll notice that the logic is still exceedingly simple.
 
Last edited by a moderator:

Hayolee

Villager
Member
Joined
Jan 18, 2015
Messages
28
Reaction score
0
First Language
English
Primarily Uses
I believe I copied this line by line into my event, however I get the error message "Script 'Game_Interpreter' line 1411: SyntaxError occured.   unexpected  tANDOP, expecting '='".  I'm not very experienced so be patient if I did something stupid, please.  Also, say the object I want to be pushed or pulled is a stool I want my character to stand on.... how would I do that?
 
Last edited by a moderator:

MegaDoomerX3

Veteran
Veteran
Joined
May 15, 2016
Messages
33
Reaction score
0
First Language
English
Primarily Uses
I've run into an issue using this. If my player hits a wall while the crystal can continue through a small corridor -- using crystals instead of boulders -- the crystal can continue to move but the player can't and the game hangs. Not good enough with Ruby yet to know a way to solve this, any idea how?
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

Karen: Hi, I have a condition that prevents me from wearing a mask. :)

Employee: Can you tell me what it is?

Karen: >:(
Feeling a little overwhelmed... There are so many things to do.
- Building a simple website for portfolio
- Setup *******
- Manage social media
There are so many things inside of these tasks :(
Meet the cute little ghost, FhooFhoo. :kaopride:
If anyone here is looking for another high-potential RPGMaker game--besides She Dreams Elsewhere--be sure to follow Lawmage Academy. Good social media presence will take that game far.
Lol, I was about to write a small tutorial and realized it would belong to the MZ section ....that does not exist yet.

Forum statistics

Threads
99,574
Messages
966,816
Members
131,232
Latest member
Bektos
Top