Getting Local Player Authority on Player Child - unity3d

I have got my Hand Prefab attached to Player Prefab as a child.
I want to spawn Gun Prefab in my Hand Prefab(Players child).
Is this way i can do this from Hand level?
At the moment I am spawning Gun from Hand, but it works only on single Player. While i play with 2 players, in the moment of spawning a Gun. Game crashes for client. This is the Error im getting:
NullReferenceException: Object reference not set to an instance of an object
UnityEngine.Networking.NetworkServer.SpawnWithClientAuthority (UnityEngine.GameObject obj, UnityEngine.Networking.NetworkConnection conn) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkServer.cs:1565)
HandHolder.CmdGetGun () (at Assets/Scripts/HandHolder.cs:27)
HandHolder.Update () (at Assets/Scripts/HandHolder.cs:19)
Thats my Code for Hand:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class HandHolder : NetworkBehaviour {
[SerializeField] GameObject gun;
private GameObject playerGun;
// Update is called once per frame
void Update () {
if(GetComponentInParent<NetworkIdentity>().isLocalPlayer)
{
if (playerGun)
{
playerGun.transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z - 1);
playerGun.transform.rotation = transform.rotation;
}
else if (Input.GetKeyDown(KeyCode.I))
{enter code here
CmdGetGun();
}
}
}
// [Command]
public void CmdGetGun()
{
playerGun = Instantiate(gun, transform.position, transform.rotation) as GameObject;
NetworkServer.SpawnWithClientAuthority(playerGun, GetComponentInParent<NetworkIdentity>().connectionToClient);
}
}
When im adding [Command] before CmdGetGun() method, in the moment of spawning a gun i got this error:
There is no NetworkIdentity on this object. Please add one.
UnityEngine.Networking.NetworkBehaviour:get_isServer()
HandHolder:CallCmdGetGun()
HandHolder:Update() (at Assets/Scripts/HandHolder.cs:19)
But when i add NetworkIdentity to my child Hand Prefab it shows me that hasAuthority, isLocalPlayer is False for both Hand and Player.
I have no idea how can i spawn Gun in Hand Prefab.
Here are Components and tree:

Your network object is your Player, so you need to route your network functions through your player.
Call CmdGetGun()* in your player class and call it from there.
*CmdGetGun() should not be a [Command]
Optionally, you can call CmdGetGun as a Command, but then you don't want networked instantiation, just local (the command will be called all clients, so all clients will locally create the gun).
EDIT
CmdGetGun will create the object on all clients, but it won't parent it like you are expecting. You should probably try the second approach I mentioned (call it as a command, but instantiate locally)

Related

how can i fix "GameObject has been destroyed but you are still trying to access it" - unity

I am in process to make a 3D fps game with unity. But when i wrote the code to destroy the bullet after a particular time say - 5 seconds, till 5 seconds it spawnes the bullet and after 5 seconds the bullets spawned bullets get destroyed. But after that when i try to again spawn bullets, they dont get spawned and it shows the error GameObject has been destroyed but you are still trying to access it.
here is the destroy bullets code
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float speed = 8f;
public Camera playerCamera;
public float lifeDuration = 2f;
private float lifeTimer;
void Start()
{
lifeTimer = lifeDuration;
}
// Update is called once per frame
void Update()
{
transform.position += playerCamera.transform.forward * speed * Time.deltaTime;
lifeTimer -= Time.deltaTime;
if (lifeTimer <= 0f)
{
Destroy (gameObject);
}
}
}
I believe that when you destroy the GO, the reference to that object may be lost too. In your case, in which you're using bullets, I recommend you to instead of using Destroy(gameObject), use gameObject.SetActive(false) and recycle the bullets with a pool of bullets that you instantiate at the start of the game. That way is easier to have a reference to the GO and optimize the cost of instantiating x bullets.
This error occurs when you try to access a gameobject which has been destroyed.
how do you spawn the bullet?
if it's a prefab,i suggest you do like this,and DO NOT call this gameobject in any other script after its "lifeTime":
var go = GameObject.Instantiate( Resources.Load( "bullet prefab path" ) ) as GameObject;
go.AddComponent<Bullet>();
if not, i think you're Instantiate a gameobject already exist in Hierarchy.
//if the "prefab" is already exist in Hierarchy with script "Bullet" and is active
//this "prefab" will be destroyed by your bullet script after "lifeTime"
//when the next time you Instantiate() with this "prefab"
//you'll get the error:"GameObject has been destroyed but you are still trying to access it"
GameObject go = GameObject.Instantiate(prefab) as GameObject;

Instantiate doesn't work in unity with navmesh

I'm having some problems on instantiating an enemy in unity, it gives me always this error setDestination" can only be called on an active agent that has been placed on a NavMesh., and then it places a 2d model somewhere in my map
public GameObject enemy;
public Transform spawn;
// Start is called before the first frame update
void Start()
{
spawnNew();
}
void spawnNew() {
Instantiate(enemy, spawn.transform.position, Quaternion.identity);
}
After you have spawned an enemy, call the NavMeshAgent.Warp() function to position the agent on the desired position, which of course must be inside the navmesh.

Pooled GameObject in Unity destroys itself after SetActive if Force is applied

When I retrieve an object from a list of created objects and reactivate it, it destroys itself, but only if I have force applied to start it moving. If I never apply force, everything works as intended and I can keep activating the object over and over.
I've tried putting a debug.log in the OnCollision and it isn't colliding with anything.
As said above, if I never initiate speed, the rest works fine.
When speed is applied, the first projectile works, the second destroys itself.
I've tried manually activating them instead of by code. Same result.
Exception: If I manually activate/deactivate objects myself through the inspector, they work if I only have 1 shot in the pool.
The one shot rule doesn't work if I do it through code. It breaks even if I only have one shot.
All the code worked when I wasn't using pooling.
Spawn code:
void CreateBullet(int GunID)
{
GameObject ShotFired = Guns[GunID].GetComponent<Weapon>().FireWeapon();
if (ShotFired == null)
{
ShotFired = Instantiate(Guns[GunID].GetComponent<Weapon>().Projectile,Guns[GunID].gameObject.transform);
Physics.IgnoreCollision(ShotFired.GetComponent<SphereCollider>(), Guns[GunID].GetComponentInParent<CapsuleCollider>());
Guns[GunID].GetComponent<Weapon>().AmmoPool.Add(ShotFired);
}
ShotFired.transform.position = Guns[GunID].transform.position;
ShotFired.transform.rotation = Guns[GunID].transform.rotation;
ShotFired.SetActive(true);
}
Projectile Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
public class Projectile : NetworkBehaviour
{
public float Speed;
public float LifeTime;
public float DamagePower;
public GameObject ExplosionFX;
// Start is called before the first frame update
void Start()
{
GetComponent<Rigidbody>().AddRelativeForce(Vector3.up * Speed, ForceMode.VelocityChange);
StartCoroutine(DeactivateSelf(5.0f));
}
private void OnDestroy()
{
Debug.Log("WHY!");
}
IEnumerator DeactivateSelf(float Sec)
{
yield return new WaitForSeconds(Sec);
Explode();
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.GetComponentInChildren<Vehicle>())
{
collision.gameObject.GetComponentInChildren<Vehicle>().CmdTakeDamage(DamagePower);
}
Explode();
}
void Explode()
{
GameObject ExplosionEvent = Instantiate(ExplosionFX, this.transform.position, Quaternion.identity);
NetworkServer.Spawn(ExplosionEvent);
GetComponent<Rigidbody>().AddRelativeForce(Vector3.zero);
gameObject.SetActive(false);
}
}
Any thoughts welcome. Thanks in advance!
In case anyone else stumbles across this, here's what happened and why assigning a movement speed to the object was what caused the issue.
My projectiles had a 'trailrenderer' on them.
By default 'Autodestruct' is true.
Autodestruct does not destruct the trail, but the game object when the trail disappears. So if you disable anything that had a trail, and activated it through movement, when the object stops moving (say to reposition in an object pool) it will destroy the parent object.
If the object never moves, it never generates a trail, and never needs to be destroyed.
To fix, just uncheck autodestruct.
https://docs.unity3d.com/ScriptReference/TrailRenderer-autodestruct.html

Children collider not getting called

Context
I'm working in a pickup system in my game. I've a component called AbstractSightCollider that has a sphere collider and some AbstractPickupableObject that are the objects meant to be picked up.
AbstractSightCollider is attached to the main character, but could be attached to any alive entity or anything that is able to contain inventory objects.
The way i designed it, it's that when AbstractSightCollider detects an object, it fires an UnityEvent called PickupDetected and when the player leaves the range of pickup, it call an UnityEvent called PickupLeave
The problem
I can't make OnCollisionEnter and OnCollisionExit trigger.
Some code
This is attached to AbstractSightCollider
public class AbstractObjectSight : MonoBehaviour
{
public OnPickupableDetected pickupDetected;
public OnPickupableLeave pickupLeave;
private void OnCollisionEnter(Collision col) {
GameObject gameObject = col.gameObject;
AbstractPickupableObject abstractPickupableObject =
gameObject.transform.GetComponent<AbstractPickupableObject>();
if (abstractPickupableObject != null) {
pickupDetected.Invoke(abstractPickupableObject);
}
}
private void OnCollisionExit(Collision col) {
GameObject gameObject = col.gameObject;
AbstractPickupableObject abstractInventoryObject =
gameObject.transform.GetComponent<AbstractPickupableObject>();
if (abstractInventoryObject != null) {
pickupLeave.Invoke(abstractInventoryObject);
}
}
[System.Serializable]
public class OnPickupableDetected : UnityEvent<AbstractPickupableObject> { }
[System.Serializable]
public class OnPickupableLeave : UnityEvent<AbstractPickupableObject> { }
}
And here are the properties:
Thanks for your time
Make sure both objects (the one with the script and the one that will cause the trigger) have colliders and rigidbodys, I find if one doesn't have those the triggers and collisions will not work.
I just found out the problem.
OnCollisionEnter and OnCollisionExit aren't the events that i needed to listen, because they work with rigidbody. My AbstractSight is that, a non body abstract sphere where the entities are allowed to grab items.
Instead, i used OnTriggerEnter, OnTriggerExit and now it works like a charm.
Rigid body on the parent object means the collider will work on ANY child component but the rigid body MUST be on the parent object.
Example:
MyProjectile has a rigid body and a collider - set isTrigger = true
MyEnemy parent object just holds scripts
MyEnemy childObject holds the enemy prefab including its collider,
Everything is set to isKinematic = true, with no gravity.
Everything works fine.

Unity: NullReferenceException: Object reference not set to an instance of an object

I am following an Unity tutorial. I face a problem when trying to detect collision in the game. This is the error:
NullReferenceException: Object reference not set to an instance of an object
This is the script:
using UnityEngine;
public class Collide : MonoBehaviour
{
public Movement movement; // A reference to our PlayerMovement script
// This function runs when we hit another object.
// We get information about the collision and call it "collisionInfo".
void OnCollisionEnter(Collision collisionInfo)
{
// We check if the object we collided with has a tag called "Obstacle".
if (collisionInfo.collider.tag == "Obstacle")
{
movement.enabled = false; // Disable the players movement.
Debug.Log("Coollision occured");
}
}
}
As i saw in the second image you have not added the movement reference to the movement field. At the same in the script also you are not assigning the reference. Try to assign at editor or you can create object.
The reason is you have not set the movement field in your Collide component.
You can add it from the Unity Editor or add the following line in your Start function of Collide :
void Start()
{
movement = GetComponent<Movement>();
}