RaycastHit to work only when hitting collider - c#

I am making find a difference game. I made a ray that hits an image with collider and changes certain text property. Problem is that where ever i touch the screen it detects a ray, and changes the text. How can i solve that, i also unchecked Raycast Target for all other objects on screen.
public List<GameObject> hiddenObjects;
public Text test;
public Text click;
GameObject[] objekti;
private RaycastHit2D result;
// Start is called before the first frame update
void Start()
{
//This is for a list that will fill with GameObjects
objects= GameObject.FindGameObjectsWithTag("Slike");
hiddenObjects = new List<GameObject>();
for (int i = 0; i < objects.Length; i++)
{
hiddenObjects.Add(objects[i]);
}
}
// Update is called once per frame
void Update()
{
if (Input.touchCount > 0)
{
Vector2 camRay = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
result = Physics2D.Raycast(camRay, (Input.GetTouch(0).position));
if (result.collider.gameObject)
{
click.text = "CLICK";
}
//This is supposed to delete 1 object at time as it is clicked on
for (int j = 0; j < hiddenObjects.Count; j++)
{
if (hiddenObjects[j] = null)
{
hiddenObjects.RemoveAt(j);
}
}
//Simple if statement if list is empty, print "Win"
if (hiddenObjects.Count == 0)
{
test.text = "WIN";
}
}
}

From the information you have provided. I think in those code:
if (result.collider.gameObject)
{
click.text = "CLICK";
}
You need to identify the object that has been hit either by the name property or by comparing a tag.
Let's do it by name for simplicity.
In the left hand side menu in Unity A.K.A "Hierarchy" let's say the GameObject is called "ObjectToDetectHitOn" you would change the above code to:
if (result.collider.gameObject.name.Equals("ObjectToDetectHitOn"))
{
click.text = "CLICK";
}
This will not work if you Instantiate gameobject in code because they will then have (Clone) added to the end of the name. In this scenario you would have to use the .Contains() method of the String class.
The preferred method would be to actually add a tag to the GameObject, let's say you created a tag with the name "ObjectToDetectRay" and use the code like this:
if (result.collider.gameObject.CompareTag("ObjectToDetectRay"))
{
click.text = "CLICK";
}
Let me know if this doesn't work or if it is unclear and I will try to explain further.

Related

Instantiating an object after updating a variable isn't working as expected

Let's say I have two scripts:
SpawnManager
Enemy
In SpawnManager, I have the function SpawnEnemyWave that should instantiate 3 enemies, if the random number generator is lower than 5, then one of them should have a higher movement speed, the other shouldn't move at all.
In SpawnManager:
bool toughEnemy = true;
int waveNumber = 3;
float randomNumber = Random.Range(0, 10);
void Start()
{
SpawnEnemyWave(waveNumber);
}
void SpawnEnemyWave(int enemiesToSpawn)
{
float randomNumber = Random.Range(0, 10);
print(randomNumber);
for (int i = 0; i < enemiesToSpawn; i++)
{
if ((randomNumber < 5) && toughEnemy)
{
print("Tough");
Instantiate(enemyPrefab, GenerateSpawnPosition(), enemyPrefab.transform.rotation);
toughEnemy = false; //I make sure there is only one tough enemy per wave
}
else
{
print("Weak");
Instantiate(enemyPrefab, GenerateSpawnPosition(), enemyPrefab.transform.rotation);
}
}
}
In Enemy, I'm checking if the toughEnemy variable is set to true to modify the enemy speed before the instantiation, I put those if statements in the start function because I think than when an enemy is instantiated is when it is called.
void Start()
{
spawnManager = GameObject.Find("Spawn Manager").GetComponent<SpawnManager>();
if (spawnManager.toughEnemy)
{
speed = 1;
print("Speed " + speed);
}
else
{
speed = 0;
print("Speed " + speed);
}
}
The problem is, when the random number is 0 in the logs i see this...
random number:0
Tough (the i in the for loop is 0)
Weak (the i in the for loop is 1)
Weak (the i in the for loop is 2)
speed 0
speed 0
speed 0
And what I was expecting was something like below, because I'm modifying the variable in the SpawnManager script first before instantiating the enemy.
random number:0
Tough (the i in the for loop is 0)
speed 1
Weak (the i in the for loop is 1)
speed 0
Weak (the i in the for loop is 2)
speed 0
What am I missing here?
Timing. You’re partly correct thinking that Start will be called when the object is instantiated. But, it will be called when the next frame starts. In your current loop, you’re setting up the objects to be instantiated, then you set the toughEnemy to true. When the next frame starts, all the enemies think that a tough enemy has been set, and the output you see is correct.
If you want the manager to control the enemies, I’d personally include something like a Setup method, called by the manager. For example, in the Enemy script:
public bool isSetup { get; private set; }
public bool isTough { get; private set; }
void Setup(bool tough)
{
if ( isSetup ) return;
isSetup = true;
isTough = tough;
speed = tough ? 1 : 0;
}
Then, when you instantiate the enemy in your manager, do something like:
for (int i=0; i<enemiesToSpawn; i++)
{
var enemy = Instantiate( enemyPrefab, GenerateSpawnPosition(), enemyPrefab.transform.rotation );
var e = enemy.GetComponent<Enemy> ( );
if ((randomNumber < 5) && toughEnemy)
{
print("Tough");
toughEnemy = false; //I make sure there is only one tough enemy per wave
e.Setup(true);
}
else
{
print("Weak");
e.Setup(false);
}
}
Here’s the life cycle of a ‘frame’
Notice that the Update method processing occurs about in the middle of the frame lifecycle. You’re Instantiating 3 objects in the same Update method. THEN … the frame ends, and at the beginning of the next frame, the Start event for each of the new objects is triggered.
I would do this by putting the toughEnemy flag in the Enemy script itself. I would then add a SetTough() method to Enemy, which I would call from SpawnManager via GetComponent() after the Enemy is instantiated.

Instantiate a Object below another object instead of above

First of all, i created a GIF to show what is currently happen.
GIF with my current problem
and
Awhat I want
I have a List of GameObject which add the bodyParts temp and Instantiate it in the correct time and position.
Now this is working like expected, but i want this new bodyParts below another object instead of above.
As you can see the Head is "under" the new body parts, but it should always on Top and every new part should spawn under the next. (only should looks like! I dont want to change the Z position.)
i tried :
bodyParts.transform.SetAsFirstSibling();
to change the Hierarchy, but this do nothing. I also can drag and drop the Clones to a other position in Hierarchy but they just stay at the same position (above another).
Is this possible and what should i have to do?
Here some of my Code which makes the process:
private void CreateBodyParts()
{
if (snakeBody.Count == 0)
{
GameObject temp1 = Instantiate(bodyParts[0], transform.position, transform.rotation, transform);
if (!temp1.GetComponent<MarkerManager>())
temp1.AddComponent<MarkerManager>();
if (!temp1.GetComponent<Rigidbody2D>())
{
temp1.AddComponent<Rigidbody2D>();
temp1.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp1);
bodyParts.RemoveAt(0);
}
MarkerManager markM = snakeBody[snakeBody.Count - 1].GetComponent<MarkerManager>();
if (countUp == 0)
{
markM.ClearMarkerList();
}
countUp += Time.deltaTime;
if (countUp >= distanceBetween)
{
GameObject temp = Instantiate(bodyParts[0], markM.markerList[0].position, markM.markerList[0].rotation, transform);
if (!temp.GetComponent<MarkerManager>())
temp.AddComponent<MarkerManager>();
if (!temp.GetComponent<Rigidbody2D>())
{
temp.AddComponent<Rigidbody2D>();
temp.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp);
bodyParts.RemoveAt(0);
temp.GetComponent<MarkerManager>().ClearMarkerList();
countUp = 0;
}
}
Finally i found the working Solution.
It has nothing to do with which hierarchy order GameObjects spawn in.
Just the Layer and the LayerOrder are responsible for it.
So I give my parent object a specific layer name (manually in the inspector under "Additional Settings" or programmatically)
I chose the programmatic way...
Any newly spawned GameObject that is Child would get a lower number
yourGameObject.GetComponent<Renderer>().sortingLayerID = SortingLayer.NameToID("Player");
yourGameObject.GetComponent<Renderer>().sortingOrder = -snakeBody.Count;

Unity Question: How Can I Switch From One prefab To Another With GetAxis?

I want to bind the up and down arrow keys to cycle through different sprites upon being pressed. If one end is reached, it would loop back to the first sprite. I've tried using the following code:
public class PhaseChanger : MonoBehaviour
{
// saved for efficiency
[SerializeField]
public GameObject prefabMoon0;
[SerializeField]
public GameObject prefabMoon1;
[SerializeField]
public GameObject prefabMoon2;
[SerializeField]
public GameObject prefabMoon3;
[SerializeField]
public GameObject prefabMoon4;
// needed for new phase
GameObject currentPhase;
bool previousFramePhaseChangeInput = false;
/// <summary>
/// Start is called before the first frame update
/// </summary>
void Start()
{
currentPhase = Instantiate<GameObject>(prefabMoon0);
}
/// <summary>
/// Update is called once per frame
/// </summary>
void Update()
{
// change phase on up arrow or down arrow
if (Input.GetAxis("ChangePhase") > 0)
{
// only change phase on first input frame
if (!previousFramePhaseChangeInput)
{
previousFramePhaseChangeInput = true;
// Save current position and destroy current phase
Destroy(currentPhase);
// instantiate next phase
if (currentPhase = prefabMoon0)
{
currentPhase = Instantiate(prefabMoon1);
}
else if (currentPhase = prefabMoon1)
{
currentPhase = Instantiate(prefabMoon2);
}
else if (currentPhase = prefabMoon2)
{
currentPhase = Instantiate(prefabMoon3);
}
else if (currentPhase = prefabMoon3)
{
currentPhase = Instantiate(prefabMoon4);
else
{
// no phase change input
previousFramePhaseChangeInput = false;
}
}
}
}
When I attach the script to my main camera and run it, I'm able to make a single change with the up arrow, and then nothing else happens on subsequent presses.
I feel like I'm really close to making this work, but I also may being doing the whole thing inefficiently. Help would be much appreciated, thanks!
Also: I know I said sprites in my post and am sharing a script that calls on prefabs. I didn't know how to approach this using just the sprites without making a prefab for each. Is it possible to do this without separate prefabs for each sprite?
Problems
First of all you are using assignments
currentPhase = XY
where you should be using
currentPhase == XY
The reason why it still compiles is the implicit conversion operator for UnityEngine.Object -> bool. Basically your assigning equals writing
currentPhase = XY;
if(currentPhase)
It won't work like this either way because you are using Instantiate to create a new clone of a prefab which will of course have a different reference than the original prefab it was cloned from.
So even if your checks where looking like
if(currentPhase == XY)
they will ever be true.
Solution
Instead of checking for reference equality I would rather store all prefabs/instances in an array
public GameObject[] phases;
and then simply have an int index for this array so you can simply move to the next element from the array by increasing the index.
private int currentPhase;
And you can increase it and make it wrap around using e.g.
currentPhase = (currentPhase + 1) % phases.Length;
so it will always grow from 0 up to phases.Length - 1 and then start over from 0 again.
And then I don't know the exact requirements of your use case but I would suggest to rather not all the time use Instantiate and Destroy but rather have already all the objects as instances under your object and just (de)actívate them!
you could do this like e.g.
public GameObject[] phases;
private int currentPhase;
private void Awake ()
{
Init();
}
private void Update ()
{
if (Input.GetAxis("ChangePhase") > 0)
{
if (!previousFramePhaseChangeInput)
{
previousFramePhaseChangeInput = true;
NextPhase();
}
}
else
{
previousFramePhaseChangeInput = false;
}
}
// Disables all phases except the first one and sets the current index to 0
private void Init()
{
for(var i = 1; i < phases.Length; i++)
{
phases[i].SetActive(false);
}
phases[0].SetActive(true);
currentPhase = 0;
}
// Disables the current phase and enables the next one
// wraps around at the end of the array
public void NextPhase()
{
phases[currentPhase].SetActive(false);
// increase the counter and wrap around at the end of the array
currentPhase = (currentPhase + 1) % phases.Length;
phases[currentPhase].SetActive(true);
}
If you still want to Instantiate the objects because having them already in the scene is no option (for whatever reason) you could do it right before calling Init like e.g.
public GameObject[] phasePrefabs;
private GameObject[] phases;
private void Awake ()
{
var amount = phasePrefabs.Length;
phases = new GameObject [amount];
for(var i = 0; i < amount; i++)
{
phases[i] = Instantiate(phasePrefabs[i]);
}
Init();
}
Though as said I would prefer to already have them right away as this is way less error prone ;)

Prefab Instantiate performance improvment

so I have a multiple category buttons in every category there is more than 20 items. So make prefab and instantiate when button is pressed. The problem is When I instantiate prefab I have to initialize it with info like name, sprite, btn click event etc. this will take time to instantiate prefabs and game hangs. Here is code
for (int i = 0; i < prefab.Length; i++)
{
GameObject go = Instantiate(basePrefab) as GameObject;
go.SetActive(true);
go.transform.SetParent(prefabParent, false);
go.transform.localScale = Vector3.one;
go.GetComponent<Category>().Init(prefab[i]);
}
This code is called in button click. Alternatively to achieve some performance in start method I already instantiate some basePrefab on a empty gameobject and on button click just change parent and initialize them. this approach will give some benefits but still game is hangs for 2 sec here is that code
for (int i = 0; i < prefab.Length; i++)
{
GameObject go = InstantiatedGameObject[i];
go.SetActive(true);
go.transform.SetParent(prefabParent, false);
go.transform.localScale = Vector3.one;
go.GetComponent<Category>().Init(prefab[i]);
}
Any solution to improve performance ??
So Here is About prefab contenet: Basically prefab contains Background image, Mask image in this image Catgory image, Selection image, and
a Text. In init function the category image change and Text value change Also Background contains button in init set this button's click event
If you know maximum quantity of categories and category items you don't have to instantiate them. You can use slots that you activate and deactivate based on the amount of items. During activation you can also bind and unbind them to objects which you can use to assign correct sprites and texts to your button elements.
If you don't know the quantity of category items in advance you can still do the same but using pagination which can be preferable from user perspective as well if there's a lot of items.
public class SomeUI : MonoBehavior
{
public GameObject categorySlotsParent;
public GameObject categoryItemSlotsParent;
public Category[] categories;
CategorySlot[] categorySlots;
CategoryItemSlot[] categoryItemSlots;
public void Awake(){
categorySlots = GetComponentsInChildren<CategorySlot>();
categoryItemSlots = GetComponentsInChildren<CategoryItemSlot>();
AssignCategories(categories);
if(categories.Length > 0 && categories[0] != null)
SelectCategory(categories[0]);
}
void AssignCategories(Category[] categories){
for(int i = 0; i < categorySlots.Length; i++){
if(i < categories.Length)
{
categorySlots[i].SetActive(true);
categorySlots[i].AssignCategory(categories[i]);
}
else
{
categorySlots[i].SetActive(false);
categorySlots[i].AssignCategory(null);
}
}
}
void AssignCategoryItems(CategoryItem[] categoryItems){
for(int i = 0; i < categorySlots.Length; i++)
{
if(i < categoryItems.Length)
{
categoryItemSlots[i].SetActive(true);
categoryItemSlots[i].AssignCategoryItem(categoryItems[i]);
}
else
{
categoryItemSlots[i].SetActive(false);
categoryItemSlots[i].AssignCategoryItem(null);
}
}
}
void SelectCategory(Category category){
AssignCategoryItems(category.categoryItems);
}
}
If your instantiated object is a common object , you can improved it pooling the prefab avoiding to instantiate & destroy this object each time. You have a good example of workflow here

spawn obstacles c# and accessing from another script

I'm Getting a error when i try to instantiate in the manager class. saying
Error CS1061: Type UnityEngine.Object' does not contain a definition forGetComponent' and no extension method GetComponent' of typeUnityEngine.Object' could be found. Are you missing an assembly reference? (CS1061) (Assembly-CSharp)
Add this to your Obstacle class:
void Start()
{
manager = GameObject.FindWithTag("ObstacleManager").GetComponent<ObstacleManager>();
}
The tag obviously has to be the tag of the gameobject the manager is attached to.
Also: Always start class names with a capital letter (I did that in the snippet, keep that in mind, you will get an error right know with that).
Maybe you want to actually change your spawning a bit though. Have two lists, one for free spawnpoints and one for occupied. When you destroy an obstacle, pass the position to the spawning function to move the position to the free list.
Edit:
Another option to create the reference is to set it in your ObstacleManager on spawning. You need to grab a reference to the instantiated obstacle for this. I believe this should work without actually grabbing the obstacle gameobject, but you could do that too.
Obstacle obs = ((GameObject)Instantiate(TypeOfObstacles[j], pointsAvailiable[pointsIndex].position, Quaternion.identity)).GetComponent<Obstacle>();
obs.SetManagerReference(this);
And in Obstacle add
public void SetManagerReference(ObstacleManager obsManager)
{
manager = obsManager;
}
For the free position you can do something like this:
// in Obstacle.cs
public void OnMouseDown()
{
manager.SpawnNewObstacle(transform.position); // you might be able to actually pass the transform, but I'm not sure if it will get destroyed before used in the other function
Destroy(gameObject);
}
In the Manager:
public int noOfObsacles;
public float[] xPercent;
public GameObject[] TypeOfObstacles;
float y;
// to keep track of which spawn points are free and which aren't use these lists
private List<Transform> freePositions;
private List<Transform> occupiedPositions;
private void Start()
{
freePositions = new List<Transform>(spawnPoints);
occupiedPositions = new List<Transform>();
SpawnObstacles();
}
private void SpawnObstacles()
{
// just use this for initial obstacles
// call Spawn as often as needed
for(int i = 0; i < noOfObstacles; i++)
{
Spawn();
}
}
// you call this function from the obstacle that gets destroyed
public void SpawnNewObstacle(Vector3 freePos)
{
// find the spawnpoint in the occupied points
// and move it to the free ones since the obstacle got destroyed
for(int i = 0; i < occupiedPositions.Count; i++)
{
if(occupiedPositions[i].position == freePos)
{
freePositions.Add(occupiedPositions[i]);
occupiedPositions.RemoveAt(i);
break;
}
}
// and call Spawn
Spawn();
}
private void Spawn()
{
y = Random.value;
int pointsIndex = Random.Range (0, freePositions.Count);
for (int j =0; j<xPercent.Length; j++)
{
if ( y < xPercent[j])
{
// these 4 lines are essential for the spawning
Obstacle obs = ((GameObject)Instantiate(TypeOfObstacles[j], freePositions[pointsIndex], Quaternion.identity).GetComponent<Obstacle>();
obs.SetManagerReference(this);
occupiedPositions.Add(freePositions[pointsIndex]);
freePositions.RemoveAt(pointsIndex);
break;
}
}
}
had a bracket issue! my bad
obstacle obs = ((GameObject)Instantiate(TypeOfObstacles[j], freePositions[pointsIndex].position, Quaternion.identity)).GetComponent();

Categories