Welcome to the next episode of our Farming RPG game. So far what we've done is that our player can move around with the camera following him. Today we'll be starting on the farming system for our game. But before all that, there's a small change I want to make: Let's rename this scene from 'Ranch' to 'Farm'. It's just a minor thing. Mostly because we're not just messing with animals but we'll be working on the farming system first. Anyway, for the farm, we'll need some ground the player can walk on. So let's get some textures. What I do is I go to polyhaven.com and go to the texture section... And look for the textures that I want. So we'll need three textures: 1. One for the regular dirt 2. One for when you've tilled the soil 3. And one for when you water it. So after we're done here, right-click and just extract files and then we'll take our textures... Go to Assets > Imported Asset Create a 'Farmland' folder And bring all of this in. Now, let's create a 3D object to preview our materials. We create a new material by dragging the albedo texture onto the cube. See there's a new material here so let's configure it. So we bring the normal map
here, mark it as a normal map This one is 'disp'; 'aerial_ground_rock_disp' We'll go to... go to the occlu... sorry the height map. Okay, got it set up for this material. Let's rename it: call it 'Dirt'. Move our ground texture files. *Do the same for 'Tilled' Land* Rename the folder 'Tilled Land' So this is the texture for when we've watered it; it's going to look like that. Adjust the height map accordingly a little bit. And now we have all three materials ready. so we have 'Dirt', 'Tilled' and 'Watered'. Create a new prefab. Let's reset its transform. Drag it to the prefabs folder, and now let's create our Land script. We need a script to handle the changes to these materials. Create a C# script and open it. Since we're using three materials we need to declare all three materials: soilMat, farmlandMat, wateredMat. These are the three materials we'll be using. Likewise, we'll make an enum to reflect the three states that our land will have in the farming system. So there's the Soil, Farmland, and Watered. Material - Enum State
Dirt - Soil
Tilled Land - Farmland
Watered - Watered Let's drag this new script over (to the prefab), And assign these materials. So Dirt goes here and Tilled Land goes to Farmland. Watered Land goes to Watered. So if you were to test it, nothing's going to happen. We also need a LandStatus variable from the enum, landStatus. So let's make a function to handle the Land Status. Let's call this public void SwitchLandStatus(). And we'll take in an enum parameter: LandStatus. We'll call it statusToSwitch. Our current LandStatus which is this, is going to be... So this function switches the landStatus variable to the status we want to switch to... and then we also reflect the changes on our cube. So to do this we'll use a switch. Switch accounts for each of the possible cases, like the case LandStatus.Soil. *Add a case for each possible LandStatus* Okay so now we have a switch to account for every possible LandStatus. But how do we actually change the material? So first we need a renderer so we'll declare a Renderer. So this renderer is going to be the renderer component on our land prefab. It's going to access this and change the material. So we're going to access the component. Now that we've accessed this component... We can change the material accordingly. Declare a new variable, call it materialToSwitch. By default, it's going to be (the material of) LandStatus.Soil. That's what we start with. It's the soil material (soilMat). We're setting it as the default material that we're using. So if (its state is ) Soil, it's going to switch to the soil material (soilMat). If it's watered, we switch to wateredMat. So after this switch decides what material to switch to. Get the renderer to apply the changes. Now it's going to switch accordingly. So by default, our landStatus is going to be set to be the Soil (LandStatus.Soil). Well, nothing's happened because we haven't coded any way for the player interact with this land. Let's set it up. Going on to the interaction system, so on the Player GameObject Let's make a new script, a new GameObject within the player. It's just gonna be slightly in front of him. This thing is going to control the player's range. Call it the 'Interactor'. And create a new script to handle all the Player interactions, 'PlayerInteraction'. Drag it on here and edit. Obviously, we'll need to access the PlayerController class. Because this (PlayerInteraction) is going to work in line with this (PlayerController). So to access it, just type (in the Start() method) playerController = ... We can't use GetComponent() because the GameObject this is attached to is a child of the Player GameObject. So we need to access the parent GameObject like this. Now, how do we interact with it? First, we'll draw a Raycast to directly below this to detect what's in front of the player. So to do this, in our Update() function, declare a RaycastHit. This will store whatever the Raycast hits. if(Physics.Raycast(transform.position)) We start with our Player's position, and we want the Ray to go
directly downwards, like this. So, Vector3.down... x distance should be about 1. So before we declare the distance, "out hit". This would be our hit info assigned to this. So whatever the Ray hits it will be stored in this variable. And we'll create a function that handles this. OnInteractableHit(RaycastHit hit) For now let's leave a Debug.Log() to see if it works. If you try the game now and I walk over this it says "Raycast.Hit". Yup, it's working. But we shouldn't just debug this Raycast hit. That's because if we were to add something. Like any random quad as well;
let's say a big piece of land... ...that the player can walk on, It will also output the same message So we need the player to be able to distinguish whether it's standing on land or if it's standing on just anything that isn't interactable. Let's declare a Collider, 'other', get the hit.collider So you can tell this is a mesh and this is a box but it isn't enough because we wanted to identify this as 'Land' Let's create a new tag, call it 'Land', and assign this to our Land prefab. And now let's make an if statement, so if(other.tag == 'Land'), debug it with this message. Yup, it works. Let's give it a bit more feedback. Create a new object. Reduce the scale a little bit (on the y axis). Set it to inactive. Call it 'Select'. So if the Player is going to be standing on land, it will set this (GameObject) to be active. Let's clone (the land prefabs) a little bit. So when the player 'selects' the Land (we create a function for it). We'll get a way to access this (the 'Select' GameObject) as 'select'. This is the selection GameObject that will be enabled when the player is selecting the land. Toggle it. There. So now if the player's selecting this, this GameObject will be set active and
if it's not it's going to be set to false. So now if the player is standing over the land, let's get its Land component, other.GetComponent, so now that we have our Land GameObject, a Land component we can then do "Land.Select();". So now when we get on here, we just set this to Select. And when our player walks over it, it's set to true. Now we just need to set a find a way to deselect it. When the player is selecting something, When a player walks over here, we want this interaction system to keep track of what it's selecting. So in this way, when the interactable is hit... Let's create a new separate function to handle the selecting of Lands. To handle this process, it's going to select the Land component so let's move this here. SelectLand(land), so instead we don't want
to just set this to true; We need to check (if there is already a Land object that is selected) First, we have to set the selectedLand But if we we were already selecting some land before this, selectedLand is not going to be null because by default, selectedLand is null. but when the player had already
selected another Land object before that, It's not going to be null, so we need to... so if it was already selected we just set (the currently selectedLand) to false (to deselect it). Now let's try this. Yup, much better now. But you see when the player walks off it, it remains selected. So now we need to figure out a way to handle the selection when the player walks off the land completely. So let's go back to PlayerInteraction.cs, to OnInteractableHit(). Add this. Okay so the code logic is that if the player is standing on land it's just going to do this and nothing else happens. But if this doesn't get triggered we want to... check if it's null. Set it to false and selectedLand is reset to null. Oh it works better now, but we have one problem: it gets jittery. Why? Because when the player is standing over this... Oh. We just have to remove the collider for this
(the Select GameObject on the Land prefab) Yup, much better now So to improve our selection UI, let's open an image editor. Create a new file.
It can be photoshop or any free image... Or any image editing software that supports transparency really (I use Paint.NET). Then expand it. So it's going to be a transparent little box. Oops. Yes, so there's a transparent box like that with transparency underneath. Save As 'selecttexture' So this will be our select
texture, so let's import it over. selecttexture (into Imported Asset). Create a UI folder for this like that and bring it over. This is our Select (GameObject). Bring it over like that, and now we have a material. But we want the middle to be transparent so we'll change this (Rendering Mode) to transparent and our color can be red like that. There. So now when it's selecting something it's going to look red. Looks much better. Let's move this down a little bit. You can find better textures for this but this is the quickest fix that i prefer. Well, we don't want to keep deactivating the select GameObject. Let's set the Select to be off at Start(). So this way you can just leave it on just to preview. More or less works. So now we need to make the player interact with this. So, if the land is being selected, we'll create a new method. This is on our Land script, Interact(). For now we'll just Debug.Log(). So now let's go to the PlayerController
script. We need a whole interaction system (to control). Call this method Interact(). So if our player clicks the mouse button... What's our mouse button (Input Name)? You can get our mouse button from Edit > Project Settings > Input Manager. "Fire1". so this is the button key. So the player presses the "Fire1" button, it'll Interact. So how do we interact? We'll declare a PlayerInteract variable. So since this is in the child object of this GameObject, we GetComponentInChildren(). We also need an interaction function. Interact(). I'm just linking the PlayerController so they'll send us our input. So when I press the input (mouse click) it's going to send an interaction here. Our interaction system so in our interaction system, When it's triggered when the Player presses the interact key, or rather, the tool button because this is tools. We will need another interaction
system when the player picks up other items We'll do this in a later part but for now we'll just do two interactions so. This is triggered when the player presses
the tool button it's going to interact. So we first need to check if the player is on any of our Land. Oh if it's selecting any Land it's going to do this, else it's going to Debug.Log(). Tell me that it's not on any land. Let's see how it works out. So when our player is not standing on any land, it's going to say "Not on any land" when i click my mouse. So now if I were to stand here... Click my mouse, it's going to say "Interact". It's coming from here (Land.cs). Let's do something more interesting. So instead of interacting when i click, we want to switch the Land to something. So let's pretend our player is hoeing it. So now when our player stands over here, and clicks, it changes into Farmland. That's it for this tutorial I hope you enjoy it. We will be doing more in the following parts so that's it, see you next time.