WIP The Legend of Squishy
-
Gothon answered my plea for help. My array of cats wasn’t moving because either a) My function was relying on structs being passed by reference, and that wasn’t happening because my cat structs were passed as an array index whereas my player character was passed directly, or b) My function was designed to modify global structs, and therefore didn’t work because the cat structs weren’t global, or c) something similar to, but not exactly the same as a and b.
Anyway, after considering writing out all the npc structs (or at least a varied chunk of them) by hand so that they’re definitely global, and then doing whatever calculations on each of them by name, which would take a lot of copy and pasting and would result in a really long program, I decided to go back in and have a look at my functions instead. They now return the relevant values and it wasn’t as much trouble as I thought it was going to be.
-
So. I now have 50 pink cats in my village, with a bit of randomness, and some reaction to the player character, and immediately, physics glitch hilarity ensues.
-
That’s cool! Nice one!
-
So, it’s been a while. I have now got the beginnings of a basic HUD, with functions cleverly written to take in a few numbers (number of coins, player health and max health, and how many health points one heart represents), and firstly spits out an array of numbers corresponding to 2d sprites on a spritesheet (one of the ’platformer’ ones by KENNEY), and secondly takes that and displays the sprites on screen in the right places. Converting a number 4321 to seperate digits took a bit of thought, but I did it something like this:
Array digitsarray[5] Thousands = int (number*0.001) Hundreds = int ((number-(thousands*1000)*0.01) Tens = int (((number-(hundreds*100))-(thousands*1000))*0.1) Ones = ((number-(tens*10))-(hundreds*100))-(thousands*1000) Digitsarray[0] = Sprzrodig+thousands Digitsarray[1] = sprzrodig+hundreds Digitsarray[2] = sprzrodig+tens Digitsarray[3] = sprzrodig+ones Digitsarray[4] = -1 Return digitsarray
Where sprzrodig was the number of the ’0’ character on the spritesheet, according to the way Fuze does stuff like that. It looks pretty cool :) The function for hearts does a similar type of maths trick, but to find out how many hearts shouöd be displayed, and how many should be full and how many empty, and if there’s a need for a half-heart. There’s no animation, but it seems to display things right.
I also put in a way to get ’Surface Normals’ from the terrain. It works okay! Good enough to have hills hard to climb and bouncy balls bounce off in mostly somewhat realistic directions. It’s far from perfect though - at the moment I have a very crude way to drop things under gravity, but stop when they hit the ground, and I think to get it better would involve figuring out a more sophisticated way of doing that. Too much for me at the moment, this will have to do, at least until after I’ve taken care of more important stuff. No complaints about ’Surface Normals’ though. The maths was short. I copied a few lines from a blog post I read, and that was basically it. 3 points in 3d space are 3 vectors. Imagine them. It’s a triangle. Call the 3 points a,b, and c. a-b is an edge. b-c is also an edge. There’s a function in fuze called cross(), which gets the *cross product” of 2 vectors and returns another vector that’s 90 degrees to them both. That’s your surface normal. There’s also a function in fuze called reflect() which is easy to use too. To bounce a moving 3d object off of a surface, all you need are 3 points on that surface, the acceleration vector of the moving object, and the cross() and reflect() functions. Just a few lines, and it looks and works great! Sliding it along the surface isn’t that hard either. Amazing!
I have removed the random objects, gravestones, houses and trees, the way I had that done made collision detection maybe impossible. I am working on it.
Anyway, here’s the vid:
-
The last minute of the vid shows some weird physics glitches that only happen in the water. That too can stay like that until more important stuff is done.
-
That is so cool. Really interesting about the surface normals, too!
-
Wow, it’s been a long time since I posted here.
It turns out there are some problems with the way I’m getting my surface normals, and my heights. I had made a bad assumption about how the terrains are put together, and while it works fine 95% of the time, it’s not right at the corners. In this one you can only see his hat!
And in this one he’s floating! :o
It turns out that the terrain points you define are (except for the edges and corners) made of 8 triangles each, not two. And there are midpoints that are generated automatically. So a 3x3 terrain with the middle terrain point set at a height of 3, while the rest are set at 0 actually has 15 points! The midpoint heights seem to be averages of the two either side and up and down. I haven’t wrapped my head around it completely, but I drew this, then temporarily put some ’measuring dots’ into the game, and it checks out. The numbers circled in green are the user-settable terrain points. The other numbers are generated automatically by Fuze.
-
I now have a function called getpreciseterrainpoints(). I’ve put some small white spheres as markers. As you can see, they line up nicely!
The centre dot is the closest actual terrain point to the player. Not shown are the 8 user-defined terrain points around it. The surrounding dots are my prediction of the fuze-generated points. The fact that they line up means I’m using the right method to predict it (just averaging horizontally/vertically). I haven’t noticed any mismatches so far anyway. I have a feeling I might be doing some redundant calculations, but ’so what?’ For now...
-
The next step is to find out which three points the player is between. That will give me a triangle that I can use to get the surface normal (in a similar way to before) or the height, which I’ll come to later. First to get the points.
There’s a thing you can do with dot() to find out if a point is on one side of a line or the other. The dot() of two normalise()d vectors gives you a single number between -1 and 1 as an answer. It only works up to 180 degrees though for some reason, so I first had to find out which quarter of the square the player is in, which was easy but confusing. if playerposition.x > middlepoint.x and playerposition.z > middlepoint.z then it’s in the Southeast quarter. Easy maths, but confusing. Here’s why (for me)
In 2d, +x is across to the right, and +y is down. The fact that y starts at 0 at the top and then goes down is also a bit confusing, but I’ve been doing that for years and am used to it.
In 3d, there are apparently 2 ways of doing it- a ’left handed’ coordinate system, and a ’right handed’ coordinate system. Apparently also these are used differently by maths people and computer people so if you try to google a pic it might not help! Just like googling a pic for a 2d coordinate system is pretty likely to get you one where y starts off at zero at the bottom!
Anyway, Fuze is ’right handed’ which means +x goes to the right, +y goes up (which makes sense to me), but +z goes towards the screen somehow and you have to go -z instead to go ’forward’. Or something?
So there’s that, and the fact that I keep wanting to use x,y for ground coordinates , forgetting that y goes up, not across. And that a 2d array looks like this[][] but the first number is rows, not columns... and depending on where your camera is at everything might be flipped anyway... It’s a lot to keep track of!I’ve started using compass directions in my code. Hopefully it’ll help.
...anyway, I found the quarter of the square the player character was in, and using the dot() function, and 4 hours of trial and error, I now have a reliable way of finding out which triangular segment of the square the player is in.
Proof: he’s standing inside those 3 square posts. The red cone is pointing north, btw.
-
So. That all worked well! I found what triangle my object was in, then used a thing called ’barycentric coordinates’ to interpolate between the three heights of the triangle points to find out the precise height of the land. Here’s a screenshot of the ’get barycentric coordinates’ function.
It works, and it’s pretty efficient, but only because the terrains are sortof mostly 2d. If you look at them from the top and ignore the heights, they’re just a grid! If I was trying to get these ’barycentric coordinates’ in proper 3d where the points could be any old place, then there’d be a lot more checks and complexity needed, but in this case everything’s cool. See here:
There he is, standing on one of those awkward corners from before. But properly this time :)
-
I didn’t do the maths for the barycentric coordinates, btw. Luckily, clever mathematicians and programmers had already worked it all out and after some googling and reading, I found that on stackexchange. ’Barycentric coordinates’ is not crazy difficult to understand if you can handle some trigonometry and pythagoras, but I’m glad that it was there to google. I would have never worked it out by myself, that’s for sure!
-
I’ve also applied this new way of getting triangles to getting the surface normals that my fireballs bounce off, and it’s had 2 effects: one is that I’m no longer getting ’NaN’ results (which is a thing that happens when you put certain values into dot() and cross() - in this case I was putting two identical values in occasionally.) NaN means ’Not a Number’. It’s like a ’divide by zero’ error. Not good. It was causing crashes. Nowadays it hardly crashes at all!
The second effect is that my ball bounces look much better! Impossible to show in a picture, unfortunately, but if you use the download code z6v23mnds3 , you can try it out.I have also made improvements to object placement, and I encourage anyone wanting to try it out to delete the save file (by uncommenting a line near the start of the program, running it, quitting out, recommenting that line, then starting the program again, sorry if that’s complicated). As it is though, I have a cute little island to explore..
-
So it looks like this now, with nicely bouncing fireballs, and accurate standing-on-the-ground, and better ’placing objects to make a level’. There’s only one island now though, and I’ve kindof broke the boat. And it’s not really all that much closer to being a proper game. Here it is for now though. Z6v23mnds3.
-
This is great! Very well done! The environment is very nice, and the controls feel accurate and reliable.
I am intrigued by certain mysteries of this world, such as the cars parked on what looks to me like a volcano!
-
Thanks! I’m hoping to have that sense of mystery and discovery in the finished game.
I’ve (mostly) fixed it for the patch. There were a lot of refs needed, and one small tweak to the map struct, to give the whatever-it-is that the createterrain() function returns, and the whatever-it-is that the placeobject() function returns separate places to be. 7vk73mlhs3. For some reason, it starts in ’object placement’ mode now...
-
@toxibunny Is there anything you implemented to keep the fps close to 30? I looked through your code, but nothing "jumped out" at me. Does the fps stay close to 30 because you are doing a consistent amount of work/computation every frame?
-
I haven’t done anything special other than adjusting numbers of npcs or objects drawn when things start chugging too slow. Fuze has some sort of autoadjusting feature to help keep the framerate at 30 if it can’t manage 60, so maybe that’s what you’re seeing?
-
Yes, I do do a similar amount Of work per loop though. The only thing that changes is work due to throwing a lot of fireballs out, which does have a noticeable effect..
-
@toxibunny That makes sense! Thank you!
-
Okay so here’s a quick update: I got some animations in :)
I changed a whole load to get this working properly. I have scrapped structs for the npcs/spiders/cats/skellys etc, and now have loads and loads of arrays. So instead of entitylist[i].pos I have entitypos[i] instead. A minor change, but I think having such complicated structs was screwing things up somewhere, and this works, so good.
Also, another little thing which I think is cool is that I’ve written a small function that finds a model in the big list of 3d models I’ve got. There’s no ‘spidermodel = loadmodel(“quaternius/spider”) here - just one big array of strings of all the models, and another big array that I put all the results of loadmodel() into. I’ve got 90 models loaded now, which is cool, but I was having to scroll back to the start of my program and read through the list and make a note of it if I wanted to use a model, which was a) a pain, and b) meant I couldn’t insert anything at the start of my array due to it shifting everything following it. So now instead of hardcoding the model into the npc definition, I have a line that reads ‘entitymodel[i] = findTheThingInTheList(biglist,”spider”)’ and even if I modify the list, it still works! It even lets me know if it finds something more than once, or if it doesn’t find it in the list, which has prevented my cats from morphing into cattail ferns.
I’ve deleted all that stuff I had decorated my island with. They were just for testing anyway and it was taking ages to load. I wonder if there’s a better way. Minecraft loads things in in chunks as you’re playing. Maybe if I load things in it might be a more pleasant coding experience than having to wait for the whole lot to load in while looking at a blank screen (or even a loading screen). Something to think about anyway.
Animations are kindof weird. They’re measured in seconds, and represented by a float. There’s a few built in functions in fuze that gets the number of animations a model has, and their lengths. I have variables for currentanimation, animations (an array of lengths), currentframe, ‘walkinganimation’/‘jumpinganimation’ etc. (the different models have more or fewer animations, and not all in the same order), and a ‘stride length modifier’ that I’ll be using to make sure they don’t look like they’re skating or pedalling their legs. I’ve written a function called ‘animateEntity’ that takes in the array index of the entity, and a float representing speed, which updates the animation with the right values based on those variables I mentioned above making sure it loops properly. Tricky to get your head around at first because it’s not frame numbers you use in fuze, it’s a float between 0 and the animation length. Unexpected (for me), but once you grok it, it’s not so bad.
Anyway, that’s my update :)
Edit: one thing I forgot to mention is that I have ‘speed’ as an input into my ‘animateEntity’ function so that I can match the animation cycle to the actual speed they’re moving. That’s how those skeletons keep their feet planted firmly on the ground. They slow down their running as they slow down, type of thing. Doesn’t look so great on steep hills but Everywhere else it looks really natural :)
Speed is calculated using the distance() function, btw.
speed = distance( {0,0,0} , entityacceleration)
It gives you the length of the acceleration vector. Very handy to know.
I also changed their ‘point towards’ to ‘CurrentPosition+((CurrentPosition-PreviousPosition)*10000)’ - which keeps them pointing the direction they’re going (or close enough). Things get weird when you omit the *10000. Now when the cats run off cliffs, it looks like they’re deliberately diving down, and my spiders climb up and down slopes quite convincingly..