Game objects scaling up when transform.parent set to null - c#

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

Related

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!

Why Line renderer in unity not updating?

I am trying to draw a line from camera to a instantiated object.I am using the scene UnityARHitTest Example.When I touch on a vertical plane the object gets instantiated and i want to draw a line from camera to the object.When I move my device the line should show from the centre of my camera.For some reason line renderer is not showing when I call it in the Late update.
LineRenderer lins;
public GameObject Lineprefab;
bool HitTestWithResultType (ARPoint point, ARHitTestResultType resultTypes)
{
List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface ().HitTest (point, resultTypes);
if (hitResults.Count > 0 && check==true)
{
foreach (var hitResult in hitResults)
{
Debug.Log ("Got hit!");
if (Select == 0)
{
Debug.Log("hit-zero!");
Instantiate(Instaobj[0], ForSelect);
check = false;
}
if (Select == 1)
{
Debug.Log("hit-one!");
Instantiate(Instaobj[1], ForSelect);
check = false;
}
if (Select == 2)
{
Debug.Log("hit-two!");
Instantiate(Instaobj[2], ForSelect);
check = false;
}
if (Select == 3)
{
Debug.Log("hit-three!");
Instantiate(Instaobj[3], ForSelect);
check = false;
}
if (Select == 4)
{
Debug.Log("hit-four!");
Instantiate(Instaobj[4], ForSelect);
check = false;
}
if (Select == 5)
{
Debug.Log("hit-five!");
Instantiate(Instaobj[5], ForSelect);
check = false;
}
m_HitTransform.position = UnityARMatrixOps.GetPosition (hitResult.worldTransform);
m_HitTransform.rotation = UnityARMatrixOps.GetRotation (hitResult.worldTransform);
Debug.Log (string.Format ("x:{0:0.######} y:{1:0.######} z:{2:0.######}", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z));
obj.StopPlaneTracking();
if (GameObject.Find("debugPlanePrefab(Clone)"))
GameObject.Find("debugPlanePrefab(Clone)").SetActive(false);
else
Debug.Log("no prefab");
//lins.SetPosition(0, m_HitTransform.position);
//lins.SetPosition(1, obj.m_camera.transform.position);
return true;
}
}
return false;
}
When I use lins.setposition() in the above method(which is commented) a line is shown in the output.When I use lins.setposition() in the below LateUpdate() the output is not shown nothing comes.
private void Start()
{
spawngenerator();
}
void spawngenerator()
{
GameObject newline = Instantiate(Lineprefab);
lins = newline.GetComponent<LineRenderer>();
//lins.SetPosition(0, m_HitTransform.position);
//lins.SetPosition(1, obj.m_camera.transform.position);
}
private void LateUpdate()
{
lins.SetPosition(0,obj.m_camera.transform.position );
lins.SetPosition(1,m_HitTransform.position );
}

(Monogame) Update method causes extreme lag

I am currently creating a 2d platformer in monogame.
I created a block that, when hit by the player, starts dissapearing. When it dissapears, i draw a rectangle around it. Every Tile of the same type (BrittleTile) also starts dissapearing, untill the entire connected mob of BrittleTiles has dissapeared.
The problem is that for every BrittleTile that is destroyed, my game runs noticably slower, until it becomes a slideshow after 10 or so BrittleTiles destroyed. I have no idea as to what may cause this, i've been trying to ease the Update method of the class but nothing seems to help.
Any idea as to what may cause this?
class BrittleTile: Tile, IIncludeSound
{
public Rectangle DestroyedCheckRectangle;
private BrittleTile brittle;
private bool _isPlayed;
private bool _hasBroken;
private SoundEffect _sfxBreak;
private SoundEffectInstance _sfxiBreak;
private TimeSpan _breakTimer = new TimeSpan();
public Rectangle BrokenViewRectangle { get; set; }
private bool _isBreaking = false;
public BrittleTile(Texture2D texture, Rectangle baseViewRectangle, Rectangle brokenViewRectangle):base(texture, baseViewRectangle, true )
{
this.BrokenViewRectangle = brokenViewRectangle;
this.ViewRectangle = baseViewRectangle;
}
public void Update(GameTime gameTime, Hero hero, Entity[,] grid)
{
if (!this._hasBroken)
{
if (!this._isBreaking && !this._hasBroken && this.hasCollision)
_checkCollision(hero, grid);
if (this._isBreaking)
{
if (!this._isPlayed)
_sfxiBreak.Play();
this._isPlayed = true;
this._breakTimer += gameTime.ElapsedGameTime;
if (this._breakTimer.TotalMilliseconds < 250)
this.GhostMode(gameTime);
else
{
this._isBreaking = false;
this.hasCollision = false;
this._breakTimer -= this._breakTimer;
}
}
else
{
if (!this.hasCollision && this.DestroyedCheckRectangle.Width == 0)
{
this.DestroyedCheckRectangle.X = this.DestinationRectangle.X - 10;
this.DestroyedCheckRectangle.Y = this.DestinationRectangle.Y - 10;
this.DestroyedCheckRectangle.Height = this.DestinationRectangle.Height + 20;
this.DestroyedCheckRectangle.Width = this.DestinationRectangle.Width + 20;
this._hasBroken = true;
}
}
}
}
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(this.Texture, this.DestinationRectangle, this.BrokenViewRectangle, Color.White);
if (this.hasCollision)
spriteBatch.Draw(this.Texture, this.DestinationRectangle, this.ViewRectangle, Color.White * this.GhostDraw * 0.9f);
spriteBatch.Draw(this.Texture, DestroyedCheckRectangle, new Rectangle(1, 1, 1, 1), Color.Yellow);
Console.WriteLine(this.DestroyedCheckRectangle.X + ", " + this.DestroyedCheckRectangle.Y + ", " + this.DestroyedCheckRectangle.Width + ", " + this.DestroyedCheckRectangle.Height);
}
private void _checkCollision(Hero hero, Entity[,] grid)
{
if (this.DestinationRectangle.Intersects(hero.AttackHitBox))
{
this._isBreaking = true;
}
foreach (Shuriken star in hero.Stars)
{
if (this.DestinationRectangle.Intersects(star.DestinationRectangle))
this._isBreaking = true;
}
foreach (Entity entityObject in grid)
{
if (hasCollision && entityObject.GetType() == typeof(BrittleTile)){
brittle = entityObject.DeepCopy() as BrittleTile;
if (this.DestinationRectangle.Intersects(brittle.DestroyedCheckRectangle))
this._isBreaking = true;
}
}
}
override public void LoadSounds(ContentManager content)
{
this._sfxBreak = content.Load<SoundEffect>("SFX/brittleBreak");
_sfxiBreak = _sfxBreak.CreateInstance();
_sfxiBreak.Volume = 0.2f;
}
}
First, this line:
if (!this._isBreaking && !this._hasBroken && this.hasCollision)
the !this._hasBroken is superfluous.
My first warning sign is this line:
brittle = entityObject.DeepCopy() as BrittleTile;
I assume DeepCopy() makes a new version of the object, and copies all it's properties, right? Seeing the code of that might help pin it down, but on that assumption...
For every Tile, you're cycling through every object in you're grid and you're completely duplicating that object as a BrittleTile, why?
My first change would be to modify that foreach to have this within it:
var brittle = entityObject as BrittleTile
if (brittle != null && hasCollision){
if (this.DestinationRectangle.Intersects(brittle.DestroyedCheckRectangle))
this._isBreaking = true;
}
}
Not sure this is the primary cause of the slowdown, but it is definitely an issue. If you're cloning an object and then immediately throwing an object away, you're almost certainly doing something wrong. If you're cloning an object and you're not editing the properties of that copy at all (and using those edits), you're definitely doing something wrong. I'd be wary before using a method like that unless you're really sure that's what you want.

How can i make the checkboxes to effect also in editore mode and when the game is running?

I have 3 checkboxes
public bool stateForward = false, stateReverse = false, stateRandom = false;
I want that i will be able to chose each time only one checkbox. But also in editor mode and also when the game is running. And when the game is running i want to make it effect on the game.
In the top of the script i added:
[ExecuteInEditMode]
I tried to do in the Start function
void Start()
{
while (true)
{
if (stateForward == true)
{
stateRandom = false;
stateReverse = false;
}
else if (stateReverse == true)
{
stateRandom = false;
stateForward = false;
}
else if (stateRandom == true)
{
stateForward = false;
stateReverse = false;
}
}
anims = GetComponent<Animations>();
waypoints = GameObject.FindGameObjectsWithTag("ClonedObject");
objectsToMove = GameObject.FindGameObjectsWithTag("Robots");
originalPosition = objectsToMove[0].transform.position;
}
But i'm getting on anims: Unreachable code detected
And in the Update function:
void Update()
{
if (MyCommands.walkbetweenwaypoints == true)
{
DrawLinesInScene();
anims.PlayState(Animations.AnimatorStates.RUN);
WayPointsAI();
}
}
And in WayPointsAI
private void WayPointsAI()
{
if (stateForward == true)
{
if (targetIndex == waypoints.Length)
targetIndex = 0;
}
if (stateReverse == true)
{
if (targetIndex == 0)
targetIndex = waypoints.Length;
}
waypoint = waypoints[targetIndex].transform;
float distance = Vector3.Distance(objectsToMove[0].transform.position, waypoint.transform.position);
objectsToMove[0].transform.rotation = Quaternion.Slerp(objectsToMove[0].transform.rotation, Quaternion.LookRotation(waypoint.position - objectsToMove[0].transform.position), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
objectsToMove[0].transform.position += objectsToMove[0].transform.forward * slowDownSpeed * Time.deltaTime;
}
else
{
objectsToMove[0].transform.position += objectsToMove[0].transform.forward * moveSpeed * Time.deltaTime;
}
if (distance < 2)
{
if (stateForward == true)
targetIndex++;
if (stateReverse == true)
targetIndex--;
}
}
First off, you are never going to get out of your Start() method. You have a while(true) loop that you never break out of, so you will be stuck there.
Instead of multiple checkboxes that you can only choose one of, just use an enumeration. Unity will give you a drop down and you can choose one of the three available states.
enum MyStateEnum{ Forward, Reverse, Random }
public MyStateEnum State;
If you want them to be exclusive, you should probably use a single enum field instead of three bool fields. Your enum would have values Forward, Reverse, and Random, while your MonoBehavior would have a single field of your enum type. This ensures that only one will be chosen at any given time, and the editor should show a drop-down for selecting which value rather than a series of checkboxes. This not only fixes the editor UI issue, but will also lead to cleaner code with fewer potential bugs.

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