- jMonkeyEngine 3.0 Cookbook
- Rickard Edén
- 784字
- 2021-09-03 10:00:48
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
:
- The first is the time between shots.
- 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 followingonFire
method:public void onFire(){ cooldown = cooldownTime; }
- Lastly, in the update method, we need to subtract
cooldown
bytpf
if it's more than zero.
In InputAppState
, we also have to make some changes:
- We begin by introducing a
List<Geometry>
calledtargets
. This is the list of things the fired rays will check for collisions against. In theaddInputMapping
method, add another mapping forFire
. A suitable button is the left mouse button. This is implemented as follows:inputManager.addMapping(InputMapping.Fire.name(), new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
- In the
onAction
method, change the logic slightly. We add a new check for the fire action and we put the existing logic inside theelse
clause. We're tellingcharacter
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); }
- 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 newRay
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());
- Then, create a new
CollisionResults
instance, which we will use to keep track of collisions. We parse through the target list to see whetherRay
collides with any of them. All collisions are stored in theCollisionResults
instance as follows:CollisionResults collRes = new CollisionResults(); for(Geometry g: targets) { g.collideWith(r, collRes); }
- 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()); }
- 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.
- Boost C++ Application Development Cookbook(Second Edition)
- OpenNI Cookbook
- Mastering Python Networking
- GeoServer Beginner's Guide(Second Edition)
- Ext JS 4 Web Application Development Cookbook
- 從0到1:Python數據分析
- C語言課程設計
- Learning ArcGIS for Desktop
- Java系統化項目開發教程
- AIRIOT物聯網平臺開發框架應用與實戰
- 自學Python:編程基礎、科學計算及數據分析(第2版)
- Learning Nessus for Penetration Testing
- Arduino電子設計實戰指南:零基礎篇
- 基于GPU加速的計算機視覺編程:使用OpenCV和CUDA實時處理復雜圖像數據
- Python機器學習開發實戰