Basically I have a game where the player jumps from a truck to other trucks, and I want to reset my trucks position when the player dies so he can restart the level. Some of the irrelevant code such as the player movement I removed as it is not necessary for me to include. The code is like this so far:
using System;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Vector3 truckStart;
Vector3 startPoint;
//Assingables
public GameObject Truck;
public GameObject Player;
public GameObject Spawn;
public GameObject lava;
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
startPoint = Spawn.transform.position;
truckStart = Truck.transform.position;
}
private void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.gameObject == lava)
{
Player.transform.position = Spawn.transform.position;
Truck.transform.position = truckStart;
}
}
How do I make my code so that instead of there being just 1 gameobject for my truck there is multiple? I've tried using tags to find gameobjects but when I do that it won't let me reset its positions.
Not sure what you want, but It seems it's not that difficult. As you already know how to assign serializable values in Unity via inspector, just declare an array of Truck, like:
private Vector3[] truckStartPoints;
public GameObject[] Trucks;
And, as a general Unity coding tip, the following is better (unless you have to access it from other class):
[SerializeField]
private GameObject[] trucks;
This makes trucks not accesible from other code, but still can be serialized via inspector.
And initialize like this:
private void Start()
{
truckStartPoints = new Vector3[trucks.Length];
for (int i = 0; i < trucks.Length; i++)
truckStartPoints[i] = trucks[i].transform.position;
}
And reset like this:
private void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.gameObject == lava)
{
Player.transform.position = Spawn.transform.position;
for (int i = 0; i < trucks.Length; i++)
trucks[i].transform.position = truckStartPoints[i];
}
}
Related
I've tried 2/3 different ways of removing my gameObject from my List but none are working. When I debug the method the debug log is showing up as it should yet the gameobject still isn't removed from the list.
When my teammates kill an enemy I want the enemy to be removed from the list and then destroyed so I can continue to iterate through the List to find the closest enemy to begin attacking. Because the gameObject's are not being removed I get a null reference and i can loop through my for loop to check.
1st Script: List is created and used in a for loop, removing and destroying the enemy also occurs in here.
public class FriendlyManager : MonoBehaviour
{
public NavMeshAgent navMeshAgent;
public Transform player;
public static FriendlyManager singleton;
public float health;
public float minimumDistance;
public int damage;
public List<GameObject> enemies;
private GameObject enemy;
private GameObject enemyObj;
// animations
[SerializeField] Animator animator;
bool isAttacking;
bool isPatroling;
// attacking
[SerializeField] Transform attackPoint;
[SerializeField] public GameObject projectile;
public float timeBetweenAttacks;
bool alreadyAttacked;
private void Awake()
{
navMeshAgent = GetComponent<NavMeshAgent>();
animator = GetComponent<Animator>();
enemyObj = new GameObject();
}
private void Start()
{
singleton = this;
isAttacking = false;
isPatroling = true;
animator.SetBool("isPatroling", true);
}
private void Update()
{
for(int i = 0; i < enemies.Count; i++)
{
if(Vector3.Distance(player.transform.position, enemies[i].transform.position) <= minimumDistance)
{
enemy = enemies[i];
Attacking(enemy);
}
}
}
private void Attacking(GameObject enemy)
{
// stop enemy movement.
navMeshAgent.SetDestination(transform.position);
enemyObj.transform.position = enemy.transform.position;
transform.LookAt(enemyObj.transform);
if (!alreadyAttacked)
{
isAttacking = true;
animator.SetBool("isAttacking", true);
animator.SetBool("isPatroling", false);
Rigidbody rb = Instantiate(projectile, attackPoint.position, Quaternion.identity).GetComponent<Rigidbody>();
rb.AddForce(transform.forward * 32f, ForceMode.Impulse);
alreadyAttacked = true;
Invoke(nameof(ResetAttack), timeBetweenAttacks);
}
}
private void ResetAttack()
{
alreadyAttacked = false;
animator.SetBool("isAttacking", false);
}
public void DestroyEnemy(GameObject enemy)
{
enemies.Remove(enemy);
Debug.Log("AHHHHHHH M GOING CRAZY");
Destroy(gameObject);
}
}
}
2nd Script: Deals with the damage and checks enemy's currentHealth. (I have to post it as an image because for Stack Overflow is being annoying.) ._.
Shouldn't it just be
Destroy(enemy);
and not
Destroy(gameObject);
The idea is to add a Rigidbody to some doors and use Is Kinematic so when a npc/enemy will walk through the doors they will open/close.
But when I'm running the game the doors open automatic without anything triggering it.
Maybe I didn't add the Rigidbody to the right place in the door the right object/child ?
A screenshot of a door one of few I have:
Each Collision (1) to Collision(11) have attached a Box Collider. On each one the Is Trigger is unchecked !
The Horizontal_Doors_Kit have attached a Box Collider and the Is Trigger is check enabled ! And also attached to it a script Hori Door Manager:
using UnityEngine;
using System.Collections;
public class HoriDoorManager : MonoBehaviour
{
public DoorHori door1;
public DoorHori door2;
public static bool doorLockState;
void OnTriggerEnter()
{
if (doorLockState == false)
{
if (door1 != null)
{
door1.OpenDoor();
}
if (door2 != null)
{
door2.OpenDoor();
}
}
}
}
Wall_Door_Long_01 have attached a Mesh Renderer.
Door_Left and Door_Right have a Mesh Renderer , Audio Source , a script and a Box Collider where Is Trigger unchecked !
The script is attached to door left and to door right:
using UnityEngine;
using System.Collections;
public class DoorHori : MonoBehaviour {
public float translateValue;
public float easeTime;
public OTween.EaseType ease;
public float waitTime;
private Vector3 StartlocalPos;
private Vector3 endlocalPos;
private void Start(){
StartlocalPos = transform.localPosition;
gameObject.isStatic = false;
}
public void OpenDoor(){
OTween.ValueTo( gameObject,ease,0.0f,-translateValue,easeTime,0.0f,"StartOpen","UpdateOpenDoor","EndOpen");
GetComponent<AudioSource>().Play();
}
private void UpdateOpenDoor(float f){
Vector3 pos = transform.TransformDirection( new Vector3( 1,0,0));
transform.localPosition = StartlocalPos + pos*f;
}
private void UpdateCloseDoor(float f){
Vector3 pos = transform.TransformDirection( new Vector3( -f,0,0)) ;
transform.localPosition = endlocalPos-pos;
}
private void EndOpen(){
endlocalPos = transform.localPosition ;
StartCoroutine( WaitToClose());
}
private IEnumerator WaitToClose(){
yield return new WaitForSeconds(waitTime);
OTween.ValueTo( gameObject,ease,0.0f,translateValue,easeTime,0.0f,"StartClose","UpdateCloseDoor","EndClose");
GetComponent<AudioSource>().Play();
}
}
When I added the Rigidbody it was to the parent Wall_Door_Long_01 also added to it a Box Collider.
Then when running the game the door/s opened automatic without anything to trigger it like a npc enemy character.
The doors are working fine and when I'm moving my FPSController through the doors they open and close fine. My problem is when a npc/character that move automatic when the game start through the doors I could not find to make the character walking fine with a Rigidbody so I decided to add a Rigidbody to the doors. But now the doors open automatic when running the game.
This script I'm using to check if the doors are locked or unlocked using the public static doorLockState variable:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DoorsLockManager : MonoBehaviour
{
public bool locked;
public bool lockStateRealTime = false;
public Renderer rend;
private Shader unlitcolor;
private GameObject[] doorPlanes;
private void Start()
{
doorPlanes = GameObject.FindGameObjectsWithTag("DoorPlane");
ChangeColors(new Color32(255, 0, 0, 255), new Color32(0, 255, 0, 255));
}
private void ChangeMaterialSettings()
{
unlitcolor = Shader.Find("Unlit/Color");
rend.material.shader = unlitcolor;
rend.material.SetFloat("_Metallic", 1);
}
private void ChangeColors(Color32 lockedColor, Color32 unlockedColor)
{
for (int i = 0; i < doorPlanes.Length; i++)
{
rend = doorPlanes[i].GetComponent<Renderer>();
ChangeMaterialSettings();
if (locked)
{
HoriDoorManager.doorLockState = true;
rend.material.color = lockedColor;
}
else
{
HoriDoorManager.doorLockState = false;
rend.material.color = unlockedColor;
}
}
}
// Update is called once per frame
void Update()
{
if (lockStateRealTime)
{
ChangeColors(new Color32(255, 0, 0, 255), new Color32(0, 255, 0, 255));
lockStateRealTime = false;
}
}
}
I think your problem is in your collision detection for the door. A rigidbody on the door itself should have no effect.
First: Your OnTriggerEnterfunction does not have the correct pattern to work since it is missing the Collider collidername parameter.
private void OnTriggerEnter(Collider other)
{
}
You can call you collider whatever you want (IntelliSense defaults to other) but your method has to have a parameter like this to be recognized as a OnTriggerEnterfunction by Unity.
Second: You never check if the object triggering your collider is actually the object you want to open the door.
Try tagging your player and NPCs with some kind of tag like Playeror something like that.
Then you check if the colliding object has this tag attatched to it and only then you open the door.
Something like this:
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
//do stuff
}
if(other.CompareTag("NPC"))
{
//do other stuff
}
}
Or if there is only one action for every tag:
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player") || other.CompareTag("NPC"))
{
//do stuff
}
}
Note that for OnTriggerEnterto detect a collision one or more objects colliding need a RigidBodycomponent attached to them, so you are not entirely wrong with that.
TL;DR: Why can I only "kill" my first enemy, but the rest aren't affected by my attempt to turn their components off?
Overview
Hi Guys and Gals, I'm running into issues with my AI switching to Ragdoll when the player kills them, but more specifically it's not deactivating any other components either.
Basically, I have an AI script that runs a statemachine via IEnumerators and coroutines, I also have a RagdollDeath script just to keep code seperate.
Ideally, when the player shoots the enemy, they switch to a ragdoll state.
How I accomplish this
Besides turning off all the animator, navmesh, and other components when health reaches 0, I also turn off ALL rigid bodies using
void IsKinematic(bool newvalue)
{
foreach (Rigidbody rb in bodyparts)
{
rb.isKinematic = newvalue;
}
}
this creates a beautiful and seamless ragdoll transition from animation.
My Issue
The issue i'm running into, is that when I fire at an enemy, they do exactly as expected, but when I fire at another enemy, it doesn't run my script at all, even though I can see it running via using Print("Something") prompts. I have made sure to prefab my enemy and apply changes to said prefab.
What is even stranger, is that if I clone 2 enemies, and shoot the new one, the first one will go ragdoll across the level! It's almost as if monobehavior is not working. Any insight into what may be causing this would be greatly appreciated.
Full Code that is causing issues
public class ZombieStateMachine : MonoBehaviour {
[SerializeField] GameObject player;
[SerializeField] GameObject los;
[SerializeField] GameObject[] waypoints;
[SerializeField] int timeBetweenWaypoints = 1;
[SerializeField] AudioSource jumpyscare;
private int health = 100;
private SuspenseAudioScript suspensescript;
private NavMeshAgent agent;
public bool canSeePlayer;
public float distanceBetween;
public string routine = "null";
private Animator animator;
public bool isAttacking = false;
private ShootScript shootscript;
private Rigidbody[] bodyparts;
private CapsuleCollider capsule;
void IsKinematic(bool newvalue)
{
foreach (Rigidbody rb in bodyparts)
{
rb.isKinematic = newvalue;
}
}
// Use this for initialization
void Start () {
shootscript = GameObject.FindGameObjectWithTag("Player").GetComponent<ShootScript>();
suspensescript = GetComponent<SuspenseAudioScript>();
animator = GetComponent<Animator>();
agent = GetComponent<NavMeshAgent>();
StartCoroutine(Eyesite());
StartCoroutine(Wander());
bodyparts = GetComponentsInChildren<Rigidbody>();
capsule = GetComponent<CapsuleCollider>();
IsKinematic(true);
}
public void KillZombie()
{
this.StopAllCoroutines();
IsKinematic(false);
animator.enabled = false;
agent.enabled = false;
capsule.enabled = false;
this.enabled = false;
}
Here is the accompanying shoot script
public class ShootScript : MonoBehaviour {
[SerializeField] public int health = 100;
[SerializeField] AudioSource gunshotsound;
[SerializeField] Light gunshotflash;
public float impactforce = 2f;
private ZombieStateMachine enemyscript;
private Rigidbody rb;
private CharacterController m_CharacterController;
private Camera cam;
private CapsuleCollider enemycol;
public UnityStandardAssets.Characters.FirstPerson.FirstPersonController fpscontrol;
// Use this for initialization
void Start () {
fpscontrol = GetComponent<UnityStandardAssets.Characters.FirstPerson.FirstPersonController>();
enemycol = GameObject.FindGameObjectWithTag("Enemy").GetComponent<CapsuleCollider>();
enemyscript = GameObject.FindGameObjectWithTag("Enemy").GetComponent<ZombieStateMachine>();
cam = GetComponentInChildren<Camera>();
rb = GetComponent<Rigidbody>();
m_CharacterController = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update () {
Shoot();
}
public void Shoot()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
Debug.DrawRay(cam.transform.position, cam.transform.forward, Color.red, 1.0f);
RaycastHit hit;
if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, 2000f))
{
if(hit.transform.tag == "Enemy")
{
enemyscript.KillZombie();
gunshotsound.Play();
StartCoroutine(GunshotFlash());
}
else
{
gunshotsound.Play();
StartCoroutine(GunshotFlash());
print("You MIssed!");
}
}
}
}
IEnumerator GunshotFlash()
{
while (true)
{
gunshotflash.enabled = true;
yield return new WaitForSeconds(0.05f);
gunshotflash.enabled = false;
yield return new WaitForSeconds(1);
break;
}
}
public void PlayerDeath()
{
rb.AddForce(this.transform.forward * 2);
rb.isKinematic = false;
m_CharacterController.enabled = false;
fpscontrol.enabled = false;
//rb.useGravity = true;
}
}
Not sure if you ever came across the solution to this but I believe the problem is within your start method for the Shoot script.
enemycol = GameObject.FindGameObjectWithTag("Enemy").GetComponent<CapsuleCollider>();
Doing this will return the first game object that has the "Enemy" tag, it would be be better to just do:
enemycol = GetComponent<CapsuleCollider>();
Probably the same thing needs to be done with the line right under that, the enemyscript variable:
enemyscript = GameObject.FindGameObjectWithTag("Enemy").GetComponent<ZombieStateMachine>();
change to
enemyscript = GetComponent<ZombieStateMachine>();
In the Hierarchy i have 2 ThirdPersonController.
In the Window > Animator i created new empty state called it Walk and set it to HumanoidWalk so when running the game both players are walking.
On one of them i added the script and as Prefab the second ThirdPersonController(1).
Then when running the game it's making clones of the ThirdPersonController(1).
So i see in the Hierarchy more N ThirdPersoncontrollers.
Today to change the speed of walking for each ThirdPersonController i change in the Inspector the Move Speed Multiplier.
But if i want in the script already when creating the clones to set to each one another speed how can i do it ?
using UnityEngine;
using System.Collections;
public class Multiple_objects : MonoBehaviour {
public GameObject prefab;
public GameObject[] gos;
public int NumberOfObjects;
void Awake()
{
gos = new GameObject[NumberOfObjects];
for(int i = 0; i < gos.Length; i++)
{
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos [i] = clone;
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
What i tried now is to get the Animator component of the Prefab and set the speed to all the clones:
using UnityEngine;
using System.Collections;
public class Multiple_objects : MonoBehaviour {
public GameObject prefab;
public GameObject[] gos;
public int NumberOfObjects;
private Animator _animaotr;
void Awake()
{
gos = new GameObject[NumberOfObjects];
for(int i = 0; i < gos.Length; i++)
{
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos [i] = clone;
_animaotr.speed = 10;
}
}
// Use this for initialization
void Start () {
_animaotr = prefab.GetComponent<Animator> ();
}
// Update is called once per frame
void Update () {
}
}
But the main problem is that on my first ThirdPersonController in the Hierarchy the original one i created in Window > Animator empty state called it Walk and set HumandoidWalk.
Now to set the speed for some reason changing the Animator speed never effect of anything for example:
_animaotr.speed = 10;
Only when changing the speed in the ThirdPersonController > Inspector > Third Person Character (Script) > Move Speed Multiplier. And it's changing the same speed to all ThirdPersoncontrollers in the Hierarchy including this i clone.
But how do i change each clone speed to another vlaue of speed ? And why the _animator.speed not changing anything and i need to use this Move Speed Multiplier ?
The Move Speed Multiplier property that is showing in the Editor is declared as m_MoveSpeedMultiplier in the ThirdPersonCharacter script. It is delacre as float m_MoveSpeedMultiplier = 1f; which means that it is a private variable and cannot be accessed from another script. The reason it is showing up in the Editor is because it has [SerializeField] on top of it which means that it is a serialized private variable.
To access it during run-time, you have to change from float m_MoveSpeedMultiplier = 1f; to public float m_MoveSpeedMultiplier = 1f; in the ThirdPersonCharacter script.
Use GetComponent to get instance of ThirdPersonCharacter from the gos GameObject then save it somewhere for re-usual. Since you have 2 ThirdPersonCharacter, you can create two ThirdPersonCharacter arrays to hold those instances. It should look like the code below:
using UnityEngine;
using System.Collections;
using UnityStandardAssets.Characters.ThirdPerson;
public class Multiple_objects : MonoBehaviour
{
public GameObject prefab;
public GameObject[] gos;
public int NumberOfObjects;
private ThirdPersonCharacter[] thirdPersonCharacter;
void Awake()
{
thirdPersonCharacter = new ThirdPersonCharacter[2];
gos = new GameObject[NumberOfObjects];
for (int i = 0; i < gos.Length; i++)
{
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos[i] = clone;
thirdPersonCharacter[i] = clone.GetComponent<ThirdPersonCharacter>();
}
}
// Use this for initialization
void Start()
{
thirdPersonCharacter[0].m_MoveSpeedMultiplier = 5f;
thirdPersonCharacter[1].m_MoveSpeedMultiplier = 5f;
}
// Update is called once per frame
void Update()
{
}
}
First of all, how original of me to post another dreaded
NullReferenceException: Object reference not set to an instance of an object
but I have scoured the web looking for a solution for like 2 hours now and have come up with nothing... Here is are the two scripts i have :
GROUNDED:
using UnityEngine;
using System.Collections;
public class GroundCheck : MonoBehaviour {
private Player player;
void Start()
{
player = GetComponent<Player>();
}
void OnTriggerEnter2D(Collider2D col)
{
player.grounded = true;
}
void OnTriggerExit2D(Collider2D col)
{
player.grounded = false;
}
}
PLAYER:
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
public float maxSpeed = 3;
public float speed = 50f;
public float jumpPower = 150f;
public bool grounded;
private Rigidbody2D rb2d;
private Animator anim;
// Use this for initialization
void Start () {
rb2d = gameObject.GetComponent<Rigidbody2D>();
anim = gameObject.GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
anim.SetBool("Grounded", grounded);
anim.SetFloat("Speed", Mathf.Abs(Input.GetAxis("Horizontal")));
}
void FixedUpdate()
{
float h = Input.GetAxis("Horizontal");
rb2d.AddForce((Vector2.right * speed) * h);
if (rb2d.velocity.x > maxSpeed)
{
rb2d.velocity = new Vector2(maxSpeed, rb2d.velocity.y);
}
if (rb2d.velocity.x < -maxSpeed)
{
rb2d.velocity = new Vector2(-maxSpeed, rb2d.velocity.y);
}
}
}
The exact error is:
NullReferenceException: Object reference not set to an instance of an object
GroundCheck.OnTriggerEnter2D (UnityEngine.Collider2D col)
(atAssets/scripts/GroundCheck.cs:15)
Here is my scene:
Here is my boxcollider (if it helps):
If both of the GroundCheck and PLAYER classes are on same GameObject then change the Start() method of GroundCheck class like this:
void Start()
{
player = gameObject.GetComponent<Player>();
}
If they are not on same GameObject then use the following code:
void Start()
{
GameObject playerObj = GameObject.Find("Name of gameObject that player script is in that");
player = playerObj.GetComponent<Player>();
}
In PLAYER class add static modifier to defination of grounded:
public static bool grounded;
Your ground check script isn't on the same object as the player script, that means you can't use getcomponent to get the player script. So you haven't set the player var to anything which is causing the error. Set the player var to the gameobject that has the player script in the editor then in your start method use player.GetComponent();
void OnTriggerEnter2D(Collider2D col) <-- in collider param request gameObject, getcomponent to col is prefered, only control if object collision is player. col.gameObject.getcomponent<Player>().grounded=true;
if(col.Name.Equals("Player")
{
col.gameObject.getcomponent<Player>().grounded=true;
}
I had a similar problem. I hope it's helps
http://docs.unity3d.com/ScriptReference/Collision2D.html
Collider2d have gameobject component, trigger enter get Collider this object.
in http://docs.unity3d.com/ScriptReference/Collider2D.OnCollisionEnter2D.html
see example use in collider (not trigger is only example) to use, acces gameObject.
Not necessary findtag when object(player) is passing for parameter in event OnTriggerEnter, Exit or Stay