تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Project Overview and Title0:00

OK, let's create a game in the terminal, and we're going to recreate Pong, the classic arcade game, but we're going to recreate it as Prong because we're using prompts to make Pong. So, it's going to be the classic game, it's going to have two paddles, it'll have a ball moving back and forth, and then in the end we might layer in some extras just to make it a little more interesting. So the first thing I want to do is I want to show a title screen like they would in the old arcade games, and when they press enter, I want the title screen to sort of like fade away from the bottom up. So first, since we're not waiting for key presses, we need to pull in the setup and

like fade away from the bottom up. So first, since we're not waiting for key presses, we need to pull in the setsUp and resets from Chewy, and then we also need to pull in loops because we're going to be doing some looping here. So let's pull those in. Use setsUp and resets, and use loop. So when we fire this off in the scriptRunner, we're going to be firing it off using a play method. public function play, and this will run our setup method. This setup, and then we'll do this showTitle, because that's the first thing we want to

public function play, and this will run our setup method. This setup, and then we'll do this showTitle, because that's the first thing we want to do when we fire up the game. Let's implement that method. protected function showTitle. So our title is going to be a loopable because it's going to animate a value. We have a titleLoopable that I've set up already, so let's just pull that in. public title title, and all this is is just a blank loopable setup ready to go. So let's go ahead and register that. This title equals new Title, and we will register the loopable.

So let's go ahead and register that. This title equals new Title, and we will register the loopable. This registerLoopable, this title. Let's also set up a keyPressListener property so we can use that throughout the application. protected keyPressListener listener, and then we can go ahead and instantiate that here. This listener equals keyPressListener for this. We're going to be using the built-in state property and changing that to tell our renderer what state the game is in, so our initial state is going to be title. So let's set that.

what state the game is in, so our initial state is going to be title. So let's set that. Okay, great. So we've got a new loopable title, we've registered the loopable, and now we have a key press listener setup. So let's implement this showTitle method. Just to keep this isolated, we're going to move this registerLoopable down here so that it only gets registered when we are showing the title. Then we're going to set up some basic key press listeners. So this listener, clear existing, listen for quit, and then on key enter, we are just going

Then we're going to set up some basic key press listeners. So this listener, clear existing, listen for quit, and then on key enter, we are just going to do nothing. We're just going to say return false, and we're going to listen now. So what we're saying here is when we would use listen now, we're saying start an infinite while loop and wait for one of these key presses before we move forward. So this will pause execution of the script and wait for a key press, and it's going to wait for either a quit or an enter to continue with the script. Now in order to show the title correctly, we need to render beforehand. So let's do render here.

Building Title Loopable3:20

Now in order to show the title correctly, we need to render beforehand. So let's do render here. So this will register our loopable, we'll render, and then we'll wait for the key press. Okay, let's dive into our title loopable, and let's just set that up real quick. So let's dive in here. So we are going to have a public animatable value, and when we construct, we're going to set this value equal to animatable from value 8, lower limit 0. So we're going to go from an initial value of 8, and our lower limit is 0. So why is it 8? We're going to be typing out the big text of the title prong, and it's 8 lines long,

So why is it 8? We're going to be typing out the big text of the title prong, and it's 8 lines long, so we just know we can set 8 as the value. Okay, let's set up our onTick method, public function onTick, and we'll just do this value animate. And so onTick, we're just going to animate the value. Let's add a method that allows us to easily hide our title. So we'll say public function hide, and we go this.value to 0. So when we're ready to hide, we can call this method, and we can just say, great, start animating the value to 0.

So when we're ready to hide, we can call this method, and we can just say, great, start animating the value to 0. So while we're waiting, this tick will do nothing, but when we say hide, it'll start animating every tick one notch down to 0. So this is our entire title loopable, this should do it for us. Okay, back in our application, now that we've gotten an enter, we can continue, and we can say this title hide, so we're saying start animating that to 0 when we start looping. And we'll do this loop function, this render, and remember, when we want to break this loop, we can return false. So if we return false from the function that is being passed to this loop, it will break.

we can return false. So if we return false from the function that is being passed to this loop, it will break. And when do we want to break it? We want to break it when the title is equal to 0, because we want to move on to the next step. So we can say return this title value current greater than 0. So as long as it's greater than 0, it's going to be true. Once it hits 0, it'll return false, and it'll break the loop. Okay, let's go over to our renderer and actually render our title out to the screen. In our renderer, let's actually return match prompt state, and we'll say if it's the title,

Rendering Title Screen5:42

Okay, let's go over to our renderer and actually render our title out to the screen. In our renderer, let's actually return match prompt state, and we'll say if it's the title, we'll render the title, we'll pass the prompt along. Otherwise, for now, we'll just return this as a default. Cool, let's work on this renderTitle method. function renderTitle, and this receives a prompt and returns itself. So we're going to render out prompt in what we call big text, like big ASCII art. So we'll say use drawsBigText, and in order to use this, we need to tell Chewy where it can find our big text characters. So let's come back here, and we can set the art directory here, where you're going to

can find our big text characters. So let's come back here, and we can set the art directory here, where you're going to point the art directory to art characters from Chewy. Cool, so now it knows where to look. So back in renderTitle, we're going to say the title is equal to this big text prong. We're just going to print it out as big text. We're going to push a line afterward because this returns a collection of lines. So we're going to push a blank line, and we'll push a little message so that the user knows what exactly they can do. We'll say press this bold, this cyan, enter to start.

knows what exactly they can do. We'll say press this bold, this cyan, enter to start. Now how do we go about erasing these lines one by one? Well, let's try it out. So we're going to map these lines, and we will say function lineIndex, and we'll say if the index is greater than the promptTitle value current, if it's greater than the current value, we are going to quote unquote erase that line. We're going to replace that line with just a blank line. So we'll say string_repeat(spaces), and we'll do mb_string_width(). Now we're about to measure the width, but here we have a formatted line.

So we'll say str_repeat spaces, and we'll do mb_str_width. Now we're about to measure the width, but here we have a formatted line. So we need to know how long to print this without those escape characters. So Chewy has a little strip_escape_sequences helper method that we just imported, and we'll say lines. We want to measure that without the escape sequences. Otherwise, just return the line. Lastly, we want to center this on the screen. So we'll say this->center, and we'll pull in the aligns trait from Chewy, title this with this->height, and then we'll just print that to the screen.

So we'll say this center, and we'll pull in the aligns trait from Chewy, title this with this height, and then we'll just print that to the screen. And we'll return this. Great. I think that should do it. Oh, it looks like I missed a space here. Space. OK, let's check it out. OK, let's come back and run our script. And if we run the script, we get our nice big prong title and press Enter to start.

OK, let's come back and run our script. And if we run the script, we get our nice big prong title and press Enter to start. And we press Enter, and the title disappears. Great. So far, so good. So once the title disappears, we should show the game. And let's show a little countdown. So we'll show a countdown 3, 2, 1 before the game starts to give the player a moment to adjust to the new view of the game. OK, here we are back in our application.

Starting Game and Countdown9:16

moment to adjust to the new view of the game. OK, here we are back in our application. And so once this loop breaks, once the title has disappeared, let's run this startGame. And let's figure out what that method looks like. protected, function startGame. So the game has a couple of elements. We have a countdown from 3. We have a ball, and we have two paddles. Those are sort of like the main components of the game.

We have a ball, and we have two paddles. Those are sort of like the main components of the game. So let's start with that countdown. Let's just make that a public property so we have access to it. public int countdown. And then we'll have a public Ball loopable, which I already have set up the bare bones of, ball. And then we'll have a public Paddle loopable for player 1, which is us. And then we'll have a public Paddle loopable for the computer player. So down in startGame, let's initialize some of these properties.

And then we'll have a public Paddle loopable for the computer player. So down in startGame, let's initialize some of these properties. So we'll say this countdown is equal to 3. We want to start the countdown at 3. We want to say this ball equals new Ball. We'll say this player1 equals new Paddle. And we'll do the same thing for the computer. We want to set the state equal to playing, because we're now playing our game. We want to clear any registered loopables.

game. We want to clear any registered loopables. So this will clear anything registered, in this case the title, from the loopable loop so that they won't run anymore. And then we'll say this playGame. And we'll just extract the actual playing of the game aside from the starting of the game so that later we can reuse this to restart the game if we want to. OK, let's figure out what playGame looks like. playGame.

OK, let's figure out what play game looks like. Play game. So what do we want to do? The first thing we want to do is set up our listener again. This listener, clear existing. Clear out any existing listeners. And we want to listen for quit. Always a good start. OK, we want to count down. So we want to count down from 3 to 0.

OK, we want to count down. So we want to count down from 3 to 0. So we can set up a little simple while loop. This countdown is greater than 0. We want to render. We want to listen once, just in case they press the quit key. We want to decrease the countdown. And then we want to sleep for one second. So we're going to run this manually just to make it a little clearer about what's going on here.

So we're going to run this manually just to make it a little clearer about what's going on here. Then let's go ahead and register our loopables. This registerLoopables. And we're going to do this ball. This player1 and this computer. Let's start a loop. And all we'll do in this loop for right now is just render and listen for a key press. So we can actually come up here.

Rendering Game Layout12:12

listen for a key press. So we can actually come up here. We can grab this. And we can pop it down here. We'll say render. And then we just want to listen for a key press once, just in case they press a key in here. OK, let's go back to our renderer and set up the space for the game itself. So first, let's change our default state of the match statement to be this render game.

So first, let's change our default state of the match statement to be this renderGame. So our default state, if nothing else is going on, is that we're going to be playing the game. So let's go ahead and fill out that method, renderGame. All right, let's think about how we want to render the game. I think the game is going to be rendered pretty statically. It's not going to depend on the width and the height of the terminal. Instead, it's just going to be a box with static dimensions.

It's not going to depend on the width and the height of the terminal. Instead, it's just going to be a box with static dimensions. So your terminal will have to be a certain width and height in order to render this. But I think that's OK for the purposes of our game. So in our application state, we're actually going to just define this here, because a couple of different items we'll need to know about these values in order to share them. So we're going to set public int gameHeight equal to 26. And we'll say public int gameWidth is going to be equal to 100.

So we're going to set public int gameHeight equal to 26. And we'll say public int gameWidth is going to be equal to 100. So the height of the game will be 26. The width of the game will be 100. Those are just static values. They're not going to depend on the size of the terminal. So now that we have these set, let's set up some of our loopables so that we have some dimensions for how they're going to render. So let's go down here, and we'll set up the paddle first. So first, let's set up a public animatable value.

So let's go down here, and we'll set up the paddle first. So first, let's set up a public animatable value. And this value will be the position of the paddle within the game. So we'll say public function construct(). And we're going to need to accept prong as an argument here. So we're going to go back to our application and make sure that we pass that in. This, this, cool. This value equals animatable::from(value). And we want the paddle to start at the vertical center of the screen.

This value equals animatable from value. And we want the paddle to start at the vertical center of the screen. So when the game starts, the paddles will be in the center vertically on their respective sides. So let's calculate that. So we'll say halfGameHeight is equal to the floor of this promptGameHeight divided by 2. And halfPaddleHeight will be equal to the floor of this. And then we don't have a property set for this. So we should probably say public height is equal to, and we'll say the

And then we don't have a property set for this. So we should probably say public $height is equal to, and we'll say the $height is going to be 5. So we'll say $height divided by 2. Cool. So we have a halfGameHeight and a halfPaddleHeight. And if we subtract these two, and we say halfGameHeight minus halfPaddleHeight, that should position us in the vertical center of the screen. And just for specificity's sake, we're going to cast this to an int. OK, so that's our initial value is half.

And just for specificity's sake, we're going to cast this to an int. OK, so that's our initial value is half. Our lower limit is 0. We can't go below 0. And our upper limit is this prompt. Oh, I just realized this is prong, not prompt. gameHeight minus this height. So the paddle can't go past the bottom based on the height of the paddle. Then let's define our onTick public function onTick void. And we'll say this value animate.

Then let's define our onTick public function onTick void. And we'll say this value animate. If there's something to animate, go ahead and animate it onTick. Let's take a look at our ball for a second. It's just a blank one. So let's do public void onTick. And for right now, we're not going to do anything here. We just want to define this method so that it doesn't break when we render it. And now let's go back to our renderer. OK, so there's a lot of components to the actual game screen.

And now let's go back to our renderer. OK, so there's a lot of components to the actual game screen. But what we really want to think about is that there's kind of basically three columns. The left column is the paddle. The center column is either the countdown or the ball, depending on what the state is. And the right column is the second paddle. So let's just build those out. Let's just scaffold that out for a second.

So let's just build those out. Let's just scaffold that out for a second. paddle1 is going to be equal to this paddle. We're going to extract this out to a separate method, because we're going to be doing the same thing for both paddles. So prompt player1. And let's define a color for this paddle. We'll say red. And so paddle2 is going to be the computer. And we'll say the color is green.

And so paddle2 is going to be the computer. And we'll say the color is green. Let's take a look at what this paddle method could possibly be. protected function paddle accepts a paddle. We're going to return a collection of lines from this paddle. OK, so what we need to do is we need to render blank lines above the paddle for whatever the current value of the animatable value of the paddle is. So that's the current y position of the paddle. So we need to render any blank lines at the top.

So that's the current y position of the paddle. So we need to render any blank lines at the top. Then we render the paddle itself. So we're going to return, collect, and we'll say array_fill(0, $paddle['value']['current'], just a blank line). That's it. So we'll just fill an array of blank lines up to the current value. And we'll say merge_array_fill(0, $paddle['height'], this). Oh, and I forgot to pass the string color, color, and then a bar. And that should get us our paddle line.

Oh, and I forgot to pass the string color, color, and then a bar. And that should get us our paddle line. So a bunch of blank lines up until the y position. And then we fill a bunch of vertical bars in one after the other based on the color of the paddle. So those are our paddle lines. Next, let's talk about this center column. So we'll say center is equal to. And for now, we'll just say this countdown prompt. So we can just copy this method.

And for now, we'll just say this countdown prompt. So we can just copy this method. And we'll call this countdown. And we don't need a color here. And it's going to also return a collection. And all this is going to do is render a countdown as a big number in the middle of the game. So we're going to pull in use drawsBigNumbers from Chewy. We'll come back down. We'll return this center, this big number.

We'll come back down. We'll return this center, this big number. And we'll do prompt countdown. And then we want to make this bold. And we want to make this cyan. So we'll say map function line. And this bold, this cyan line. And then we will say center this within the game area itself. width, prompt, game, height. And that should do it, except that it doesn't accept a paddle,

Width, prompt, game, height. And that should do it, except that it doesn't accept a paddle, it accepts prong and prompt. Cool. So we have our left column, we have our center column, and we have our right column. Let's create lines from columns. linesFromCalls equals linesFromChewy from columns. And we have paddleOne, center paddleTwo. And we'll get the lines.

And we have paddle one, center paddle two. And we'll get the lines. For right now, let's just print those out. Lines from columns, each this line. And we'll return this to appease that. OK, great. Let's try this out. OK, so we have our title screen. If we press Enter, great. So this is great.

This is just a simple explanation with no code.

And then let's also maybe center this within the screen. That should also be easy enough. So in order to create a box, we're going to grab the useDraws boxes. We're also going to use captures output from Chewy. And so let's come down here, and we'll say boxed is equal to this capture output. And we'll draw a box. This box, no title. And the content is going to be the lines from columns,

This box, no title. And the content is going to be the lines from implode, php, end of line. Sweet. OK, we've got boxed. And now this is returning a string. So let's get this back into a collection. lines equals collect, explode, php, end of line, boxed. And then let's just center that in the screen. This center lines, this width, this height, each line.

And then let's just center that in the screen. This center lines, this width, this height, each this line. OK, this should do us. Let's see. Press Enter. OK, implode does not exist. You are correct, because it's implode. Next. Oh, yeah. OK, great.

Adding Paddle Movement21:44

Oh, yeah. OK, great. So now we can tell where our game borders are and what's going on here. We can see that these paddles are vertically centered. The countdown is also centered. This is looking really good. OK, next up, let's handle paddle movement. Let's actually do some key bindings here and allow us to move the paddles.

Let's actually do some key bindings here and allow us to move the paddles. OK, back in our Application class, down in the playGame method, right before we start looping the game, let's just register some additional key press bindings. So this listener. And we'll say on up, we want to this playerOne. And let's add a little helper function in here. We'll say moveUp.

And let's add a little helper function in here. We'll say moveUp. And you guessed it. If we moveUp, we also need to moveDown. So we'll say down and down. And let's actually implement these methods. So let's go to Paddle. And we'll do public function moveUp. And this will be $this value to relative. And we'll say -1.

And this will be this value to relative. And we'll say -1. So whatever the current value is, subtract 1. And we can go ahead and say move down. And when we move down, we want to move it up by 1. OK, so because we already have this animatable value and we're already calculating the position in the render, this should just work. Let's just see. OK, back in the game.

Let's just see. OK, back in the game. Once the countdown is over, if we move up, yes. If we move up and down, it works. This looks good. So I'm pressing arrow keys, and it's going up and down. Awesome. So we've got working paddles. Now let's talk about the ball.

Now let's talk about the ball.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟