GMS2 One-hour challenge - Heightmapped racing game

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

In this video I show you how to generate a random heightmap and use it for true 3D collisions. Everything is made in an empty GameMaker Studio 2.3 project in one hour.

πŸ‘οΈŽ︎ 3 πŸ‘€οΈŽ︎ u/TheSnidr πŸ“…οΈŽ︎ Oct 05 2020 πŸ—«︎ replies
Captions
okay so it's time for another 3d one hour challenge today i'm going to try to make a 3d racing game or some kind of off-road height-mapped racing game um the challenge today will be to do everything that i have planned because i do have a plan today i have like tested this and one hour is a very tight time schedule for something like this so it will be exciting to see what i managed to make i probably won't be talking much so i'll be asking future snitter to um to make some comments are you there yep i've got you covered yes excellent okay so i'm gonna turn off my mic and i'll be vibing this music and i'll just concentrate completely on what's on screen and hopefully what futures nader says will make sense so my plan is to start by creating [Music] some kind of randomized height map and then i will create a player movement and then i will try to to make a proper heightmap collision the wrong way of doing hype map collisions is to just read the z position and displace the player up or down depending on what the height map says now it works for some games but it does not work well for a racing game um i want the the player to be displaced in all directions not just up or down so i have a plan for that we'll see what i managed to do i'm starting in five seconds okay so the first thing i do is to just delete all the default folders and create my system object i usually like to just keep one object around for projects like this and it will contain all the functions everything that needs to be done in create event i begin by creating a height map function now this will take in a size and a number of octaves so the the whole point of the height map generation is that you create several surfaces of different sizes and then finally you add them all together and they will produce a smooth surface so you'll see in a second how i scale each surface down so the first one is the same size as the the final surface the second one is half as big the third one is even smaller and for each surface i draw a set of random pixels now i only use the red channel because that is easier so i have a total of 255 possible random numbers now this is a very very slow way of making uh a height map but it's faster code so that's the whole point i need a system that is quick to uh to create uh easy to create but um and well it works okay so now i've created a set of random surfaces at different sizes now i create a composite surface which will basically just combine all the um the smaller surfaces i use a blend mode where i blend the source with its alpha and i just add it to the the destination so be in one destination uh i draw the surface scaled up now since since the surfaces are smaller i need to scale them up so that they are covering the same area as the total um the whole height map so the scale is oh that's wrong actually so i probably fix that later but the scale should be 2 to the power of i not i to the power of s so i'll see what happens when i draw this this will be interesting actually i don't remember what happened but i can see that that's wrong already um but that's the the fun about this i need to code very very quickly and i just need to assume that what i write is correct and then later maybe i'll see that oh well that's wrong then i can fix that to create a draw event i've created the height map as a surface and i draw the surface just to test and see what it will look like and i reset the the blend mode otherwise nothing will work then i create a room and i place the object in the room just to see how it looks and i realized that it looks terrible you can see that the smaller surfaces are too small so i i figure out where the problem was and it should be 2 to the power of i now let's see what happens okay it looks good but i haven't enabled texture filtering so that's the next step uh because right now the the smallest surface is like there is no interpolation you can see the pixels very clearly so i use a gpu function i use texture p to make a repeating texture and i use text filter to make it smooth and then it looks good so the height map is done four minutes and the height map is done now i want the the level to repeat infinitely in all directions the easiest way to do this is to just draw nine instances of the level one in each direction so i do that now just to make sure that everything repeats properly without any seams and when it comes to gameplay later i will make the player loop back around from like the right side to the left side like pac-man so i did a small mistake i forgot an equal sign and i'll see what happens okay and it repeats perfectly some more variables that i need later this is just some info about the level itself so now i started creating the player these are some variables that i know that i need i need to record when the player is touching ground so that when you accelerate you you can only accelerate when touching ground also the storing the previous position is what's called verlet integration i explained this in my previous video but i'll make a short explanation here as well instead of storing the speed of the object you store the previous position and for each step you you figure out what the current speed is by subtracting the current position from the or you subtract the previous precision from the current position and that shows you how much or how quickly the object has moved from the previous frame and then you can use that you can add friction you can accelerate and you can add gravity and everything to this speed vector so i like just finishing all the um the vector logic before i even try the project so sideways friction is useful for car games where you want the car to like roll backwards and forwards but not slide sideways since the orientation of the wheels prevents the car from from sliding sideways so this will make it move more easily forwards and backwards than side to side and notice that i use the charmat the character matrix now this is a matrix that i created in create event and i will use this to keep track of what direction the player is facing and i will also use it to actually draw the player because i can use the matrix directly to um to transform the the player object so i store input as v and h input and then i add the direction vector of the character matrix to the speed vector times an acceleration factor so you can only accelerate when touching ground and i want the car to accelerate until it reaches the maximum speed so i that's why i multiply by this and variables like that are easy to change later so the absolute speed of the object is basically just the length of the speed vector and that's acceleration done actually i forgot one thing and that is gravity since this is bound to a plane i just subtract something from the z value of the speed and then you apply the speed by just adding it to the position like that and this is a temporary solution to just avoiding ground i only want the z value to be a minimum of zero but this will be useful later so i write in the wrapping here so that if you are to the left side of the map you should move to the right side of the map and this is also important that you need to move the previous position otherwise you'll get one hell of a speed like you'll shoot like a bullet if you don't change the the previous position as well and same for y so now i figured i wanted to create the the car sprite now my plan for the the car is to basically use the sprite stacking technique and that is a technique that usually is used for fake 3d games or top-down semi-3d games but it can also be used for actual 3d models now of course it does have limitations you can't view the the car from behind because then it will be then will flicker but for a one hour game this is a nice way of creating something like that looks like a 3d model so you have to like imagine the car being sliced in vertical slices sorry horizontal slices so this is the wheel i want the bottom of the wheel to be smaller than the top of the wheel and then i'll make the top of the wheel or the bottom of the chassis and then i make the top of the wheels a little smaller and i am deeply concentrated i'm struggling a little bit with the drawing tools i wish i had more tools to work with but i mean okay and that's the bottom of the car i add some headlights and some rare lights looking back i spent entirely too long making the car this is just wasteful oh well okay there we go so i set the origin to the middle of the car and i'd like to draw the car as well so i do it i only draw the bottom sprite first just to test to see if like things work and there is something there we can see two of the wheels so now i figured it was time to create the orthogonal allele or oh i can't pronounce it even orthogonalize function now this function i also described in the previous video but it is a very useful function for when you have a matrix and you'd like to like alter the vectors individually and then just fix the matrix afterwards so i use this to to steer where i just add the side vector to the looking vector for example you'll see later and i can also use this to change the up direction of the car and then just fix it afterwards so i start by normalizing the up vector which is indices 8 9 and 10. then i create the side vector and when doing cross product you get a vector that is perpendicular to the two vectors that are used to create the cross to do the cross product so here i multiply or i do the cross product between the up vector and the looking direction and this will create a perpendicular vector that is pointing to the side then i normalize it and then i do another cross product to create the looking direction making sure it is perpendicular to both the up direction and the side direction so this is just some stuff i have memorized i know what indeed index needs to go where and knowing cross product by heart is actually a nice it's a nice thing to to know especially when doing programming like this so now i can use the orthogonalize function to to update the matrix so first just make sure that the position of the matrix is is the same as the object's position and then i can use the side vector and add that to the looking direction times some variable turn speed to make the car turn from side to side so there is no rotation necessary this this will result in a rotation rotation when you orthogonalize the matrix afterwards so it only like the turn speed to change when touching ground i'd like it to increase more the faster you drive so that when you are standing still you can't turn the car but when you're driving fast you turn faster i should actually add an upper limit to this as well but i don't think i ever did so i tested it and realized that nothing happened and i didn't figure i i couldn't figure out why because i had done everything i should so i tested again and the car still wouldn't turn and i couldn't figure out why so if you look in the the corner at my face that is the face of a clueless programmer so i just do what i know i i show the the turning speed and i see that it does change it's a small value but it does change so i tried to increase the the turning speed but still nothing happened so i tried to orthogonalize the matrix because i needed to do that anyway and i just can't figure out what was wrong so i try another thing i'll try using the matrix directly to to draw the sprites and i need to reset it afterwards to identity no it still doesn't work why why doesn't it work so i try to increase it even more but nope no turning so i try drawing the turn speed again in desperation and it is up to six and then i think i realize it now yes i had used the wrong matrix i had created a new matrix and yeah that was basically just a brain fart so now they have all been replaced and okay the car turns though it turns very very quickly so i need to reduce the um the turning speed again and check the the origin because like the car was all over the place and that was because i added the translation twice so now now it works now i can control the car with the arrow keys it turns when i turn when i press right or left and it drives forward when i press forward so this is just something that i'm going to use later oh right and now i want to enable the uh the sprite stacking so i create a loop and i loop through all the the indices of the sprite and i want to draw them on top of each other so testing it you can see that the car driver draws properly although it's very small so now i'd like to enable the 3d camera so when drawing 3d cameras i like using view so i enable the view i set view 0 to visible and i create a camera for it and i create a projection matrix as well so none of these need to be changed later a is a negative fov and negative aspect just to to make sure that the projection or the the final rendering is right-handed and i enable some gpu functions that need to be enabled for 3d games and i started running the game but well i'm not finished with the the camera so i need to also create a view matrix and that's the next step so to figure out where the camera should be placed i start at the position of the player and then i want to offset it backwards so the character matrix index 0 1 and 2 are the looking direction of the car but i only want the car the camera to be displaced along the x and y direction not the z so i normalize the uh the looking direction along the x and y axis and i just offset z by a constant and then i create a camera looking direction with matrix build look at from the camera to the player with a zero zero one up direction now i don't like using zero zero one as the up direction but for this game it's it works so i i noticed that the um the map disappeared under me but i didn't realize why so i'm gonna try messing around and see what i can do to fix that something is still wrong and then i realized that i had the [Music] translation twice so i need to set it to zero there and now the map is repeating in all directions so now i want the car to actually have some height because it's very flat as it is right now and i was messing around with this a little bit because i couldn't realize i couldn't figure out why it was flat so some time wasted here in the end i realized that i just set the the scale too small for the z direction so i tried increasing that but it was still very very flat and i think i tried increasing it even more oh i'm thinking so hard okay i tried increasing it more and there we are now the car has some height as well so it now looks like a 3d object but i didn't like the way the um the uh windows looked and i didn't like the way the headlight looked and of course that needs to be fixed so i make some windows on the layer below and i test again just to see how it looks yeah it's a little better but i also want to fix the the headlights and the rear lights so just remove them from this layer so now it's time to actually create the height map i've created the the height map texture but i still need to actually make something that the player can collide with so i create a buffer and it has exactly size time size times four bytes so that's one byte per pixel or that's four bytes per pixel of the entire surface so my plan is to convert the surface into a buffer and then read from the buffer and that is pretty simple actually you can read directly from the buffer in the uh with the method that i show here so buffer get surface transforms the surface into a buffer as a very useful function so i seek through the buffer and then i just read the value directly and save it into the height map now there's one thing i forgot to do here and that is divide by 255 so the the u buffer u8 goes from zero to 255 but i only need the height map to go from zero to one so i think i'll fix that later then i create a height map from the level texture and then i'd like to uh create a vertex buffer from the the height map so that you can actually see the world so the the format contains a position and a normal and i don't need a texture coordinate because i can just use the position i create a vertex buffer i start creating one and for each cell of the level i'd like to add four or two i mean two triangles but i realized i needed another function i need a function to get the height from the height map now one way is to just read from the height map directly but i like creating a function for getting a smoother value so this will just interpolate between the nearest four four pixels or four cells i add size and use modulo size to just make sure that the the value that is put in is between zero and size to avoid reading from outside the grid so i interpolate along the x direction first and then i use those values to interpolate along the y direction okay so that function is finished and then i need to figure out the corner positions of this cell and i think i actually did something wrong here and it will take a little while until i figure it out i forget to actually change the coordinates so so they're all just x x and y y now i should add x or add 1 to x 2 and 1 to y 3 and 1 to both x 4 and y4 but i didn't so i had to waste some time trying to figure out what i did wrong but i continue because i can't test it right now and i just have to assume that everything i've written so far is correct and until proven guilty okay and this is some foreshadowing because now i'm writing a normal as well but i haven't created a normal so that needs to be the next thing i do and how can you create a normal from a height map well you do have that function cross product which is extremely useful it creates a vector that is always perpendicular to the two vectors that you used to do the cross product so if you create two vectors along the the height map then the result of the cross product will be normal or will be perpendicular to that so now i create the get normal function so we need three positions and this is a an approximation where i just get the three surrounding points one at x minus one y minus one i wanted x plus one y minus one and one at x and y minus um y plus one so i get three vector now three positions and i can use these three positions to create two vectors and a cross product between them and this is also something i've just memorized this is how you do a cross product directly without creating the the two vectors you just subtract one position from the other and it's i'd only do it like this because it's faster right okay so everything look everything's looking correct i try to or i put it in draw event instead of the draw surface and let's see what happens then all right when you use a custom vertex format that does not contain everything that the default vertex format contains which is position and color and possibly normal and texture coordinate then you need to create a custom shader so this format only contains position normal so it won't work without a custom shader i do some basic lighting and since i'm drawing using the um the height map texture i can actually add um several of these just scale differently and that will uh make the terrain have less like the texture of the drain have less repetition so it's the same principle as the the octaves when generating the height map you just add them together at different scales and this is a simple way of creating grass and mountains so i'd like the grass to be pure green so that's 0 one zero and i like the mountains to be gray so that's point five point five point five and i use the z coordinate of the texture coordinate to um or the the input coordinate to figure out if it should be grass or mountain so i multiply by 40 to just make the border smaller so i think that should work and i start the project and okay i got an error why did i get an error right there is i used a variable that was not there try again and the map is invisible why isn't the map visible i figured that maybe the map is too far above the player so i try reading from the the height map just to position the player on top of the of the level so let's see what happens then now i can replace the ground height with the value i read from the height map and now i get an error i forgot to replace that variable try again nope still no level why not hmm is the shader wrong no it looks correct but i'll try just setting it to white and see what happens so vect31 is pure white just in case something went wrong with the other calculations but no nothing so the shader is okay and everything here looks okay hmm where did i go wrong okay i'll try to add a height scale to the the normal function because i know that's needed since uh the normal should be changed when uh or the the more extreme the scale the the steeper the hill and the more the normal should be changed i still think it didn't catch it hmm i'm thinking so hard and then and then i see it oh that was a stupid mistake and i wasted a lot of time and something drew but not what i expected so why why is it looking like that so i need to figure out that as well i'll try removing the height scale just to see what happens and the ground disappears set it to 2 instead but no nothing so try setting the fov to 90 just to see if it is like outside my uh view and i can see it down there but why is it all the way down there hmm and there i find it i forgot to divide by 255. another stupid error okay we've gotten somewhere now the car is moving smoothly although it is only being displaced vertically so even if you drive right into a mountain wall you'll just pop on up on top of it uh so i think that's the next thing i will fix i like using the square of the height so that the valleys are more pronounced and now you can also see that the grass works and the mountains are colored gray and the grass is colored green so i'd like the up vector of the player to face or to to react to the normal of the ground so i get the normal of the ground at the position of the player and i create a new vector since i don't want the up vector to just point towards the uh the up vector of the ground i'd like to like make it bounce a little make it live so i create basically a vector speed variable and i can accelerate and decelerate so at this point i only accelerate and decelerate when the the player is touching the ground i also need to initialize the the variable in creativeness and then i can add the speed vector to the up vector and let's see what that looks like nope didn't work did i forget something i forgot the height scale now let's see what happens okay it's starting to look like something now the up vector of the player is rotating to fit the up vector of the ground now i'd like for the the player to avoid ground the ground properly and to do that i'm actually gonna use uh i'm gonna avoid the nearest cells as if they are triangles so i'm gonna displace the player as if it's a sphere out of the triangles this is a very useful script try get nearest point this script will return the nearest position within a triangle to the given position so you just supply the vertices of the triangle v1 v2 v3 and the position and it will return the nearest position on the triangle and if that nearest position is closer than the radius of the player then you can just depl displace the player out of the triangle and this is the same script that can be used for for mesh collisions like to to make the player avoid a 3d model just do this for every triangle in the model and of course you also have to do some optimization like subdivide the model and only do a triangle collision check for the nearest ones um but this is a very very useful script and the whole point is that you first create the normal vector and then you check each edge of the triangle individually so since you do a cross product between the um the vector from the first vertex to the player and the vector from the first vertex to the second vertex now if they are parallel the result will be zero if the um if the player is on the same side as the triangle itself meaning that if the the player or if the position is above the triangle then the cross product will be uh pointing in the same no the opposite direction of the normal vector and then the dot product between that and the normal vector will be larger than zero and you can use that to just interpolate along the edge so i'm first checking the first edge v1 to v2 and this is a script that i've memorized i've written it so many times that i know it by heart so that's basically the first edge the second edge and the third edge and the way i write it is so that it's incredibly easy to just copy the entire piece of code for the uh remaining edges so if the the triangle is inside all the three edges you can just project the the coordinate down onto the plane defined by the triangle and this is how you do that okay that's that script so i have the script for avoiding or for figuring out the the nearest position of a triangle now i need to figure out what triangles to avoid so i create a loop and i loop through the nine nearest cells of the height map so for each cell i need to find the four coordinates of the the corners and i did a mistake here so i i can see that now and i think it will take a little while until figure out because x1 is equal to x over level scale plus x x now the problem is that i also said y one equals x over level scale plus yy so that will bite me in the alice later and i just changed the variable names to make it easier to copy the code okay so now i have all the for the four corners i get the nearest point on one of the triangles and i check the distance to it and if that distance is closer than the radius of the player then you need to displace the player out of the triangle and then i just copy it for the other triangle of this cell and i realized i forgot to create the radius variable so just initialize that and test it and see what happens then i get an error of course i do it's rare that things work at the first try and here i realize that i've done a mistake i made a made a mistake i forgot to write the height map there but i didn't realize that i used x and y instead of underscore x and underscore y oh it's fun looking back at this because like i just did it it's literally been like one hour since i did this and still the the mistakes i do are so so obvious so now i see that it kind of works but it does not work the way i had expected and i wonder where did i do anything wrong well now it's just janky so try messing around with the up vector yeah but it's still janky as hell and i noticed that the car was scaled strangely so there must be something wrong here there there's the error okay it's better but it's still not perfect the movement is still very very janky so i try as well as as good as i can just to figure out why is it moving like this so while i think i just do other stuff to try to focus on something else but i realized that the movement is way way too janky so something must be wrong so i check every calculation that i do now each of these variables should only show up twice in each line and they do so i haven't done anything wrong here and this is also correct and that should be larger or equal so that is correct so i can't see anything wrong in this script everything looks everything's looking good here and i try just changing the the failsafe down here to see what happens then and now i really messed things up so that made me realize maybe this is the problem so i do another test and i realized that the collision script doesn't work at all hmm there i noticed that i used the x instead of the y and that is a pretty serious mistake but it still doesn't work so something is still wrong scratching my head and then i noticed that as well so i think that was the last piece of the puzzle there we go the car isn't able to drive properly up the hill but now the movement is working properly at least okay it's starting to look good but the friction and the gravity is too high so i need to reduce that and just like play around with the values and see what feels good and there we have it some more variable tweaking okay so this is the final result i'd say this is pretty good for one hour of work um i had a lot of trouble uh i didn't i i used the wrong variables some places uh so i had some trouble trying to figure that out but i did figure it out and i think this is pretty good so this is what i'm able to create in one hour i don't think i could have added anything more to this so the car is drawn by just stacking sprites it's the simple way of making a 3d model now it kind of flickers when you look at it directly from behind but i mean for a one hour game that's okay so you can draw infinitely or you can you can drive infinitely in any direction the map repeats and once you get to the edge you are transported to the other side pac-man so you can play around with this there's a lot you can do with it obviously it's not very optimized this is like fast and dirty coating but it works now i could play around with the colors make it the grass a little less green perhaps and make the the border between the mountain and the grass a little like curvy i can actually do that right now because that is pretty simple because i do have the uh the noise texture here so if i just do another sample and i'll just do point times point it needs to be uh i'll do one to five times 0.5 let's do that actually no i'll just do 0.25 and i'll use this offset here actually i'll subtract 0.5 i'll multiply it by 0.1 let's see what happens then yeah so now now there's a little noise um i'd like it to be a little less reduce the frequency a little bit so i'll try like this and there we go also i could reduce this to 0.17 and you can play around with this like as long as you like just mess around with the values and gets make something that looks good now i think the um the terrain is a little too extreme so i will increase the the octave number a little bit and i'll also increase the uh scale now when you increase the octave by one you should double the scale to get this like the same result i i will reduce the scale a little bit there we go okay so i will leave the source for this in the description i'll add some comments and um then i'll upload i hope you learned something from this even though it went really really quickly um like i i did practice before i started this so i knew what i wanted to create so i i was just writing non-stop and i hope that the uh the commentary that i'll add after will clear up some uh some things you didn't understand and if you like just ask in the comment section and i'll try to answer so that's it for this challenge this was a fun little challenge and i look forward to the next one see you around
Info
Channel: TheSnidr
Views: 809
Rating: undefined out of 5
Keywords:
Id: 6G8mSJJ4Rx8
Channel Id: undefined
Length: 66min 57sec (4017 seconds)
Published: Sun Oct 04 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.