Static value "lost" in Unity when called on second script - c#

I'm trying to implement a system with platforms and OntriggerExit that spawn new platforms. The objects that contain the trigger function are children to said platforms. I.e, I have 4 triggers as children in each platform.
I'm trying to use the trigger object's global position to instantiate the new platform but it keeps doing so according to the prent's position.
For instance, the parent (platform) is at (0,0,0). The trigger, child to the platform, is at (-15,0,0) globally. I try to add (-35,0,0) and it instantiates at (-35,0,0) instead of (-50,0,0).
I do this with a static value. I obtain the Trigger position when OnTriggerExit() and then use it on a different script as follows:
public class TriggerExitN : MonoBehaviour
{
public delegate void ExitAction();
public static event ExitAction OnChunkExitedN;
public static Vector3 positionN;
private bool exited = false;
private void OnTriggerExit(Collider other)
{
Player player = other.GetComponent<Player>();
if (player != null)
{
if (!exited)
{
exited = true;
OnChunkExitedN();
positionN = gameObject.transform.position;
}
}
}
}
If I debug positionN here it returns (-15,0,0) as expected.
Then the platform generator:
public class MapGenerator : MonoBehaviour
{
public GameObject[] mapprefabs;
public GameObject[] startprefabs;
private List<GameObject> platformslist = new List<GameObject>();
public Vector3 spawnOrigin;
private Vector3 spawnPosition;
// Start is called before the first frame update
void Start()
{
platformslist.Add(startprefabs[0]);
}
void OnEnable()
{
TriggerExitN.OnChunkExitedN += PickAndSpawnN;
}
private void OnDisable()
{
TriggerExitN.OnChunkExitedN -= PickAndSpawnN;
}
void PickAndSpawnN()
{
spawnPosition = TriggerExitN.positionN + new Vector3(-35f, 0, 0f);
foreach (GameObject platform in platformslist)
if(platform.transform.position == spawnPosition)
{
return;
}
else
{
continue;
}
GameObject objectFromChunk = mapprefabs[Random.Range(0, mapprefabs.Length)];
Instantiate(objectFromChunk, spawnPosition + spawnOrigin, Quaternion.identity);
platformslist.Add(objectFromChunk);
Debug.Log(TriggerExitN.positionN);
}
If I debug poisitionN here, it returns (0,0,0).
I know I'm missing something, I'd just like some help with it because it's driving me mad.
Please disregard the rest of the code that does not concern the value and usage of positionN for it is not yet well articulated.
Thank you in advance.

OnChunkExitedN();
positionN = gameObject.transform.position;
Just switch these two you are calling onchunkexitedn before setting positionN
positionN = gameObject.transform.position;
OnChunkExitedN();

Related

Raycast hits 2 objects at the same time

I have recently developed in unity and I have a problem with using the raycast.
I created 3 scripts:
Interactor: Hooked to the player, it manages all the raycast features
InteractionObject: Hooked to the objects that need to animate
InteractionRaycast: Hooked to objects that need to be destroyed
Everything works, the only problem is that when the animation of the gameobject takes place (In my case the gameobject was a pillow), the other gameobject (It is under the pillow) is destroyed at the same time as the animation begins.
My goal is to first move the pillow and then click on the gameobject to be destroyed, what can I do?
Thank you in advance for your help
Interactor.cs
public class Interactor : MonoBehaviour
{
[SerializeField]
private float _interctRange;
private InteractionObject _interactObject;
private InteractionRaycast _interactionRaycast;
private Camera _camera;
private RaycastHit _hit;
// Start is called before the first frame update
void Start()
{
_camera = Camera.main;
}
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out _hit, _interctRange);
if (_hit.transform)
{
_interactObject = _hit.transform.GetComponent<InteractionObject>();
}
if (_interactObject)
{
_interactObject.PerfomAction();
}
}
}
}
InteractionObject.cs
public class InteractionObject : MonoBehaviour
{
[SerializeField]
private Vector3 _openPosition, _closePosition;
[SerializeField]
private float _animationTime;
private Hashtable _iTweenArgs;
[SerializeField]
public bool _isOpen;
// Start is called before the first frame update
void Start()
{
_iTweenArgs = iTween.Hash();
_iTweenArgs.Add("position", _openPosition);
_iTweenArgs.Add("time", _animationTime);
_iTweenArgs.Add("islocal", true);
}
public void PerfomAction()
{
if (Input.GetButton("Fire1"))
{
if (_isOpen)
{
_iTweenArgs["position"] = _closePosition;
}
else
{
_iTweenArgs["position"] = _openPosition;
}
_isOpen = !_isOpen;
iTween.MoveTo(gameObject, _iTweenArgs);
}
}
}
InteractionRaycast.cs
public class InteractionRaycast : MonoBehaviour
{
[SerializeField]
private float _range;
Ray _myRay;
RaycastHit _hit;
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out _hit, _range);
if (_hit.transform)
{
Destroy(gameObject);
}
}
}
}
Tip: Use RaycastAll() and filter out the objects you want based on conditions.
It might help you with your problem, although you first should pay attention to #derHugo answer. It points out many aspects that you will want to improve in your code.
Your InteractionRaycast will destroy this own gameObject it is attaced to completely regardless of what exactly you are hitting.
You either want to additionally check like e.g.
if (Input.GetButton("Fire1"))
{
var ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f));
if(Physics.Raycast(ray, out var hit, _interctRange))
{
// Is this actually the object that was hit?
if(hit.transform == transform)
{
Destroy(gameObject);
}
}
}
Or - and in general I would do that - instead of having such a component on each and every object you can interact with and shooting hundreds of redundant raycasts, I would rather have a component on your player object, shoot one single raycast and interact with whatever you hit.
Both your target objects can have a common interface
public interface IInteractionObject
{
void PerfomAction();
}
meaning both types need to implement a method called PerformAction without parameters.
And rather interact directly with that in
public class Interactor : MonoBehaviour
{
[SerializeField]
private float _interctRange;
private Camera _camera;
// Start is called before the first frame update
void Start()
{
_camera = Camera.main;
}
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
var ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f));
// was something hit at all? => Check the API and return values of methods!
if(Physics.Raycast(ray, out var hit, _interctRange))
{
// Did we hit an IInteractionObject
if(hit.transform.TryGetComponent<IInteractionObject>(out var interactable))
{
// This class has no idea what exactly it is interacting with and doesn't need to know
interactable.PerfomAction();
}
}
}
}
}
and then you have different implementations:
public class AnimatedInteractionObject : MonoBehaviour, IInteractionObject
{
[SerializeField] private Vector3 _openPosition;
[SerializeField] private Vector3 _closePosition;
[SerializeField] private float _animationTime;
[SerializeField] public bool _isOpen;
private Hashtable _iTweenArgs;
private void Start()
{
_iTweenArgs = iTween.Hash();
_iTweenArgs.Add("position", _openPosition);
_iTweenArgs.Add("time", _animationTime);
_iTweenArgs.Add("islocal", true);
}
public void PerfomAction()
{
_isOpen = !_isOpen;
// use ternary makes it easier to read
_iTweenArgs["position"] = _isOpen ? _openPosition : _closePosition;
iTween.MoveTo(gameObject, _iTweenArgs);
}
}
and
public class DestroyInteractionObject : MonoBehaviour, IInteractionObject
{
public void PerfomAction()
{
// This is only called by the Interactor
// it already does a key and raycast check so no need to do that here
Destroy(gameObject);
}
}

How can I bring a Player to ignore collision with another GameObject in code?

Im currently trying to code a health system for my game and I want my Player GameObject to ignore collision with the Health Potion GameObject if the Player has max health. My problem is that I cannot simply turn off the collision between the Player Layer and Health Potion Layer because I only want to ignore collision if the Player has Max Health. I tried doing it myself but it didn't work. Here's my code:
public class ExampleCodeUA : MonoBehaviour{
public int PlayerMaxHealth = 100, PlayerCurrentHealth;
public HealthBar healthBar;
private void Start()
{
PlayerCurrentHealth = PlayerMaxHealth;
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("HealthPotion"))
{
if (PlayerCurrentHealth == PlayerMaxHealth)
{
Physics2D.IgnoreLayerCollision(6, 7);
}
else if (PlayerCurrentHealth > 50)
{
GetHealth(PlayerMaxHealth - PlayerCurrentHealth);
}
else if (PlayerCurrentHealth <= 50)
{
GetHealth(50);
}
}
}
void GetHealth(int healing)
{
PlayerCurrentHealth += healing;
healthBar.SetHealth(PlayerCurrentHealth);
}
}
You don't want to modify your physics configuration at runtime. What you could do to avoid the collision... is to make it impossible, by having nothing to collide with. Both of your objects have a collider. What you could do is to disable all existing HealthPotion colliders by modifying their code. An implementation could be the following:
using System.Collections.Generics;
using UnityEngine;
public class HealthPotion : MonoBehaviour
{
private static List<HealthPotion> _existingPotions = new List<HealthPotion>();
public static void EnablePotionsCollider(bool value)
{
foreach (var potion in _existingPotions)
{
potion._collider.enabled = value;
}
}
private Collider _collider;
void Awake()
{
_collider = GetComponent<Collider>();
}
void OnEnable()
{
_existingPotions.Add(this);
}
void OnDisable()
{
_existingPotions.Remove(this);
}
}
Then you just have to call the method when you want to enable/disable by doing
HealthPotion.EnablePotionsCollider(value you want);

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);
}
}

Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption

I checked this code with a friend that has a better knowledge of Unity, but we can't find the problem.
Basically, Unity says that i can't parent a prefab, but i'm trying to change the parent of an instanced object, not the prefab.
I can't understand the error (but I think that it's in the Update method)
public GameObject[] Weapons;
public float projectileSpeed;
public float bulletTime;
public Rigidbody bullet;
private bool canShoot = true;
private float t = 0f;
private int actualBullets;
private GameObject actualWeapon;
private void Update()
{
t += Time.deltaTime;
if (actualWeapon != null)
return;
actualWeapon = GameObject.Instantiate(Weapons[0], gunPosition.position, gunPosition.rotation) as GameObject;
actualWeapon.transform.parent = GameManager.instance.player.transform;
}
public virtual void Fire()
{
if (canShoot)
{
actualBullets--;
var nBullet = GameObject.Instantiate(bullet, bulletSpawn.position, Quaternion.identity) as Rigidbody;
nBullet.AddForce (new Vector3(Vector3.forward.x, Vector3.forward.y, projectileSpeed));
canShoot = false;
}
else if (t > bulletTime)
(canShoot, t) = (true, 0);
}
Your problem is that
actualWeapon.transform.parent = GameManager.instance.player.transform;
is trying to get a parent that is not instantiated. You need to go to the GameManager and actually instantiate the player GameObject. Then you can keep a reference to the Player and make weapons children of the Player.
In my case, when developing Glitch Garden in Unity 5.5
private void Fire (){
GameObject newProjectile = Instantiate(projectile) as GameObject;
newProjectile.transform.parent = projectileParent.transform;
newProjectile.transform.position = gun.transform.position;
}
solved by Instantiate prefab projectileParent:
private void Fire (){
GameObject newProjectile = Instantiate(projectile) as GameObject;
newProjectile.transform.parent = Instantiate(projectileParent).transform;
newProjectile.transform.position = gun.transform.position;
}
Then, It caused another problem that is multiple projectiles. Finally,
I removed that line and it worked with following code:
private void Fire (){
GameObject newProjectile = Instantiate(projectile) as GameObject;
newProjectile.transform.position = gun.transform.position;
}
Above solution is a temp solution. Finally next video it will be fixed and current code is as follows:
public class Shooter : MonoBehaviour {
public GameObject projectile;
public GameObject gun;
private GameObject projectileParent;
void Start ()
{
projectileParent = GameObject.Find ("Projectiles");
if (!projectileParent) {
projectileParent = new GameObject();
projectileParent.name = "Projectiles";
}
}
private void Fire (){
GameObject newProjectile = Instantiate(projectile) as GameObject;
newProjectile.transform.parent = projectileParent.transform;
newProjectile.transform.position = gun.transform.position;
}
}

Camera shake on collision in unity 3d

http://answers.unity3d.com/questions/212189/camera-shake.html
I've followed the question's answer above to try and get a camera shake working for my first person camera. But I've tried to modify it so that the camera shakes from an invisible collision box.
So far my camera shake script looks like this;
public bool Shaking;
private float ShakeDecay;
private float ShakeIntensity;
private Vector3 OriginalPos;
private Quaternion OriginalRot;
void Start()
{
Shaking = false;
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.name == "ShakeTrigger")
{
DoShake();
Debug.Log("The camera trigger has hit");
}
}
void Update ()
{
if(ShakeIntensity > 0)
{
transform.position = OriginalPos + Random.insideUnitSphere * ShakeIntensity;
transform.rotation = new Quaternion(OriginalRot.x + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.y + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.z + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.w + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f);
ShakeIntensity -= ShakeDecay;
}
else if (Shaking)
{
Shaking = false;
}
}
void OnGUI() {
if (GUI.Button(new Rect(10, 200, 50, 30), "Shake"))
DoShake();
//Debug.Log("Shake");
}
public void DoShake()
{
OriginalPos = transform.position;
OriginalRot = transform.rotation;
ShakeIntensity = 0.3f;
ShakeDecay = 0.02f;
Shaking = true;
}
I know the code works 100% via the gui button. This script is attached to the camera on the first person controller. An invisible collision box with the tag ShakeTrigger is in the game. However, the debug log doesn't get called at all and I'm unsure why.
If anyone needs any more information just let me know.
Thanks in advance :)
If the script is attached to your camera, then OnTriggerEnter is looking at the camera for a trigger call, not the collision box.
One thing you could do is stick the OnTriggerEnter into a new script and put that inside the collision box. Then have that do a SendMessage along these lines:
GameObject.Find("Camera").SendMessage("DoShake");
EDIT: To answer Jerdak's questions.
The code bellow would be within the TriggerBox:
void Start()
{
...
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.name == "ShakeTrigger")
{
GameObject.Find("Camera").SendMessage("DoShake");
Debug.Log("The camera trigger has hit");
}
}...
and this would be within the Camera:
void Start()
{
...
}
public void DoShake()
{
OriginalPos = transform.position;
OriginalRot = transform.rotation;
ShakeIntensity = 0.3f;
ShakeDecay = 0.02f;
Shaking = true;
}...
This way, triggerbox is responsible for detecting triggers and only ever sends a message to the camera when right kind of object goes through it. The camera is then responsible for doing the shaking.
To shake your camera on the collision or Trigger you need to first Make function of Your Shake that you can also call from other scripts
Something like
public class ShakeCamera : MonoBehavior
{
public bool canShake;
private void Update()
{
if(canShake)
DoShake()
}
public void DoShake()
{
// Shake Logic
}
public void StartShake()
{
canShake = true;
}
public void StopShake()
{
canShake = false;
}
}
And from your other script whenever you trigger the target object you can call it like this
public class TriggerScript: MonoBehavior
{
public ShakeCamera shakeCamera;
private void Start()
{
shakeCamera = FindObjectOfType<ShakeCamera>();
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.tag == "targetTag")// Change Tag accroding to your requirement
{
cameraShake.StartShake();
}
}
void OnTriggerExit(Collider collision)
{
if(collision.gameObject.tag == "targetTag")// Change Tag accroding to your requirement
{
cameraShake.StopShake();
}
}
}
I am attaching one reference video for you, May be it would help you in better camera shake. Hope it was helpful.
You can refer to this video I made https://youtu.be/9X_JXexwfR4
If you have set up rigidbody then change the interpolate from none to interpolate.

Categories