Placeholder Image

字幕列表 影片播放

  • Hello, Let's make a retro arcade racing game.

  • So in the classic retro arcade style, we've got a car moving into the middle of the screen and we've got Ben's on the track on.

  • We can see how fast the car is moving because we've got lines scrolling towards a different speeds.

  • We can see the turning of the car because I've got very terrible sprites.

  • My apologies for the crudity of the model, the mountains in the background school around is necessary to give us the illusion that we're turning around the bends.

  • Andi, the objective of the game is to get around the track in the fastest possible time on.

  • There is actually some dynamics that affect this.

  • So if we go off the track, we slow the car down artificially.

  • And as I'm just about to cross the start, finish straight, we can see what lap time I clocked in this time was 42.8 seconds.

  • I'm sure we can do much better than that if I attempt to go too fast around the corner and pushed out to the side, so I have to use the accelerator quite carefully, like some of my other coded yourself videos.

  • This one looks like there's a lot going on, but actually there isn't.

  • And I think this is really what this video is about is we can take a really mundane, simple mathematical concept.

  • But if we present it in an interesting way, becomes a playable gay.

  • And as usual, we'll be doing all of this in the command from seances all asking about.

  • We don't need any third party libraries to get us going.

  • Asked for the current trend of my videos, I'm going to be using the one lone code, a console game engine which you can read about in the little Linka book.

  • The game engine is based class that we must inherit from and over right to methods on user create and on user update.

  • All this class does is wrap up the user input on displaying to the council, so I don't have to type it out for every single video.

  • All my in main function is going to do is create an instance of the class.

  • In this case, it's Formula One lone Koda, Please don't sue me if I on this time I'm creating a console which is 160 characters wide on 100 characters.

  • High on each character is going to be eight by eight pixels.

  • Let's start by getting some of the easiest stuff out of the way first, so I'm going to draw a track.

  • And, of course, this occupies the bottom half of the screen.

  • So I went to take the screen height and divided by two.

  • Then free dro.

  • I need to break up the row into whether it's, ah, grass clipping board all road, so each character going across the console we're going to classify as one of the other.

  • To make things a little simpler, I'm going to assume that the width of the screen is going to be a normal life Specs or zero for the left, on one for the right so the middle of the screen will be 10.5.

  • This is useful because we'll probably want to scale things up, depending on the size of screen, but it also makes some of the mathematics a little simpler later on, too.

  • So the first thing to establish when drawing the track is where is the track on the screen?

  • I'm going to assume we're drawing.

  • It's in the middle to begin with.

  • So that's a 0.5 in our normal life space.

  • I'm also for now going to assume that the road occupies about 60% of the screen, and so everything remains proportional.

  • I can assume that the clipboard with they're the red and white lines going around the track.

  • I'm going to assume that they're 15% of the road with.

  • And just to make the next section a little simpler, I'm going to assume that the road is symmetrical around its middle point.

  • So actually, I can have the road width and we'll see why this comes in handy now.

  • As we're retreating across the screen column by column, it's important to classify.

  • Each character is being grassed, clipping board or road.

  • So I need to work out where those boundaries are in the ex direction.

  • So, for example, I know that my grass begins at zero, but it will stop when we get to the middle point of the track, minus the road with which is no half on minus the clipping board with.

  • I can then scale that into the screen space for the council in a similar manner.

  • I can work out where my left clipping board is.

  • So it's exactly the same calculation.

  • But I don't need to include the clip width this time on because we have the road with before Doing the right inside is the same now, except we're doing pluses instead of minuses.

  • So we're exploiting the symmetry in order to make things simpler for us to do.

  • Now we just need to call them in.

  • So we know that if X is greater than zero on dhe exes left than our end left grass variable that we're going to shade that character green, and I'm going to use the drawer functions.

  • This is provided by the one lone Koda class, and all it does is take the X and Y coordinates, and we can use the solid character to basically fake a pixel on will use the constant F G green, the grass.

  • Some of you may have noticed that actually, why represents zero to screen height divided by two.

  • So that means we'll be drawing the top of the screen, which isn't what we want.

  • We want to be drawing in the bottom half of the screen, so I'm going to just cash here a little row position, which is that calculation already.

  • So instead of ah, drawing to this white corner, I'm going to draw to the row instead.

  • On that, make sure we're in the bottom half of the screen in a similar way.

  • We control the other types of road section, so the next thing to be drawn is the clipping board, and we're going to draw that using red pixels.

  • I'm not his type all of these out, but it goes without saying it looks the same for the other types of sections.

  • So in between the left clip and the right clip, we've got Road.

  • So I'm going to draw that in as Gray, and then we've got the right and clipping board.

  • So that's red again and finally going to the edge of the screen.

  • We've got more grass.

  • One final thing before we try This is to make sure that we raised the background first, So I'm just going to draw black empty spaces across the whole council screen.

  • Using the fill function, which is provided by the Gay mentioned not just deter rates through two loops until it's call it in all of the pixels.

  • Let's have a look what this looks like.

  • Well, we can see that we've got the bottom half of the screen cooler Didn't know and we've got grass clipping board and road clearly defined a sections.

  • The next thing, of course we're going to need is a car.

  • I'm going to add a variable to the class to store our car position on the track.

  • This time, I want the cart's position on the track to go from minus 12 plus one minus won't be on the left hand side.

  • M plus one will be on the right hand side.

  • That means zero will be dead center in the middle of the track.

  • So we need to do some scaling toe work out where that is in console space screen with divided by two will give us the middle off the track, and I want to offset from the middle of the track where we're going to put it in the car.

  • I'm also going to curiously ad because I've seen into the future, of course, a minus seven here, and I know that's going to be the width of the spite of the car.

  • So the middle off the spite of the car will align with zero.

  • On this way, when we draw the car, it's not going to be offset to the right.

  • So to draw the car, I'm calling the drawstring Alfa function here, which simply takes any space and doesn't draw it in the background.

  • So it pretends that that character is transparent and I'm going to draw the car in.

  • The exposition will be defined by En Chara.

  • Posit we've just worked out on the white position is going to be fixed on the screen, so let's take a quick look at this.

  • Brilliant.

  • We can see we've now got a car in the middle of the road, but something doesn't look quite right.

  • We're lacking perspective.

  • Perspective isn't as complicated as you might think to implement what we're going to do is take the current row and divide it by half of the screens That basically gives us a percentage on there, for the perspective tends toward zero when, why small and it tends towards one when wise large in a linear fashion.

  • The road with variable is what governs all of the other metrics used to define our road.

  • So if we modify the road with for any given row, everything else should adapt accordingly.

  • Now, if I just set the road with to be equal to the perspective, it probably wrote, Look very convincing.

  • Let's see, it does work, but our road disappears at the horizon and it takes up all of the screen at the bottom.

  • So we want to add some more sensible boundaries to this.

  • I wanted to be a minimum 10% off screen wit, so I'm going to throw that in his a constant Andi.

  • I'm not going to take 100% of the perspective.

  • I'm only going to take up to 80% off.

  • This means we have a road with going from 10% to 90% of the screen with Let's have a look.

  • Now that's a lot better.

  • We need to create the illusion of the car moving forward.

  • Of course, the car doesn't move forward in the screen.

  • What we do is change the scenery to give the illusion that the car is going forward.

  • I'm going to create another variable now, which represents the distance the car has traveled around the track.

  • I'm going to add in a little bit of user input to help was debug things.

  • So I'm going to make it that when we hold down the up key as a cold by this, if function here, the distance is increased proportionally according to a prolapse time.

  • If we remember from other videos, the computers don't run at the same speed.

  • So we need to take that into account when we're building a game engine to give the illusion of forward movement.

  • I want to draw some lines on the grass of different colors, but these lines appear to flow towards the player.

  • We also need to take into account perspective so I could just have lines that are all equally spaced apart and moved these towards the player.

  • However, this would lose the illusion off the perspective, which is bad.

  • Instead what I wanted in the distance, my lines to be very close together.

  • Unless they approached the player, they get further apart.

  • We can achieve this line positioning by using some simple maths here, using the Desmond Starcom platform, I'm graphing a sine wave, but it's a sine wave has been modified by two things.

  • Firstly, the ex direction represents the perspective on as we can see the as the ex gets larger like it does when we're drawing it, the peaks get further and further apart.

  • But I also need to take into account the face on the phase is going to be represented by the distance the cars moved around the track.

  • So by moving the slider and changing the face, we can see we get this nice transition.

  • So by using nothing but a periodic a solitary function, we can do all of this in one line.

  • So I got my sign here on here.

  • I've got my ex.

  • I'm taking the Cube off one minus the perspective here.

  • This is a bit of fine shooting and we'll find with this application that actually we do need to just hand tune some values sometimes to make it feel right.

  • The result of this sine function will, of course, be between minus one and plus one.

  • So I'm going to threshold it to tell me whether the pictures should be green or dark green.

  • And so now, instead of hard coding the value into the grass drawing routine here we use our variable instead.

  • Let's take a look.

  • That's quite nice.

  • We can see we've got narrow alternating light green and dark green colors where far away from us on, the bands get bigger and bigger as they approach the screen.

  • And if I hold down the button, our distance should increase, although at this point is doing it very slowly.

  • No matter we can go into the code.

  • Remember, this is input just for debugging.

  • Right now.

  • We'll change that to one hundred's.

  • There should be 10 times faster time another go.

  • Very nice.

  • We'll do exactly the same for the clipboards, but I want to use a higher frequency because I think the red and white boundary should be closer together.

  • So whereas without grass color, our frequency was dominated by this 20.

  • I'm going to use for the clipboard color eight.

  • So it should be a four times denser frequency.

  • I'm also into said, this time going to choose red on white collars, and we'll set this for both clipboards.

  • Take a look.

  • Very nice stuff.

  • Things looked like a racing game.

  • So how do we go about defining a track?

  • Well, in most modern games, of course, the track is a three D model, and it may be tempting to do something similar, for example, having a whole load of nodes on defining some properties between the nodes in three D space.

  • I propose we do something simpler than this.

  • I'm going to break the track up into discrete sections.

  • I'm going to label each section with curvature and distance.

  • And as the player moves around the track, we look how far we've traveled.

  • Accumulate the distance for all of the sections on work Out.

  • Which section there in and all we need to display on the screen is the tracks curvature.

  • The thing with this approach, though, is it allows us to create completely nonsense tracks.

  • But the play will never know that their nonsense, because you can only see the curvature for one section of track at any given time.

  • In fact, we don't even need to make the ends joined together, because we could simply loop around our list of segments back to the start.

  • To stall the track, I'm going to use a vector of pers where the first element of the perp is the curvature.

  • On the second is the distance on will create our track in the on user, create function, and we're creating a track is very simple.

  • So here I've added a straight section because the curvature is zero.

  • I was going to last for 10 units.

  • I'm going to use this.

  • Ask the start finish line, so just label this one separately.

  • We'll come back to that in a bit.

  • Then I'm going to want a nice, long straight section.

  • So again, zero curvature.

  • But this time it's going to be for 200 units.

  • Of course, units are meaningless in this.

  • Let's assume their meters.

  • After the long straight, I'm going to add in a sharp bend.

  • So here I've got a full curvature off one to the right, and again the length of this curve is going to be 200.

  • Let's go into a nice, long, straight again, this time twice as long as the previous one, and we'll have a sharp bend the opposite way going to throw in a chicane here.

  • So we've gone from minus one curvature for a short distance to plus one curvature for a short distance.

  • Now I've got a vector full of track sections.

  • I need to work out where I am on the track according to my car's distance and to do this, I'm going to track two more variables and we'd have offset.

  • I would have track section, which will be an interview representing the index of the vector.

  • I'm going to be lazy this time.

  • I'm just going to Witter, ate through the vector section by section, accumulating the distances on when the total distance is greater than my current car distance.

  • I noticed.

  • Stop.

  • I know which section I'm in, and I can work out how much my car is offset into the track.

  • Once I know this index, I can work out the target curvature for that section of track, and I can display that now on the screen displaying curve, actually, simply angling the track off at a particular radius.

  • It's a good job we have this middle point variable because all we need to do is change the middle point.

  • Depending on the perspective.

  • On the curvature of that section of the track, the rest of the drawing routines will update accordingly.

  • So let's start naively by just offsetting the middle point by the target curvature.

  • We know that on the straights, the target curvature zero so the middle point stays in the middle or we plus or minus one to it is necessary.

  • Let's see what happens now as we move forwards got straight section and we've got some wild curve inches to the right.

  • Another straight section.

  • Wildcard pictures of the left.

  • It's not very good, but we can see that we're going through track sections.

  • The problem here is our target coach goes indiscreet steps.

  • This can't happen, so we need to slowly inter plate between different target coaches to give us the impression that the road is turning left or right.

  • Because this is persistent.

  • Between update loots, I'm going to add a variable F curvature, which represents the curvature of the track at a given point in time to perform the linear interpellation.

  • I'm going to calculate the difference by subtracting the current curvature from the target.

  • Curvature are multiplying it by every elapsed time on going to update my F curvature with that difference, and instead of now using the target curvature variable, I'm just going to use the curvature variable.

  • Let's see how this works.

  • So I'm pressing the forward key and we can see there was a nice, smooth transition between the track sections.

  • Different curvature no, is that she came.

  • However, it clearly still doesn't look right.

  • And that's because our interpellation off the perspective is linear.

  • We shouldn't be the case.

  • Our roads need to look like the curved.

  • So I need to know factor into our middle point calculation, that perspective.

  • And I'm going to do this by multiplying to one minus perspective to the power of three.

  • Let's take a look because I go forward.

  • We can see it.

  • Ben's on what works.

  • Then we can see.

  • It continues to bend because the elapsed time is still ticking over on the game loop, even though the car's not moving forward.

  • So we'll address that in a minute.

  • We can see now that the curves smoothly move around.

  • And if we get to the next bend, what we're actually looking at here is the cubic function of the perspective, which I believe looks about right now.

  • We don't necessarily have to use the cubic we can use this the quadratic instead, but I feel that these Ben's don't feel quite right.

  • I guess it's a matter of personal taste.