.SendMessage Crashes Unity C# - c#

I am using a OverlapSphere to detect all colliders within a certain radius of my object. I then filter out a few I don't care about. With the remaining few, I attempt to send a message to those objects to update their render color. Whenever it sends the message, unity freezes. I tried to do some research and the best thing i could find is that infinite loops can freeze it. But i don't see a potential for that. Here is the code:
Object to send the message:
void sendmyMessage(bool status)
{
Collider[] tiles = Physics.OverlapSphere(gameObject.transform.position, 10);
int i = 0;
while (i < tiles.Length)
{
if(tiles[i].tag == "Tile")
{
//turn light on
if (status == true)
{
tiles[i].SendMessage("Highlight", true);
i++;
}
//turn light off
if (status == false)
{
tiles[i].SendMessage("Highlight", false);
i++;
}
}
}
}
Object Receiving Message:
void Highlight(bool status)
{
//turn light on
if(status == true)
{
gameObject.GetComponent<Renderer>().material.color = new Color(0, 0, 0);
}
//turn light off
if(status == false)
{
gameObject.GetComponent<Renderer>().material.color = new Color(1, 1, 1);
}
}
Any help is much appreciated!

It freezes because of logic if(tiles[i].tag == "Tile") here's your answer. Now imagine that object that you collide with has tag "not a tile"? then the loop never ends.
foreach(var tile in tiles) {
if (tile.tag == "Tile") {
tiles[i].SendMessage("Highlight", status);
}
}

while (i < tiles.Length)
{
if(tiles[i].tag == "Tile")
{
//snip
}
// else - loop forever?
}
Here's your problem. If the tag != "Tile" then you never increment i.

Related

my c# code works when i replace obj.SetActive(true) with Debug.Log("hit"); Why?

I am trying to make it so when you get in a certain range of the GUI it activates. It was working perfectly when I was testing with Debug.Log("hit") however I changed it to obj.setActive and it stops working. I don't understand why this is?
here's the code:
private void Update()
{
if (IsInRange())
{
canvas.SetActive(true);
} else
{
canvas.SetActive(false);
}
}
private bool IsInRange()
{
origin = canvas.transform.position;
direction = canvas.transform.forward;
Collider[] hitColliders = Physics.OverlapSphere(origin, range, playerLayer);
foreach (var hitCollider in hitColliders)
{
if (hitCollider.name == player.name)
{
playerInRange = true;
playerInRangeF1 = true;
}
else
{
count += 1;
}
}
if (count != hitColliders.Length)
{
return true;
} else
{
return false;
}
}
I have finally figured out the answer to my problem. The script I was running was a component of the game object I was trying to hide and show. When you hide a game object it obviously stops all of its components from working. All I had to do was place the script on an object that would not be hidden.

Take the full path waypoints of a railway

I'm developing a Train Valley game, just like 2D. The construction of the railway is different. I have a grid and I can take a rail and put it somewhere in the grid and so the railway is built. The main problem I am facing is that I do not know how to create the way for the train to follow .... each sine has a point, this point I add in a Vector3 type list, but if I jump over a cell and put there sine , then go back to the skipped cell and put the rail there, then the list will no longer be sorted.
I'm attaching code, but it's a lot. In general, if anyone has any idea how to build this path, the train will run correctly by all means.
void Start()
{
foreach (TrainCell cell in Grid)
cell.gameObject.AddComponent<TrainCellMouseEvent>().MouseEvent += OnCellClickEvent;
MouseEventProccessor.Instance.captureMouseMouveEvents = true;
StartCoroutine(SpawnStations());
}
private void OnCellClickEvent(TrainCell target, MouseEventType type)
{
if (type == MouseEventType.CLICK)
{
if (canDestroy)
{
if ((int)target.CurrentChildIndex != 0)
{
target.CurrentChild.GetComponent<PolygonCollider2D>().enabled = false;
target.setCurrentChildIndex(0);
Instantiate(BoomFX, target.transform.position, Quaternion.identity);
target.used = false;
WayPoints.Remove(target.transform.position);
if (BuildDone != null)
BuildDone(price, false);
}
foreach (TrainCell cell in Grid)
{
if (!cell.underBuilding)
{
if (cell.CurrentChildIndex == 0 && cell.used)
cell.used = false;
}
}
return;
}
if (rail.SelectedRail == null || target.used || outOfGridBounds)
return;
int railIndex = (int)rail.SelectedRail.GetComponent<ObjectSequence>().CurrentChildIndex;
target.setCurrentChildIndex(railIndex + 1);
target.CurrentChild.GetComponent<PolygonCollider2D>().enabled = true;
MouseEventProccessor.Instance.captureMouseMouveEvents = true;
SpriteRenderer render = target.CurrentChild.GetComponent<SpriteRenderer>();
render.color = Vector4.one;
target.used = true;
if (BuildDone != null)
BuildDone(price, true);
if (target.CurrentChild.transform.childCount == 0)
WayPoints.Add(target.transform.position);
else
{
for (int i = 0; i < target.CurrentChild.transform.childCount; i++)
WayPoints.Add(target.CurrentChild.transform.GetChild(i).transform.position);
}
}
else if (type == MouseEventType.OVER)
{
if (mainLevel.isHammer)
{
if (target.CurrentChildIndex != 0)
target.CurrentChild.GetComponent<SpriteRenderer>().color = Color.red;
return;
}
if (rail.SelectedRail == null || target.used)
return;
foreach (TrainCell cell in Grid)
{
if (cell.CurrentChildIndex != 0)
{
if (cell != target)
{
if (cell.CurrentChild.GetComponent<SpriteRenderer>().color != Color.white && !cell.used && !cell.underBuilding)
{
if (cell.GetComponentInChildren<ObjectSequenceNumberDisplay>() != null)
Destroy(GetComponentInChildren<ObjectSequenceNumberDisplay>().gameObject);
cell.setCurrentChildIndex(0);
}
}
}
}
int railIndex = (int)rail.SelectedRail.GetComponent<ObjectSequence>().CurrentChildIndex;
target.setCurrentChildIndex(railIndex + 1);
RenderColor(target);
}
else if (type == MouseEventType.EXIT)
{
if (target.CurrentChildIndex != 0)
target.CurrentChild.GetComponent<SpriteRenderer>().color = Color.white;
if (shapePrice != null)
Destroy(shapePrice.gameObject);
if (target.used)
return;
target.setCurrentChildIndex(0);
}
}
As far as I suppose based on your code I'll try to answer your question based on the following statements:
You have a station where you generate the train
You define a direction for the train to follow from the station (left, right, up, down) depending on the railway design
Having that statements I propose the following solution:
Create a method that takes the station point as the origin and the starting direction from that point. Having that you start following the railway, first on that direction and after that you search in 4 directions for more rails, adding all of them to the Vector3 array. This way you will have the array sorted.
Example:
private void SortedRailway(Vector2 origin, Vector2? direction = null, Vector2? lastDirection = null){
if(direction.HasValue){
if(origin + direction is TrainCell){
WayPoints.add(origin + direction);
SortedRailway(origin + direction, null, direction);
}
//(else will be used if direction (just used for the initial one) is wrong, so error there)
}else{
if(origin + Vector2.up is TrainCell && Vector2.up != lastDirection){
WayPoints.add(origin + Vector2.up);
SortedRailway(origin + Vector2.up, null, Vector2.down); //lastDirection is always the opposite to the matching one
}
//... (Repeat for all 4 directions. If none fits, then we asume is the end of the railway)
}
}
Hope this helps you!

Tiles not following input in a branching board game path

I'm making a board game in Unity with multiple branching paths where the players can decide which one to follow. (Think the Mario Part game board) But the player input on which path to follow isn't working.
So I have a script that has the tokens follow a set path and (Linked List for the path) with a for loop to have the tokens move their allocated amount. Within the loop, I have an If statement that checks how many paths the tile has and if there are more than one if runs a separate script that pauses the game and offers a menu prompt for the players to pick one path or the other.
I've tried tossing in a few contitions for continuing the game within the loop but they either ignore the player input, are an input behind or just break the code.
`for (int i = 0; i < spacesToMove; i++)
{
if (final_Waypoint == null)
{
final_Waypoint = Starting_Waypoint;
}
else
{
if (final_Waypoint.Next_Waypoint == null || final_Waypoint.Next_Waypoint.Length == 0)
{
//we are overshooting the final waypoint, so just return some nulls in the array
//just break and we'll return the array, which is going to have nulls at the end.
Debug.Log(Current_Waypoint);
break;
}
else if (final_Waypoint.Next_Waypoint.Length > 1) // && if playerID == 0 so it only appears for players.
{
menu.Pause_for_Choice();
//if (menu.ButtonPress == true)
final_Waypoint = final_Waypoint.Next_Waypoint[menu.choice_int];
//***There's a bug here. It calls menu as per normal, and the game pauses.
//But when I click 'no', choice_int isn't updated first: this function finishes first,
//so this function gets old choice_int. THEN the button function executes.
Debug.Log("test 3 " + menu.choice_int);
}
else
{
final_Waypoint = final_Waypoint.Next_Waypoint[0];
}
}
listOfWaypoints[i] = final_Waypoint;
Debug.Log("i:" + i);
}
return listOfWaypoints;
}`
`{
public GameObject choice_Menu;
public bool isPaused = true;
public int choice_int=0;
public int choice_placeholder = 0;
public void Pause_for_Choice()
{
isPaused = true;
choice_Menu.SetActive(true);
Time.timeScale = 0;
}
public void Yes()
{
choice_placeholder = 0;
UnPause_Game();
}
public void No()
{
choice_placeholder = 1;
UnPause_Game();
}
public void UnPause_Game()
{
isPaused = false;
choice_Menu.SetActive(false);
Time.timeScale = 1;
}
}`

Game objects scaling up when transform.parent set to null

I'm working on a small game where objects are put in a boat, then a key press makes the boat "sail".
To move all the objects that are standing on the boat, i am setting the parent of each object to an empty guide object in the boat then changing the position of the boat. (I have also tried parenting the objects into the boat object itself)
The following is a script applied to the boat object.
variables set in the BoatScript class:
public class BoatScript : MonoBehaviour {
public List<string> boatList;
public KeyCode interact;
public GameObject tempObject;
public string whichSide;
public string direction;
public bool canSail;
}
Start and Update method:
void Start () {
canSail = false;
whichSide = "start";
direction = "toFinish";
speed = 0f;
}
void Update () {
if (canSail == true)
{
SetSail();
}
if (boatList.Contains("FARMER") && whichSide == "start" && Input.GetKeyDown(interact))
{
speed = 0.5f;
CharacterCheck();
}
else if (boatList.Contains("FARMER") && whichSide == "finish" && Input.GetKeyDown(interact))
{
speed = -0.05f;
CharacterCheck();
}
}
Here are my OnTrigger methods:
void OnTriggerEnter(Collider other)
{
Debug.Log(other.gameObject.name + " collided with " + gameObject.name);
promptText.text = "";
if(CheckValidObject(other.gameObject.name) == true) {
boatList.Add(other.gameObject.name);
logBox.text = logBox.text + "\nThe " + other.gameObject.name + " is in the boat";
}
if (other.gameObject.name == "FARMER")
{
promptText2.text = "Press E to set sail";
}
}
void OnTriggerExit(Collider other)
{
boatList.Remove(other.gameObject.name);
logBox.text = logBox.text + "\nThe " + other.gameObject.name + " has left the boat";
promptText.text = "";
if (other.gameObject.name == "FARMER")
{
promptText2.text = "";
}
}
Setting sail:
void SetSail()
{
promptText.text = "";
promptText2.text = "";
addParents();
if (whichSide == "sailing" && direction == "toFinish")
{
speed = 0.05f;
gameObject.transform.Translate(speed, 0, 0);
}
else if (whichSide == "sailing" && direction == "toStart")
{
speed = -0.05f;
gameObject.transform.Translate(speed, 0, 0);
}
else if (whichSide == "start" || whichSide == "finish")
{
gameObject.transform.Translate(speed, 0, 0);
removeParents();
}
}
void addParents()
{
foreach(string o in boatList)
{
GameObject obj = GameObject.Find(o);
obj.GetComponent<Rigidbody>().useGravity = false;
obj.GetComponent<Rigidbody>().isKinematic = true;
if (obj.name == "FARMER") { obj.transform.parent = playerGuide.transform; }
else {obj.transform.parent = itemGuide.transform; }
}
}
void removeParents()
{
foreach (string o in boatList)
{
GameObject obj = GameObject.Find(o);
obj.GetComponent<Rigidbody>().useGravity = true;
if(obj.name != "FARMER") {obj.GetComponent<Rigidbody>().isKinematic = false; }
obj.transform.parent = null;
}
}
The problem: Once the boat reaches and hits the collider for the other side, the boat stops as expected but the objects that were just removed from the parent begin to scale up continuously like this:
e.g 1 https://i.gyazo.com/d35ae729757b8e71c25fd1b4a3857dae.mp4
e.g 2 https://i.gyazo.com/80637919bfd114a42d187300b7faef25.mp4
I'm not too sure what is causing this. Any help is much appreciated, thank you.
Instead of setting the parent via transform.parent, use transform.SetParent(targetTransform, false);. The second, bool, parameter determines if the game object's transform will maintain it's position, orientation, and scale. By setting it to false, the transform will maintain it's current values, while setting it to true will modify the position, orientation, and scale to maintain the world position. You can check this for further info transform.SetParent
Are youu sure it is scaling up and not moving up in the Z axes? From what im looking it is going towards the camera but not scaling up. You should debug the position and scale in the update method to see whats really happening there.
Below comment: "Well then you will have to debug it more carefully, i would first try, setting canSail to false as soon as it reaches the end. Perhaps the method addParent which is always being executed is wrong, what does the object itemGuide does? edit: i just seen the second video, from my perspective that seems gravity, what you meant with problems with scaling up is because its moving out from the boat?"
Solution:
void SetSail()
{
promptText.text = "";
promptText2.text = "";
addParents();
if (whichSide == "sailing" && direction == "toFinish")
{
speed = 0.05f;
gameObject.transform.Translate(speed, 0, 0);
}
else if (whichSide == "sailing" && direction == "toStart")
{
speed = -0.05f;
gameObject.transform.Translate(speed, 0, 0);
}
else if (whichSide == "start" || whichSide == "finish")
{
gameObject.transform.Translate(speed, 0, 0);
canSail = false; //Adding this line solves the issues, must have been affecting transform properties to child objects.
removeParents();
}
}

Unity timer issue

I'll explain what I'm trying to do first of all.
I have an object with a material attached to it. From my camera I have a ray being projected and when it collides with my object I'm wanting the material of that object to be placed onto another, second object. This is what I have working.
Now I am trying to expand this by having the user hold the ray over the object for a protracted period of time before the material is taking off object 1 and placed on object 2. And I'm having some issues.
When my timers gets close to the allotted time, my ray seems to stop hitting it and the highlight (which appears when the ray touches it object) disappears. Then when I look away and back at the object, instead of resetting the time, the material instantly appears on my other object. So I think I might not be resetting my timer properly.
Infact I'm positive this is to do with my timer as even when my ray isn't hitting anything, it seems to trigger my change material event.
Could someone take a look at my code and tell me?
method which checks what object has been hit with a ray and casts its material onto the correct object
void CastRay()
{
if(hit.collider.gameObject.tag == "Colour1" && change_material == true)
{
new_colour1.ChangeObjectMaterialColour(hit.collider.gameObject.renderer.material.color);
var colums = GameObject.FindGameObjectsWithTag("column");
foreach( GameObject c in colums)
c.GetComponent<MeshRenderer>().materials[1].color = new_colour1.orignalMaterial;
}
}
void ResetTimer()
{
start_time = Time.time;
running_time = 0f;
track_timer = false;
}
Code that cast the highlights the object my ray has hit:
void HighLight(GameObject nextHitObject)
{
// Case1: Last ray and new ray both hit objects
if(lastHitObject != null && nextHitObject != null)
{
//1a: same objects, do nothing
if(lastHitObject.tag == nextHitObject.tag)return;
{ //1b: different objects, swap highlight texture
lastHitObject.renderer.material = oldMat;
lastHitObject = nextHitObject;
oldMat = lastHitObject.renderer.material;
lastHitObject.renderer.material = highlight_material;
track_timer = true;
return;
}
}
// Case2: Last ray hit nothing, new ray hit object.
if(lastHitObject == null && nextHitObject != null)
{
ResetTimer();
lastHitObject = nextHitObject;
oldMat = lastHitObject.renderer.material;
lastHitObject.renderer.material = highlight_material;
track_timer = true;
if(spawned_amount == 0)
{
StatusBar();
}
return;
}
// Case3: Last ray hit something, new ray hit nothing
if(lastHitObject != null && nextHitObject == null)
{
lastHitObject.renderer.material = oldMat;
lastHitObject = null;
track_timer = false;
return;
}
else
{
spawned_amount = 0;
Destroy (GameObject.FindWithTag("StatusBar"));
change_material = false;
}
}
Timer code
void Check(bool updateTimer)
{
if(updateTimer)
{
start_time = Time.time - end_time;
running_time += Time.deltaTime;
if ( running_time >= end_time )
{
track_timer = false;
}
}
else
end_time = Time.time;
}
My last method that should be set my change material bool to true once start_time is over 4 seconds.
void StatusBar()
{
if(start_time >= 4)
{
spawned_amount = 0;
Debug.Log("status bar function");
change_material = true;
Debug.Log("chaging amterial");
}
else
{
change_material = false;
ResetTimer();
Debug.Log("resetting timer");
}
}
Try working with this timer functionality instead:
float? start_time;
void ResetTimer()
{
start_time = null;
}
bool IsTimerDone()
{
if (start_time == null)
start_time = Time.time;
else if ((Time.time - start_time) >= 4)
{
ResetTimer();
return true;
}
return false;
}
It stores your data in a single float? start_time. If the value is null, you aren't timing anything. If it has a value, the timer is active. To start it or check on it, you use IsTimerDone(): the first time it is called 4 or more seconds after it starts, it will return true and reset the timer.

Categories