Introducing Diagonal Movement into RPG Maker

Help spread the word!Share on FacebookShare on TumblrTweet about this on TwitterShare on Google+Share on LinkedInShare on RedditFlattr the author

EightDirMovement1

This article explains how to introduce diagonal movement into RPG Maker. By default, RPG Maker assumes characters can only face four directions. Although there is support for eight directions, it is not implemented.

We start by seeing how we can turn our four-directional movement into eight-directional movement.

Direction Numbers

RPG Maker uses numbers to represent each direction.

1 - down-left
2 - down
3 - down-right
4 - left
6 - right
7 - up left
8 - up
9 - up right

If you have a keyboard with a numpad, you might notice the directions correspond to the layout, with 5 in the center.

Handling Direction Input

RPG Maker provides two ways to check your direction.

  1. Input.dir4. This returns the number corresponding to 4-directional movement.
  2. Input.dir8. This returns the number corresponding to 8-directional movement.

When you press one key, it will be a 4-dir move. When you hold down two keys, the game will automatically find which two keys you’re pressing and calculate the appropriate direction value.

Note that if you hold two keys and use dir4, the game will simply round it off to one of the 4-dir directions.

Performing Diagonal Movement

RPG Maker already supports diagonal movement. They come with the move route editor. This means that most of the work is done for you: all we need to do is call it.

Here is the signature for diagonal movement, defined in Game_Character

#--------------------------------------------------------------------------
# * Move Diagonally
#     horz:  Horizontal (4 or 6)
#     vert:  Vertical (2 or 8)
#--------------------------------------------------------------------------
def move_diagonal(horz, vert)

Player Movement

One of the easiest places to start is converting the player’s 4-dir movement into 8-dir movement. Going through the scripts, we see that movement input is handled like this

def move_by_input
  return if !movable? || $game_map.interpreter.running?
  move_straight(Input.dir4) if Input.dir4 > 0
end

There are some conditions that prevent you from moving, but other than that, if you press any of the direction keys, you will move straight in that direction.

Instead of working with 8 directions directly, you instead pass in the orthogonal movement for each axis. So let’s rewrite the move_by_input method as follows

def move_by_input
  return if !movable? || $game_map.interpreter.running?
  return if Input.dir4 == 0
  return move_straight(Input.dir4) if Input.dir8 % 2 == 0
  case Input.dir8
  when 1
    return move_diagonal(4, 2)
  when 3
    return move_diagonal(6, 2)
  when 7
    return move_diagonal(4, 8)
  when 9
    return move_diagonal(6, 8)
  end
end

Instead of using 8 different cases, I simply checked if it was even or not. If it’s even, then it’s one of the orthogonal movements. If it’s odd, then it’s diagonal movement.

At this point, you can test your game and see that you do move diagonally.

EightDirMovement2

Updating Character Direction

When the character is moving diagonally, you want to properly update the character’s direction. This is how the method is defined by default

def move_diagonal(horz, vert)
  @move_succeed = diagonal_passable?(x, y, horz, vert)
  if @move_succeed
    @x = $game_map.round_x_with_direction(@x, horz)
    @y = $game_map.round_y_with_direction(@y, vert)
    @real_x = $game_map.x_with_direction(@x, reverse_dir(horz))
    @real_y = $game_map.y_with_direction(@y, reverse_dir(vert))
    increase_steps
  end
  set_direction(horz) if @direction == reverse_dir(horz)
  set_direction(vert) if @direction == reverse_dir(vert)
end

Because the default engine uses 4-dir movement, all directions are stored in four directions. You can see that it simply picks one of the orthogonal directions. If we are changing to 8-dir movement, we can now store our actual diagonal value in the direction. For compatibility purposes, I decide to take the long way around:

class Game_CharacterBase
  alias :th_8dir_movement_move_diagonal :move_diagonal
  def move_diagonal(horz, vert)
    th_8dir_movement_move_diagonal(horz, vert)
    if horz == 4
      if vert == 2
        set_direction(1)
      elsif vert == 8
        set_direction(7)
      end
    elsif horz == 6
      if vert == 2
        set_direction(3)
      elsif vert == 8
        set_direction(9)
      end
    end
  end
end

Inserting Diagonal Sprites

Your diagonal movement works, but it doesn’t look very good.  Let’s use some diagonal sprites. I grab some Kaduki Sprites and put them together like this

EightDirMovement3

 

For convenience, I stick with Ace’s 8 groups per sheet format and place the diagonal sprites right beside the orthogonal sprites.

Now if you just stick this in your project and assign it to your actor, it won’t work, because the engine doesn’t know how to use it.

How Movement Sprites Work

We need to understand how the engine figures out which sprite to draw. The answer lies in Sprite_Character

def update_src_rect
  if @tile_id == 0
    index = @character.character_index
    pattern = @character.pattern < 3 ? @character.pattern : 1
    sx = (index % 4 * 3 + pattern) * @cw
    sy = (index / 4 * 4 + (@character.direction - 2) / 2) * @ch
    self.src_rect.set(sx, sy, @cw, @ch)
  end
end

There are a lot of different properties being used. See this diagram:

EightDirMovement4

  • Index – Which block of sprites to use
  • Pattern – frame in the animation
  • Direction – character’s current direction

Recall that in Ace, you can have 8 blocks of character sprites per sheet. This is what the index is for. The math is used to take all of those values and figure out which region on the spritesheet should the sprite be copied from.

Spritesheet Format

We need to decide on a format to use for our spritesheets. Once we have chosen a format, every movement spritesheet must follow that format. For simplicity, let’s just place them side-by-side, since that’s what the sheet I found comes with.

We have something like this

 EightDirMovement5

So basically, we have our orthogonal movement as index 0, and our diagonal movement as index 1. Now the question is how all of the calculations from above can be used to figure out whether we want to use orthogonal or diagonal, and which is direction to use.

Math Tricks

We notice a relationship between the orthogonal movement and diagonal movement.

For directions 1 and 2, we use the top row.
For directions 4 and 7, we use the second row.
For directions 6 and 3, we use the third row.
For directions 8 and 9, we use the fourth row.

Now, there is an easy way to calculate this. We use the follow formula

for even dir, use dir
for odd dir, use dir * 2 % 10

So when we are moving in direction 7, the row to use is equal to 7 * 2 % 10 =  4, and so when the engine sees 4, it will know to pick the second row.

This math isn’t really necessary. You could just as easily use a set of conditional branches.

Updating the Sprite or the Character?

At this point, you as the scripter have to make some more decisions. We know that we need to update the index and the direction, but there are two places where this can be done.

  1. `Game_Character` – these objects stores all of the properties
  2. `Sprite_Character` – these objects take the characters and draw them

In Game_Character, the direction is set whenever you move. This value should accurately reflect the character’s direction, in case you need to use the direction for other purposes (and there are quite a few cases where we actually need a character’s direction)

I would prefer to modify the sprite method. As mentioned above, we need to handle the orthogonal and diagonal cases separately, so I simply overwrote the method completely:

class Sprite_Character < Sprite_Base
  def update_src_rect
    if @tile_id == 0      
      if @character.direction % 2 == 0
        index = @character.character_index
        pattern = @character.pattern < 3 ? @character.pattern : 1
        sx = (index % 4 * 3 + pattern) * @cw
        sy = (index / 4 * 4 + (@character.direction - 2) / 2) * @ch
        self.src_rect.set(sx, sy, @cw, @ch)
      else
        index = @character.character_index + 1  # add one, as per the specs
        pattern = @character.pattern < 3 ? @character.pattern : 1
        sx = (index % 4 * 3 + pattern) * @cw
        sy = (index / 4 * 4 + (((@character.direction * 2) % 10) - 1) / 2) * @ch # our new formula
        self.src_rect.set(sx, sy, @cw, @ch)
      end
    end
  end
end

Now that we have updated the character’s direction to reflect 8-dir, and the sprite knows how to render the character, we should have a pretty good 8-dir movement behavior.

Other Character Objects

The player is only one type of character. We also have events, followers and vehicles. However, because they all use the same methods that we have customized, they will automatically support proper 8-dir movement as well!

EightDirMovement6

Triggering Events

One thing you will immediately notice is that you can’t trigger events diagonally. I will cover this topic in a later tutorial, where we look at how a simple direction affects whether an event is triggered or not.

Recap

The purpose of this tutorial was to demonstrate how you can implement 8-dir movement in your game. Basically

  1. Modify the player input to support 8 directional input
  2. Store the player’s direction
  3. Update the sprite to use the new format

This can be done in any number of ways. I have shown one way, but it may not be the most optimal or compatible way. A copy of the script can be found here.

Moving Forward

While we have successfully introduced diagonal sprites, we have revealed issues with other aspects of the engine. Because the engine assumed everything was 4-dir, all of the logic involving character direction is based on this assumption. There are still several other things to cover before we have a fully functioning 8-dir movement script.

Credits

All of the sprites shown in the screenshots are taken from this website: http://usui.moo.jp/rpg_chadot.html

Spread the Word

If you liked the post and feel that others could benefit from it, consider tweeting it or sharing it on whichever social media channels that you use. You can also follow @HimeWorks on Twitter or like my Facebook page to get the latest updates or suggest topics that you would like to read about.

Help spread the word!Share on FacebookShare on TumblrTweet about this on TwitterShare on Google+Share on LinkedInShare on RedditFlattr the author

You may also like...

23 Responses

  1. celadonk says:

    Yesterday, I got this working up until the part where you insert the custom sprites, and I messed my game up pretty badly. I fixed it and tried to give it another go today. I got up until the “def move_by_input” part (so, basically, I got nothing done) where I replaced what was there with what you made, and it gave me an error. Something about “expecting keyword end, got $end” or something like that. What did I do wrong?

    • Hime says:

      It’s a syntax error. You might be missing a closing bracket, or an end quote, or something.

      • celadonk says:

        Ah, okay, thanks. I’ll try to work it out.

      • celadonk says:

        Okay, I got past that part, you were correct, but got stuck on the next part. Got error “undefined method ‘th_8dir_movement_move_diagonal’ for #” Guessing it has to do with “alias :th_8dir_movement_move_diagonal :move_diagonal” but when I kept that in I got error “undefined method ‘move_diagonal’ for class Game_characterbase”. I also tried to move the “alias” part UNDER “def_movediagonal” but I got an error saying “stack level too deep”.

        (I know, I’m hopeless. I have no coding experience :T )

        • Hime says:

          The alias defined a new method called th_8dir_movement_move_diagonal which is defined to be whatever move_diagonal does at the time of aliasing. The class name is Game_CharacterBase, so if you defined your own class called Game_characterbase, you are basically creating a new class instead of working with the existing one.

          • celadonk says:

            Alright, that makes sense. So the alias basically made it so th_8dir_movement_move_diagonal does the same thing as move_diagonal. So where do I put the alias? Also, could I potentially replace all instances of th_8dir_movement_move_diagonal with move_diagonal or would that not work at all?

            By the way, thank you so much for helping me.

          • Hime says:

            You can define them anywhere. Aliasing is just like any other line of code.

            The reason why aliasing is done is so when you make modifications to existing methods, you can still call upon the old logic so you don’t completely replace them.
            It would be more useful to experiment with aliasing in a separate script to understand what it’s doing.

          • celadonk says:

            Oh, okay, I think I understand now. But no matter where I put the alias line, I get some sort of error (“stack too deep”, “undefined method”, “expecting ‘end'” stuff like that).

            Where and how would I experiment with aliasing?

          • Hime says:

            It would be easier to download and install ruby directly and look for a ruby IDE to play around with.

  2. glasfen says:

    Hime,

    Thanks for providing such comprehensive explanations for your many scripts.

    I can’t seem to get diagonal movement to work in VX Ace. For starters, the diagonal movement signature (“def move_diagonal(horz, vert)”) is in Game_CharacterBase for me, not Game_Character. Is this a versioning issue? Regardless, I’ve tried to include your scripts in Game_Character and Game_Player but I’m still stuck with four directions of movement. I don’t see a core script requirement for this one, but did I not look close enough?

    Thanks for the scripts!

    ~glasfen

    • Hime says:

      It is in Game_CharacterBase, but Game_Character would simply inherit it so it shouldn’t be too much of an issue.

      This tutorial allows you to implement diagonal movement from a default project. Have you tried the full eight-dir movement script directly to see if it is the way you’ve implemented it?

      • glasfen says:

        Thanks for the reply!

        I took a look at your scripts again and found that it was an input error on my end. Simple mistake, so sorry to post about it!

        I’m interested in eight-direction movement with isometric maps, so I’m going to try your script with MGC ISO Engine. Also, I know that Victor has a script for pixel (non-grid) movement that I’d also like to include. Any thoughts on these?

        Thanks again!

        • Hime says:

          I did not test with victor’s script but I have tried Quasi’s pixel movement and it worked with very little modifications (mainly just setting “diagonal movement” to true)

          • glasfen says:

            Thanks, Hime! That is very helpful and exactly what I’ve been trying to find for quite a while!

  3. Arsist says:

    The reason I take interest in diagonal movement is because I take an interest in the idea of isometric parallaxing and what it has to offer in reward for hard work, as there isn’t much you can show using the regular default 2D rpgmaker view. Something however is a concern is the way that the maps come out when you make them match the axis of diagonal movement, yet when you try less harsh of angles, the character ends up running into the wall, all in like this diagram: https://drive.google.com/file/d/0B9oxIE0LXB7cS2ppbTBjdkVidXM/view?usp=sharing
    Do you have any suggestions? I’m basically just trying to see what’s best in order to accomplish something that’s visually revealing.

  4. Ian Bach says:

    is there a way to make i t so key 7 on keyboard num pad would make u go up left like keys 4 and 8 together would do

  5. Ian Bach says:

    DO we edit scripts in operating sys scripts?

    or is there a script?
    I dont see download for script??

    • Ian Bach says:

      nm i found the script and later figured out how to make the sprite…thanks…is there a way to make it so it use 2 dif running sprite sheet for when character is in run mode?

  6. Jeppe Beier says:

    Whenever i try this my game just comes with this error

    Script ‘Game_CharacterBase’ line 431: NameError occurred.
    undefined method ‘move_diagonal’ for class ‘Game_CharacterBase’

    Can you help me figuring out how to fix it?

  7. rubydragon44 says:

    When the player attempts to make an impossible diagonal movement, the character stops, instead of say, you’ve got trees on either side left and right of you and you’re trying to move diagonally left-down so the character instead moves down only.
    Do you think it would be better that way? Or do you prefer the movement process this script uses?

Leave a Reply

Your email address will not be published.

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax