- Home /
Activate event on key press?
I've been trying to figure out how to get something to happen on a key press (like the typical "Press E to use" in games) but only have something happen when faced with the object I want to be used in stead of the event happening anywhere I press the button. So for reference, currently I have a plant that gets picked when pressing "E". I also want to use "E" to activate a bed and sleep (currently just makes the screen black with a UII image). I have been using Raycast for these things so pressing "e" only works on the objects with the correct scripts. The problem occurs when trying to use "E" for multiple things. It doesn't work and I assume it's because of how raycasting works. I know there must be some way to activate objects and events using a better method...but how does one do that?
The two scripts I'm using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField] Camera FPCamera;
[SerializeField] float range = 5f;
WildPlants plantTarget;
Sleep sleepTarget;
void Start()
{
plantTarget = GetComponent<WildPlants>();
sleepTarget = GetComponent<Sleep>();
}
void Update()
{
PressUseKey();
}
public void PressUseKey()
{
if (Input.GetKeyDown(KeyCode.E))
{
RaycastHit hit;
var raycastThing = Physics.Raycast(FPCamera.transform.position, FPCamera.transform.forward, out hit, range);
if (raycastThing)
{
WildPlants plantTarget = hit.transform.GetComponent<WildPlants>();
if (plantTarget == null) { return; }
plantTarget.ProcessPlantPicking();
}
else { return; }
}
/*if (Input.GetKeyDown(KeyCode.E))
{
RaycastHit hit;
if (Physics.Raycast(FPCamera.transform.position, FPCamera.transform.forward, out hit, range))
{
Sleep sleepTarget = hit.transform.GetComponent<Sleep>();
if (sleepTarget == null) { return; }
sleepTarget.Fade();
print("does it work?");
}
else { return; }
}*/
}
}
And:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WildPlants : MonoBehaviour
{
[SerializeField] float growthRate = 5f;
[SerializeField] GameObject grownPlant;
[SerializeField] GameObject pickedPlant;
[SerializeField] Collider emptyCollider;
[SerializeField] bool isPicked = false;
Renderer cubeRenderer;
void Start()
{
emptyCollider = GetComponent<Collider>();
cubeRenderer = grownPlant.GetComponent<Renderer>();
isPicked = false;
cubeRenderer.material.SetColor("_Color", Color.green);
}
void Update()
{
}
public void ProcessPlantPicking()
{
if (isPicked == false)
{
StartCoroutine(PlantIsPicked());
print("Plant put in inventory");
}
else if (isPicked == true)
{
return;
}
}
IEnumerator PlantIsPicked()
{
//cubeRenderer.material.SetColor("_Color", Color.red);
grownPlant.SetActive(false);
emptyCollider.enabled = false;
pickedPlant.SetActive(true);
yield return new WaitForSecondsRealtime(growthRate);
isPicked = false;
emptyCollider.enabled = true;
grownPlant.SetActive(true);
pickedPlant.SetActive(false);
}
}
Answer by yummy81 · Jan 04, 2021 at 12:20 AM
Use interfaces. I will show you general idea, and then you will be able to built your system upon it. So, at first create interface. I called it IInteractable and it has one method - DoSomething. You can put it in any script you like - I put it just below your Player class in Player script. It means that every class which want to implement the IInteractable interface is forced to create DoSomething method. So, I make your WildPlants and Sleep classes implement it. Finally, when you press E you search for IInteractable component. As WildPlants and Sleep implement this interface, Unity will invoke proper method from proper class. Player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField] Camera FPCamera;
[SerializeField] float range = 100f;
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
RaycastHit hit;
var raycastThing = Physics.Raycast(FPCamera.transform.position, FPCamera.transform.forward, out hit, range);
if (raycastThing)
{
IInteractable interactable = hit.transform.GetComponent<IInteractable>();
if (interactable!=null)
interactable.DoSomething();
}
}
}
}
public interface IInteractable
{
void DoSomething();
}
WildPlants script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WildPlants : MonoBehaviour, IInteractable
{
public void DoSomething()
{
Debug.Log("WildPlants");
}
}
Sleep script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sleep : MonoBehaviour, IInteractable
{
public void DoSomething()
{
Debug.Log("Sleep");
}
}
Ok I guess I've run into a snag already just trying to implement the interface. When I put it next to $$anonymous$$onobehavior in WildPlants it just gives a red squiggle and acts like it doesn't know what it is.
Answer by Zaeran · Jan 04, 2021 at 12:18 AM
Raycasting is what you'll need know what object to press. You can probably just put a single forward raycast in every frame if you want the 'press E' prompt to appear.
You can have one script that tests every possible script, but that's not efficient at all, and is almost guaranteed to lead to spaghetti code. You'd be better off either using an interface with the Interact method in it, and then each object's script will use that interface. That way, you can just check if what you're pointing at is has the interface, and just call the Interact method if it does.
Thanks that's super helpful! I'm pretty new to coding so I don't know everything I can do, and Interact seems like it's exactly what I need!
Answer by Cepheron · Jan 04, 2021 at 08:43 PM
I compared with other scripts to make sure I at least typed it all correctly and it does appear I have. I have named the methods the generic stuff you had in your example just for testing What am I missing here?
Player
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField] Camera FPCamera;
[SerializeField] float range = 5f;
WildPlants plantTarget;
Sleep sleepTarget;
void Start()
{
plantTarget = GetComponent<WildPlants>();
sleepTarget = GetComponent<Sleep>();
}
void Update()
{
PressUseKey();
}
public interface Interactables
{
void DoSomething();
}
public void PressUseKey()
{
if (Input.GetKeyDown(KeyCode.E))
{
RaycastHit hit;
var raycastThing = Physics.Raycast(FPCamera.transform.position, FPCamera.transform.forward, out hit, range);
if (raycastThing)
{
Interactables interactable = hit.transform.GetComponent<Interactables>();
if (interactable != null)
interactable.DoSomething();
}
else { return; }
}
/*if (Input.GetKeyDown(KeyCode.E))
{
RaycastHit hit;
if (Physics.Raycast(FPCamera.transform.position, FPCamera.transform.forward, out hit, range))
{
Sleep sleepTarget = hit.transform.GetComponent<Sleep>();
if (sleepTarget == null) { return; }
sleepTarget.Fade();
print("does it work?");
}
else { return; }
}*/
}
}
WildPlants
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WildPlants : MonoBehaviour, Interactables
{
[SerializeField] float growthRate = 5f;
[SerializeField] GameObject grownPlant;
[SerializeField] GameObject pickedPlant;
[SerializeField] Collider emptyCollider;
[SerializeField] bool isPicked = false;
Renderer cubeRenderer;
void Start()
{
emptyCollider = GetComponent<Collider>();
cubeRenderer = grownPlant.GetComponent<Renderer>();
isPicked = false;
cubeRenderer.material.SetColor("_Color", Color.green);
}
void Update()
{
}
public void DoSomething()
{
Debug.Log("WildPlants");
}
public void ProcessPlantPicking()
{
if (isPicked == false)
{
StartCoroutine(PlantIsPicked());
print("Plant put in inventory");
}
else if (isPicked == true)
{
return;
}
}
IEnumerator PlantIsPicked()
{
//cubeRenderer.material.SetColor("_Color", Color.red);
grownPlant.SetActive(false);
emptyCollider.enabled = false;
pickedPlant.SetActive(true);
yield return new WaitForSecondsRealtime(growthRate);
isPicked = false;
emptyCollider.enabled = true;
grownPlant.SetActive(true);
pickedPlant.SetActive(false);
}
}
You put interface declaration inside your Player class:
public class Player : $$anonymous$$onoBehaviour
// your code
public interface Interactables
{
void DoSomething();
}
// your code
}
If you do this that way you should implement interface as follows:
public class WildPlants : $$anonymous$$onoBehaviour, Player.Interactables
But if you look at my example once again, you will see that I put the declaration outside the Player class. Of course that depends on you where you want to place the code. If it's inside the class it must be Player.Interactables. If outside, then Interactables is enough.
Oooh I didn't realize it was actually after the very last bracket, whoops! $$anonymous$$y bad
Your answer