- 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.
- UNIX編程藝術(shù)
- JSP網(wǎng)絡(luò)編程(學(xué)習(xí)筆記)
- Delphi程序設(shè)計(jì)基礎(chǔ):教程、實(shí)驗(yàn)、習(xí)題
- Game Programming Using Qt Beginner's Guide
- Manga Studio Ex 5 Cookbook
- 數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)明教程(第2版)微課版
- 編寫高質(zhì)量代碼:改善C程序代碼的125個(gè)建議
- 秒懂設(shè)計(jì)模式
- Visual Basic程序設(shè)計(jì)實(shí)驗(yàn)指導(dǎo)(第4版)
- 名師講壇:Java微服務(wù)架構(gòu)實(shí)戰(zhàn)(SpringBoot+SpringCloud+Docker+RabbitMQ)
- Flux Architecture
- Python數(shù)據(jù)可視化之Matplotlib與Pyecharts實(shí)戰(zhàn)
- Go并發(fā)編程實(shí)戰(zhàn)
- RabbitMQ Cookbook
- 智能搜索和推薦系統(tǒng):原理、算法與應(yīng)用