官术网_书友最值得收藏!

  • jMonkeyEngine 3.0 Cookbook
  • Rickard Edén
  • 758字
  • 2021-09-03 10:00:49

Firing non-instant bullets

In the previous recipe, we implemented a basic form of firing that will work for many cases. The exit velocity for a bullet is usually around 300 m/s (or close to 1000 feet/s) and might seem near-instant at close range. For ranges over 30 m (approximately 90 feet), the delay starts to get noticeable though, and more realistic games might need another model. In this recipe, we'll look into a type of bullet that travels in the game world. It's still an invisible bullet, but it can easily be visualized if required.

Getting ready

This recipe can be seen as a more advanced version of the previous recipe and won't require many changes to what we did there but will mainly contain additions. Almost all of the functionalities will be implemented in a new class, called Bullet (not to be confused with the physics engine with the same name that we use in a Chapter 8, Physics with Bullet).

How to do it...

Perform the following steps to fire non-instant bullets:

  1. Let's begin by defining our Bullet class. The worldPosition and direction variables are used by the Ray class as a starting position each step it takes. The RANGE field is a static field that defines the maximum range, inside which the bullet will be effective. The distance variable is the distance the bullet has traveled since it was instanced. It also needs to keep track of whether it's alive or not, for cleanup reasons. It should be said that this particular bullet is rather slow and short lived.
    private Vector3f worldPosition;
    private Vector3f direction;
    private float speed = 10;
    private Ray ray;
    private final static int RANGE = 10;
    private float distance;
    private boolean alive = true;
  2. To avoid unnecessary object creation, we instance Ray in the constructor as follows, which we'll reuse throughout the lifespan of the bullet:
    ray = new Ray(origin, direction);
    ray.setOrigin(worldPosition);
  3. It's in the update method that most of the work is done. At the beginning, we set the ray's origin to be the current position of the bullet. The direction will stay the same, so no need to change that. We do, however, need to set limit factorized by the time passed for the update (tpf). The limit is also the distance the bullet has traveled since the last update, so we use this to update the current position of the bullet:
    ray.setLimit (speed * tpf);
    distance += ray.limit;
    worldPosition.addLocal(direction.mult(ray.limit));
  4. If the total distance is longer than the range, the bullet can be considered beyond its effective range. We set alive to false as follows so that it can be removed:
    if(distance >= RANGE){
      alive = false;
    }
  5. The Bullet class also has a checkCollision method. It takes a list of targets as input and tries a collision between each of them and the ray. If any collision is detected, alive will be set to false and the closest CollisionResult will be returned to the calling method as follows:
    public CollisionResult checkCollision(List<Collidable> targets){
      CollisionResults collRes = new CollisionResults();
      for(Collidable g: targets){
        g.collideWith(ray, collRes);
      }
      if(collRes.size() > 0){
        alive = false;
        return collRes.getClosestCollision();
      }
      return null;
    }
  6. Next, we'll add some code to the application class. It needs to keep track of List<Collidable>, called targets and List<Bullet>, called bullets.
  7. The simpleUpdate method updates the movement of all bullets by calling their update method before seeing whether any collisions have occurred or not. Any depleted bullets are removed in a way that avoids ArrayIndexOutOfBounds exceptions:
    Bullet b = bullets.get(i);
    b.update(tpf);
    CollisionResult result = b.checkCollision(targets);
    if(result != null){
      System.out.println("hit " + result);
    }
    if(!b.isAlive()){
      bullets.remove(b);
      bulletAmount--;
      if(i > 0){
        i--;
      }
    }
  8. Create a fire() method that creates a new bullet by using the camera's location and direction as follows:
    bullets.add(new Bullet(cam.getLocation().clone(), cam.getDirection().clone()));
  9. The method is called from the InputAppState's onAction method, which is similar to how it looked in the previous recipe:
    if (isPressed && character.getCooldown() == 0f){
      ((CharacterInputTest_Firing_NonInstant) app ).fire();
      character.onFire();
    }

How it works...

The Bullet class can almost be seen as a slow ray. The Ray instance we have in Bullet is mostly out of convenience, since it's already prepared to collide with targets. By incrementing the position of the ray and having a short limit for it, we have a Ray instance that takes little steps forward in the game world, checking for collisions in each update.

If a collision has occurred, the returned CollisionResult contains information about where the collision has occurred, with what, and whether it can be used to build further functionalities.

主站蜘蛛池模板: 红桥区| 乌苏市| 太谷县| 象山县| 灌南县| 尼勒克县| 岳池县| 石台县| 东台市| 海口市| 稷山县| 上蔡县| 红河县| 沧源| 仙居县| 武冈市| 安平县| 汝阳县| 子洲县| 称多县| 都昌县| 正镶白旗| 咸丰县| 海兴县| 高尔夫| 贡山| 平泉县| 永吉县| 洪洞县| 乌鲁木齐县| 盘锦市| 大连市| 庆城县| 昌都县| 海口市| 抚远县| 大冶市| 互助| 财经| 崇文区| 平泉县|