Calculated the walk direction vector for the character control and made the player the target. So the enemy now follows the character for attack.
I finally implemented the Save/Load game feature in my FPS framework.
It took some effort but thankfully at the end it wasnāt as troublesome as I expected.
I chose to use javaās serialization for simplicity, I know it has some issues (mainly security vulnerabilities) but I donāt expect or recommend people to share or download save files from unreliable sources.
In my framework entities have three parts:
- Spatial: I serialize a factory object for each spatial (usually loads a model from a file).
- Physic Object: I take advantage jMEās Savable implemented by Minieās PhysicsCollisionObject and serialize them (most are simple capsules or box collision shapes).
- Behavior: The entities behavior is scripted in groovy by implementing my EntityBehavior interface, I extended Serializable and forced all scripts to be serializable (had to make some fixes, but most scripts have simple logic anyway).
For the level map (itās an entity) I omitted it in the save file and instead save which level(s) are loaded.
Then in the load process I load the level without spawning its entities, then spawn entities as I continue reading records from the file.
With all that I didnāt have to dump any mesh buffer, so the file is acceptably compact (around 200kb for 100 entities).
I implemented all the strictly functional parts but I still miss some things, like active particles, decals and playing sound effects. These are not as important but are nice to haves, depending on how hard or slow to load they are.
I split the video in two parts, first showing the normal save and load and then showing a multi-level setup, the two rooms are two different ālevelsā in my game, so I can support Hexen like multimap hub levels and keep the whole state, not just the active level.
Be careful with java serialisation. I used to do my save game formats that way but i found them very difficult to retain compatibility of the save file between game versions (changing the java classes would make the binary file incompatible with limited options for migration). (I think the jme capsule system might be nore resistant to that problem)
Yeah, thanks for the heads up! I considered it but I decided to ignore backward compat for the time being.
I know how much work it is to maintain data models from my work and since this is my hobby I want to keep the complexity low and focus on making games.
I donāt expect to have that many players at the same time I deliver updates, and if that happens Iāll make it easy to skip levels to recover progress.
There are many options if you plan aheadā¦ but yes, Java serialization is not great for code that is still influx.
I think all of the ways you can fix something in JMEās capsule system, you can fix them in Java serialization. They will both ābreak completelyā under certain types of changes.
Java serialization is a lot more flexible than many realize.
Edit: and I would never in a million years replace Java serialization with JMEās binary capsule. There are perhaps better ways to do save games than Java serializationā¦ but JMEās approach is definitely not one of them. Itās effectively a ānot invented hereā version of Javaās serialization thatās only original āhigh claim for existenceā was being able to target XML as well as pure binary.
Otherwise, it has all of the same limitations as Java serialization (and perhaps a few more).
Interesting, ive never used the capsule system so was hedging my bets.
I typically serialise to json and use a library (jackson) to map to java DTOs. Which is relatively easy to begin with and gives me options to hand write deserialisation code if anything weird happens (and can transparently handle things like new fields). Obviously might have issues if the save file was huge
Yeah, I use JSON (google gson) if I need to read it or editā¦ but use Java serialization for a lot of stuff. (For close to 20 years now.)
I have some custom binary formats, too, but for cases where I wanted to tightly control the size of the results. Java serialization is pretty compact and you can control the whole serialization of the data yourself, if you wantā¦ but itās always going to serialize class info and stuff.
I use Derby to save game states, high scores, key config, etc.
ā¦yeah, a relational database is one way, too. Tends to be a little opaque for playerās local save games.
I prefer hsqldb, personally.
In the gif the camera is moving up and down in the Y axis. The triangle moves clock or counter clock wise to match the camera position on the x and y coordinates. In other words the triangle looks at the player.
Iām adding the ability for the enemy to rotate to look at the player.
Iām currently using: Vectors, Trigonometry and Quaternions to achieve this. Iām almost done.
Problems to solve:
undefined tangent at 90, -90, 270 degrees.
angles donāt go higher than 90 degrees.
Summary of things Iāve completed in the last 7 days:
- The CharacterControlled enemy follows the player.
- The geometry looks at the player.
- Used Breadth First Search navigate the enemy from point a to point b.
So in short, Iām almost finished adding intermediate Ai to my game.
Didnāt use a third party Ai library, so I learned A LOOOT.
Frequently trig is unnecessary with steering algorithms.
Are you setting the object to look at the player instantly or will there be some physical limitations involved like rotation speed?
If you havenāt read about steering algorithms yet then I highly recommend. Tons of fun.
ā¦and usually the inputs to a steering system can be done with only dot products.
Thank you for the suggestion.
But Iām not currently looking for steering algorithms.
Time Stamp: 16m 37s. This shows the exact problem Iām trying to solve. Though Iām solving it even before I look at the video.
āFrequently trig is unnecessary with steering algorithms.ā Even if that was the case I would still go the Trig route. Iād prefer build my Math foundation with practice. Iām very careful not to jump from internet article to internet article when looking for an answer. Iād prefer to spot the solutions inside college textbooks, then as a laaaast resot I look at google and youtube.
āAre you setting the object to look at the player instantlyā Yes, instantly. Maybe in the future I add turrets that take some time to look at the player. But right now Iām exercising my brain muscles with: Vectors, Trigonometry and Quaternions.
Trig is only nice because we get to see angles. Itās just frequently an extra unnecessary math stepā¦ trig to find the angle, some potentially dodgy angle math, then trig to turn the angle back into sin/cos again. So while itās nice to āseeā those angles, the math usually doesnāt need it.
The dot product will already magically provide the cosine between two vectors and it doesnāt worry about angle math, quadrants, or any of that stuff. Much less to go wrong. Thatās why itās good for steering algorithms because dot product = 0 means donāt turn. 0ā¦1 means turn left, 0ā¦-1 means turn rightā¦ already nicely scaled by how fast to turn.
Even for āinstant lookā, trig is unnecessary as Quaternion().lookAt() will already do that. In the days before quaterions, weād just construct the three vector axes and turn that into a rotation matrix. (A rotation matrix is just the three orthogonal axis vectors.) And so a ālookā matrix is a vector and two cross products away.
So to me, itās not really about āmath foundationsā but using math not really suited to the task.
Yes. Thatās a lot of math. It may be simple for you, but for a mere mortal like me Iām not there yet. Iāll get there eventually. But for now thatās why I mention what I said.
Iāve been meaning to create torch fire for some time, so here it is using the emitter.
Some gameplay itās happening underground, so it makes sense to have torches light the way.
Are you trying to avoid using JMEās Quaternion class? Like, to learn the basics or something?
You got me. Yes.
I try to understand things like Quaternion.lookAt() I donāt use it right now because Iāll learn how to do it at some time.
But functions like the ones below I already know to how code so I use them in development.
Vector3f.normalize()
Quaternion.mult()
In short, if a class or a function is fundamental and I donāt understand it. I try to solve it myself. This doesnāt add to development time because I do it in off-hours. But itās the only way I learn how to create games and 3D tools.
Itās true.
From experience, invest a lot of time in vectors, dot product, and cross product. 90% of all 3D math can be done with just simple vector math, the dot product, the cross product, and the pathagorean theorem. (length()).
I spent a good deal of my life poorly grasping a āwhole lot of mathā that I was able to dismiss by knowing the vector ātricksā. And it turns out that āwhole lot of mathā was not helpful in the end.
Coming up, I spent a lot of time reading Michael Abrashās books on graphics, 3D, whatever he wrote. (For you youngunās, he was āthe manā when it came to performance optimization back in the early daysā¦ and even worked for āidā for a while to help John Carmack with the original Quakeā¦ and wrote some nice books/articles out of that.) Between that and Foley and Vandam (still on my shelf), my head was swimming with stuff.
By then, I well knew about dot products because they were the magic that made Doom possible at all. Dot product and cross product are really simple functions to understand āthe mathā but have so many magic uses that I feel like I still find new ones.
The day I got to throw āthe restā away was when reading one of Abrashās later books where he mentioned in passing āIt just occurred to me that multiplying a vector by a rotation matrix can be thought of as 3 dot products with the axis vectors.ā That blew my mind. A matrix went from a magic thing to something I could read with my own eyes.
In modern times, it can be useful to get a sense of a Quaternion, tooā¦ by turning it into a 3x3 rotation matrix and just reading it. Which is literally identical to:
System.out.println(q.mult(Vector3f.UNIT_X));
System.out.println(q.mult(Vector3f.UNIT_Y));
System.out.println(q.mult(Vector3f.UNIT_Z));
ā¦the axes of rotation and a 3x3 rotation matrix.
If youāve read this far, the math of the dot product is easy to understand (few multiplies and adds). What it means is:
v1.dot(v2) = v1.length() * v2.length() * cos(angle between v1 and v2 in 3 space)
You can spend a lot of very complicated math figuring out the āangle between v1 and v2 in 3 spaceāā¦ but itās already right there.
When v1.length() == 1 and/or v2.length() == 1 (unit vectors) is where things get really interesting.
Maybe I should write an article.
Behold! My dear jmonkeys!
What I solved:
The enemy follows the player.
The enemy looks at the player.
To do:
Provide the position of the player when the enemy is far away via Breadth First Search.
Math used:
Vectors addition and subtraction.
Math.atan2(z,x). I think of this function as atan(rad) but it takes care of the edge cases.
Quaternion.
In case it helps others learn.
I personally find that learning calculus and applied linear algebra solves a major part of this problem and links the low-level mathematics with the algorithms you trying to use. Trigonometry is a very fundamental in itself, useful, but cannot be translated directly to data structures unlike vectors and matrices.