WIP The Legend of Squishy
-
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..
-
I tried running this and got an error saying getmapsurfacenormal() doesn't exist
-
Sounds like an old version not working with the latest patch. I’ll post an update here in the morning, with the latest version code. Thanks for the heads-up :)
-
Okay, try FGZ33MNDS3
-
Somehow I missed before that the game id was available for this :)
Took a while before I figured out I needed to press x to start, but then it works.
There are a lot of things going on with this project, so it's nice to see the progress on this :)
-
Since the last time I posted about this, I’d made the npcs more independent. I added variables for ’thing it’s interested in’, boredom level, home object, etc., and made it so they become interested in a nearby object of the type they like, and go to it. They stay there until they get bored, then become interested in something else and go to that. After a while they’ll become homesick, and go to home pos until they’re happy enough to go out again. The version shared has got much fewer npcs than previously, but it doesn’t help the framerate much so if you look for the ’numofcats’, ’numofspiders’ etc variables, you can bump it up quite a lot at the cost of only a few frames.
I also started to add a menu system in preparation for character customisation, weapon selection and an inventory, but got way confused about ’current menu choice’, current menu position, current cursor position, etc. Menu is on the right shoulder button - you can try it out to see it go screwy and crash with an ’index out of range’ error.
I had put squishy on hold waiting for the new patch with the added search function in the editor. Probably time to dip my toes back in now that the compo’s over. Maybe take some code from the competition game and give squishy a bit of a custom soundtrack...
-
X also switches between build mode and game mode. In build mode you can choose objects to place, delete, resize and rotate them, and you can also save it by clicking in the left stick. Saving takes over a minute and it’ll seem like it’s jammed, but it completes eventually.