Firing in FPS

There are several ways to perform firing, and the requirements depend heavily on the type of game. This recipe will start off with the basics, which can then be extended to support the different forms of firing. We'll create the necessary functionalities to fire instant bullets; they are performance-friendly and suitable for a fairly close-quarters FPS.

Getting ready

This example will be based on GameCharacterControl and InputAppState from the Creating a reusable character control and Attaching an input AppState object recipes of this chapter, respectively. Familiarity with the recipes is beneficial. Further, we'll use the Ray class in combination with CollisionResults to check whether the bullet has hit anything or not.

Rays can be imagined as infinite lines and are very common in game development. This is a fast way of detecting intersections with game objects and is thus suitable for instant firing. The targets might consist of any kind of spatial. In this case, it's a bunch of spheres generated by the recipe's test class.

We will let the InputAppState class handle the firing logic, and the GameCharacterControl class will keep track of cool down time of the weapon, or how long it takes between each shot. The reason we don't keep everything in AppState is that this way, the class can be used for things other than the player's character.

How to do it...

We will start by making some updates to the GameCharacterControl class. For the GameCharacterControl class, we introduce two new variables, cooldownTime and cooldown:

  1. The first is the time between shots.
  2. The second is the current countdown until the character can fire again. We need to add a getter for cooldown and the value itself is set in the following onFire method:
    public void onFire(){
      cooldown = cooldownTime;
    }
  3. Lastly, in the update method, we need to subtract cooldown by tpf if it's more than zero.

In InputAppState, we also have to make some changes:

  1. We begin by introducing a List<Geometry> called targets. This is the list of things the fired rays will check for collisions against. In the addInputMapping method, add another mapping for Fire. A suitable button is the left mouse button. This is implemented as follows:
    inputManager.addMapping(InputMapping.Fire.name(), new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
  2. In the onAction method, change the logic slightly. We add a new check for the fire action and we put the existing logic inside the else clause. We're telling character to handle all actions, except when we fire. This is implemented as follows:
    if (name.equals("Fire")) {
      if (isPressed && character.getCooldown() == 0f){
        fire();
      }
    } else {
      character.onAction(name, isPressed, tpf);
    }
  3. Now, create a new method called fire. This is where we're going to add most of the new functionalities. Inside this, we first define a new Ray class that we place at the camera's location (if it is an FPS), and we set the direction to be the same as the camera's direction, as shown in the following code:
    Ray r = new Ray(app.getCamera().getLocation(), app.getCamera().getDirection());
  4. Then, create a new CollisionResults instance, which we will use to keep track of collisions. We parse through the target list to see whether Ray collides with any of them. All collisions are stored in the CollisionResults instance as follows:
    CollisionResults collRes = new CollisionResults();
    for(Geometry g: targets) {
      g.collideWith(r, collRes);
    }
  5. Afterwards, check whether there have been any collisions. If so, get the nearest one and display the location as follows:
    if(collRes.size() > 0){
      System.out.println("hit " + collRes.getClosestCollision().getContactPoint());
    }
  6. Finally, call the character's onFire method, character.onFire();.

How it works...

With this implementation, we handle most of the actual logic that happens when firing inside InputAppState. The GameCharacterControl class is left to keep control of whether firing is possible or not. Some further work on this could have the character play an animation and keep track of the ammunition.

The Ray object we're using is being fired out of the camera. This makes it easy to set both the location and direction. This would be the case for a game in iron sights or sniper mode. If you want to fire from the hip, for example, it would be slightly more complicated.

Rays are normally very fast. Using them can, however, become performance-demanding in large game worlds with complex collision shapes. This is one reason for keeping track of the items to be checked against in a list rather than using the whole rootNode. In other cases, it's good to first filter down the list, maybe based on the distance from the player.

The CollisionResults class stores collisions between spatial or ray. It contains a list of CollisionResult objects, which in turn has a number of useful methods for determining where a collision has occurred and between what.

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

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