I am working on an Unity3d project in which I want to dynamically add 3d objects to the scene, at the same position, orientation, upon a keyPress. For doing so, I imported these objects and made a prefab for all of these.. I am using the following script to do so.. bt it doesn't do the needfull.. The new object gets instantiated and gets added to the scene but the old objects don't get destroyed. And also ,the position of the new objects is not always the same. The scene consists of a Cube initially. After I press '1' ,'2' etc .. I want a new Object to replace the 1 being displayed (at the same position).
script :
var myCar : GameObject;
var Cube : GameObject;
var Globe: GameObject;
var Clock : GameObject;
var Pistol : GameObject;
var LCD : GameObject;
var prev : int;
var temp: GameObject;
function Start ()
{
prev =0;
temp=null;
}
function Update ()
{
if(prev==0)
temp=Cube;
else if(prev==1)
temp=myCar;
else if(prev==2)
temp=Globe;
else if(prev==3)
temp=Pistol;
else if(prev==4)
temp=Clock;
else if(prev==5)
temp=LCD;
if(Input.GetKey(KeyCode.Alpha1 || KeyCode.Keypad1))
{
if(prev!=1)
{
Instantiate(myCar,temp.transform.position ,temp.transform.rotation);
myCar.transform.localScale = Vector3(0.06,0.06,0.06);
Destroy(temp);
prev=1;
}
}
else if(Input.GetKey(KeyCode.Alpha2 || KeyCode.Keypad2))
{
if(prev!=2)
{
Instantiate(Globe,temp.transform.position ,temp.transform.rotation);
Globe.transform.localScale = Vector3(0.04,0.04,0.04);
Destroy(temp);
prev=2;
}
}
else if(Input.GetKey(KeyCode.Alpha3 || KeyCode.Keypad3))
{
if(prev!=3)
{
Instantiate(Pistol,temp.transform.position ,temp.transform.rotation);
Pistol.transform.localScale = Vector3(0.03,0.03,0.03);
Destroy(temp);
prev =3;
}
}
else if(Input.GetKey(KeyCode.Alpha4 || KeyCode.Keypad4))
{
if(prev!=4)
{
Instantiate(Clock,temp.transform.position ,temp.transform.rotation);
Clock.transform.localScale = Vector3(0.08,0.08,0.08);
Destroy(temp);
prev=4;
}
}
else if(Input.GetKey(KeyCode.Alpha5 || KeyCode.Keypad5))
{
if(prev!=5)
{
Instantiate(LCD,temp.transform.position ,temp.transform.rotation);
LCD.transform.localScale = Vector3(0.04,0.04,0.04);
Destroy(temp);
prev=5;
}
}
}
There are several things to consider:
It would be better to store a reference to the currently active object intead of the if-else-if part.
Destroy is called on the prefab objects but not on the existing GameObject instance and thus doesn't do anything.
The same for all manipulations of transform. They affect the prefabs only.
Not really a bug but really an enhancement: Scale your models in our 3D software so that they can be instantiated without localScale adjusting.
UPDATE: If you don't have access to the original models of the 3D software like Blender, Maya, ... you can easily define the appropriate scale in the prefab. The keypoint is to minimise repeating stuff in your code and to put all kind of property settings into the prefab.
Putting this altogether, my suggestions:
Refine your original models like in 4.
Introduce a member variable to store a reference to the current object.
UPDATE: If you want a special object for example Cube initialised on start, you first drag it into the scene, and then drag the newly created Cube from hierarchy view to the varable current. So you see the cube first when starting the scene and your script knows that this is current when it has to replace it.
Create a method for object instantiation
I'm working in C# but it should be something like
var current : GameObject;
...
function ReplaceObject (prefab : GameObject, newKey : int) {
if (newKey != prev) {
GameObject newObject = Instantiate(prefab, current.transform.position,
current.transform.rotation);
Destroy(current);
current = newObject;
prev = newKey;
}
}
...
function Update ()
{
if(Input.GetKey(KeyCode.Alpha1 || KeyCode.Keypad1)) {
ReplaceObject (myCar, 1);
} else if(Input.GetKey(KeyCode.Alpha2 || KeyCode.Keypad2))
ReplaceObject (Globe, 2);
Related
How do I assign different prefabs to different images?
right now, I have all my prefabs loading in on top of each other but how do I get it so each prefab loads in only once on top of one image so each image has a different prefab?
I've modified the sample code (the frame corners) to load in my own prefab and used a dictionary to pair the prefabs with images from the database but when the program runs it instatiates all the prefabs in the same place rather than putting one prefrab on each image it puts all the prefabs on every image - this is the code I've been using:
public GameObject obj1, obj2, obj3, obj4;
Dictionary<int, GameObject> imagePrefabPairs;
public AugmentedImage Image;
void Start()
{
instantiateDictionary();
}
// TODO: make initialisation dynamic, to match the size of the db.
public void instantiateDictionary()
{
imagePrefabPairs = new Dictionary<int, GameObject>
{
{ 0, obj1 },
{ 1, obj2 },
{ 2, obj3 },
{ 3, obj4 }
};
}
public void Update()
{
if (Image == null || Image.TrackingState != TrackingState.Tracking)
{
obj1.SetActive(false);
obj2.SetActive(false);
obj3.SetActive(false);
obj4.SetActive(false);
return;
}
Pose _centerPose = Image.CenterPose;
imagePrefabPairs[Image.DatabaseIndex].transform.localPosition = _centerPose.position;
imagePrefabPairs[Image.DatabaseIndex].transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
imagePrefabPairs[Image.DatabaseIndex].SetActive(true);
}
I figure that I need to have some kind of if statement to ask if one prefab is loaded in and then just choose to load in the next one and have them instantiate one at a time but I am not sure how to do that, or if there is a more direct way to make that happen...?
You could change your AugmentedImageVisualizer prefab, so that at the start all your different objects are inactive (.SetActive(false);) For changing a prefab you can add it to your hierarchy, set all the objects inactive and then apply the changes. After apply you can delete the prefab from your game hierarchy.
Img 1: Add existing prefab to game hierarchy
Img 2: set all the attached objecty to inactive (1) and apply the changes to the prefab (2)
So when you detect a image from your imagedatabase the AugmentedImageVisualizer prefab is attached and only the object with the given index is set to active. Then your code should work.
Because at the start all your objects are inactive you could change this part of your code:
if (Image == null || Image.TrackingState != TrackingState.Tracking)
{
obj1.SetActive(false);
obj2.SetActive(false);
obj3.SetActive(false);
obj4.SetActive(false);
return;
}
so that you only deactivate the active object:
if (Image.TrackingState != TrackingState.Tracking)
{
imagePrefabPairs[Image.DatabaseIndex].SetActive(false);
return;
}
I assign different prefabs to different images by this way:
I modified the AugmentedImageExampleController.cs:.
I added a list for prefabs:
public List<AugmentedImageVisualizer> prefabs = new List<AugmentedImageVisualizer>();
For the related image for the prefab I did a reference by using the image.DatabaseIndex in the visualizer:
visualizer = (AugmentedImageVisualizer)Instantiate(prefabs[image.DatabaseIndex], anchor.transform);
In the inspector of ExampleController you can put in the prefabs (AugmentedImageVisualizer) now.
That's it, and its working fine!
Flip a 2d GameObject and place it on exact the same position works well on both sides.
Rotating 2d GameObject works well on both sides
However, if i flip the GameObject, by executing Cmd_DoTheSpawn, the rotation is not reflected on the "other" client.
I would need help to get this work.
Here is the code I use:
[Command]
void Cmd_DoTheSpawn(GameObject myGameObject) {
// Check if front or back?
char lastChar = myGameObject.tag[myGameObject.tag.Length - 1];
if (lastChar == 'B') {
convertedObjectTag = unet_Back2Front [myGameObject.tag];
} else {
convertedObjectTag = unet_Front2Back [myGameObject.tag];
}
GameObject my1 = Resources.Load (convertedObjectTag) as GameObject;
float z = myGameObject.transform.localEulerAngles.z;
//var go = (GameObject)Instantiate (my1, myGameObject.transform.position, myGameObject.transform.localRotation);
var go = (GameObject)Instantiate(my1, myGameObject.transform.position, Quaternion.Euler(0f, 0f, z));
go.name = go.name.Remove(go.name.Length - 7); // Remove the '(Clone)' in name
NetworkServer.SpawnWithClientAuthority(go, base.connectionToClient);
print ("myGameObject: " + myGameObject.transform.position);
if (myGameObject == null)
print ("myGameObject NULL" + myGameObject.transform.position);
Rpc_DoTheRot (go, myGameObject);
myNetID = myGameObject.GetComponent<NetworkIdentity> ();
Cmd_DestroyGameObject (myNetID.netId);
}
Thanks to #LumbusterTick i did get it to work, meaning the rotation is ok. However, i did get another problem that i do not fully understand.
I added the following code:
[ClientRpc]
public void Rpc_DoTheRot(GameObject newGO, GameObject oldGO) {
print ("Rpc_DoTheRot");
if (newGO == null)
print ("newGO NULL");
if (oldGO == null)
print ("oldGO NULL");
newGO.transform.rotation = oldGO.transform.rotation;
}
...which i call after spawn but prior to destroy, see updated code above.
It does place the flipped prefab in correct rotation but i do get the following messages:
oldGO NULL
as well as:
NullReferenceException: Object reference not set to an instance of an object
I know perfectly well what that means but not why it happen as all values of the myGameObject is nulled when i send them. ...and i do it prior to destroy.
I do not understand why it is null but still effect the rotation on the new object.
From you command function after you network spawn call a clientrpc function and pass instantiated gameobject to it and change its rotation there.
Right now I am making a timed "escape" themed game where the player must find pieces of their ship that have scattered upon crashing in order to get off the planet. The player must avoid the enemies that will end up chasing them throughout the level.
My current problem is figuring out how to get multiple enemies to follow once it is triggered.
Right now only the one follows but the other 5 in the area don't move. I was thinking about putting them into an array but I'm unsure if that will work as I need to access the navmeshagent component
Here is my code:
#pragma strict
//script by Kyle Crombie & Paul Christopher
//will add negative action if collision is detected with player
var target : Transform; //the enemy's target
var isSeen: boolean = false;
var agent: NavMeshAgent;
function OnTriggerStay() {
isSeen = true;
target = GameObject.FindWithTag("Player").transform; //target the player
}
function Update () {
if(isSeen){
agent.SetDestination(target.position);
}
}
You can change the type of the agent variable from NavMeshAgent to array of NavMeshAgent. In the editor you can set the size of the array and then assign all the enemies you want to react. Then you can iterate over them and update them all. This is what it looks like:
#pragma strict
//script by Kyle Crombie & Paul Christopher
//will add negative action if collision is detected with player
var target : Transform; //the enemy's target
var isSeen: boolean = false;
var agents : NavMeshAgent[]; // This is now an array!
function OnTriggerStay() {
isSeen = true;
target = GameObject.FindWithTag("Player").transform; //target the player
}
function Update () {
if(isSeen){
for (var agent in agents) {
agent.SetDestination(target.position);
}
}
}
Alternatively you could tag the enemies and use FindGameObjectsWithTag in combination with GetComponent. Then it looks like this:
#pragma strict
//script by Kyle Crombie & Paul Christopher
//will add negative action if collision is detected with player
var target : Transform; //the enemy's target
var isSeen: boolean = false;
function OnTriggerStay() {
isSeen = true;
target = GameObject.FindWithTag("Player").transform; //target the player
}
function Update () {
if(isSeen){
for (var agent in GameObject.FindGameObjectsWithTag("Enemy")) {
agent.GetComponent(NavMeshAgent).SetDestination(target.position);
}
}
}
I'm making a game where the player touches the screen and an object instantiates . However, I only want the object to instantiate if the Raycast hits an object that is on a specific layer (or of a specific tag if that is easier). I can get the ray to cast out and instantiate a prefab where I'd like it, but when I add in the section of checking for the layer, it sends me an error (NullReferenceException:Object reference not set to an instance of an object). This seems like such a simple thing but I can't get it to work. Any help would be GREATLY appreciated!
var box : Transform;
function Update ()
{
if (Input.GetMouseButtonDown(0))
{
if(roomController.noMore == false){
var hit : RaycastHit;
var mousePos : Vector3 = Input.mousePosition;
mousePos.z = 9;
var worldPos : Vector3 = camera.ScreenToWorldPoint(mousePos);
Debug.Log("Mouse pos: " + mousePos + " World Pos: " + worldPos + " Near Clip Plane: " + camera.nearClipPlane);
if(hit.collider.gameObject.layer == "Ground" && HierarchyType.collider != null){
clone = Instantiate(box, worldPos, Quaternion.identity);
noMore = true;
Destroy(this);
}
}
}
}
Try use Physics.Raycast.
var hit : RaycastHit;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), hit))
{
if (hit.collider != null && hit.collider.gameObject.layer == LayerMask.NameToLayer("Water"))
{
Debug.Log("Instantiate");
}
}
but when I add in the section of checking for the layer, it sends me
an error (NullReferenceException:Object reference not set to an
instance of an object).
This is probably because actually you are not casting any ray and checking that an hit actually occurred( avoid the relative collider field could be null).
I suggest you to have a look at Physics.RayCast overloads, it could be significant more efficient to specify the layer (through LayerMask) while casting instead of filtering the results later.
I have a number of sprites on top of each-other on the game board. When i use the mouse and select the sprite on top, all sprites under the mouse position is selected. My question is how to only select the sprite that I click on and not catch the ones below?
Here is the code i am using for my tests, attached to the sprites:
function Update () {
if(Input.GetMouseButtonDown(0)) {
var theActualMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// print("ALL MousePosition: " + theActualMousePosition);
if(collider2D.OverlapPoint(theActualMousePosition)) {
// print("HIT theActualMousePosition: " + theActualMousePosition);
print(collider2D.gameObject.tag);
}
}
}
The results you are getting are completely expected as your code is placed on the GameObjects, what you should do is to push your script out of those objects or use another function than OverlapPoint (because overlap point does not check for collision it simply checks if you are in the bounds of an object, which means it is valid for all object)
Some ideas :
Using OnMouseDown should provide you with an event only for the first collider encountered
Using A raycast from the camera : Camera.ScreenPointToRay should be able to be used for a raycast and then to check only the first collider encountered
Using Layers depending on layer collision Order.
EDIT :
Or you could also cast the ray the other way around :
function Update () {
if(Input.GetMouseButtonDown(0)) {
var theActualMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = (transform.Position-theActualMousePosition).normalized;
Ray ray = Ray(transform.Position,direction)
if(Physics.Raycast(ray) == null) {
//Then there is nothing between the object and the camera then the object is the first one
print(collider2D.gameObject.tag);
}
}
}