How to send commands from non - player objects UNET - c#

I am unable to send commands from my gun prefabs in my game. How can I send commands from non-player game objects? Every time I try to send a command, I get an error. I do not want to move my shooting scripts from my guns to my player, as this method will require me to change my game drastically.
My Shooting Script:
[Command]
void CmdOpenFire(){
if (Physics.Raycast(camTransform.TransformPoint(startPosition), camTransform.forward, out hit, range)){
gunsMasterScript.CallEventShotDefault(hit.point, hit.transform);
if (hit.transform.parent != null) {
if (hit.transform.parent.CompareTag ("Enemy")) {
gunsMasterScript.CallEventShotEnemy (hit.point, hit.transform.parent);
}
}
if (hit.transform.tag == "Player") {
gunsMasterScript.CallEventShotEnemy (hit.point, hit.transform);
}
}
}
My shooting Detection script:
public PlayerManager_AmmoBox localPlayerTester;
public GunsManager_GunsAmmo gunsAmmo;
public GunsManager_GunsMaster gunMaster;
float nextAttack;
public float attackRate = 0.5f;
Transform myTransform;
public bool isAutomatic = true;
public bool hasBurstFire;
bool isBurstFireActive;
public string attackButtonName;
public string reloadButtonName;
public string burstFireButtonName;
NetworkIdentity netID;
// Use this for initialization
void OnEnable () {
if (transform.root != null) {
if (transform.root.GetComponent<PlayerManager_AmmoBox> () != null) {
localPlayerTester = transform.root.GetComponent<PlayerManager_AmmoBox> ();
}
}
SetInitialReferences ();
}
// Update is called once per frame
void Update () {
if (localPlayerTester != null) {
CheckIfWeaponShouldAttack ();
CheckForReloadRequest ();
CheckForBurstFireToggle ();
}
}
void SetInitialReferences(){
netID = GetComponent<NetworkIdentity> ();
myTransform = transform;
gunMaster = transform.GetComponent<GunsManager_GunsMaster> ();
gunsAmmo = transform.GetComponent<GunsManager_GunsAmmo> ();
gunMaster.isGunLoaded = true;
}
void CheckIfWeaponShouldAttack(){
if (gunMaster != null) {
if (Time.time > nextAttack && Time.timeScale > 0 && myTransform.root.CompareTag ("Player") && gunMaster.isGunLoaded) {
if (isAutomatic && !isBurstFireActive) {
if (Input.GetButton (attackButtonName)) {
AttemptAttack ();
}
} else if (isAutomatic && isBurstFireActive) {
if (Input.GetButtonDown (attackButtonName)) {
StartCoroutine (RunBurstFire ());
}
} else if (!isAutomatic) {
if (Input.GetButton (attackButtonName)) {
AttemptAttack ();
}
}
}
}
}
void AttemptAttack(){
nextAttack = Time.time + attackRate;
if (gunMaster.isGunLoaded) {
gunMaster.CallEventPlayerInput ();
} else {
gunMaster.CallEventGunNotUsable ();
}
}

Commands have to be called on the gameobject (which has a NetworkBehavior) that is associated with a certain player. Thus, I do not believe they can be called from non-player objects.
However, you could make the gun script communicate to the script that is on the player (the one which is a NetworkBehavior that is associated with a player's connection). This would cause the error to stop. (This communication could be done through SendMessage or just regular function calls to the player object.)
In conclusion, currently, UNet does not allow Commands from non-player objects, so some change will need to be done to the layout of your player/gun scripts in order to use commands. However, this could be done with only a few lines of code by just making the gun script "talk to" the player script -- the script inheriting from NetworkBehavior that is associated with a player's connection.
More information on commands can be seen here:
https://docs.unity3d.com/ScriptReference/Networking.CommandAttribute.html

Related

Everything breaks after using PlayOneShot

In a game I am working on, the player uses a cube to open doors. When the player interacts with the cube while near the door, the door slides open. I am now trying to make it so that while it slides open, it makes a sound as doors should but I kinda ran into some issues. When I tried to add in the sound for the door, it ignored the part where the door should open altogether.
Here is what I did:
I added an AudioSource to the Cube's child object, CORE because the Cube object already contains an AudioSource which will play at the same time as this sound and assigned it in my Cube's script...
public AudioSource Woosh;
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
Here's the interactive part of the script, it runs a check but for some reason, it is pretending CanBeKey is false...
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
Now here is the IEnumerator part of the script, PlayDaWoosh()
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
I am aware that the last code snippet is a bit messy but it was the best thing I can think of.
Here is the full script in case you are that curious.....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CUBE_CORE : MonoBehaviour
{
public AudioSource Hm; // HM!!!
private bool HasPlayed = false;
public float PlayTimeOut = 0.0f;
public static bool CanBeKey = false;
public Animator anim;
public static bool HasPlayedFirstTime = false;
public static AudioSource Door;
public AudioSource Woosh;
// Start is called before the first frame update
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(HasPlayed == true)
{
if (PlayTimeOut < 0.0f)
{
HasPlayed = false;
}
PlayTimeOut -= Time.smoothDeltaTime;
}
}
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "SecondDoor")
{
anim = GameObject.Find("DoorSecond").GetComponent<Animator>();
}
if(other.gameObject.name == "ThirdDoor")
{
anim = GameObject.Find("TheThirdDoor").GetComponent<Animator>();
}
if(other.gameObject.name == "FourthDoor")
{
anim = GameObject.Find("TheFourthDoor").GetComponent<Animator>();
}
}
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
}
Expected result: Door opening and sound playing
Actual result: Neither happening unless I remove anything that has to do with the Woosh
I apologize ahead of time if I wasn't specific enough or if the question was answered somewhere else. I am relatively new here.

Stop coroutine from other script

I am making a game with a maze with audio hints on what way to go. I have the sounds playing on a coroutine and it work in terms of only starting once. However, what I need to do is be able to stop it from another script with a trigger on so that the audio does not keep playing when the player passes a certain point. This is my code so far.
public AudioSource direction;
private bool running = false;
IEnumerator AudioPlay()
{
while (true)
{
direction.Play();
yield return new WaitForSeconds(2);
}
}
void OnTriggerEnter(Collider col)
{
if (col.gameObject.CompareTag("Player"))
{
if (running == false)
{
StartCoroutine(AudioPlay());
Debug.Log("Started");
running = true;
}
else if (running == true)
{
Debug.Log("Void");
}
}
}
Use StopCoroutine(previouslyRunCoroutine)
If you use the IEnumerator form (StartCoroutine(AudioPlay());) to start the coroutine, the Unity documentation recommends that you save a reference to the IEnumerator and use that in future calls to StopCoroutine:
public AudioSource direction;
private bool running = false;
public IEnumerator audioPlayCoroutine;
IEnumerator AudioPlay()
{
while (true)
{
direction.Play();
yield return new WaitForSeconds(2);
}
}
void OnTriggerEnter(Collider col)
{
if (col.gameObject.CompareTag("Player"))
{
if (running == false)
{
audioPlayCoroutine = AudioPlay();
StartCoroutine(audioPlayCoroutine);
Debug.Log("Started");
running = true;
}
else if (running == true)
{
Debug.Log("Void");
}
}
}
Then, in your other script you can use StopCoroutine:
OtherScript.StopCoroutine(OtherScript.audioPlayCoroutine);
If you were to use the method name form such as StartCoroutine("AudioPlay");, the documentation would recommend using the method name form to stop it:
OtherScript.StopCoroutine("AudioPlay");

Bug with my object un-snapping objects in Unity

I have basic snapping of objects in place in my test scene, but when I go to unsnap the objects the snapped object will resnap unless I move it away very quickly.
For context of the scene setup, I want to implement snapping objects in VR so I have a smaller parent object inside the object I'm moving to represent the hand the object would be parented to in VR. Also I have the snapping script (first one below) attached to the 'hand' object, and am using trigger colliders on the hand & the square obj it attaches to.
Any clue how I can fix this so I don't need to pull quickly to decouple the object?
Video of the bug happening
Below are the scripts I'm using to implement this
public class snap : MonoBehaviour, collider_helper.collider_help_reciever
{
public Transform snapObj;
public Transform SnapTarget;
bool snapped = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void OnTriggerEnter (Collider other)
{
Debug.Log("enter");
if (snapped == false && other.transform.GetInstanceID() == SnapTarget.GetInstanceID())
{
snapObj.position = SnapTarget.position;
snapObj.rotation = SnapTarget.rotation;
snapObj.parent = SnapTarget;
snapped = true;
}
}
public void OnTriggerExit(Collider other)
{
Debug.Log("exit");
if (snapped == true && other.transform.GetInstanceID() == SnapTarget.GetInstanceID())
{
snapObj.position = transform.position;
snapObj.rotation = transform.rotation;
snapObj.parent = transform;
snapped = false;
StartCoroutine(noSnap);
}
}
}
public class collider_helper : MonoBehaviour {
public GameObject recieving;
collider_help_reciever reciever;
// Use this for initialization
void Start () {
reciever = recieving.GetComponent<collider_help_reciever>();
}
// Update is called once per frame
void Update () {
}
void OnTriggerStay (Collider other)
{
reciever.OnTriggerEnter(other);
}
public interface collider_help_reciever
{
void OnTriggerEnter(Collider other);
}
}
Well the answer turned out to be adding a float (lastSnapTime) to store the last time the objects decoupled, and only allowing snapping to happen if some time had passed since they last decoupled:
public void OnTriggerEnter(Collider other)
{
Debug.Log("enter");
if (other.transform.GetInstanceID() == SnapTarget.GetInstanceID())
{
if (lastSnapTime + 0.1f < Time.time)
{
snapObj.position = SnapTarget.position;
snapObj.rotation = SnapTarget.rotation;
snapObj.parent = SnapTarget;
}
}
}
public void OnTriggerExit(Collider other)
{
Debug.Log("exit");
if (other.transform.GetInstanceID() == SnapTarget.GetInstanceID())
{
lastSnapTime = Time.time;
snapObj.position = transform.position;
snapObj.rotation = transform.rotation;
snapObj.parent = transform;
//StartCoroutine(noSnap);
}
}

Unity3D using OnTriggerStay

I'm using the event OnTriggerStay2D to destroy an object, the reason i'm using this instead of OnTriggerEnter2D is because i'm dragging the object using the touchscreen, and i want to destroy it after it is released, the problem i have is that OntriggerStay2D is not always called, so sometimes the object is not destroyed after it is released and it has to be moved again to work, i've read the docummentation from Unity
OnTriggerStay is called almost all the frames for every Collider other that is touching the trigger.
public void OnTriggerStay2D(Collider2D other)
{
if (gameObject.tag == other.tag) {
Destroy (other.gameObject);
}
}
I would like to know if there's any way to call OntriggerStay2D everytime i release the object.
Thanks.
Edit
Dragging code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Drag : MonoBehaviour {
private bool draggingItem = false;
private GameObject draggedObject;
private Vector2 touchOffset;
void Update ()
{
if (HasInput)
{
DragOrPickUp();
}
else
{
if (draggingItem)
DropItem();
}
}
Vector2 CurrentTouchPosition
{
get
{
Vector2 inputPos;
inputPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
return inputPos;
}
}
private void DragOrPickUp()
{
var inputPosition = CurrentTouchPosition;
if (draggingItem)
{
draggedObject.transform.position = inputPosition + touchOffset;
}
else
{
RaycastHit2D[] touches = Physics2D.RaycastAll(inputPosition, inputPosition, 0.5f);
if (touches.Length > 0)
{
var hit = touches[0];
if (hit.transform != null && hit.rigidbody != null)
{
draggingItem = true;
draggedObject = hit.transform.gameObject;
touchOffset = (Vector2)hit.transform.position - inputPosition;
}
}
}
}
private bool HasInput
{
get
{
return Input.GetMouseButton(0);
}
}
public void DropItem()
{
draggingItem = false;
}
}
Avoid using OnTriggerStay2D for this. You can use a boolean variable that you set to true and false in the OnTriggerEnter2D and OnTriggerExit2D function.
bool isTouching = false;
void OnTriggerEnter2D(Collider2D collision)
{
Debug.Log("Entered");
if (collision.gameObject.CompareTag("YourOtherObject"))
{
isTouching = true;
}
}
void OnTriggerExit2D(Collider2D collision)
{
Debug.Log("Exited");
if (collision.gameObject.CompareTag("YourOtherObject"))
{
isTouching = false;
}
}
You can now check the isTouching variable when the object is released.
if(isTouching){
....
}
Note that I suggest you abandon your current code that uses Raycast and Input.GetMouseButton(0); since you are using this on mobile devices too. You should be using Unity's new EventSystem for this since it is made to be mobile friendly too.
Since you are using 2D collider, see #7 from this answer.
Here is a complete example of how to drag a Sprite with the new EventSystem. Combine that with the answer above and you get a much more better solution.

Trouble reveling objects using layer based colliderspheres C# Unity

I'm having some trouble getting a mechanic to function correctly in my game. Basically I have two zones: Collidersight and Colliderfatal, defined by collider spheres that are attached to a artillery shell that is fired by a cannon. Essentially, I want any objects that are within the Collidersight zone to be revealed as long as they are within that particular zone, if they are outside that zone they should return to being invisible. If an object collides with Colliderfatal, then that object should be revealed perminantly, even after the shell has been destroyed. I have tried to initialize the gameObjects individually but this appears to be extremely confusing and not applicable to the situation. The gameobjects already exist within the scene and I am not instantiating them in. I have been told, that I may need to create a list of the gameobjects to loop inside the collidersphere as a potentional solution for this problem but I am totally lost on how this might be achieved.
here is the code.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class zone : MonoBehaviour {
public LayerMask m_SightLayers;
public LayerMask s_FatalityLayers;
public Vector3 m_Position;
public float m_Radius;
public float m_Force;
public float s_Radius;
public Vector3 s_Position;
public Invisible1 revealenemy1;
public Invisible1 enemykill1;
public float s_Force;
public InvisibleKill makeDead;
public GameObject EnemyCube1;
//public GameObject enemydead;
bool hasExploded;
public bool inZone;
public bool inKillzone;
//public float removeTime = 5.0f;
bool hasBeenhit;
void Awake(){
GameObject[] objects = GameObject.FindGameObjectsWithTag("Enemy");
}
void start (){
makeDead = (InvisibleKill) EnemyCube1.GetComponent (typeof(InvisibleKill));
revealenemy1 = (Invisible1) EnemyCube1.GetComponent(typeof(Invisible1));
}
void OnCollisionEnter(Collision explode){
if(explode.gameObject.name == "Terrain" || explode.gameObject.name == "EnemyCube" || explode.gameObject.name == "EnemyCube1" ){
hasExploded = true;
}
}
void FixedUpdate ()
{
if (hasExploded == true){
Collider[] collidersight;
Collider[] colliderfatal;
Rigidbody rigidbody;
collidersight = Physics.OverlapSphere (transform.position + m_Position, m_Radius, m_SightLayers);
foreach (Collider collider in collidersight)
{
if(collider.tag == "Enemy"){
Debug.Log("stuff");
}
rigidbody = (Rigidbody) collider.gameObject.GetComponent (typeof (Rigidbody));
if (rigidbody == null)
{
continue;
}
if(rigidbody.gameObject.tag == "Enemy"){
inZone = true;
if(inZone == true){
revealenemy1.Reveal1();
// revealenemy2.Reveal2();
//revealenemy3.Reveal3();
Debug.Log ("hit");
hasBeenhit = true;
if(hasBeenhit == false){
hasBeenhit = false;
// revealenemy1.Hidden();
//// revealenemy2.Hidden2();
// revealenemy3.Hidden3();
}
}else{
}
}
}
//Debug.Log (hasExploded);
colliderfatal = Physics.OverlapSphere (transform.position + s_Position,s_Radius,s_FatalityLayers);
foreach (Collider collider in colliderfatal)
{
inKillzone = true;
rigidbody = (Rigidbody) collider.gameObject.GetComponent (typeof (Rigidbody));
if (rigidbody ==null)
{
continue;
}
rigidbody.AddExplosionForce (s_Force * -1, transform.position + s_Position,s_Radius);
Debug.Log("hasbeenkilled");
}
}
if(hasBeenhit == false){
revealenemy1.Hidden();
//revealenemy2.Hidden2();
//revealenemy3.Hidden3 ();
hasBeenhit = false;
}
if(inKillzone == true){
//EnemyCube1.GetComponentInChildren (typeof(Invisible1));
hasBeenhit = true;
//revealenemy.renderer.enabled = true;
// makeDead.isdead = true;
//(typeof(Invisible1));
}
if(makeDead.isdead == true){
revealenemy1.Reveal1();
}
}
void OnDrawGizmosSelected () {
Gizmos.color = Color.red;
Gizmos.DrawWireSphere (transform.position + m_Position, m_Radius);
//Gizmos.DrawWireSphere (transform.position + s_Position, s_Radius);
}
void OnDrawGizmos()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere (transform.position + s_Position, s_Radius);
}
}
Why not simply activate/deactivate the object renderers when they enter the zones? You stipulate that objects that have entered the 'Fatal' zone are permanently visible, I took this to mean they are visible even if they leave that zone.
Visibility class to be placed on any object whose visibility you want to change:
public class Visibility : MonoBehaviour {
public bool LockVisibility = false;
public void SetVisible(bool state){
if(LockVisibility)return;
renderer.enabled = state;
}
}
And the collision detection class.
public class CollisionRender : MonoBehaviour {
//example collision types
public enum CollisionTypes { Fatal,InSight };
public CollisionTypes CollisionType = CollisionTypes.InSight;
//turn renderer on.
void OnTriggerEnter(Collider other) {
Visibility vis = other.gameObject.GetComponent<Visibility>();
if(CollisionType == CollisionTypes.InSight){
vis.SetVisible(true);
}else if(CollisionType == CollisionTypes.Fatal){
//if in 'fatal' zone, make visible and lock visibility.
vis.SetVisible(true);
vis.LockVisibility = true;
}
}
void OnTriggerExit(Collider other) {
Visibility vis = other.gameObject.GetComponent<Visibility>();
vis.SetVisible(false);
}
}
I'm using triggers so your collider will need to have 'IsTrigger' checked to work.

Categories