Is there a reason why I should use TryGetComponent instead of GetComponent - unity3d

I recently found out about TryGetComponent and did some research on it. I found out that main difference is that TryGetComponent "does not allocate in the Editor when the requested component does not exist". I don't really know what that means since I am still new to Unity and why would someone request component that does not exist in the first place, so could somebody explain if I should stop using GetComponent, and if so why?
Thanks in advance.

There might be some reasons to use GetComponent without being sure if this component exists. In this case it is necessary to check if the component is actually there or not.
For example, you have an array of game objects (in this case it is RaycastHit2D array and was aquired from Physics2D.GetRayIntersectionAll method). You need to invoke a specific method from every game object that contains a specific component. You can use GetComponent and check if it equals null or not.
RaycastHit2D[] ray = Physics2D.GetRayIntersectionAll(_mainCamera.ScreenPointToRay(Input.mousePosition));
foreach (RaycastHit2D item in ray)
{
var myClass = item.transform.GetComponent<MyClass>();
if (myClass != null )
{
myClass.MyMethod();
}
}
Or you can use TryGetComponent. In this case you do not need to use GetComponent multiple times or create an additional variable. And the code looks cleaner.
RaycastHit2D[] ray = Physics2D.GetRayIntersectionAll(_mainCamera.ScreenPointToRay(Input.mousePosition));
foreach (RaycastHit2D item in ray)
{
if (item.transform.TryGetComponent(out MyClass myClass))
{
myClass.MyMethod();
}
}
TryGetComponent seems to be more useful in some particular cases, but not always.

Related

#Unity3D - Attach an object to another using a face of the last object as a target and deleting the last one detected

Let's say that I have three simple box-like objects and I want to make different compositions by adding to the first object, already present in the scene, another one and then the other in whatever order i want by pressing a specific key on the keyboard (let's say W key for the Object 2 and S key for the Object 3).
For example:
After that I would like to delete the last present object every time I want by pressing Q key.
For example, I press W,W,S,W,S (Obj2, Obj2, Obj3, Obj2, Obj3).
After, I press Q three times (obtaining the composition Obj2, Obj2 because i destroyed the last three with Q).
And after that I press W one time (obtaining Obj2, Obj2, Obj2).
The modular part is made by a script put in an Empty GameObject(which is inside the Objects 1, 2 and 3.
public class Placement : MonoBehaviour
{
public GameObject shape1, shape2, shape3;
public Counter count;
void Start()
{
count = FindObjectOfType<Counter>();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
count.Array[count.i] = Instantiate(shape3, transform.position, transform.rotation);
this.enabled = false;
}
if (Input.GetKeyDown(KeyCode.S))
{
count.Array[count.i] = Instantiate(shape2, transform.position, transform.rotation);
this.enabled = false;
}
if (Input.GetKeyDown(KeyCode.Alpha6))
{
Destroy(count.Array[count.i]);
count.i = count.i - 1;
}
}
Then I used a counter and a GameObject array to "save" each clone put in the scene in another generic script always present in the scene.
public class Counter : MonoBehaviour
{
public int i = 0;
public GameObject[] Array = new GameObject[50];
public void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
i = i + 1;
}
if (Input.GetKeyDown(KeyCode.S))
{
i = i + 1;
}
}
}
The problems are:
The first script is "reloaded" each time because it's inside every instantiated object I put in the scene, so I have to use an external single script where I save every counter and/or GameObject reference I need;
If I remove this.enabled = false; from every Instantiate process the script partially works but it creates too many clones of the same object(because it's using every Empty GameObject in the scene as reference to where to put the clones and not just the last one present);
By creating too many clones(even if I press W/S one single time), if I try to destroy the last one, it will destroy many others and if I try to put others after the destroying process, it will clone the object in every position available and not the last one.
I'm starting to lose my mind in a dumb process.... :')
Use one empty game object as ObjectSpawnManager to manage your object spawn/despawn behaviour, put the first script to this object, so that you don't need put the same script on multiple instances.
Remove the Update part of Counter script, add public UpdateCounter method, and call it from ObjectSpawnManager script when the corresponding key down.
Since you have stated a few problems and no specific questions, I am going to propose to you an approach that should work and implement what you ask for. If you want clarification on a specific question, feel free to ask.
As you have stated yourself, it makes more sense to have a single script manage the whole process of placing objects. Attach this script, let's call it SpawnManagerScript to an empty GameObject e.g. SpawnManager in the scene.
I would also suggest you to use a List instead of an array if you are not sure how many objects will be there at some point in time. If you do want to limit the number of objects to 50 (looking at your code), an array is totally fine.
Assuming you were able to figure out the correct positioning of the objects upon spawning already, I won't go into detail regarding that.
If I understood you correctly, what's left to do now is the following:
Spawning GameObject:
Differentiate the user input (e.g. S & W) in your SpawnManagerScript. For each type of object you could have a public field in your SpawnManagerScript class which references e.g. a Prefab or template GameObject in the scene. You can use the template object for cloning on user input. Add this clone to the List or array of objects and position it correctly.
Deleting GameObject:
Again, detect your desired input key for deletion in the script. Grab the last element in the List or the last added element in the array from the respective container. Destroy it or set it to inactive. Remove the entry from the container.

Unity Server GameObject destruction

I have a 2D game made in Unity in which I want the characters to be able to shoot bullets and when a bullet hits a player to push him back destroying the bullet. Due to server not syncing perfectly there are some cases in which the bullet is destroyed locally, but not on all clients so I want to make it a [Command] to destroy it on server and on all clients too.
The code for local looks like this:
void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.tag == "bullet")
{
Explode (Mathf.Sign (col.attachedRigidbody.velocity.x));
Destroy(col.gameObject);
}
}
And I tried to make it for multiplayer like this
[Command]
void CmdOnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.tag == "bullet")
{
Explode (Mathf.Sign (col.attachedRigidbody.velocity.x));
Network.Destroy(col.gameObject);
}
}
But it doesn't work.. it tells me that
CmdOnTriggerEnter2D parameter [col] is of the type [Collider2D] which is a Component. You cannot pass a Component to a remote call. Try passing data from within the component.
I understand that I should use another parameter, but how..? How can I do this function to work for my server?
As your output says you can not pass the Collider component reference as parameter since the Network does not know, where this reference belongs to on other instances.
In the Unity Documentation for Command you can find which values are allowed as parameter:
The allowed argument types are;
Basic type (byte, int, float, string, UInt64, etc)
Built-in Unity math type (Vector3, Quaternion, etc),
Arrays of basic types
Structs containing allowable types
NetworkIdentity
NetworkInstanceId
NetworkHash128
GameObject with a NetworkIdentity component attached.
=> Note especially the most bottom element in the list:
You can simply pass in the col.gameObject as long as it has a NetworkIdentity component on it!
Than I would make the check if the Tag Matches already before stressing the bandwidth of the networking. Meaning: Do the tab check local and only call the Command with the valid GameObject reference.
So your code should than look like
//local
void OnTriggerEnter2D(Collider col)
{
if (col.gameObject.tag != "bullet") return;
CmdOnTriggerEnter2D(col.gameObject);
}
// This is executed only on the server
[Command]
void CmdOnTriggerEnter2D(GameObject col)
{
// Depending on what Explode does you might want to pass it pack to all clients as well
// Since currently this will only happen on the server itself!
Explode (Mathf.Sign (col.attachedRigidbody.velocity.x));
Network.Destroy(col);
}
Requirement as said before is that you bullet object has a NetworkIdentity component but I assume this is already the case if this is a Network spawned prefab as I guess.
Note
As I mentioned in a comment I also guess Explode is something that should probably happen on all clients instead of only on the server. In this case you can do the following
//local
void OnTriggerEnter2D(Collider col)
{
if (col.gameObject.tag != "bullet") return;
// Already get the value needed for the Explode method
var value= Mathf.Sign (col.attachedRigidbody.velocity.x)
// Also pass the value so we can provide it to all clients later
CmdOnTriggerEnter2D(col.gameObject, value);
}
// This is executed only on the server
[Command]
void CmdOnTriggerEnter2D(GameObject col, float value)
{
// Tell all clients to Explode using the passed value
RpcExplode(value);
Network.Destroy(col);
}
// This is called by the server but executed by ALL clients
[ClientRpc]
void RpcExplode(float value)
{
Explode (value);
}
As I don't know what you Explode needs to work: Be careful with destroying the bullet object if it is needed to execute Explode ;)

How to copy a bsoncxx::builder::basic::document to another?

Is there any way to safely copy a bsoncxx document to another.
In following code I am not able to do that
class DocClass
{
private:
bsoncxx::builder::basic::document m_doc;
public:
bsoncxx::builder::basic::document& copy(bsoncxx::builder::basic::document& obj)
{
obj = m_doc; //Not allowed
//Error C2280 attempting to reference a deleted function
}
};
There should not be any harm to the object even after copy.
Please help.
Thanks,
Shibin
If you want to copy a bsoncxx::document::value, you can construct a new one from its view:
bsoncxx::document::value foo = ...;
bsoncxx::document::value bar{foo.view()};
bsoncxx::builder::basic::document is only movable, not copyable. However, you can get view to the underlying document from the builder with the view() method, which might be able to help you depending on your use cases. You'll still only be able to extract from the builder once though, so you'll have to rely on constructing a second document::value if you need more than one.

Walker boys tutorial... NullPointerException error

I am seeing the Walker boys tutorial, Im on the Project #2, where you make a ship that destroys asteroids... and there's a part where the bullet must call a function from other object... (here's the video http://vimeo.com/19463502) I do everything that is here but I get this error:
"NullReferenceException: Object reference not set to an instance of an object
scriptBullet.OnTriggerEnter (UnityEngine.Collider other) (at Assets/scriptBullet.js:39)
and that line of code is :
sceneManager.transform.GetComponent(scriptsceneManager).AddScore();
scriptsceneManager.js
var gameTime : float = 60;
static var score : int = 0;
function Update ()
{
print("score : " +score);
}
public function AddScore()
{
score +=1;
}
From what i can see (it's been a while since i did the walkerboy tutorials) there are only 2 reason why you can get a NullPointerException here.
The SceneManager has not been initialised in scriptBullet.js. If you are finding the GameObject by Tag make sure you have the tag assigned and is spelled correctly in the script. I always forget to assign tags until it's no late.
The scriptsceneManager hasn't been added to the SceneManager object. This results in the GetComponent call returning null.
If you are using Monodevelop, remember if you hit the attach button, you can attach UnityEditor to monodevelop, then you can use breakpoints to find out where the NullPointer is coming from.
https://docs.unity3d.com/Documentation/Manual/Debugger.html
Hope this helps.

Calling a method in Objective-C that references another class file

I am trying to call a method that checks whether the player is bigger or smaller than the enemy. (this is a fish game)
In EnemyFish.m I am using this method
-(void) compareSize:(Player*)player{
if (self.fSize > player.pSize){
isBigger = true;
}
else{
isBigger = false;
}
}
Then I want to call this method during the update so I am doing this:
-(void) update {
[self compareSize];
//Check to see if bigger than player fish
if( isBigger == true){
//code for if bigger
}else{ //etc. }
I am getting an exception: sharedlibrary apply-load-rules all
not sure what the best way to set up this method would be, and the best way to call it, since [self compareSize] is definately not working.
Any help would be greatly appreciated, Thanks!
------UPDATE----------
What about if I use this
update:(Player *)player{
The problem I was running into here, is how to call this correctly, I wasn't sure how to change this to correctly call the new update method:
[self schedule:#selector(update) interval:1.0f/60.0f];
It is unclear what you are asking, but let's look at your code and see if it helps.
Your first method can be written more concisely as:
- (void) compareSize:(Player *)player
{
isBigger = self.fSize > player.pSize;
}
There is no point in using an if/else to assign a true/false (or YES/NO) value.
Looking at this method raises the obvious question of whether it would be better returning a value rather than assigning to an instance variable. This would look like:
- (BOOL) compareSize:(Player *)player
{
return self.fSize > player.pSize;
}
and now you can use a call to compareSize in an if.
Assuming the second version of compareSize your second method is:
-(void) update
{
//Check to see if bigger than player fish
if ([self compareSize]) // OOPS, no Player
{
//code for if bigger
}
else
{
//etc.
}
}
But this doesn't work as you need an instance of Player to pass to compareSize:, e.g. [self compareSize:somePlayerInstance]. So you now have to ask yourself where you expect the Player to be found; it could be an argument to update (e.g. - (void) update:(Player *)somePlayerInstance), or you might have a method to call which returns a whole collection of players and you need to test against each one, etc., etc. I can't give an answer as I've no idea of your game and algorithm!
Following comment
You must store a reference to your Player object somewhere in your application. If there is only a single player is Player designed as a singleton with a sharedInstance, or similarly named, class method that returns the single instance? If so then your update will contain:
if ([self compareSize:[Player sharedInstance]])
etc.
Another design pattern is to have your application delegate store the reference and to provide a method (or property) for accessing it. Following this pattern (and making up a class MyDelegateApp and property player names) your code might look like:
if ([self compareSize:((MyAppDelegate *)[NSApp delegate]).player])
Yet another model is to create the single player in the application's main XIB/NIB file - etc., etc., there are many application models!
You "simply" (its not simple of course) need to design your application model so that your single player is accessible, one way or another, where you need it...
Your compareSize: method (note the colon) requires a player parameter.
So you need to call it like this:
[someEnemyFish compareSize:somePlayer]
If self is the instance of EnemyFish you want, you can do [self compareSize:somePlayer], but your title seems to indicate self isn't an EnemyFish?