Tracking the mouse position
For the purpose of this game, the only input device we need to manage is mouse. Basically, we just need a way to know which tile it's hovering on, and then take some action if we click while we're on it (Let's not worry about any buttons for now, we're going to handle user interface later).
The steps to follow are:
- get the position of the mouse in screen space (x and y coordinates on our monitor)
- transform those coordinates to world position. This is based on where the camera is positioned.
- create a line perpendicular to our screen, starting from the mouse cursor.
- See if this line intersects any object. If that object is a hexagon, then that's the tile we're hovering on.
Since I need to constantly check for the mouse position, I'm going to implement this in the Tick() method of my class, so that a check will be performed every frame:
As you can see, I gave my PlayerController class a variable for the game state: player turn, player movement, AI turn and AI movement. I need to check where the mouse is only in the first state, when the game is waiting for my action. This is why when I'm in gameState == 0 I call the method ManageMouseHover(), where all the computations will be performed. Let's take a look at it:
Here I create 2 new vectors, LookDirection and WorldLocation. They will be the output parameters of the DeprojectMousePositionToWorld function, doing exactly what the name suggests: it converts the mouse cursor location in a 3D World Space location and direction. We're going to store the return values in the WorldLocation and LookDirection vectors.
If it returns true (always, hopefully... if it returns false this means it was unable to determine valid values, and it should never happen!), then we pass both WorldLocation and LookDirection to our next method: HighlightTile().
In this method, I will trace a ray from the mouse position: I first set the EndLocation of my ray, adding the LookDirection to WorldLocation. Since LookDirection is a normalized vector of length 1, I multiply it for a large number to be sure that it will hit something.
After that, I call the LineTraceSingleByChannel() method of the World class. The PlayerController knows what its own world is, and we can retrieve it by calling the GetWorld() function.
This function has 1 output parameter, HitResult, and it's the object being hit by our ray. The input parameters are: WorldLocation (the starting point of the line), EndLocation (the end point), and ECC_Visibility, basically telling it to hit everything visible.
The method returns true when any object was hit, and in this case I will go on with my logic: just to be sure that I didn't hit anything else, I will check if I'm on a tile: if the Component that was hit (our map has many Static Mesh components, remember?) has a name that starts with "Tile", then I'm going to change its material to "highlight" it:
Here I simply set a new material, chosen from the ones we selected before in the Blueprint, and I keep track of the currently selected tile so that I can set it back to its normal material when leaving it.
That's it, now we have a working map with selectable tiles! Since actualSelection is a pointer to the currently selected tile, it will be easy to have its position when we click, so that we can move the player ship there. But we'll take a look at that in the next post.
One final note on the HighlightTile() method: if LineTraceSingleByChannel() returns false, the mouse cursor is outside of the map. In this case, if there was a tile selected I reset it to normal material, and then I set the actualSelection to nullptr.
I hope this procedure makes sense for you, it's a bit complicated because it involves vector operations and transformations in 3D space, that luckily Unreal manages for us, hiding all the math.
Thanks for reading and game you next time!
As you can see, I gave my PlayerController class a variable for the game state: player turn, player movement, AI turn and AI movement. I need to check where the mouse is only in the first state, when the game is waiting for my action. This is why when I'm in gameState == 0 I call the method ManageMouseHover(), where all the computations will be performed. Let's take a look at it:
Here I create 2 new vectors, LookDirection and WorldLocation. They will be the output parameters of the DeprojectMousePositionToWorld function, doing exactly what the name suggests: it converts the mouse cursor location in a 3D World Space location and direction. We're going to store the return values in the WorldLocation and LookDirection vectors.
If it returns true (always, hopefully... if it returns false this means it was unable to determine valid values, and it should never happen!), then we pass both WorldLocation and LookDirection to our next method: HighlightTile().
In this method, I will trace a ray from the mouse position: I first set the EndLocation of my ray, adding the LookDirection to WorldLocation. Since LookDirection is a normalized vector of length 1, I multiply it for a large number to be sure that it will hit something.
After that, I call the LineTraceSingleByChannel() method of the World class. The PlayerController knows what its own world is, and we can retrieve it by calling the GetWorld() function.
This function has 1 output parameter, HitResult, and it's the object being hit by our ray. The input parameters are: WorldLocation (the starting point of the line), EndLocation (the end point), and ECC_Visibility, basically telling it to hit everything visible.
The method returns true when any object was hit, and in this case I will go on with my logic: just to be sure that I didn't hit anything else, I will check if I'm on a tile: if the Component that was hit (our map has many Static Mesh components, remember?) has a name that starts with "Tile", then I'm going to change its material to "highlight" it:
Here I simply set a new material, chosen from the ones we selected before in the Blueprint, and I keep track of the currently selected tile so that I can set it back to its normal material when leaving it.
That's it, now we have a working map with selectable tiles! Since actualSelection is a pointer to the currently selected tile, it will be easy to have its position when we click, so that we can move the player ship there. But we'll take a look at that in the next post.
One final note on the HighlightTile() method: if LineTraceSingleByChannel() returns false, the mouse cursor is outside of the map. In this case, if there was a tile selected I reset it to normal material, and then I set the actualSelection to nullptr.
I hope this procedure makes sense for you, it's a bit complicated because it involves vector operations and transformations in 3D space, that luckily Unreal manages for us, hiding all the math.
Thanks for reading and game you next time!
Commenti
Posta un commento