My Plan A before I started this course had been to use a NavMesh Agent for the player, but once we’d implemented the Straight Line Click to Move, I decided I much preferred that and wanted to keep it (and I made plans to expand it). But then, of course, the new raycaster script broke it.
After much messing this afternoon, I finally got it working, and I want to share my code here for 2 reasons:
- For anyone else who wants to to have the playerCharacter move directly as told, and
- So that any smart people can have a look and tell me if there is a better way.
The problem I had was that if ProcessMouseClick was not in Update, the playerCharacter misbehaved, but if it was inside Update, it couldn’t receive data from the delegate.
My solution was to make member variables for the data from the delegate, and a method to assign them, then used those in ProcessMouseClick, which I could now call from Update.
Anyway. This is my code:
using System;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;
[RequireComponent(typeof(ThirdPersonCharacter))]
public class PlayerMovement : MonoBehaviour
{
[SerializeField] float walkMoveStopRadius = 0.2f;
[SerializeField] float approachEnemyRadius = 0.5f;
ThirdPersonCharacter thirdPersonCharacter; // A reference to the ThirdPersonCharacter on the object
CameraRaycaster cameraRaycaster;
Vector3 currentDestination, clickPoint;
// TODO solve fight between const and serialize
[SerializeField] const int walkableLayerNumber = 8;
[SerializeField] const int enemyLayerNumber = 9;
[SerializeField] const int playerLayerNumber = 10;
RaycastHit clickedPoint;
int clickedLayer;
void Start()
{
cameraRaycaster = Camera.main.GetComponent<CameraRaycaster>();
thirdPersonCharacter = GetComponent<ThirdPersonCharacter>();
currentDestination = transform.position;
cameraRaycaster.notifyMouseClickObservers += GetLayer;
}
void Update()
{
ProcessMouseClick();
}
void GetLayer(RaycastHit raycastHit, int layerHit)
{
clickedPoint = raycastHit;
clickedLayer = layerHit;
}
void ProcessMouseClick()
{
clickPoint = clickedPoint.point;
switch (clickedLayer)
{
case enemyLayerNumber:
// navigate to the enemy
currentDestination = ShortDestination(clickPoint, walkMoveStopRadius);
break;
case walkableLayerNumber:
// navigate to point on the ground
currentDestination = ShortDestination(clickPoint, walkMoveStopRadius);
break;
case playerLayerNumber:
print("Show the player menu.");
break;
default:
Debug.LogWarning("Don't know how to handle mouse click for player movement");
return;
}
WalkToDestination();
}
void WalkToDestination()
{
var playerToClickPoint = currentDestination - transform.position;
if (playerToClickPoint.magnitude >= walkMoveStopRadius)
{
thirdPersonCharacter.Move(playerToClickPoint, false, false);
}
else
{
thirdPersonCharacter.Move(Vector3.zero, false, false);
}
}
Vector3 ShortDestination(Vector3 destination, float shortening)
{
Vector3 reductionVector = (destination - transform.position).normalized * shortening;
return destination - reductionVector;
}
void OnDrawGizmos()
{
// Draw movement gizmos
Gizmos.color = Color.black;
Gizmos.DrawLine(transform.position, clickPoint);
Gizmos.DrawSphere(currentDestination, 0.15f);
Gizmos.DrawSphere(clickPoint, 0.1f);
// Draw attack sphere
Gizmos.color = new Color(255f, 0f, 0, .5f);
Gizmos.DrawWireSphere(transform.position, approachEnemyRadius);
}
}