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
  • 39626 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 123 next last
chana
(Socrates would certainly not contadict me!)
1584
Not much to say, but : this is really amazing, never seen or even imagined anything like this (though in fps the AIs as they call them do exactly that, I imagine(?))!
It is kind of amazing to be able to do this, but to be quite honest, the amount of work it takes for each and every collision object or NPC, it just isn't worth it.
at :55 of the video. How did you get that screen pan to the next map??? that's just like Zelda. How did you do that?
It's described in the last section of the tutorial. Basically, you take the pixel coordinates of the hero and check if it's outside the screen, and if so, panorate the map accordingly.

I suppose you could download the sample project, look at the code (the one for the screen should be on the second map somewhere as a map event i think), and crosscheck it with the variable values using the debug menu (F9 while in tesplay)
A fairly good question. I guess I might have forgotten to add that there's (probably) an autostart event with a show picture command at the start before calling erase event. Even if there weren't a picture, this doesn't result in an error message or anything in rm2k3, as it then just ignores the command, meaning that this would only cause a bug in terms of there being no sprite the second you start the game, but everything working as normal the second you move.

This is actual a reason why I tend to include sample projects, as I might forget to explain something which seems obvious to me (or which just gets lost of the sheer amount of stuff I try to explain), such that it's possible to look up on ones own for the details which might seem unclear.

But yes, thanks for pointing it out. I guess I'll have to reread a bit and try to find a fitting spot to include that detail

edit: seeing your new edit, I guess you might have seen something, I didn't while quickly skimming through my own tutorial?
Well not in the tutorial, but you had an auto start event set up in the first map that executed show picture. So I figured that was how to do it. It's still a bit confusing if you didn't download the sample project.
Nah, I walked through the tutorial again and I'm still not getting it. All this stuff is just so complicated, I can't figure out what's wrong. I think I'll just merge the switches / variables / events from your sample into my own project.
What kind of bug are you facing? Assuming you've been trying to implement it on your own
It just didn't work, at all. Did the common events, did the collision, did the animator event, did the mapref event, nothing happens when I test play. I skipped the treasure chest and the interactactable NPC though, so maybe that's why.
Alright, I'm taking it step by step. So far I'm up to animation and I've run into one problem:

- Sometimes when I take a few steps, and then release the button, the character picture is stuck on one of the "walking" pictures instead of the "idle" picture. Even though I have the Conditional Branch that says "If Animation Frame is 4, show idle picture".
This is fixed by having a branch catch if the key input = 0. If so, then the animation frame variable is set to 1, i.e. the first image.
This is shown in the sample project under the common event "Movement 3"

Remember, there are 4 states of movement, so just checking for animation frame 4 isn't sufficient, since frame 2 should show a walking image as well
Well, I have that branch in my Movement event, I just don't have the Call Event in there. Because whenever I put Call Event with event number stored in variable Animation Event, when I testplay it says I'm referencing to an event that does not exist.
might be worth checking what value is in varible for the event ID then, by removing the call event, replace it with a message using \v to show both variables. They should correspond to the event ID of the animator event, and the page corrsponding to the direction
It says "0", but I'm not sure what to do now
if it's the variable you use for giving the event ID, you should set it to the ID of the animator event in an autostart event.
if it's the variable for giving the page, then there's an error somewhere in the code I guess, as this one should be set when direction keys are being hit. You might want to just set it to 1 or something as an initial value as well though, just for the sake of not getting an error if there's a parallel process event in the background triggering prematurely
Alright, well I set the variable to 2 in an Auto Start event (the ID of my animation event), and while I get no more error, I'm still experiencing the bug where sometimes the character is standing still while displaying the "stepping" picture.

Edit: No, wait. I accidentally put the Call Event in an older common event. Now I've put it in the one I'm using, but once again I get an error even though I set the variable ID to 2 (the ID of my animation event).

Edit2: Alright, I did what you said an set Direction Hero to 1 as well in my auto start event. Now I get no more errors and the walking bug is gone. Yay! Gonna wrestle with the collisions now.
I noticed in your tutorial you don't really mention the remaining three pages for the collision event, you only really talk about the first page. This kinda leaves me confused as to what to put on the other pages.
Thank you for these points. Ought to tell me the stuff which needs to be fixed up, so that it will be easier to understand.

The other pages in the collision event correspond to the other directions.
Basically, this system only checks if there is a collision in the direction you're walking. So while you need to subtract from y to check if there's and obstacle above, you'll have to add to y to see if there's an obstacle below.
Same apply for left and right, just with the x coordinates.

Notice that there should be two sets of coordinates as well. when facing up, you'll subtract and add to the x coordinate to get the upper left and upper right coordinates for detecting an obstacle.

If you look at the event, you'll get an idea of which coordinates to use. These could of course vary, depending on the size of your sprite.
Well I guess that makes sense, but I took a look at the collision event in your sample, and on the page for obstacles below, it subtracts from y, and for the obstacles above, it adds to x... I don't fully understand this.
Pages: first 123 next last