1
\$\begingroup\$

I'm building a Pacman clone in Unity 3D and am now thinking about how to restrict the movement of the player to the hallways that the map is made up of.

Basically, the player is only allowed to change direction at an intersection. When he is not at an intersection, he can only continue to move in the same direction, or reverse by 180°.

The simplest idea I have come up with so far is, to have little un-rendered Boxes with Colliders at every intersection, and when the player collides with one of those, the collider knows which directions the player can go from there.

Example

This seems easy, but a lot of work. Is there an easier way to restrict the movement of the player to given track (as in a railroad), but allow him to change "onto" a different "track" at given points (those would be the intersections)?

\$\endgroup\$
2
\$\begingroup\$

You're over thinking this - using collisions is way too complex for the goal of a Pac-Man clone. Consider a grid approach instead, and forget collisions. If your goal is to create a clone, this is about functionality, not fads.

Why a Grid?

Give yourself the following conditions for a grid approach...

  1. Movement is only at 90 degree angles (up, down, right, left)
  2. Pellets exist only on grid points
  3. The player will come to a stop on a grid point if no arrow keys (or joystick or whatever) is pressed
  4. Wall/solid elements also exist only on a grid point
  5. Map is wrappable - player can exit one side and come back onto the other side

Keep It Simple!

So basically create a 2d array the size of your map, where each entry has a value of 0 for empty, 1 for a pellet, and 2 for solid (no walk). Then, when you are trying to decide if the player can move to an adjacent cell, simply check the target cell location to see if it's solid or not. If so, no movement allowed. If solid, movement allowed. If occupied by a pellet, award a point and remove the pellet.

Put your player at some starting coordinate that is not solid with no movement. Now whenever the player wants to move, check the target cell to see if it is free (i.e., not solid). If so, move player to that spot. If there's a pellet there, give them a point. And if you allow coordinates to wrap, you can easily handle maps that wrap (e.g., go off left side of map and come back on on right).

The best thing about this approach is that it's massively portable. You could use this to make it with JavaScript/CSS or even text-based.

Advantages

  • Super simple implementation
  • Handles map wraparound in a trivial way
  • Extremely portable to any environment (even a text based game on an old DOS box)
  • Simple solution lets you concentrate on the real challenge, which is modelling Ghost movement :)
  • Probably how the original works
  • Bragging rights over how "incredibly simple the implementation is"

Disadvantages

  • Doesn't work for curved, non-linear paths
  • No bragging rights over how "incredibly complex the implementation is"

Pseudo Code

Here's some pseudocode...

// --- Map constants and vars    
define MAP_WIDTH 20
define MAP_HEIGHT 20
define EMPTY 0
define PELLET 1
define SOLID 2
var map[GRID_WIDTH, GRID_HEIGHT];

// --- Built map
map = build_map();

// --- Start player at [1,1], which is open
player_x = 1;
player_y = 1;

while (game_active) {
   // --- Scoring check
   if (map[player_x, player_y] == PELLET) {
      // --- Pellet in player spot, so give point and remove pellet
      score += 1;
      map[player_x, player_y] = 0;
   }

   // --- Set target_x/y to where player will go (default to nowere)
   target_x = player_x;
   target_y = player_y;

   // --- keypress check for movement.  If target cell is free, player moves there
   switch (key) {
      case 'W': // up
         // --- Test one map cell up, allowing for possible map wrap
         target_y = player_y - 1;
         if (target_y < 0) target_y += MAP_HEIGHT;
         break;
      case 'A': // left
         target_x = player_x - 1;
         if (target_x < 0) target_x += MAP_WIDTH;
         break;
      case 'S': // down
         target_y = player_y + 1;
         if (target_y >= MAP_HEIGHT) target_y -= MAP_HEIGHT;
         break;
      case 'D': // right
         target_x = player_x + 1;
         if (target_x >= MAP_WIDTH) target_x -= MAP_WIDTH;
         break;
      default:
         // --- Do nothing
         break;
   }
   // --- Check if target for player movement is not solid
   if (map[target_x, target_y] != SOLID) {
      // --- target point is NOT solid
      // --- Presume this handles animating movement and setting player pos to target
      move_player_to(target_x, target_y);
   } else {
      // --- Player target is solid, so do nothing
   }

   // --- Perform monster logic
   move_monsters();

   // --- Render map
   render_map();
}
\$\endgroup\$
  • \$\begingroup\$ This would have been my fallback option, it's how I implemented it before. I thought there might be a "nicer" way to do it. \$\endgroup\$ – Lukas Knuth Dec 22 '14 at 21:27
  • \$\begingroup\$ Keeping your code and approach simple is best. You need to spend your time thinking about Ghost logic and how that will work, not how you manage what can be a very simple approach to path management :) \$\endgroup\$ – Tim Holt Dec 22 '14 at 21:44
  • \$\begingroup\$ Also, consider that this approach nicely handles the ability for your game map to wrap. Straight up collision based doesn't handle that. \$\endgroup\$ – Tim Holt Dec 22 '14 at 21:51
  • \$\begingroup\$ What do you mean by "wrap"? \$\endgroup\$ – Lukas Knuth Dec 23 '14 at 0:50
  • \$\begingroup\$ By wrap, I mean the player goes off one side of the screen and pops onto the other side. \$\endgroup\$ – Tim Holt Dec 23 '14 at 1:20
2
\$\begingroup\$

Since this is a small game, I think a 2D collision map would work great...that's what I call them. I don't know if that's the correct name.

Basically, you have an image of where they can move, defined by 2 colors. Black, White. In my example they are allowed to move on the white part. It's a duplicate of the play screen but just containing 2 colors. Ideally your sprite that you want to move would be the exact width of the movement lane, so that it would move in the appropriate directions. There wouldn't be any small pixel movements to the sides.

enter image description here

In a large game this would be unfeasible, however for a small pac man clone it works great. You just detect the pixel color of the collision map they are trying to move onto by its coordinates. You will always get a RGB(0,0,0) or RGB(255,255,255) back. If it's white, don't halt sprite, if its black, stop movement.

\$\endgroup\$
  • \$\begingroup\$ That sounds good. Do you know the "standard term" for this? \$\endgroup\$ – Lukas Knuth Dec 22 '14 at 17:01
  • \$\begingroup\$ katyscode.wordpress.com/2013/01/18/… She calls it "The Mask approach" From a gfx side you're applying an image mask to an image. Maybe someone else would know a better term. \$\endgroup\$ – Joe Swindell Dec 22 '14 at 17:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.