I have the following problem:
I have a classs that takes a position as a Vector2 in its constructor. Now, in some cases I don't know the position and want to change it at another place in the code. Since you can't make a Vector2 = null in C#, is there any default value for a Vector2 to signalize: "This Vector has no Value"?
This is the constructor in question:
public SoundEvent(SoundEffects sound, Vector2 soundOrigin)
I want to be able to call this constructor either with the SoundEffect only, or with the SoundEffect and position.
This is a better way:
public SoundEvent(SoundEffects sound, Vector2? soundOrigin = null)
Vector2 so;
if (soundOrigin == null)
so = new Vector2(); // default to origin?
else
so = soundOrigin;
The extra variable so guarantees null uses will not throw errors later.
Nullable<Vector2> is what I was looking for.
Related
I am learning C# coming from a PHP background. What is the correct way to handle the problem below?
In PHP, because we are not bound by strict var types, we can return FALSE if a function is unable to return a valid result like so:
function hitPosition(){
$offset = 10;
if ( $this->hitPosition < 100 || is_int($this->hitPosition) ){
return $this->hitPosition + offset;
}
return false;
}
In C# however, we are unable to do so. As seen in the invalid code:
Vector3 MousePositionOnFloor(){
Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit floorHit;
if (Physics.Raycast (camRay, out floorHit, 100f, floorMask)) {
return floorHit.point - transform.position;
}
return null;
}
Note: The code here is only meant to illustrate the problem.
This is the typical example of the issues PHPs strange conventions cause (I know, opinion, but shared by many). Functions should return one (base) type and that's it. With objects it is always possible to return null, but with structs it's different.
There are some ways to handle erroneous situations.
Exceptions. That doesn't really apply here: not hitting the floor is not an exception, it is a normal expected thing.
Return value in an out/ref argument and return the success boolean. This also is a bit cumbersome and the code is clearer if the function just returns what you ask.
Known objects. If the return type is an object, you can have a singleton "null object" that you return on this occasion (like DBNull etc), which you can compare to. This doesn't necessarily work on structs, only objects.
Known values. I assume your Vector3 is floating point values, so naturally it would be simplest to just return NaN values in the coordinates when there is no hit.
So the fourth one is the logical: no hit, no numbers to give out. Of course if you would need more differentiation on why there was no hit found, you would need more complex structure. But since here there are only three options - found, not found, exception - it is quite simple.
So in case of no result being found (assuming XNA Vector3) just return new Vector3(float.NaN) and on the receiving side if (float.IsNaN(vec.X)) ... to determine if there is a valid result or not.
Vector3 MousePositionOnFloor(){
Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit floorHit;
if (Physics.Raycast (camRay, out floorHit, 100f, floorMask)) {
return floorHit.point - transform.position;
}
return null;
}
Ok First This Line
Vector3 MousePositionOnFloor() <- You don't even have a Access Modifier for this so this make it a private as default and cannot be accessed by other classes or other object in your game that Have Scripts. If you are sure you want this to stay like this, make sure that the operation stays inside that script or else you will get an error trying to access it outside.
Vector3 MousePositionOnFloor() <- Again it says It will return a Vector3 so A Vector 3 needs to have a value of 3 pairs of float X,Y,Z but this line
return floorHit.point - transform.position; <- is a Vector3 so this should be Valid.
return null <- This is a Major Problem. Because you said you want to return Vector3 but you're Returning null. At lease change this to a blank Vector3 or do nothing in the calculation. What ever you do, you cannot return Null for Vector3. As you will get Null Exception.
Returning a blank Vector3 is like this return new Vector3(0,0,0); <- Notice there is a 0 added. Cause Unity3D Vector3 Does not allow a nullable value for x y z. Cause Transform Position is an Absolute Rule in Unity3d. ANY OBJECT NEEDS TO HAVE A TRANSFORM POSITION in the GameWorld. When I say Any Object, I mean Any GameObject. That exclude components.
How can we add Vector3 as default parameter for a method? for example:
Void SpawnCube(Vector3 p = new Vector3(0,0,0)){...}
I just tried the line about I got an error:
Expression being assigned to optional parameter `p' must be a constant or default value
I want to customise a function to spawn some game objects that if I did not provide the transform.position, it will go to (0,0,0).
I know this is already answered but I just want to add other ways to do this. Vector3? p and Vector3 bar = default(Vector3) should do it.
public void SpawnCube(Vector3? p = null)
{
if (p == null)
{
p = Vector3.zero; //Set your default value here (0,0,0)
}
}
As htmlcoderexe pointed out,
To use p, you have to use p.Value or cast the p back to Vector3 with ((Vector3)p).
For example, to access the x value from this function with the p variable, p.Value.x, or ((Vector3)p).x.
OR
public void SpawnCube(Vector3 bar = default(Vector3))
{
//it will make default value to be 0,0,0
}
In the general case, you can't. The default arguments are somewhat limited. See this MSDN page.
Each optional parameter has a default value as part of its definition. If no argument is sent for that parameter, the default value is used. A default value must be one of the following types of expressions:
a constant expression;
an expression of the form new ValType(), where ValType is a value type, such as an enum or a struct;
an expression of the form default(ValType), where ValType is a value type.
In the specific case you posted however, I suspect that new Vector3() will be equivelent to new Vector3(0,0,0), so you may be able to use that instead.
If you need a non-zero default value, you may be able to use method overloading instead.
Hi I just ran into this issue where I needed the Vector3 to be optional. But it would keep saying i need a compile time constant. To get around this issue I used this :
public void myMethod(Vector3 optionalVector3 = new Vector3())
{
//you method code here...
}
As a workaround You can overload method.
INSTEAD THIS
void SpawnCube(Vector3 p = new Vector3(0,0,0)){...}
USE THIS
void SpawnCube(Vector3 p)
{
//Implementation
}
//overloaded method without parameter which calls SpawnCube with given default parameter
void SpawnCube()
{
SpawnCube(new Vector3(0,0,0));
}
You've got one implementation of SpawnCube method body and you can use it with or without parameter :)
I've declared the following struct:
struct StartPositions
{
public Vector2 pacman;
public Vector2[] ghosts;
// Constructor accepts a Vector2, and an array of Vector2's.
public StartPositions(Vector2 pacmanPosIn, Vector2[] ghostsPosIn)
{
pacman = pacmanPosIn;
for(int a=0;a<ghostsPosIn.Length;a++)
{
ghosts[a] = ghostsPosIn[a];
}
}
}
However I get a compiler error saying the ghosts field must be fully assigned. What I want to do is pass in a Vector2, and a Vector2 array when I create a StartPositions object - making a copy of that array.
How can I do this correctly?
You did not initialize the ghosts array. You need to add a call to new.
public StartPositions(Vector2 pacmanPosIn, Vector2[] ghostsPosIn)
{
pacman = pacmanPosIn;
ghosts = new Vector2[ghostsPosIn.Length];
....
}
And you can simplify the code by replacing the for loop with a call to Array.Copy().
Array.Copy(ghostsPosIn, ghosts, ghosts.Length);
You have to initialize your ghosts array first:
struct StartPositions
{
public Vector2 pacman;
public Vector2[] ghosts;
// Constructor accepts a Vector2, and an array of Vector2's.
public StartPositions(Vector2 pacmanPosIn, Vector2[] ghostsPosIn)
{
pacman = pacmanPosIn;
ghosts = new Vector2[ghostsPosIn.Length];
for(int a=0;a<ghostsPosIn.Length;a++)
{
ghosts[a] = ghostsPosIn[a];
}
}
}
You didn't initialize ghosts array.
public StartPositions(Vector2 pacmanPosIn, Vector2[] ghostsPosIn)
{
...
ghosts = new Vector2[ghostsPosIn.Length];
...
}
From C# Language Specification;
Actual array instances are created dynamically at run-time using the
new operator. The new operation specifies the length of the new array
instance, which is then fixed for the lifetime of the instance.
One annoying quirk in .net is that unless one is using "unsafe" code the concept of a value-type array does not exist. The struct as shown contains a position for the "pacman" and a reference to a mutable array that holds the positions of the ghosts. This is an evil combination, since the struct may appear to encapsulate the positions of the ghosts, but it does not. Thus, if one were to say:
StartPositions p1 = whatever();
... do some stuff
StartPositions p2 = p1;
p2.pacman.X += 3;
p2.ghosts[0].X += 3;
the code would add three to p2.pacman and p2.ghosts[0]; it would not affect p1.pacman.X but would add three to p1.ghosts[0]. Such behavior would likely cause confusion.
If your intention is that StartPositions will be read-only, it should probably never expose the ghosts array directly; instead, ghosts should be a property of type IList<Vector2>, and your constructor should set it to something like a new ReadOnlyList<Vector2> initialized with a copy of the passed-in positions. If it does that, then ghosts can simply be a read-only property that returns such positions.
I will first illustrate my issue with some code:
class ExampleClass
{
private Vector2 _myVector;
public Vector2 MyVectorProperty { get { return _myVector; } set { _myVector = value; } }
private void MyMethod()
{
_myVector = Vector2.Zero; // Setting to zero
MyVectorProperty.X = 5; //Cannot modify the expression because it is not a variable (returns an error)
_myVector.X = 5; //Works fine!
}
}
As you can see, I am getting the error "Cannot modify the expression because it is not a variable" when trying to change the value of X and Y on the vector using the property. I am unsure why this happens and haven't had any luck looking on the net and i was wondering why this is and how (if) I can fix it?
Another sub question, is it good programming practice to use the public properties or the private/protected fields when working inside the class they belong to?
You should be happy compiler does not let you do so, otherwise you'll be really surprised with result of operation being lost.
MyVectorProperty is property - which means getting the value is call to a function returning the value (something like this.get_MyVectorProperty()).
Since type of the MyVectorProperty is Vector2 which is struct it means that value returned by the get_... function is a copy of value, not reference like it would be in case of normal class.
Changing field X of above copy would simply change X inside of copy of the value, and since that copy of the value is not assigned to anything it will be lost.
Vector2 is a struct (value type), so your property returns the value of _myVector (i.e. a copy) and you can't change that.
I have a method caller addColisionBox and when i call it and setting values to it i get nullpointer at the place im calling it.. I will show some code:
public void addCollisionBox(int x, int y, int arrayNum)
{
//Creating a new rectangle at the x & y cord passed in
rectangle[arrayNum] = new Rectangle(x, y, R_Width, R_Height);
}
And i created inside another class like this:
CollisionHandler collision;
....
//CurrentX and CurrentY position to pass into addCollisionBox method and at the array number i
collision.addCollisionBox(currentX, currentY, i);
And it says in a message box that Visual C# express give out that: "Object reference not set to an instance of an object."
You didn't initalize your collision object. You should have something similar to the following. e.g.
CollisionHandler collision = new CollisionHandler();
...or how ever you are creating/grabbing an instance of your object prior to using it.
You have not created another instance, all you have done is create a variable of a given type.
CollisionHandler collision = new CollisionHandler();
// ^ variable ^ instance of object
You have not created an instance of your CollisionHandler object. Try something like this:
CollisionHandler collision = new CollisionHandler();
....
//CurrentX and CurrentY position to pass into addCollisionBox method and at the array number i
collision.addCollisionBox(currentX, currentY, i);
you need
CollisionHandler collision = new CollisionHandler();
You've just declared the variable but not set it to anything, hence the null reference exception.
I would suggest that collision is null when you're trying to call the addCollisionBox method on it, thereby causing the null dereference. If it definitely has a value at some stage then you're probably deleting it somewhere, but given the code you've pasted it seems more likely that you just need to create an instance of CollisionHandler as it doesn't appear that you're doing so.
CollisionHandler collision = new CollisionHandler();