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.
Related
The script is attached to empty gameobject
At this line i'm using the mouse left button to fire a bullet one time.
If i'm using a break point it will shot one bullet once. but if i'm not using a break point it will shot two bullets in a row one after the other.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
public class Shooting : MonoBehaviour
{
public CinemachineVirtualCamera cmf;
[Header("Main")]
public Rigidbody bulletPrefab;
public float launchForce = 700f;
public bool automaticFire = false;
public float bulletDestructionTime;
public bool go = false;
[Space(5)]
[Header("Slow Down")]
public float maxDrag;
public float bulletSpeed;
public bool bulletsSlowDown = false;
public bool overAllSlowdown = false;
[Range(0, 1f)]
public float slowdownAll = 1f;
public List<Transform> firePoints = new List<Transform>();
public Animator anim;
private void Start()
{
if (anim != null)
{
anim.SetBool("Shooting", true);
}
}
public void Update()
{
if (overAllSlowdown == true)
{
Time.timeScale = slowdownAll;
}
if (firePoints.Count > 0))
{
for (int i = 0; i < firePoints.Count; i++)
{
if (Input.GetMouseButton(0))
{
anim.SetTrigger("Shoot");
}
if (Input.GetMouseButton(1))
{
cmf.enabled = false;
}
if (go)
{
LaunchProjectile(firePoints[i]);
go = false;
}
}
}
}
private void LaunchProjectile(Transform firePoint)
{
Rigidbody projectileInstance = Instantiate(
bulletPrefab,
firePoint.position,
firePoint.rotation);
projectileInstance.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
cmf.enabled = true;
cmf.Follow = projectileInstance.transform;
cmf.LookAt = projectileInstance.transform;
projectileInstance.AddForce(new Vector3(0, 0, 1) * launchForce);
if (bulletsSlowDown == true)
{
if (projectileInstance != null)
{
StartCoroutine(AddDrag(maxDrag, bulletSpeed, projectileInstance));
}
}
}
IEnumerator AddDrag(float maxDrag, float bulletSpeed, Rigidbody rb)
{
if (rb != null)
{
float current_drag = 0;
while (current_drag < maxDrag)
{
current_drag += Time.deltaTime * bulletSpeed;
rb.drag = current_drag;
yield return null;
}
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
rb.drag = 0;
}
}
}
This script is attached to my player with animator and i'm using this method to reference event i added to animation in the animator controller. when the event happens the variable bool flag go is set to true.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThrowObject : MonoBehaviour
{
public Shooting shooting;
public void ThrowEvent()
{
shooting.go = true;
}
}
This is a screenshot of the animator controller.
I added a new state name Throwing with two transitions from and to the Grounded state.
The Grounded state is playing idle animation.
In the transition from the Grounded to the Throwing i added a condition name Shoot type trigger.
In the transition from the Throwing state to the Grounded there is no any conditions.
Afaik animator triggers are stackable!
So since you call this in a for loop it might happen that it adds multiple triggers at once but each transition only consumes one at a time!
What I ended up using in combination with triggers in an animator is this script
public class AnimatorTriggerResetter : StateMachineBehaviour
{
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
foreach(var p in animator.parameters)
{
if (p.type == AnimatorControllerParameterType.Trigger)
{
animator.ResetTrigger(p.name);
}
}
}
}
attach this to no specific state at all but directly to the Basic layer of your AnimatorController itself => It is called for each and every state that is entered => resets all existing triggers.
In your specific case though, why not rather pull the general calls out of the loop and rather make it
if (firePoints.Count > 0 && go)
{
if (Input.GetMouseButton(0))
{
anim.SetTrigger("Shoot");
}
if (Input.GetMouseButton(1))
{
cmf.enabled = false;
}
for (int i = 0; i < firePoints.Count; i++)
{
LaunchProjectile(firePoints[i]);
}
go = false;
}
I'm having trouble keeping this method running. It runs for 1 frame them shuts itself off. I am having trouble working out the logic to keep it running. Yes, the enemy does detect if the player is within a field of view, and it does stop when it collides with walls, it also stops detecting the player when they leave the fov.
I've tried changing around if statements, but it's overall very important that the code runs based on the player existing in the FOV collider. The problem is pretty specific, so I don't know what research to do.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(AudioSource))]
public class EnemyAI : MonoBehaviour
{
public float patrolSpeed, chaseSpeed, chaseWaitTime, patrolWaitTime, castRadius;
public Transform[] wayPoints, wayPOI;
public Transform player;
public Animation flinch, die;
public AudioClip grunt, callOut, death;
public LayerMask mask;
public PlayerCheck check;
private float chaseTimer, patrolTimer, wanderTimer;
private int destIndex, destInit, destStart, health;
private bool playerIn;
protected string aggro, resting, warned, sawPlayer;
protected bool patroling;
public bool aggress;
private NavMeshAgent agent;
private Transform playerLP;
private Vector3 startPos;
private Animator anim;
private AudioSource aud;
MeshCollider fieldOfView;
void Awake()
{
//patroling = true;
startPos = transform.position;
destInit = destIndex;
agent = GetComponent<NavMeshAgent>();
agent.autoRepath = true;
agent.autoBraking = true;
fieldOfView = transform.GetChild(0).GetComponent<MeshCollider>();
if (fieldOfView == null)
{
Debug.LogError("The first object MUST be FOV, otherwise the script will not work correctly. 1");
}
check = GameObject.FindObjectOfType<PlayerCheck>();
anim = GetComponent<Animator>();
}
void FixedUpdate()
{
if (check == null)
{
check = GameObject.FindObjectOfType<PlayerCheck>();
}
playerIn = check.playerIn;
RaycastHit hit;
if (Physics.Linecast(transform.position, player.position, out hit, ~mask))
{
if (!playerIn)
{
Debug.DrawLine(transform.position, hit.point, Color.red);
}
else if (hit.collider.gameObject != null)
{
if (!hit.collider.CompareTag("Player"))
{
Debug.DrawLine(transform.position, hit.point, Color.blue);
return;
}
else
{
Debug.DrawLine(transform.position, hit.point, Color.green);
aggress = true;
}
}
}
if (aggress)
{
Aggro(sawPlayer);
}
if (patroling)
{
GoToNext();
}
}
void GoToNext()
{
patroling = true;
aggress = false;
if (wayPoints.Length == 0)
return;
if (playerIn)
return;
if (agent.remainingDistance < .7f)
{
agent.SetDestination(wayPoints[destIndex].position);
destIndex = (destIndex + 1) % wayPoints.Length;
}
}
void Aggro(string condition)
{
if (condition == sawPlayer)
{
Chase(player);
}
}
void Chase(Transform transform)
{
patroling = false;
if (playerIn)
{
agent.SetDestination(transform.position);
print("Chasing");
}
else
{
**Wander(sawPlayer, 15);**
print("Wander, please");
}
}
**void Wander(string condition, float time)**
{
patroling = false;
print(time);
wanderTimer += Time.deltaTime;
print("Wandering");
//this is where I'm having the most trouble, it will print wandering for 1 //frame then stop and go back to patroling
//once the player leaves the fov. I'm trying to figure out where the method //stops and why.
if (condition == sawPlayer)
{
if (wanderTimer >= time)
{
wanderTimer = 0;
if (!agent.pathPending && agent.remainingDistance < .5f)
{
GoToNext();
}
}
else
{
Vector3 vec;
vec = new Vector3(Random.Range(agent.destination.x - 10, agent.destination.x), Random.Range(agent.destination.y - 10, agent.destination.y), Random.Range(agent.destination.z - 10, agent.destination.z));
agent.destination = transform.InverseTransformDirection(vec);
if (agent.remainingDistance < .3f || wanderTimer > time)
{
GoToNext();
}
}
}
}
}
I get the null reference exception error when trying to change the boolean to right (or left in that regard). My prefab should spawn at FirepointL.
My script does recognise the prefeb as it does not return a Null for finding the prefab (tested this).
I made sure my boolean was set to Public and i had dropped all the GameObjects to their designated places in the Inspector.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public GameObject bullet;
private Rigidbody2D myRigidbody;
private float speed = 15;
private bool facingRight;
private bool ground = false;
private float jump = 23;
// Start is called before the first frame update
void Start()
{
facingRight = true;
myRigidbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void FixedUpdate()
{
float horizontal = Input.GetAxis("Horizontal");
bullet = GameObject.FindGameObjectWithTag("Button");
Movement(horizontal);
Flip(horizontal);
if (Input.GetKey("w"))
{
if (ground)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, jump);
}
}
// this is the part that returns the error
if (facingRight == true)
{
bullet.GetComponent<weapon>().right = true;
}
if (facingRight == false)
{
bullet.GetComponent<weapon>().right = false;
}
}
void OnTriggerEnter2D()
{
ground = true;
}
void OnTriggerExit2D()
{
ground = false;
}
private void Movement(float horizontal)
{
myRigidbody.velocity = new Vector2(horizontal * speed,myRigidbody.velocity.y);
}
private void Flip(float horizontal)
{
if (horizontal > 0 && !facingRight || horizontal < 0 && facingRight)
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class weapon : MonoBehaviour
{
// Start is called before the first frame update
public bool right;
public Transform firepointR;
public Transform firepointL;
public GameObject bulletPrefab;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("space"))
{
Debug.Log("It's the space key");
Shoot();
}
}
void Shoot()
{
if (right == true)
{
Instantiate(bulletPrefab, firepointR.position, firepointR.rotation);
}
if(right == false)
{
Instantiate(bulletPrefab, firepointL.position, firepointL.rotation);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class bullet : MonoBehaviour
{
public float speed = 20;
public Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb.velocity = transform.right * speed;
}
// Update is called once per frame
void Update()
{
}
}
Not sure if it's the exact issue, but there's a problem in PlayerMovement class. When you retrieve bullet you assume that an object with Button tag is present.
In my opinion, you should check for it with
// this is the part that returns the error
if (bullet && facingRight == true)
{
bullet.GetComponent<weapon>().right = true;
}
if (bullet && facingRight == false)
{
bullet.GetComponent<weapon>().right = false;
}
I think there is a problem with naming conventions. You are trying to find out a bullet whose name is "Button" but when you are instantiating the gameobject, it names it to something like Button(clone).
One solution that comes in my mind:
1st step:
Make a public static variable inside PlayerMovement script.
public static PlayerMovement Instance;
private void Awake()
{
Instance = this;
}
Set its value inside the awake function so that you can call it from anywhere.
2nd step:
I modified the shoot function.
void Shoot()
{
GameObject _firePosition = right == true ? firepointR : firepointL;
PlayerMovement.Instance.bullet = Instantiate(bulletPrefab, _firePosition.position, _firePosition.rotation); // we are setting the reference on runtime whenever we spawn a new bullet.
}
3rd Step:
Remove this line from PlayerMovement script as it is not needed now.
bullet = GameObject.FindGameObjectWithTag("Button");
PS: Code will not work if you don't follow step 3. Let me know if it helps. :)
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);
}
}
I'm writing a script controlling the enter and exit of a space ship in a 2D game.
The problem I'm running into is when exiting the ship, the player is reactivated, and you regain control, but it does not move the player to the empty gameobject I have attached to the ship, in a beginners attempt to move the player to the location of the ship, without ever destroying the player. The player simply spawns on its last known location prior to entering the ship, and I seem to be forced into that location, I can try to move, but its like I'm anchored to that position.
using UnityEngine;
using System.Collections;
public class SpaceShipActivator : MonoBehaviour
{
//Takeoff
public bool inRange;
public SpaceShipAtmosphereController SpaceShipAtmosphereController;
public PlayerController playerControl;
public GameObject Player;
public bool onBoard;
//End Takeoff
//Landing
public Transform Spawn;
public Transform landingGear2;
public Transform landingGear;
public LayerMask whatIsGround;
float groundRadius = 0.2f;
bool getIn;
bool landingGearStable;
bool landingGearStable2;
bool shipStable;
//EndLanding
Animator anim;
void Start()
{
anim = GetComponent<Animator>();
inRange = false;
SpaceShipAtmosphereController.enabled = false;
}
void Update()
{
AnimationUpdate();
//Launch
if (inRange && Input.GetButtonDown("Interact") && !onBoard)
{
LaunchShip();
}
//End launch
CheckLandingGear();
//Land
if(onBoard && Input.GetButtonDown("Roll"))
{
LandShip();
}
//End Land
}
//*********************************Functions**************************************
void LaunchShip()
{
anim.SetBool("Get In", true);
this.gameObject.tag = "Player";
SpaceShipAtmosphereController.enabled = true;
Player.tag = "InactivePlayer";
playerControl.enabled = false;
Player.SetActive(false);
onBoard = true;
}
void LandShip()
{
anim.SetBool("Get Out", true);
this.gameObject.tag = "InactivePlayer";
Player.tag = "Player";
playerControl.enabled = true;
SpaceShipAtmosphereController.enabled = false;
onBoard = false;
Player.SetActive(true);
Player.transform.position = Spawn.transform.position;
}
void CheckLandingGear()
{
//landingGear
landingGearStable = Physics2D.OverlapCircle(landingGear.position, groundRadius, whatIsGround);
landingGearStable2 = Physics2D.OverlapCircle(landingGear2.position, groundRadius, whatIsGround);
if (landingGearStable && landingGearStable2)
{
shipStable = true;
}
//End landingGear
}
void AnimationUpdate()
{
anim.SetBool("Get In", false);
anim.SetBool("Get Out", false);
}
//For launching ship
//IF player: inRange enter/exit boolean control.
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
//Debug.Log("houi p;gberf");
inRange = true;
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
inRange = false;
}
}
//End IF player: inRange enter/exit boolean control.
}
On a side note, In that same script I've included landingGear, which is set to be two empty gameobjects that basically share Y and are spaced apart on X to check that the spaceship is not only grounded but level for the most part, so the animation doesn't look silly AND to check if the player can even get out. I know this isn't the question, but any input on it would be appreciated, but please don't feel obligated.
Thanks for the help