I think there is problem between Photon and mouse position. Because when I try to change position of object with keyboard it works successfully but when I try to change position with mouse it is not changing over the network. How can I change object position with mouse position over the network?
public class SpehreControl : MonoBehaviour
{
PhotonView PV;
GameObject sphere1;
GameObject bt;
// Start is called before the first frame update
void Start()
{
PV = GetComponent<PhotonView>();
sphere1 = GameObject.Find("Sphere 1");
bt = GameObject.Find("BT_1");
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0) && PV.IsMine)
{
PV.RPC("RPC_MoveOnline", RpcTarget.AllBuffered);
}
}
[PunRPC]
void RPC_MoveOnline()
{
transform.position = new Vector3(Camera.main.ScreenToWorldPoint(Input.mousePosition).x,
Camera.main.ScreenToWorldPoint(Input.mousePosition).y, 0);
}
}
I think the problem in RPC function.
When you call it, every user receives only a call event with no params. It means, every user work with his own Input.mousePosition but not from the sender.
You can use params to fix this:
...
PV.RPC("RPC_MoveOnline", RpcTarget.AllBuffered, Camera.main.ScreenToWorldPoint(Input.mousePosition));
...
[PunRPC]
void RPC_MoveOnline(Vector3 worldPosition)
{
transform.position = new Vector3(worldPosition.x, worldPosition.y, 0);
}
But it's really not a very good way...
As I see, you need to synchronize the position of some objects for all users.
It's much easier to deal with PhotonView component (what your GO already has). Just drag and drop your GO Transform component to the PhotonView Observed Components field in unity inpector.
Using this, ALL changes with a transform of your local GO will automatically be synchronized for all players!
more info here
Good luck!)
Related
I'm making a game where the player is a square and it changes shape when he presses a key. To do that I made different prefabs because I have different things to change. When I press the key to change to one prefab, I set active the corresponding prefab and I disable the other. The problem is here, I have three prefabs and I don't know where to connect to change the position runtime. I tried to make the position equal to the others but it didn't work.
Here there is the code I made:
public class Transformation : MonoBehaviour
{
public GameObject normal, small, big;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("q"))
{
big.SetActive(true);
normal.SetActive(false);
small.SetActive(false);
big.transform.position = normal.transform.position = small.transform.position;
}
if (Input.GetKeyDown("w"))
{
normal.SetActive(true);
big.SetActive(false);
small.SetActive(false);
normal.transform.position = big.transform.position = small.transform.position;
}
if (Input.GetKeyDown("e"))
{
small.SetActive(true);
normal.SetActive(false);
big.SetActive(false);
small.transform.position = big.transform.position = normal.transform.position;
}
}
}
If you want the location of these gameObjects to be moved to the location of the script object. It is better to try this:
void Update()
{
small.SetActive(false);
normal.SetActive(false);
big.SetActive(false);
if (Input.GetKeyDown("q"))
{
big.SetActive(true);
big.transform.position = transform.position;
}
else if (Input.GetKeyDown("w"))
{
normal.SetActive(true);
normal.transform.position = transform.position;
}
else if (Input.GetKeyDown("e"))
{
small.SetActive(true);
small.transform.position = transform.position;
}
}
Make one empty GameObject and apply all movement logic to it. Put normal, small and big objects as children of the main GameObject and just enable/disable them as you need.
I want to add doors to my Unity project and so far I have gotten the code to work with my door but I can open it from any distance in the scene. I want to be able to only open it from a small distance away but I cannot figure out how.
public class Door : MonoBehaviour
{
public bool doorIsOpen;
public Transform closedPos;
public Transform openPos;
public float openSpeed;
public float openRadius;
void Update()
{
if(Physics.CheckSphere(gameObject.transform.position, openRadius))
{
if(Input.GetKeyDown(KeyCode.E))
{
doorIsOpen = !doorIsOpen;
}
}
if(doorIsOpen == true)
{
OpenDoor();
}
if(doorIsOpen == false)
{
CloseDoor();
}
}
void OpenDoor()
{
doorIsOpen = true;
gameObject.transform.position = openPos.position;
gameObject.GetComponent<Animator>().SetTrigger("OpenDoor");
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", false);
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", true);
}
void CloseDoor()
{
doorIsOpen = false;
gameObject.transform.position = closedPos.position;
gameObject.GetComponent<Animator>().SetTrigger("CloseDoor");
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", false);
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", true);
}
}
Prerequisites
Add a sphere with a collision body around the door instance in Unity. This sphere functions as the radius which will trigger the ChangeDoorState method.
Change the update method
The update will look at any collisions happening in the specified sphere. If there is at least one object in range (the collision sphere) of the door, then it opens or closes the door instance. Source: https://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html
void Update()
{
Collider[] hitColliders = Physics.OverlapSphere(center, radius);
if (hitColliders.Length > 0)
{
ChangeDoorState();
}
}
Merged the OpenDoor and CloseDoor methods
void ChangeDoorState()
{
doorClosureState = doorClosure ? true : false;
gameObject.transform.position = doorClosureState ? closedPos.position : openPos.postition;
gameObject.GetComponent<Animator>().SetTrigger("DoorClosure");
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", doorClosureState);
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", !doorClosureState);
}
You can increase the value of 'openRadius', the game is creating a sphere at gameObject.transform.position with a radius of 'openRadius' and checking if there is any colliders overlapping the sphere.
//...
if(Physics.CheckSphere(gameObject.transform.position, openRadius))
//...
One issue is that you permanently set the triggers in every frame. You would only want to do so when you hit the key.
Then also Physics.CheckSphere checks whether there is any collider within the range. This could be any collider, not only the player. To make sure it only works if the player is the one in range you should definitely use Layers and give the player its own layer e.g. "Player". Then you can pass the layerMask parameter to make sure the doors only check for the player's layer.
so I would simply use
// if possible already reference this in the Inspector
[SerializeField] private Animator _animator;
[Tooltip("Here select only the Layer(s) which you assigned to your Player object(s)")]
[SerializeField] LayerMask _playerLayer;
private void Awake()
{
// as fallback get it ONCE on runtime
if(!_animator) _animator = GetComponent<Animator>();
}
private void Update()
{
// Afaik the Key is way cheaper to check so do it first
// and use physics only if actually necessary
if(Input.GetKeyDown(KeyCode.E))
{
// Only check for the Player's Layer and ignore other colliders
if(Physics.CheckSphere(transform.position, openRadius, _playerLayer.value))
{
ToggleDoorState();
}
}
}
// Since manually changing the flag in the Inspector will not have
// any effect anymore in order to be able to test it you can use
// the context menu of this component in the Inspector
[ContextMenu(nameof(ToggleDoorState))]
private void ToggleDoorState()
{
// invert the flag
doorIsOpen = !doorIsOpen;
// use the flag in ternary expressions
transform.position = doorIsOpen ? openPos.position : closedPos.position;
_animator.SetTrigger(doorIsOpen ? "OpenDoor" : "CloseDoor");
// use the value of the flag diectly
_animator.SetBool("DoorIsClosed", !doorIsOpen);
_animator.SetBool("DoorIsOpen", doorIsOpen);
}
It is seems a bit though as if you have a bit of redundancy in your animator. I would either use the Bools in order to trigger a transition or use the Triggers, to have both seems odd.
I created a dot following my mouse around in 2D and I created a cube object changing position on x and y. Now when I point my mouse to cube, it deactivates I set that, and now I want to activate it again. I try on trigger exit, but it didn't work.
public GameObject tapObject;
private float respawnTime = 1f;
public float xMin;
public float xMax;
public float yMin;
public float yMax;
void Start()
{
StartCoroutine(spawnEnemyTime());
}
private void RandomSpawnObject()
{
tapObject.transform.position = new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax));
}
private void OnTriggerEnter2D(Collider2D collision)
{
tapObject.SetActive(false);
}
IEnumerator spawnEnemyTime()
{
while (true)
{
yield return new WaitForSeconds(respawnTime);
RandomSpawnObject();
}
}
Once inactive the scripts on that object are not executed anymore => messages like OnTriggerExit are not called/executed.
One solution is to simply wrap the target object in a parent object and attach your script to the parent instead but make it (de)activate the child.
So the parent stays active and receives the message.
I am just going to repeat what everyone else here said:
A inactive object in Unity is truly inactive, meaning it does not receive any updates, can't collide with stuff and all the MonoBehaviour stuff that usually calls your code does not work either. You have to manually re-activate the object using a reference that you cached somewhere.
But, instead of just flat out disabling the whole object you could disable the components that you don't want to be active.
Example:
private void OnTriggerEnter2D(Collider2D collision)
{
tapObject.GetComponent<Renderer>().enabled = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
tapObject.GetComponent<Renderer>().enabled = true;
}
This only deactivates your renderer component but leaves everything else as it is. So your object can still collide and it's still registered via e.g. OnTriggerExit.
Keep in mind that GetComponent<T>() is a pretty expensive operation so caching your component references is a good idea. The best solution would be to start out with a reference by creating a variable for it and assign it in the inspector.
Example:
//Set in inspector
public Renderer renderer
private void OnTriggerEnter2D(Collider2D collision)
{
renderer.enabled = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
renderer.enabled = true;
}
When a GameObject is not active in Unity , you can't click it(no rendering,no colliding , nothing )
But ,You can create a hotkey (new script or in other script) , that can set it back to active , if it is not active.
public GameObject GO;
Use GO.setactive(true);
whereas gameobject is the object use to define the specific thing or object which needs to be active and the whole code needs to written in the method "spawnEnemyTime" so that it could be get active after the specific time period
You can just use an empty GameObject and get a reference the object that you want to enable/disable. If you get the reference before you disable it you will be able to activate it again.
the alternative is to do what TehMightyPotato said. Disable components it's actually the best way to solve this problem, but if you have lot's of components/subcomponents disable the gameobjects is faster.
I am currently developing a Unity3D based evolutionary algorithm. The simulation is two-dimensional. A single subject is being depicted as car, being a prefab, consisting of 3 sprites(2 wheels and body), and a CarScript. Each sprite has a proper collider(BoxCollider2D for body and CircleCollider2D for wheels). CarBody also has
two WheelJoint2D. Parameters of those colliders are changed by code.
I want this car to be destroyed, if it stops moving or better - advancing. In the Game window, the car is obviously moving downhill. The problem is, that after checking for transform.position of gameobject, this value seems to be constant. It always shows the position of SpawnPoint. SpawnPoint is empty GameObject with SpawnScript, which fragment is below:
public GameObject carprefab; //Set from editor
public Transform[] spawnPoints; //transform of SpawnPoint, just one element. Set from editor.
private void GenerateCar(Chromosome chromosome)
{
int spawnPointIndex = Random.Range(0, spawnPoints.Length);
var instace = (GameObject)Instantiate(carprefab, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation);
instace.SendMessage("SetChromosome", chromosome);
foreach (Transform child in instace.transform)
{ //code omitted for clarity, changes some of parameters based on chromosome.
Instantiated object has a CarScript:
// Update is called once per frame
void Update()
{
if (frames%10 == 0)
CheckForMoving();
frames++;
}
private void CheckForMoving()
{
var pos = gameObject.transform.position; //this is never changing - why?
var diff = pos - startingPosition; //difference between local position and starting position
if (CountLength(diff) >= recordLengthYet)
{
Debug.Log("It is moving!");
recordLengthYet = CountLength(diff);
framesTillLastRecord = 0;
}
else
{
Debug.Log("It is not moving");
if (framesTillLastRecord > 4)
Destroy(gameObject, 0f);
framesTillLastRecord++;
}
}
I tried getting the position by any of the following:
var pos = gameObject.transform.position;
var pos = GameObject.FindGameObjectWithTag("player");
var pos = this.transform.position;
The question is - what did I miss, or why this is not changing? I started learning Unity just recently, and had no previous experience with any similiar software. I also wonder, if it is even the right way to do this.
Few days has passed, nobody had uploaded a proper answer, and I managed to get a workaround. As long as it is really dirty, it seems to work.
I placed a tag on child GameObject, instead of prefab, and I am geting position of this child by
var pos = GameObject.FindGameObjectWithTag("player");
I am not sure, if this is really good answer, but I hope somebody might find it useful someday.
Assuming you are using rigidbodies, you should be overriding FixedUpdate() and not Update() as per Unity doco:
This function is called every fixed framerate frame, if the MonoBehaviour is enabled. FixedUpdate should be used instead of Update when dealing with Rigidbody. For example when adding a force to a rigidbody, you have to apply the force every fixed frame inside FixedUpdate instead of every frame inside Update. - Tell me more...
Replace:
// Update is called once per frame
void Update()
{
if (frames%10 == 0)
CheckForMoving();
frames++;
}
With:
// Update is called once per frame
void FixedUpdate()
{
if (frames%10 == 0)
CheckForMoving();
frames++;
}
I am having an issue respawning a prefab after it has been destroyed. I can't seem to get it to respawn back at its original start position after a second of being destroyed. I have created an empty game object and attached the SpawnTargets.cs script to it. I'm not sure of what the best methodology to approach this situation. Another object with a script attached to it does the actual destroy of the prefab. BulletCollisionHandler.cs works fine though. Thanks for any help. Code is below:
SpawnTargets.cs:
using UnityEngine;
using System.Collections;
public class SpawnTargets : MonoBehaviour
{
public GameObject targetCircle;
public GameObject targetSquare;
public GameObject targetStar;
private Vector3 circleSpawnPosition = new Vector3(0.0f, 1.227389f, -7.5f);
private Vector3 squareSpawnPosition = new Vector3(0.0f, 1.027975f, -7.993299f);
private Vector3 starSpawnPosition = new Vector3(0.0f, 1.8f, -7f);
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
SpawnTarget ();
}
void SpawnTarget()
{
}
}
BulletCollisionHandler.cs:
using UnityEngine;
using System.Collections;
public class BulletCollisionHandler : MonoBehaviour
{
public GameObject targetCircle;
// Use this for initialization
void Start ()
{
Destroy (gameObject, 2);
}
// Update is called once per frame
void Update ()
{
}
void OnCollisionEnter(Collision other)
{
if(other.gameObject.name == "TargetSquare")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit square");
}
else if(other.gameObject.name == "TargetCircle")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit circle");
}
else if(other.gameObject.name == "TargetStar")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
((TargetMovementVertical)other.gameObject.GetComponent<TargetMovementVertical>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit star");
}
}
}
You're not calling Instantiate() anywhere, so it's hard to see where the new object would come from in the code you've supplied.
In any case, it might be better not to use Destroy. If you want to immediately reset the object, why not simply recycle it back to the start position? It's a good idea to avoid instantiating and destroying lots of objects, it's better to hide/disable the ones your don't need and unhide/re-enable them.
Here's a tutorial on the general idea. The tutorial is about groups of objects but the same trick would work for recycling single objects too.
You are better of using gameObject.SetActive( true/false ); for activating / deactivating the gameObject instead of just using Destroy.
Then if you are using Destroy you have 3 options that comes to mind for getting it into the desire position before the Player sees it.
1) You enable the game object after disabling its Renderer component. Then you equalize the transform's position / rotation the one you need. After this you re-enable the Renderer component. It should be placed where you want it.
2) You Instantiate the gameObject, but first making sure the Renderer component is disabled on its Prefab, by default, so you can re-assign its Transform values then - re-enable the Renderer again.
3) You make an invisible gameObject (an Empty gameObject) and Instantiate the wanted gameObject, you then make the Empty to be the parent of the newly created gameObject.. Provided that the parent Empty is exactly where you want it to be, when you instantiate and reset the child's position it should jump off right on top the the Empty parent.
I'm not giving code since you haven't and I don't have no idea of which method you might end up liking more. In terms of performance the Enable/Disable are the best option.
And as theodox says Object Pooling is your best friend for things like bullets, although it might be applied to many other gameObjects that might work as 'collections of objects' on your game's logic. It's totally worth learning.