Location-dependent animation – edge check

In certain games, players traverse dangerous areas where a fall off from a ledge could lead to their deaths. Sometimes, in these games, the player is not meant to fall off and their movement is restricted when they are close, or the player gets an extra warning before they plummet.

The control we'll develop can be used for any of those things, but since this chapter is about animations, we'll use it to play a special animation when the player gets too close to the edge.

Getting ready

The recipe will use similar patterns that have been used before in this chapter and we'll also use the animation manager control from earlier in the chapter. Any animation control will be fine to use, but it should have separate channels for the upper and lower parts of the body.

How to do it...

We can implement almost everything we need in a single class as follows:

  1. We begin by creating a class called EdgeCheckControl, which extends AbstractControl and contains the following fields, as shown in the following code:
    private Ray[] rays = new Ray[9];
    private float okDistance = 0.3f;
    private Spatial world;
    private boolean nearEdge;
  2. We define the nine rays that will be used for collision detection. In the setSpatial method, we instantiate them and aim them downwards, as shown in the following code:
    for(int i = 0; i < 9; i++){
      rays[i] = new Ray();
      rays[i].setDirection(Vector3f.UNIT_Y.negate());
    }
  3. In the controlUpdate method, we begin by placing one of the rays at the center of the character, as shown in the following code:
    Vector3f origo = getSpatial().getWorldTranslation();
    rays[0].setOrigin(origo);
  4. We step around the character, placing the remaining rays in a circular shape. For each, we see whether it collides with something using the checkCollision method. If it doesn't, we don't need to check the rest and can exit the loop using the following code:
    float angle;
    for(int i = 1; i < 9; i++){
      float x = FastMath.cos(angle);
      float z = FastMath.sin(angle);
      rays[i].setOrigin(origo.add(x * 0.5f, 0, z * 0.5f));
                
      collision = checkCollision(rays[i]);
      if(!collision){
        break;
      }
      angle += FastMath.QUARTER_PI;
    }
    private boolean checkCollision(Ray r){
      CollisionResults collResuls = new CollisionResults();
      world.collideWith(r, collResuls);
      if(collResuls.size() > 0 && r.getOrigin().distance(collResuls.getClosestCollision().getContactPoint()) > okDistance){
        return true;
      }
      return false;
    }
  5. In the last step in this method, we call the animation manager and tell it to play or stop playing the near-edge animation, as shown in the following code. We do this based on whether all the collisions have been detected or not, making sure we only send any change in state:
    if(!collision && !nearEdge){
      nearEdge = true; spatial.getControl(AnimationManagerControl.class).onAction("NearEdge", true, 0);
    } else if(collision && nearEdge){
      nearEdge = false;  spatial.getControl(AnimationManagerControl.class).onAction("NearEdge", false, 0);
    }
  6. Switching to our animation manager class, we modify it accordingly. The state is stored here so it can be used to see what other animations are allowed to be played, as follows:
    if (binding.equals("NearEdge")) {
      nearEdge = value;
      if(nearEdge){
        setAnimation(Animation.Jumping, Channel.Upper);
      }
    }

How it works...

With each update, the nine rays we create are placed in a circle around the character, with one in the center.

They will check for collisions with a surface below them. If any of them (this might be changed to two or three) does not hit something within okDistance, it will be reported as the character being close to a dangerous edge.

The okDistance has to be set to something suitable, higher than a step on a stair, probably at a height where the player could take damage.

When this happens, the animation manager will be called with the NearEdge action set to true. This will apply the jumping animation (wild flaying of the arms) to the upper body of the character while still allowing other animations to be played on the lower part.

The NearEdge Boolean is used to make sure that we only send the call to the animation manager once.

When doing collision checks, one should be careful about the amount and shape of the objects that are being collided. If the world is large or constructed from complex shapes (or even worse, if it has MeshCollisionShape), we should try to find optimized ways of applying the method. One way could be to separate the world into parts and have an auxiliary method to select which part to collide against. This method might use contains on BoundingVolume to see the part the player is located in.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.46.78