sorry if this seems like an idiot's question but I am very bad at programming.
Basically, I want to make a script that has an Interact method that does something different based on what the object is.
If you played something like Max Payne 2, you might have noticed how you can open doors, interact with sinks & toilets and they do something different.
What I did was attach the script that shows the button prompt to the object that I would be colliding with, but I have no idea how to move forward. Which script should check for the "E" button down once the player collides with the object, and how do I define the Interaction() method based on the object I collided with without making a script for each object? (Unless that's how you make this kind of interaction system?)
Sorry if I am not making much sense, english isn't my main langauge. Thanks in advance.
public class Interactable : MonoBehaviour {
public GameObject canvas;
void Update()
{
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Player")
{
Debug.Log("enabled");
canvas.SetActive(true);
}
}
void OnCollisionExit(Collision collision)
{
if (collision.gameObject.tag == "Player")
{
Debug.Log("exit");
canvas.SetActive(false);
}
}
CodePudding user response:
Welcome to Stack-overflow and Programming
What you are looking for is Inheritance.
It allows you to create a 'base' class to use and reference to.
In this case, you can classify those objects as Interactable
, and create another script to describe their usage further, like so:
public abstract class Interactable : MonoBehaviour {
// ...
public abstract void Interact();
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.tag == "Player") {
// EXAMPLE: Now it will interact dynamically whenever player enters it's range
Interact();
}
}
}
// ......
public class Door : Interactable {
public override void Interact() {
Debug.Log("Open Door");
}
}
// ......
public class Toliet : Interactable {
public override void Interact() {
Debug.Log("Flush Toliet");
}
}
// Or maybe on the player's POV
public class Player : MonoBehaviour {
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.tag == "Interactable") {
// We only have to concern if its an interactable,
// no need to concern what type of interactable it is
gameObject.GetComponent<Interactable>().Interact();
}
}
}
You can check some online tutorials if it is still confusing to you.
You may also want to look at Interfaces. Nearly identical to Inheritance.
CodePudding user response:
One of the best ways to handle the problem is to use an interface
. Of course, keep in mind that avoiding creating different scripts for interactive objects will take the player code out of control as the project expands. So you can create an interface like the one below outside the player class:
public interface IInteractable
{
void OnAction(Player player);
}
You can also assign this interface away as follows. Here the door will be an interactable Monobehavior
. Other game objects such as lights, books and can be defined as follows.
public class Door : MonoBehaviour, IInteractable // for e.g
{
public void OnAction(Player player)
{
// Play Open door animation
}
}
public class Book : MonoBehaviour, IInteractable // for e.g
{
public void OnAction(Player player)
{
// Read the book
}
}
public class Agent : MonoBehaviour, IInteractable // even you can use this for agents that can interact with them
{
public void OnAction(Player player)
{
Debug.Log(player.name " is talking with " name);
}
}
Finally, the only code you need to write in the player is a raycast. Because the collider only works when your player is inside the body, but the interact mode, like skyrim or max payne, works via raycast or spherecast. The code below can cover any object that benefits from Interactable. Not only can you access the player, but as the project expands, you no longer need to define multiple ifs for different objects. Also keep in mind that this is a standard way to solve this problem.
public LayerMask interactLayer; // interact layer for avoiding ray detection mistakes
public void Update()
{
if (Physics.Raycast(transform.position, transform.forward, out var hit, 2f, interactLayer.value))
{
if (Input.GetKeyDown(KeyCode.E))
{
var _interactable = hit.transform.GetComponent<Door>();
_interactable?.OnAction(this);
}
else
{
Debug.Log("Press E Key to interact with " hit.transform.name);
}
}
}