So I have stuck with the inheritance structure because, for me, it is more intuitive, and also, if I use interfaces I will have to rewrite all lot of the code, which is the whole reason Im using these classes anyway.
For example, weapon inherits from item. Item in inherits from interactable. If i use interfaces, then the code that gets called when an item is picked up, I will have to rewrite for the weapon class. Weapon having its own implementing of the method does not help me.
But maybe I am not seeing it correctly. Anyways I was using this code on each Interactable object.
public void CheckForInteraction() {
if (CanInteract()) {
// can interact
if (!isCurrent) {
// is not current
managerInstance.SetCurrentInteractObject(this);
isCurrent = true;
}
} else if (isCurrent) {
// is current, and can no longer interact
managerInstance.RemoveCurrentInteraction();
isCurrent = false;
}
}
bool CanInteract() {
// check if wihin range
if (Vector3.Distance(GetPlayerPos(), m_Trans.position) >= interactDistance) return false;
DebugPrint("is within range");
// Shoot a ray from the camera's center
Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0));
// Declare a variable to store information about the hit
RaycastHit hit;
// Check if the ray hits something within the specified max distance and on the interaction layer
if (Physics.Raycast(ray, out hit, maxRaycastDistance)) {
DebugPrint("Ray hit");
// The ray hit an object
if (hit.collider.gameObject.name == interactCollider.name) { DebugPrint("can interact"); return true; }
}
DebugPrint("cant interact");
return false;
}
public virtual void RunInteract() {
if (lockPlayer) {
player.SetCanMove(false);
playerTransform.parent = transform;
player.SetCharacterCotrollerActive(false);
}
}
Basically each interactable object checks its distance from the player, if It is in range, a ray cast is shot to determine if the player is looking at the object. But ehrre is 2 problems with this.
- If the player is near a tons of objects, there could be a lot of raycasts at once.
- (the real problem) if two gameobjects have the same name, this will cause issues, and it did. If the player is near 2 doors, with the same name, then both will get set to the current object in the same frame.
I also dont love the idea of every single interactable object in the scene calling a function to check if its close to the player, it seems messy. So instead I have written this function that is called by InteractManager:
public void CheckForInteraction() {
Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0));
// Declare a variable to store information about the hit
RaycastHit hit;
// Check if the ray hits something within the specified max distance and on the interaction layer
if (Physics.Raycast(ray, out hit, maxRaycastDistance)) {
// Ray hit
// DebugPrint("Ray hit");
// attempt to retrieve an interactable component from the collide that was hit
var interactObj = hit.collider.GetComponentInParent<Interactable>();
if (interactObj != null) {
// the cam is looking at an interact component
if (Vector3.Distance(playerTransform.position, interactObj.GetPosition()) <= interactObj.interactDistance) {
// is within range
SetCurrentInteractObject(interactObj);
// DebugPrint("did set the current object to: " + currentObj);
} else RemoveCurrentIfExists(); // ray hit interactable, but the player is not within range
} else RemoveCurrentIfExists(); // ray hit, but not an interactable
} else RemoveCurrentIfExists(); // ray did not hit anything
}
This seems much cleaner to me. Instead of every single object checking its distance, the manager shoots a ray. Attempts to retrieve a Interactable component from the collider it hits, then checks the distance, and sets if within range. The only downside to this is the GetComponent<>() call, but this ensures that the correct interactable object is set, and only shoots 1 ray per frame. Thoughts?