- 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:
- Let's begin by defining our
Bullet
class. TheworldPosition
anddirection
variables are used by theRay
class as a starting position each step it takes. TheRANGE
field is a static field that defines the maximum range, inside which the bullet will be effective. Thedistance
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;
- 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);
- 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));
- If the total distance is longer than the range, the bullet can be considered beyond its effective range. We set
alive
tofalse
as follows so that it can be removed:if(distance >= RANGE){ alive = false; }
- The
Bullet
class also has acheckCollision
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 tofalse
and the closestCollisionResult
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; }
- Next, we'll add some code to the application class. It needs to keep track of
List<Collidable>
, calledtargets
andList<Bullet>
, calledbullets
. - 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 avoidsArrayIndexOutOfBounds
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--; } }
- 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()));
- 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.
- SQL Server 從入門(mén)到項(xiàng)目實(shí)踐(超值版)
- Visual Basic程序開(kāi)發(fā)(學(xué)習(xí)筆記)
- 簡(jiǎn)單高效LATEX
- Python測(cè)試開(kāi)發(fā)入門(mén)與實(shí)踐
- Visual Basic程序設(shè)計(jì)與應(yīng)用實(shí)踐教程
- Nexus規(guī)模化Scrum框架
- Java 9模塊化開(kāi)發(fā):核心原則與實(shí)踐
- 從Excel到Python:用Python輕松處理Excel數(shù)據(jù)(第2版)
- MATLAB 2020從入門(mén)到精通
- Visual C#通用范例開(kāi)發(fā)金典
- Visual FoxPro程序設(shè)計(jì)習(xí)題集及實(shí)驗(yàn)指導(dǎo)(第四版)
- SQL Server與JSP動(dòng)態(tài)網(wǎng)站開(kāi)發(fā)
- 計(jì)算機(jī)應(yīng)用基礎(chǔ)實(shí)踐教程
- Scratch·愛(ài)編程的藝術(shù)家
- Spring 5 Design Patterns