2
\$\begingroup\$

I have a maze grid of cubes that my player navigates through. I'm trying to make it so that each cube is only visible when the player is near it (so the player can only see the 4 cubes immediately around them at any one time).

I've been trying to do this using OnCollisionEnter/Exit, and turning on/off the renderer in each cube when the player is nearby (with colliders large enough that in theory the player should be touching more than one at a time). But this just creates a flicker effect.

I've also tried doing it using OnTrigger but that's not working because I need the cubes to be solid objects the player cannot pass through.

If anyone has some ideas, that'd be awesome. I've been trying to crack this one for a while but I'm a researcher rather than a coder so my programming skills are limited.

Thanks!

\$\endgroup\$
  • \$\begingroup\$ You know you can have more than one collider, right? One solid that the player can't pass through, and another larger trigger zone for controlling visibility, for example. \$\endgroup\$ – DMGregory Jul 1 '18 at 13:29
  • \$\begingroup\$ Could you elaborate on the flickering? Do you mean you don't like that objects appear and disappear in a single frame instead of fading in? Or that objects keep turning on and off non-stop? Both situations have different answers. \$\endgroup\$ – TomTsagk Jul 2 '18 at 1:53
  • \$\begingroup\$ It was the single frame disappearance that was causing issues. Because my player is so close to the cubes, it was almost guaranteed it was going to give my subjects a headache watching it. Using the fade in/fade out has done the trick. \$\endgroup\$ – Friday Jul 3 '18 at 4:58
2
\$\begingroup\$

Assuming the flicker is just because the visibility is changing too fast, you could smooth over the flicker by making the cubes fade in and out slowly over time.

You could have them start fading in, if they aren’t already fading in inside OnCollisionEnter and fade out if they aren’t already fading out inside OnCollisionExit.

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

If your blocks are in a grid, then it should be easy enough to do it without worrying about physics.

That said, if you do want to do it with physics, I would recommend using two colliders. The first is the cube itself, then there is a child game object which has a larger one which is marked as a trigger. Use OnTriggerEnter/Exit in a MonoBehaviour on the child GameObject to show/hide the parent's mesh.

Now, if you want to do it using a multidimensional array instead, that could be a bit more precise about having exactly the walls you want to be visible.

This example will put all of the code in a player class. It will also make a few assumptions about the cubes.

Assumption 1: All cubes are tagged with a special "wall" tag.

Assumption 2: All cubes are at integer positions and are of unit size (1x1x1). For example, a cube could be located at 1,1, but not at 1.5,1. If this is not the case, then some adjustments will need to be made to find all the cubes and put them in the right places in the array.

Assumption 3: the cubes are on the X-Y plane. If they are on the X-Z plane, minor adjustments will be needed.

Assumption 4: The size of the grid is not too big. A 100x100 grid is a 10,000 element array. Keep this in mind.

Some of these assumptions are to make it easier to find the cubes when the game starts. It is also possible to do this at design time, but it's a bit more work.

First, you'll need to keep track of all of the cubes in an array.

public class Player {
  MeshRenderer[,] grid;
  Vector2 minPos;
  int gridW, gridH;

  void Start() {
    GameObject[] walls = FindObjectsWithTag("wall");

    Bounds b = new Bounds(walls[0].transform.position, Vector3.zero);
    foreach (var wall in walls) {
      b.Encapsulate(wall.transform.position);
    }

    Vector2 size = b.size;
    minPos = b.min;
    gridW = (int)size.x;
    gridH = (int)size.y;
    grid = new MeshRenderer[gridW, gridH];

    foreach (var wall in walls) {
      Vector2 pos = wall.transform.position - minPos;
      var renderer = wall.GetComponent<MeshRenderer>();
      renderer.enabled = false;
      grid[(int)pos.x, (int)pos.y] = renderer;
   }
 }

}

So far so good. The grid is populated with cubes. It is basically a representation of your maze in code. If we wanted to, we could write a solver now. But that's not what we're here for. Now we just need to turn on the cubes near the player whenever they move and turn off the old ones. We should keep track of the old and new grid positions of the player so we can turn off old cubes and turn on the new ones without iterating through the whole grid.

int oldX = int.MinValue;
int oldY = int.MinValue;

void Update() {
  Vector2 pos = transform.position - minPos;
  int x = Mathf.RoundToInt(pos.x);
  int y = Mathf.RoundToInt(pos.y);
  if (oldX != x || oldY != y) {
    SetCubesEnabled(oldX, oldY, false);
    SetCubesEnabled(x, y, true);
    oldX = x;
    oldY = y;
  }
}

void SetCubesEnabled(int x, int y, bool enable) {
  SetCubeEnabled(x-1,y,enabled);
  SetCubeEnabled(x,y-1,enabled);
  SetCubeEnabled(x+1,y,enabled);
  SetCubeEnabled(x,y+1,enabled);
}

void SetCubeEnabled(int x, int y, bool enable) {
  if (x >= 0 && x < gridW && y >= 0 && y < gridH && grid[x,y] != null) {
    grid[x,y].enabled = enable;
  }
}

This should be sufficient to do the basics of what you want without relying on the physics system. Although relying on the physics system is a viable option as well, as I outlined above.

\$\endgroup\$
  • \$\begingroup\$ Nice idea! And thank you for the detailed reply. I had a version that used an array to do the grid but needed to go back to prefab/physics for entirely unrelated reasons. \$\endgroup\$ – Friday Jul 3 '18 at 4:56

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.