INTRODUCTION TO PIXEL MOVEMENT IN RM2K3

Want to find out how much work there really is to doing basic pixel movement?

  • Kazesui
  • 07/11/2011 09:06 AM
  • 39967 views
There are probably a lot of you out there who wonder how you can do pixel movement in rpg maker 2k3... and a lot of you will probably still wonder even after reading this tutorial. This is because this isn't something for newbies. I try to make it quite simple though, so that as many as possible will get it.

Another thing I'd like to add is that there are many ways of doing this with this simply being my way. Feel free to try other stuff which you think will work as well.

This tutorial will teach you how to make a character move with pixel movement while detecting terrain obstacles and while being able to interact with events. I will also go through 2 ways of "changing" the map while using this technique.

A sample project containing the code we'll be going through can be found here:
Sample Project

Let's start with the simplest task, making the sprite move. For that, we first need a picture representing the sprite. To get one, you could simply do like me and simple make pictures out of some RTP char given in the charset. If you want to do animations, make sure that they all have the same resolution, otherwise it could become a pain to position them correctly in the game.



This is all it takes to make the sprite move. First we use an key input command where we uncheck the option "wait until key pressed" and where we only check for up and down. This is followed by 2 branches, checking if the variable we used for the key input is equal to 1 or 4, the value retrieved if either down or up was pressed.
If either of them was pressed, then the picture should move along the Y axis. In most part of the computer realm the point of origin can be found at the upper left, with the Y axis being positive downwards, and the X axis being positive towards the right. This means, that to move the picture downward, we have to add the variable containing the Y coordinate of the sprite, and to move it up, we'll have to subtract from it.

Whenever we have a list of branches, where only one of the branches can be true at the same time, it's normally good to create a label below the last branch, and jump to it at the end of each branch block, since this saves the engine from having to interpret unnecessary branches which can't be taken anyway, and when going into the realm of advance stuff like pixel movement, we'll want to have an eye on performance.

Following the label we have another key input, checking for left or right this time, and with similar setup for them as well. Checking for Left and Right like this right after checking for up and down, allows the sprite to go in both X and Y direction, meaning you'll be able to walk diagonally.
To update the position of the sprite we use move picture with the updated coordinates as position and 0.0 wait (without wait until done checked). This is pretty much the way to go to assure that the picture always is at the position we want.

And this is all you need to make a sprite move with pixel movement. At the end of an parallel process there's always a hidden "0.0 wait", so with this event, you'll be able to move 1 pixel per frame, that is equal to the speed "3: normal" in the editor.

It might not look so cool at the moment, since it's only a still picture moving about, even on the walls and stuff. But it is important to start with the simple stuff and make sure that they're working as intended before moving on. You should not try to make everything at once in bigger projects involving stuff you haven't dealt with too much before (or even then). Begin small, and build up as you go.
That said, it doesn't harm to plan ahead a little, so that implementations down the road will be easier to do.

Anyway, let's move on to how to animate our character. To do that, we will have to add some more code to our "movement event". We need to find a way to determine if the sprite is walking or not, and in which direction it is walking.



This is how it looks in the sample. We start by finding out if the sprite is standing still. This is done by placing a key input at the top similar like before, only that now all 4 directions have been checked. This way, we know that the sprite is standing still if the variable returns 0. This is caught by a branch which sets some variables, sets a switch called "hero moves" off and call an event which we will get back to later. At the end there's an "end event processing", meaning that the rest of the code below won't be interpreted, and why should it? We have already determined that the sprite isn't moving, so we won't need to update the sprite's location either.

Others than that, we can also see that a variable operation has been added to each direction, setting a variable "direction tmp" to the same value as the value retrieved from the key input. Since the same is down for both up/down and left/right pairs, the "direction tmp" variable will be overwritten if the sprite is moving either left or right. This means that in the case of walking diagonally, the sprite will be facing in either the left or right direction.
At the end we update a variable "Direction Hero" with the value in the tmp variable, which will be used to show the correct picture.



And this we do with a map event running on parallel process. Don't mind the char used for the event, he's just used for easier recognition of the event while looking in the editor. He's not visible in the game.
All the pages are the same, except for showing pictures of the character walking in another direction and the number of the variable "direction hero" has to be equal to for the page to trigger. So we let the variable decide which page to use as long as "Hero moves" is set on. The event code first does a modulo operation on the Animation Frame variable, meaning that it's value won't be higher than 3, and then adds by 1. Doing this makes the variable count from 1 to 4 and then back to 1 in an endless loop, and then we'll only have to put up some branches containing the different animations for that direction.

And voila, now we have a sprite which animates in the different directions. You might recall the movement event calling an event back up there. The variable it sets, is the Animation Frame variable, and the event which is called is this one. It has a variable which is always 2 for the sake of getting this event, and uses the "Direction Hero" variable to determine the page to call. Since the page always is called with "Animation Frame" having 1 as value, it will always become 2 after the addition, choosing the middle sprite for standing still, just like the sprites would do normally when doing tile based movement.

Now, all we need is for some detection towards terrain obstacles to have the most basic mechanics down.



This is once again done with a map event, and what better is used for an event which will block movement then a big scary knight which might would probably have done the same?
Pictures are shown at screen related coordinates, and terrain is given at tiles, so we'll have to translate the screen related coordinates to tile ones to know if there is unpassable terrain there. We do this by setting to variables, Tile X and Tile Y, equal to the respective screen related coordinates of the character, and then we add an offset depending on the direction we want to search in. This offset will depend on the size of the sprite you've chosen. Mine was 24x32, and since the pictures are set at their middle point, it'll be 16 pixels down to reach the bottom, in order to check if there is any obstacle there. Once offset is added, both variables are divided by 16 to return the tile coordinates (since each tile is 16x16 pixels). This allows us to call "Store terrain ID" as well as "Store event ID" which we will be using later. Mind you that we'll have to set the terrain ID first to the value we want to check for. In the sample project, they have been set to 4, so a branch checks if the terrain ID equals 4, and if so it subtracts 1 from the Y coordinate.

To understand why, we look at the event itself. It's not a parallel process and we won't be using action key to call it either, so it'll have to be called from somewhere else. It would make sense to check for collision when we're walking, so we'll add it to the movement code



We can see that calls for different pages are done for the different directions, right before adding to the value. This means that the -1 from before will be negated right afterwards, meaning no movement in case of an obstacle.

And with this we have basic functionality. You might have noticed that the 1 which used to added for the different directions has been changed with a variable. This has been done to allow for adjustable speed, which allows for functions like running:



This is just a simple little event which sets the speed variable to 2 if the button x is held down, and 1 if it isn't. 2 pixels per frame equals "4: twice normal" in the editor.


Now, if you test the collision detection, you will notice that the collision detection is somewhat bugged, especially with the new running system implemented. Since it's only testing in the middle right in front of the sprite, by walking a little on the side, you'll still be able to walk into walls. To prevent this, we'll need to check two points (or more if you have a very big sprite) at each edge. Also, events can be obstacles too, so we'll want to check for events standing in the way as well. So back we go to add more code to the event dealing with collision detection:



We check for both corners by doing it in two steps. First we add 15 to the Y variable as well as the current speed to get the next position along the Y axis, and the we subtract 7 from the X coordinate in order to get the lower left corner. We get both terrain and event ID and if either shows an obstacle, we subtract from the coordinates to negate movement and end the event there. If not we check the lower right corner as well. The reason we check add or subtract 7 from the X coordinate, while adding or subtracting 8 when looking left or right, is to avoid getting stuck along the walls. If you move into a wall going left, you wouldn't be able to walk up or down, since the position further up and down would still be 1 pixel into the wall, so it has to be 1 pixel less.


Now the collision works just fine and the running doesn't bug it either. Event's can block the player as well now. This means it's time to move on to the next part. Making it possible for your picture to interact with events. We'll look into how to deal with a treasure chest, as well as how to talk with an NPC.



First off, we'll need an event to handle action input, since we won't be able to use the hero for it anymore. This one is actually quite similar to the original collision event in terms of looking right in middle front of the sprite for events.
We store the event ID and if the event ID is larger or equal to the lowest ID of an obstacle event, it sets the Action Page equal to one, calls the event with the ID you just found at page 1, since we use Action page for the page number.

Doing this, we can make a treasure chest:



Hero we set the Action Page again to the value of a variable "Chest", which initially has the value 2. Then it calls the event again this time with the value just obtained. When interacting with the chest for the first time, we'll see the chest open and the Chest variable will be set to 3. This means that for all later interactions with the chest, page 3 will be executed instead. Page 3 will of course be set with an open chest and with chest variable being equal to 3 as an requirement, making it work like a normal chest.

When dealing with an NPC which always says the same though, a single page can do:



We just check for the direction of the hero, and make sure the NPC face in the opposite direction to make sure the NPC faces the hero before talking. Talking to a char like this with a picture from above, will reveal that you will be standing "on" that char. To prevent that you'll have to make a picture for the NPC as well with an higher ID than that of the hero, but I'll let that be your problem.

Doing this we're able to interact with events through a picture. The sample project also contains some crystals you can move around.

Now there are only a few things left. To be able to make a simple game, we will of course have to be able to change the map, And we'll also have to initialize certain values at start.



The first page is a one time thing, setting certain start values for the game. We set the position we want to her to start in terms of screen relative coordinates and we also set various variable values, like chest to 2, so that it goes straight to page 2 upon interaction and other similar cases.

The page 2 of this event which also is set to auto start deals with something supposed to happen each time we visit this map. The call event calls for the animation, to make sure the hero is facing the right direction. Event start ID marks the start of interact able events, while obstacle start ID marks which of them are unpassable. This means that you will have to take care in terms of Event ID's. The ones which you should be able to interact with will have to have the highest ID's with the obstacles being highest again. Not doing so, could lead to more problematic testing as well as less efficient code.

The last page of this event is merely empty, just waiting for a switch to be turned off for the next arrival at this map. Erase event could probably also be used at this point, to make sure it only happens once per map.

To make a similar effect to that of "touched by hero" for the sake of triggering an teleport event, we'll have to add yet another common event:



Fairly similar to that of the collision events earlier, but this time it suffices to check at the middle point of the sprite (which is below the middle point of the picture). Also we check that the event id found is higher or equal to the lowest ID of interactable event, but lower than that of the obstacle ones. Once this is done we can make the event triggering the teleport:



Using a picture of high ID to cover up the rest of the screen can provide for a nice way to fade out the screen, especially if the normal fades won't do, since tint screen won't cover pictures. Hero Mobile Controls the mobility of the hero so shutting it down will prevent continous update of the picture position which can give problems during transition, especially since you have to enter the new coordinates as well. the 0.0 wait is placed there since the maker sometimes need to wait a frame to update various parts of itself. I then set the switch Area initialized off so that the auto start dealing with initialization of that map can run.

In the next map, we'll come across another way to switch map, that is by panorating the map Zelda-style. This can be achieved like this:



We divide the map into "Zones" and check for them when crossing the border by same means as a normal teleport event. if you're about to enter another zone than the one you're currently in, the branch will be true. We then set Hero moves and Hero Mobile off to make sure no picture update will be done during transition, and do another show picture but with "scrolling with map checked" to make sure we don't move out of place while panning the screen. Since a screen is 320 pixels long, we subtract that amount from the X coordinate, to make sure it will relate correctly in terms of the screen, and we set a new variable, X Offset" with the value 320.

In all operations dealing with translating picture related coordinates to tile ones, the offset will also have to be added, since the picture is actually further into the map and the screen relative coordinates are always relative to the screen.
Once this is done, you will be able to move back and forth Zelda style without problem, and you'll still be able to interact with events around.


This concludes the introduction tutorial on how to do pixel based movement in rm2k3. Hope you've learned from it and will have further fun with it. Make sure to download the sample project at the top to check it's functionality and to get a better look at the code.

Thank you for reading



A video showing the end result given in the sample project

Posts

Pages: first prev 123 last
author=Kazesui
while more aesthetical ones like rounded corners (if I get you correctly),


well, whatever is possible. plausible. practical. even just diagonals on top of the standard grid. actually, diagonals would be fairly important for this one game. how hard is that to add this tutorial model?

It's perfectly possible though. One way of doing it would be to use terrain id's, and then you could use a simple mathematical function to check if the player is within the object radius and then just "push" it out to the correct position.


well, in this one game i'm testing it on. it's all parallax mapping. i could always make sure it generally fits the tile grid. so if i use terrain id on the map with invisible tiles, the x.y of the hero picture coordinates is tracked over the underlying tiles?

i just don't get the next step and how that would allow me to push the picture to anything other than outside the standard tile boundaries. would you have to define each point along the custom edge? so if you try and walk past that point, it stops you? or something.

If you plan to have a lot, using events which you can step on might be a better idea

but this whole system works by not using the hero event, how does stepping on events factor into this?
but this whole system works by not using the hero event, how does stepping on events factor into this?


This can be simulated with a pp event checking if the image of the hero is standing on an event within a certain event id range, and then executing a page of the event in question.

If you're already doing panorama mapping, terrain id's would probably be more convenient though.

i just don't get the next step and how that would allow me to push the picture to anything other than outside the standard tile boundaries. would you have to define each point along the custom edge? so if you try and walk past that point, it stops you? or something.


Basically, you do a check bounded to the current tile you have.
e.g. if locally within one tile, y + x > r with r being the radius in question, then you can check if the hero image is in the lower triangular of that tile, and you simply set the coordinates in such a way that the image will be put back on the border of the diagonal in this case, i.e. along the lines of y + x = r. Exactly which x and y should be used should depend where you crossed into the local obstacle though, and might be subject to more checks, to get a more smooth walking along these rounded edges.

The example above would give a diagonal. You'd need to modify it for the different variants though, so you would normally need 4 different id's, one for each direction so to speak.
hmm, i'll need to do more work just to reach this problem so i'll come back to this prepared

you know, when i first saw you calling an event that was a set of variables i thought it was some plugin. in my mind there were no other options in the call event function. how did i not know about this until now? it changes so many things o.o
Yep, the way to point to an arbitrary event page is indeed very powerful. It allows you to execute commands related to "this->event" based on a variable. So by having a page in all applicable events doing "flash this->event red", I can just decide with call event with variable parameters which event should flash, something you couldn't do by default.

And the second power is how it easily allows you to locate events of interest being closeby, allowing you to only make computations for those events, which can be very helpful in terms of performance.

It also allows for greater reuse of code, since instead of assigning values from a specific event into some variables to do some computations, you can just take an event page in the event itself with "this->event" and store it into some temporary variable and just copy paste the page as is. This is basically what's being done in my TBS demo, where event graphics represent the movable area, and you might quickly need as many as a hundred of them.
Using the call event, you can simply "set event location" based on some temporary variables, copy paste the exact same page to all of these events, and then have one single event easily loop over all of these events.

There are so many uses of this that it's quite interesting how I've almost never seen it being used.
just curious, if i'm going to be implementing diagonals into this model, should i set up anything special before or during? or, if i finish this tutorial, will be a "simple" addition of code to make diagonals?
It shouldn't pose to big a problem to add afterwards.
The way I imagine it, it would simply be a matter of adding some more branches in the event handling the collision detection.
It seems like if I change the size of the hero collision hitbox, it doesn't work anymore. It's not conforming to the grid of the editor/terrain id.

Does it have to be the same size for this method?
It does not have to strictly be the same size, but depending on what you do, you will face certain problems. For example, the hit collision is done by checking tiles corresponding the 4 corners of a tile. If you increase the size to height/width to be more than 16 pixels, you will not be able to properly track if there's a tile blocking the movement. To compensate for this, you would have to add another point (or more, depending on the increase in size) such that the largest distance between two points doing hit detection are never more than 16 pixels apart.

If you decrease the size too much, this should not give too much of a problem in terms of static terrain, but might give trouble in terms of detecting moving once, if the move sufficiently fast. If you have a very thin hit collision box, and you move quickly towards an event moving towards you, it's not unthinkable that you'll be able to walk right through each other. To handle this one, you'd probably have to calculate where you would be moving, and start calculating stuff "inbetween frames". A bit more difficult than the previous problem

Those would be the main 2 concerns that would pop up into my mind, could be some others which I'm overlooking at the moment though.
I'm not instantly sure how I would implement your 2 suggestions.

I decreased it to form a much smaller rectangle (around 12 wide and 6 high) and this is what happened


I dunno, maybe I just messed something up. It seems like when I'm walking right or left against a wall, then I try to follow the wall up or down, the whole hitbox of the wall is offset Y+5. If I walk directly down into the wall, the hitbox is in the right place.

You can see the problem in the first few seconds. Is it because I made it a rectangle?




There's another problem. It seems if I make any changes to the up facing detection size, it no longer lets me walk along the wall. So if I walk up and hit a wall, and while holding up I hold right. I'm stuck in place. I tried to fix it, but the best I could do was make it work with up+left. up+right still made me stick to the wall. What would cause that for only the up direction, when I made the same changes to the other directions with no issue?

It works again if I undo those changes.
Pages: first prev 123 last