- 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.
- LaTeX Cookbook
- Intel Galileo Essentials
- Visual FoxPro程序設計教程(第3版)
- Effective C#:改善C#代碼的50個有效方法(原書第3版)
- PHP 編程從入門到實踐
- 零基礎學Java(第4版)
- Eclipse Plug-in Development:Beginner's Guide(Second Edition)
- Mathematica Data Analysis
- Android開發案例教程與項目實戰(在線實驗+在線自測)
- PHP從入門到精通(第4版)(軟件開發視頻大講堂)
- PHP+Ajax+jQuery網站開發項目式教程
- Hands-On Neural Network Programming with C#
- 數字媒體技術概論
- PHP動態網站開發實踐教程
- Jakarta EE Cookbook