How to instantiate an Oculus grabbable object at runtime in Unity? - c#

Right now I have an obj file in Unity. I have a script that adds a Collider component, a Rigidbody component, and finally an OVRGrabbable component to the object. I need to add these components at runtime because eventually I will be producing procedural meshes in a script at runtime, and I need these procedural meshes to be grabbable.
My problem is that the OVRGrabbable script does not recognize the added collider as a grab point when the collider is added at runtime. I thought that it would be enough to add the collider before the OVRGrabbable in my script, but no dice. I tried attaching the collider in an Awake function and then the OVRGrabbable in the Start function, but that didn't work either. Additionally, I cannot add it in script because the grabPoints array is read-only. Here is my code:
public class AddVRComponents : MonoBehaviour {
void Start () {
public bool freeMoving = false;
public bool useGravity = false;
collide = gameObject.AddComponent<BoxCollider>();
Rigidbody rB = gameObject.AddComponent<Rigidbody>();
if (!freeMoving)
{
rB.drag = Mathf.Infinity;
rB.angularDrag = Mathf.Infinity;
}
if (!useGravity)
{
rB.useGravity = false;
}
OVRGrabbable grab = gameObject.AddComponent<OVRGrabbable>();
Collider[] newGrabPoints = new Collider[1];
newGrabPoints[0] = collide;
grab.enabled = true;
grab.grabPoints = newGrabPoints;
}
}
This obviously does not work because the final line produces the error that grab.grabPoints is read-only.
I know that it can be done because if I run my program and then in the editor manually drag my collider into the grab points field of the OVRGrabbable component, the object can be grabbed.
How can I get the OVRGrabbable script to recognize my collider?

Read-only properties are properties that can be assigned only through the script that contains them. This means that you can only change the value of grabPoints from inside the OVRGrabbable.cs script.
The best way to do this is by adding a custom function inside the OVRGrabbable.cs file so that you can access and set the read only variables.
I use this one so I can also set the snapPosition and snapOrientation fields as well as the grabPoints.
public void Initialize(bool snapPosition, bool snapOrientation, Collider[] grabPoints)
{
m_snapPosition = snapPosition;
m_snapOrientation = snapOrientation;
m_grabPoints = grabPoints;
}
You'll be good enough only adding and calling the following one:
public void Initialize(Collider[] grabPoints)
{
m_grabPoints = grabPoints;
}

I made this modification to OVRGrabbale.cs:
void Awake()
{
if (ReferenceEquals(m_grabPoints, null) || m_grabPoints.Length == 0)
{
// Get the collider from the grabbable
Collider collider = this.GetComponent<Collider>();
if (collider == null)
{
throw new ArgumentException("Grabbables cannot have zero grab points and no collider -- please add a grab point or collider.");
}
// Create a default grab point
m_grabPoints = new Collider[1] { collider };
}
}
I checked to see if m_grabPoints is null and it worked.

Related

I am having issues with coding a game of tag

So I am trying to make my first game using Unity and c#. I want my game to be a simple game of tag like I used to play when I was younger. I have tried using "OnCollisionEnter" and I was able to get that to change a counter that gave a bool a label. I realized that while this may work for tagging someone it does not help with other people tagging you. And tips on how I can make my code more like a "Tag Manager"?My current progress
you can have a Taggable.cs like this, not sure if this is even correct syntax, I don't care to actually write it out and test
class Taggable : MonoBehaviour {
bool it;
private void OnCollisionEnter(Collision collision) {
var other = collision.collider.GetComponent<Taggable>();
if(other != null) {
if(it) {
other.it = true;
it = false;
}else if(other.it) {
it = true;
other.it = false;
}
}
}
}
You could implement the logic such that your script checks to see if you are the tagger using Unity's tags property. Then, you could switch the tags of both the players, once you check if the collided object is a player.
Below is the code I suggest you use. I would personally use OnTriggerEnter to have more clearer logic between collider and player. Make sure to have a collider object and a Rigidbody on all players within the scene for the function to be called, and attach your script onto every player within the scene.
private void OnTriggerEnter (Collider col) {
// if you are the tagger
if (gameObject.tag == "Tagger") {
// and if the collided object is a regular player
if (col.tag == "Player") {
// the player is now a tagger
col.tag = "Tagger";
// depending on if you want to buildup taggers or switch them around,
// the below assignment would vary
gameObject.tag = "Player";
}
}
}

I cannot spawn new objects in Unity 2D

Alright, so I've been building a small thing in Unity 2D, and it works for the most part, however whenever I attempt to create a script to spawn a copy of the circle prefab, it just doesn't spawn without any error messages.
I've attempted to use Instantiate to spawn them at the button's location, yet no luck.
{
public GameObject circle;
public Transform circlespawn;
private bool touched = false;
void Update()
{
if(touched == true)
{
Instantiate(circle, circlespawn.position, circlespawn.rotation);
touched = false;
}
}
void OnMouseDown()
{
touched = true;
}
}
From BugFinder in the comments, he pointed out I simply needed a collider for the onmousedown to work correctly.

Can only spawn one object at once in unity

In my game I have a game object called ExclamationMark which I want to spawn above enemies heads when the player gets into range and they become "Alerted".
I've made this simple script to do that, but for some reason it will only work on one game object.
My enemy script:
void CheckForPlayer()
{
// Define player and get position
var player = GameObject.FindWithTag("Player");
var playerPos = (int)player.transform.position.x;
if (transform.Find("Graphics"))
{
// Define gameobject position
var enemyPos = transform.Find("Graphics").gameObject.transform.position.x;
// Define range to spawn tiles in
var range = 5;
var rangeInfront = enemyPos + range;
var rangeBehind = enemyPos - range;
if (playerPos >= rangeBehind && playerPos <= rangeInfront)
{
enemyIsActive = true;
if (transform.Find("ExclamationMark"))
{
var exMark = transform.Find("ExclamationMark").gameObject.GetComponent<ExclamationMarkSpawn>();
exMark.SpawnExclamationMark();
}
}
else
{
enemyIsActive = false;
}
}
}
My ! script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExclamationMarkSpawn : MonoBehaviour {
public GameObject spawnPos;
public GameObject exclamationMark;
public GameObject exclamationMarkAudio;
public void SpawnExclamationMark()
{
StartCoroutine(GameObject.FindGameObjectWithTag("MainCamera").GetComponent<CameraShake>().Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, spawnPos.transform.position, Quaternion.identity);
if (exclamationMarkAudio)
Instantiate(exclamationMarkAudio, spawnPos.transform.position, Quaternion.identity);
StartCoroutine(DestroyExclamationMark());
}
IEnumerator DestroyExclamationMark()
{
yield return new WaitForSeconds(1);
var children = new List<GameObject>();
foreach (Transform child in transform) children.Add(child.gameObject);
children.ForEach(child => Destroy(child));
}
}
Just to be sure: I assume every player has its own instance of both of your scripts attached (some maybe nested further in their own hierarchy).
I assume that since you are using transform.Find which looks for the object by name within it's own children.
In general using Find and GetComponent over and over again is very inefficient! You should in both classes rather store them to fields and re-use them. Best would be if you can actually already reference them via the Inspector and not use Find and GetComponent at all.
In general finding something by name is always error prone. Are you sure they are all called correctly? Or are others maybe further nested?
Note: Find does not perform a recursive descend down a Transform hierarchy.
I would prefer to go by the attached components. You say it has e.g. a RigidBody. If this is the only Rigidbody component in the hierarchy below your objects (usually this should be the case) then you could instead rather simply use
// pass in true to also get disabled or inactive children
Rigidbody graphics = GetComponentInChildren<Rigidbody>(true);
the same for the ExclamationMarkSpawn
// Would be even beter if you already reference these in the Inspector
[SerializeField] private Rigidbody graphics;
[SerializeField] private ExclamationMarkSpawn exclamationMark;
[SerializeField] private Transform player;
private void Awake()
{
if(!player) player = GameObject.FindWithTag("Player");
if(!graphics) graphics = GetComponentInChildren<Rigidbody>(true);
if(!exclamationMark) exclamationMark = GetComponentInChildren<ExclamationMarkSpawn>(true);
}
private void CheckForPlayer()
{
// If really needed you can also after Awake still use a lazy initialization
// this adds a few later maybe unnecessary if checks but is still
// cheaper then using Find over and over again
if(!player) player = FindWithTag("Player");
if(!graphics) graphics = GetComponentInChildren<Rigidbody>(true);
if(!exclamationMark) exclamationMark = GetComponentInChildren<ExclamationMarkSpawn>(true);
var playerPos = (int)player.position.x;
// always if making such a check also give a hint that something might be missing
if (!graphics)
{
// by adding "this" you can now simply click on the message
// in the console and it highlights the object where this is happening in the hierarchy
Debug.LogWarning("graphics is missing here :'( ", this);
return;
}
// Define gameobject position
var enemyPos = graphics.transform.position.x;
// Define range to spawn tiles in
// this entire block can be shrinked down to
if (Mathf.Abs(playerPos - enemyPos) <= 5)
{
enemyIsActive = true;
if (exclamationMark) exclamationMark.SpawnExclamationMark();
}
else
{
enemyIsActive = false;
}
}
The same also in ExclamationMarkSpawn.cs.
I would additionally only allow 1 exclamation mark being visible at the same time. For example when a player jitters in the distance especially assuming both, the player and the enemy, I would move the entire instantiation to the routine and use a flag. Especially since this is called every frame in Update while the player stays in the range!
Also re-check and make sure your enemies are not maybe referencing the same spawnPos and thus all instantiating their exclamation marks on top of each other.
public class ExclamationMarkSpawn : MonoBehaviour
{
public Transform spawnPos;
public GameObject exclamationMark;
public GameObject exclamationMarkAudio;
[SerializeField] private CameraShake cameraShake;
// only serialized for debug
[SerializeField] private bool isShowingExclamation;
private void Awake()
{
if(!cameraShake) cameraShake = Camera.main.GetComponent<CameraShake>();
// or assuming this component exists only once in the entire scene anyway
if(!cameraShake) cameraShake = FindObjectOfType<CameraShake>();
}
public void SpawnExclamationMark()
{
StartCoroutine(ShowExclamationMark());
}
private IEnumerator ShowExclamationMark()
{
// block concurrent routine call
if(isShowingExclamation) yield brake;
// set flag blocking concurrent routines
isShowingExclamation = true;
// NOTE: Also for this one you might want to rather have a flag
// multiple enemy instances might call this so you get concurrent coroutines also here
StartCoroutine(cameraShake.Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, spawnPos.position, Quaternion.identity);
if (exclamationMarkAudio) Instantiate(exclamationMarkAudio, spawnPos.position, Quaternion.identity);
yield return new WaitForSeconds(1);
var children = new List<GameObject>();
foreach (var child in transform.ToList()) children.Add(child.gameObject);
children.ForEach(child => Destroy(child));
// give the flag free
isShowingExclamation = false;
}
}
Try this;
if (transform.Find("ExclamationMark"))
{
var exMark = transform.Find("ExclamationMark").gameObject.GetComponent<ExclamationMarkSpawn>();
exMark.SpawnExclamationMark(transform.position); //Add transform.position here
}
public void SpawnExclamationMark(Vector3 EnemyPos)
{
StartCoroutine(GameObject.FindGameObjectWithTag("MainCamera").GetComponent<CameraShake>().Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, EnemyPos, Quaternion.identity);
if (exclamationMarkAudio)
Instantiate(exclamationMarkAudio, EnemyPos, Quaternion.identity);
StartCoroutine(DestroyExclamationMark());
}

Error accessing property across classes: an object reference is required for the non-static field method or property

I have been dabbling in unity and have run into a problem as I an unable to figure out how to notify another class that a ability has been used and the countdown is active. I realize that it is because I need to be making a static reference but not quite sure how to use one while still being able to change the value. I will include just the important bits so that you don't have to waste your time. (I want to be able to have the usedAbilities.canFireballnow be equal to that as when I call it in the fireball script.)
fireball
float canuseFireballtimer = 0;
bool startCooldown = false;
// Update is called once per frame
void Update()
{
if (startCooldown) {
usedAbilities.canFireballnow = false; // error
canuseFireballtimer += Time.deltaTime;
if (canuseFireballtimer >= 5) {
usedAbilities.canFireballnow = true; //error
}
}
if (Input.GetKeyDown(KeyCode.Q) && enoughmana && usedAbilities.canFireballnow) { // error
startCooldown = true;
ManaBar.mana -= 10f;
Instantiate(fireballPrefab, fireballSpawn.position, fireballSpawn.rotation);
}
}
usedAbilities script
public bool canFireballnow = true;
Thanks,
A fellow Programmer
First of all, you need to add a reference to your object. You can do that by using [SerializeField] before creating a variable of type GameObject. Like this:
[SerializeField]
GameObject obj;
Then, in Unity, you can drag the GameObject from the Hierarchy to the Inspector.
This is what you should see in the Inspector:
Then, you need to get the script component of the GameObject to finally be able to read it's value.
obj.GetComponent<name_of_your_script>().value;
Your final code should look like this:
[SerializeField]
GameObject abilities;
void Update() {
if (canuseFireballtimer >= 5) {
abilities.GetComponent<usedAbilities>().canFireballnow = true;
}
Note: Your variable should be public.

How to convert code from Unity 4 To Unity 5?

I am new to installed Unity 5. I have some problem in the following code:
void OnTriggerEnter(Collider other) {
var air = other.collider.gameObject.GetComponent<DamageManager>();
if(air){
air.HP += HPFill;
}
}
Replace var air = other.collider.gameObject.GetComponent<DamageManager>() with var air = other.GetComponent<Collider>().gameObject.GetComponent<DamageManager>();
void OnTriggerEnter(Collider other)
{
var air = other.GetComponent<Collider>().gameObject.GetComponent<DamageManager>();
if (air)
{
air.HP += HPFill;
}
}
var air = other.GetComponent<DamageManager>();
The collider in your code is actually redundant since other is already the collider component. It is like asking the reference to reach itself. And no need either to use the gameObject reference since Collider is a Component and then contains a GetComponent method.
The new versions of Unity are removing all component reference but transform and gameObject as they are always there.

Categories