Assigning and method call order - c#

I wrote such a property (representing direction for my XNA game object):
public Vector2 Direction
{
get { return direction; }
protected set
{
(direction = value).Normalize(); // ***
angle = MathHelper.WrapAngle((float)Math.Atan(direction.X / direction.Y));
}
}
set sets two equivalent fields which represent object's orientation in angle and simultaneously in normalized vector.
Starting game failed because line marked by *** failed. It doesn't normalize vector.
I changed this line to:
direction = value;
direction.Normalize();
and it works fine... Why?
I assumed that in line marked by *** first operation is assigning value and then normalising direction. But it's not true.
___ ___ ____
Normalize() is method from Vector2 class.
//
// Summary:
// Turns the current vector into a unit vector. The result is a vector one unit
// in length pointing in the same direction as the original vector.
public void Normalize();

I am assuming Vector2 is a struct, or value type, which means it is passed-by-value and not by reference. When you assign value to direction, you are setting direction to a copy of value. Additionally, the object returned by the expression (direction = value) is a copy, and not the same instance that is in direction. You are calling Normalize on an object that is never stored outside of the setter block.
For this same reason you cannot call methods or set properties on a struct returned from a property getter on a class. For instance, if the property from your example is in a class named Monkey, note:
Monkey m = new Monkey();
m.Direction = new Vector2(...);
m.Direction.X = 2; // This will not compile.
m.Direction.Normalize(); // This will not do what you expect.

Related

Set z transform position of an instantiated GameObject sprite in unity

Is there an easy (shorter) way to set the z transform position of an instantiated game object (sprite) in unity? I want to set each instance to 2 for now.
Here's the one line of code I am using, I just wanted to set the z position and it seems cumbersome to do it this way - ignore the first line as I am only including it to demonstrate how the GameObject has been instantiated:
GameObject laser = Instantiate(laserPrefab, transform.position, Quaternion.identity) as GameObject;
laser.transform.position = new Vector3(laser.transform.position.x, laser.transform.position.y, 2);
Thanks in advance!
There is no simpler way of doing this, but you can create method to make your code assigning new position shorter. For example, creating method like this:
Vector3 SetZ(Vector3 vector, float z)
{
vector.z = z;
return vector;
}
Would let you to set new object's position like this
laser.transform.position = SetZ(laser.transform.position, 2);
You have to use method, because trying to do it directly, using property would result in compile time error.
transform.position.z = 2;
Results in error: CS1612: Cannot modify the return value of 'Transform.position' because it is not a variable
There are some extension methods on Vector3 that might make it a bit more easy to read. But I don't think it will get much shorter than what you wrote:
Like Vector3.MoveTowards
https://docs.unity3d.com/ScriptReference/Vector3.MoveTowards.html
But the short answer might be this: No.
A Vector(2/3) is a struct. In this case its made safe for the user by unity and made fully readonly, as you can't really assign values to a struct, because that would result in a new instance of the struct. So you cannot do assignments like laser.transform.position.z = 2. You will always have to create a new Vector.

Passing class' own method using Func<> in C#

I'm doing a project in Unity engine and have 3 functions in a C# class, which are structured the following way:
public Vector3 FunctionOne(Vector3 point)
{
// do some vector operations and return it as var a
return a;
}
public Vector3 Sample(System.Func<Vector3, Vector3> funcOne, Vector3 samplePoint)
{
// sample the resulting vector field from funcOne and return as var b
return b;
}
public Vector3 Trace(Vector3 point)
{
// Even more vector operations, as follows
Vector3 k1 = Sample(FunctionOne(point), point);
// (...)
return k1;
}
Basically I need the Trace() function to call the Sample() function on some variable, but also need the Sample() function to take in different functions as a variable, such as for instance FunctionOne(). I get the following error inside Trace() when doing it like above:
Argument 1: cannot convert from 'UnityEngine.Vector3' to 'System.Func<UnityEngine.Vector3, UnityEngine.Vector3>'
How would I deal with this properly? As far as I can see, the Sample() function both takes in and returns a Vector3, I specify what function for it to take in as well so why doesn't this work? Do I need to pass FunctionOne() to Trace() as well, even though it's within the same class?
I left out most of the meat of the functions as it's really just vector operations and kind of irrelevant to the question.
This?
public Vector3 Sample(System.Func<Vector3, Vector3> funcOne, Vector3 samplePoint)
{
var b = funcOne(samplePoint);
return b;
}
public Vector3 Trace(Vector3 point)
{
// Even more vector operations, as follows
Vector3 k1 = Sample(FunctionOne, point);
// (...)
return k1;
}

Cannot modify the return value of 'ParticleSystem.shape' because it is not a variable

After upgrade my project i got this error." Cannot modify the return value of 'ParticleSystem.shape' because it is not a variable" anyone know what is wrong with the code.
gameObject2.GetComponentInChildren<ParticleSystem>().shape.radius = objectNode2.GameObject.GetComponent<CharacterController>().radius;
You cannot change the values of ParticleSystem.shape because it is not a variable indeed.. it is a get only property. Properties that return a struct don't allow changes to that struct's fields.
The following code would provide the same error.
Vector2 vec;
Vector2 Vec { get { return vec; } }
void ChangeVecX() { Vec.x = 2; }
For more info about the differences between variables and properties, refer to this post: https://stackoverflow.com/a/4142957/9433659
This is how you should do it:
var ps = gameObject2.GetComponentInChildren<ParticleSystem>();
// Copy the value of the property
var newShape = ps.shape;
// Make modifications
newShape.radius = objectNode2.GameObject.GetComponent<CharacterController>().radius;
You would normally have to do the following in a normal property that returns a struct, but Unity does a bit of a special thing for ParticleSystem using Pointers, so you don't have to.
// Assign the new value to the property
ps.shape = newShape;

Why I can not refer an object in Unity?

I want to create a Vector3 type object referring to my player's position so that I can use a shorter name in my script.But I failed to do it. It seems that the new object pointing at other address.
I wrote some code like this:
//PlayerController.cs
vector3 playerPos = transform.position;
Debug.Log(System.Object.ReferenceEquals(playerPos,transform.position));
The output was
false
What caused this and how can I refer to my player's position correctly?
What caused this
Below is a simple explanation.
transform.position is Vector3.
Vector3 is a struct.
Struct is a value type.
It's always good look in the Unity's Documentation to see which type Unity API is. This really matter when working in Unity.
When you do:
vector3 playerPos = transform.position;
New struct is created and values from transform.position; is copied to playerPos variable. Since the variable is value type and copied, performing System.Object.ReferenceEquals on it should return false.
I want to create a vector3-type object referring to my player's
position so that I can use a shorter
In this case, you have to change the Vector3 to Transform.
Transform myPos = null;
void Start()
{
myPos = transform;
}
then you can do:
myPos.position = newPos;
The reason you have to declare the position as Transform instead of Vector3 is because Transform is a class and a class stores the reference of the object.
class is a reference type.
Debug.Log(System.Object.ReferenceEquals(myPos, transform)); should return true because myPos and transform are both classes and the myPos variable is referencing the transform variable.
So, whenever you access your position with myPos.position;, it will give you the newest position of your GameObject which is also (transform.position) since the myPos variable is a class that has a reference of transform which is also a class.
Assuming that myPos is struct or declared as vector3 myPos instead of Transform myPos, then the myPos variable would hold the copy value of transform.position when it was assigned and will never return the latest position of your GameObject since it is a copy.
You can learn more about class vs struct here.
Finally System.Object.ReferenceEquals says "ReferenceEquals" not "ValueEquals" so this is even self-explanatory. You use it on a class/referece types not on structs or value types.

Querying A Specific 3D Point Using LINQ

Currently I am designing a method that takes a List<LineSegment> as input and outputs an altered list with the LineSegments all following a clockwise direction. The LineSegment class is essentially as follows:
public class LineSegment
{
Point basePoint;
Point endPoint;
{
with the Points as simple 3D points. My problem is that after I refactor a LineSegment into pointing in the right direction (with basePoint to endPoint being clockwise), I need to find the next line segment in the List<LineSegment>. I understand the theory behind finding it (based on whether we switched around the base and end points, the next segment's base or end point will be one of those), but I am unsure as to how to query it using LINQ. I did some research on using LINQ and came up with something like
LineSegment nextSegment =
listOfSegments.Any(p => p.basePoint.Equals(otherSegment.endPoint));
to return the LineSegment that matches the otherSegment.endPoint criteria, but only to find that .Any() returns a bool.
How can I return the LineSegment that I am searching for instead of a bool?
Use FirstOrDefault like:
LineSegment nextSegment =
listOfSegments.FirstOrDefault(p => p.basePoint.Equals(otherSegment.endPoint));
But this will assume that your Point compares its values and not references.
If you want to compare Point's fields then you can do:
LineSegment nextSegment =
listOfSegments.FirstOrDefault(p => p.basePoint.X == otherSegment.endPoint.X &&
p.basePoint.Y == otherSegment.endPoint.Y &&
p.basePoint.Z == otherSegment.endPoint.Z ));
You can use FirstOrDefault(Func<T, Boolean>).
Returns the first element of the sequence that satisfies a condition or a default value if no such element is found.
LineSegment nextSegment =
listOfSegments.FirstOrDefault(p => p.basePoint.Equals(otherSegment.endPoint));
Also, to check for value equality, you should generally use Equals. However, Equals as it is implemented by Object just performs a reference identity check. It is therefore important, when you call Equals, to verify whether the type overrides it to provide value equality semantics. When you create your own types, you should override Equals. So, I am suggesting to add these methods to your class, if you haven't:
public override bool Equals ( object obj )
{
return Equals(obj as Point);
}
public bool Equals ( Point obj )
{
return obj != null && obj.X == this.X && obj.Y == this.Y && obj.Z == this.Z;
}
You can use First also. But, it will throw Exception if nothing found.
Update1:
If you're expecting a Single record, you can use Single or SingleOrDefault.
I had to do exact same thing 2 weeks ago for drawing my 3D object and linking them. But as other state using FirstOrDefault is not good if you have the same case as me where 1 vertices have multiple end point like a cube can as example the matching set for (0,0,0) i have 3 lines :
(start) - ( end )
(0,0,0) - (15,0,0)
(0,0,0) - (0,92,0)
(0,0,0) - (0,0,32)
I personally had to go with the Where which output the whole matching list
public class LineSeg
{
Point startPoint;
Point endPoint;
{
// vertexOfOrigin is a Point object
// get every line where the point given is the starting point
var fromVertexes = lstLineSeg.Where(line => line.endPoint == vertexOfOrigin).ToList();
// get every line where the point given is the end point
var toVertexes = lstLineSeg.Where(line => line.startPoint == vertexOfOrigin).ToList();

Categories