I can't really figure out how to solve my problem. I have been looking for an answer but I couldn't find anything.
I have a button in my scene that can be pressed both by client and host. When the button is pressed, it creates a cube in the scene. The problem is that: the cube can be created only by the host and the host is the only user that can see it and manipulate it.
My code is:
public class CreateCube : NetworkBehaviour {
GameObject cubo;
float lastCollisionTime=0;
float collisionTime=0;
void OnCollisionExit(Collision other) {
collisionTime = Time.time;
if (collisionTime - lastCollisionTime >1.5) {
CmdCreaCubo ();
lastCollisionTime = collisionTime;
}
}
}
}
[Command]
void CmdCreaCubo(){
GameObject cubo=Instantiate(Resources.Load("MyPrefabs\\Oggetti\\CubeGrasp")) as GameObject;
cubo.transform.position = new Vector3 (-5.88f, 7.51f, -19f);
cubo.name = "CubeGrasp";
NetworkServer.Spawn (cubo);
}
}
Could anyone help me please?
Thank you so much
Instead using simple Instantiate you should need to use Network.Instantiate
The given prefab will be instanted on all clients in the game.
Synchronization is automatically set up so there is no extra work
involved.
Related
I am trying to create a simple scriptable object for my shoot ability. I have that aspect working, but as I try to set my Transform to my player, it does not update the shoot position. I am very new to C#, and this script isnt complete. I still need to add the functionality to destroy the created objects. Any help would be greatly appreciated. I suspect I need to add an update function but im am not certain how to do this.
using UnityEngine.InputSystem;
using UnityEngine.AI;
using UnityEngine;
namespace EO.ARPGInput
{
[CreateAssetMenu]
public class Shoot : Ability
{
public Transform projectileSpawnPoint;
public GameObject projectilePrefab;
public float bulletSpeed = 10;
public float bulletLife = 3;
public override void Activate(GameObject parent)
{
var projectile = Instantiate(projectilePrefab, projectileSpawnPoint.position, projectileSpawnPoint.rotation);
projectile.GetComponent<Rigidbody>().velocity = projectileSpawnPoint.forward * bulletSpeed;
Destroy(projectile, bulletLife);
void OnCollisionEnter(Collision collision)
{
Destroy(projectile);
}
}
}
}
I'm still new to Unity and coding also, so take my advice with a load of salt :P.
It may be best to have a transform on your character (say just past the barrel of the player's gun) that you can put as the projectileSpawnPoint. In your code the projectileSpawnPoint is never set. Your first line of code in the "Activate" method should be something like:
public override void Activate(GameObject parent)
{
projectileSpawnPoint = playerGunBarrelTransform.transform.position;
var projectile = Instantiate(projectilePrefab, projectileSpawnPoint.position, projectileSpawnPoint.rotation);
projectile.GetComponent<Rigidbody>().velocity = projectileSpawnPoint.forward * bulletSpeed;
Destroy(projectile, bulletLife);
For destroying the projectile afterward you can keep it as you have it in OnCollision. howeer, with bullets in particular, since they tend to be instantiated A LOT and then destroyed afterward it would be best to use an object pooler for them to instantiate several of them on start and then disable and enable them as needed so you can resuse them instead of making new ones every time.
you have to create a new script that derives from Monobehaviour for your projectiles. attach that script to the projectile prefab and place the OnCollisionEnter method in that script. now your projectiles should get destroyed when touching another collider. make sure that there is a rigidbody component attached to the projectile.
I am trying to access the Location of all the other Players who are connected to the same room. I first thought about getting the already instantiated gameobject but they all had the same name so that won't work. I wan't to to a Terrain Generator but when every Player generates Terrain its all messed up into each other. So i thought maybe just the first player has to generate the Terrain for himself and all other players to, but i now don't know how to get the other players positions. Therefore i can't check who is the first Player. So my second Question is: Is there maybe an other way to let only one generate the Terrain for all?
When i used the name to access my player i only got the player who i was already playing, i tried something where i send the player coordinates, but that only gives the coordinates when the photonView.isMine isnt True.
This is my Code so far this script is on the Player Prefab which gets instantiated when a player joins.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
public class NetworkPlayer : Photon.MonoBehaviour
{
private bool generatingTerrain;
private Vector3 correctPlayerPos;
private Quaternion correctPlayerRot;
private GameObject LevelGenerator;
private GameObject Player;
private float pos;
Rigidbody2D rb;
void Start()
{
LevelGenerator = GameObject.Find("LevelGenerator");
generatingTerrain = LevelGenerator.transform.Find("Level Generator");
Debug.Log(generatingTerrain);
if (photonView.isMine)
{
rb = GetComponent<Rigidbody2D>();
rb.simulated = true;
GetComponent<PlayerController>().enabled = true;
}
}
void Update()
{
//Debug.Log(generatingTerrain);
if (!photonView.isMine)
{
transform.position = Vector3.Lerp(transform.position, this.correctPlayerPos, Time.deltaTime * 5);
transform.rotation = Quaternion.Lerp(transform.rotation, this.correctPlayerRot, Time.deltaTime * 5);
Destroy(rb);
Debug.Log("Transform Position on !isMine: " + transform.position.x.ToString());
pos = transform.position.x;
}
Player = GameObject.Find("Player_1(Clone)");
if (photonView.isMine)
{
Debug.Log("photonView.isMine: " + transform.position.x.ToString());
Debug.Log("pos " + pos.ToString());
}
}
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
// We own this player: send the others our data
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
}
else
{
// Network player, receive data
this.correctPlayerPos = (Vector3)stream.ReceiveNext();
this.correctPlayerRot = (Quaternion)stream.ReceiveNext();
}
}
}
Maybe i am just failing to see something, this is my first time working with unity and photon so i am really new to this topic.
If you need something just write me, i am sorry if something here is missing
Hope to get help soon :)
As far as getting the other players position you do it correctly, by looking at if the photonView is not mine.
I am not entirely sure what you are trying to do with the terrain. Right now you are trying to generate terrain on all players in the game which will make each player object in the game generate the terrain, which will probably cause chaos.
If you want to have procedurally generated terrain I would suggest doing it with a seed and then share the seed amongst all players. The terrain generation should then only be done if photonView.isMine.
If you want to change terrain, e.g. destroy or move blocks, then you need to sync this over the network , most likely with with Events or RPC calls.
I found a solution to my exact problem:
GameObject[] players = GameObject.FindGameObjectsWithTag("Player");
foreach (GameObject p in players)
{
Debug.Log(p.transform.position);
}
This lists every Gameobject which got the Player Tag and puts them into an Array, then you can access every of them to check that its not your own position you can just do:
if (p.position != gameobject.transform.position) {
//Do here what you want to do with every Player who isnt you
}
I found the solution here
so we have a class assignment and pretty much we are doing the Survival Shooter project from the Unity tutorials. I've managed to make health packs and little boxes that give you speed when you pick them up, but now I want the power-ups to have a slight chance of dropping when enemies die. Can someone help me out? I'm not really asking for entire code, I have some down below:
public float percentDrop = 50f;
public GameObject HealthPack;
void Awake()
{
HealthPack = GetComponent<GameObject>();
}
public void TakeDamage (int amount, Vector3 hitPoint) { if(isDead) return;
enemyAudio.Play ();
currentHealth -= amount;
hitParticles.transform.position = hitPoint;
hitParticles.Play();
if(currentHealth <= 0)
{
Death ();
}
}
void Death ()
{
isDead = true;
capsuleCollider.isTrigger = true;
anim.SetTrigger ("Dead");
enemyAudio.clip = deathClip;
enemyAudio.Play ();
var randChance = Random.Range(0f, 100f);
if (randChance < percentDrop)
{
//GameObject.Healthpack.setActice(true);
}
}
I'm not too sure how to make the Game Object spawn when they die, can someone help me out?
Create a "Health Pack" prefab from the editor. If you need more information about prefabs, the information is out there on the web. To create a prefab, simply drag and drop a GameObject from the scene to your project. Its name in the scene will become blue. You can delete it from the scene and it will still be in the project.
Drag the prefab from the project to the HealthPack slot in the inspector of your ennemy. The ennemy will now have a reference to the prefab.
When you want to create a new health pack, you can use the Object.Instantiate static method as specified by rutter. Here is the official doc. When you instantiate your new Health Pack, I guess you want it to appear where the ennemy is, wich means you'll want to use one of the method's overloads which takes a Vector3D position as a parameter, which will most likely be transform.position. Since those methods also ask for a Quaternion, just pass in the Quaternion.identity constant.
This is how your code could look:
if (randChance < percentDrop)
{
Object.Instantiate(HealthPack, transform.position, Quaternion.identity);
}
Another problem, as mentioned in my comment, is the Awake function: HealthPack = GetComponent<GameObject>();.
The HealthPack prefab should be assigned to the ennemy from the inspector. That line in the Awake function will assign your ennemy's GameObject component to HealthPack, which is not desirable in the current context.
I hope this helps!
I follow the steps of PUN basic tutorial.
For now I reach to a part of what I want Send my position in the current time to another player with me in the room to move him. I can print my position each time I update it , what I need is to know how I can send position to another player to move him.
Let's say I have a desktop player and when I move him this translation moving the player on mobile.
And how I stop instantiate object on mobile, I just want to deal with the instantiated object on desktop.
I am using unity and Photon Network SDK.
Here is the code I used
using UnityEngine;
using System.Collections;
public class NetworkCharacter : Photon.PunBehaviour {
private Vector3 correctPlayerPos;
void Update()
{
if (!photonView.isMine)
transform.position = Vector3.Lerp(transform.position, this.correctPlayerPos, Time.deltaTime * 5);
photonView.RPC ("PrintPosition", PhotonTargets.All, transform.position);
}
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
// We own this player: send the others our data
stream.SendNext(transform.position);
}
else
{
// Network player, receive data
this.correctPlayerPos = (Vector3)stream.ReceiveNext();
}
}
[PunRPC]
void PrintPosition(Vector3 pos)
{
Debug.Log (pos);
//I need to send position coordinates to the other device
}
}
The other class of establish multiplayer environment:
using UnityEngine;
using System.Collections;
public class NetworkManager : Photon.PunBehaviour {
// Use this for initialization
void Start () {
PhotonNetwork.ConnectUsingSettings ("0.1");
//PhotonNetwork.logLevel = PhotonLogLevel.Full;
}
void Update () {
}
void OnGUI()
{
GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());
}
public override void OnJoinedLobby ()
{
Debug.Log("join lobby!");
PhotonNetwork.JoinRandomRoom ();
}
void OnPhotonRandomJoinFailed()
{
Debug.Log("Can't join random room!");
PhotonNetwork.CreateRoom(null);
}
void OnJoinedRoom()
{
Debug.Log("join random room!");
GameObject monster = PhotonNetwork.Instantiate("monsterprefab", Vector3.zero, Quaternion.identity, 0);
}
}
PUN has some new components since their new update.
I'd recommend you to use those components because it is really user friendly towards new users.
Some components you could use:
PhotonTransformView
PhotonRigidbodyView
PhotonAnimatorView
These three components will help you sync your position, physics and animations on the GameObject it is attached to.
If you do not want to use these components i suggest you search up: interpolation and extrapolation for Unity PUN.
Some good starting tutorials:
SkyArena PUN Tutorial This is a really good video tutorial[in parts] on PUN so you should definitely check that out.
Exit Games Marco Polo This tutorial is created by someone from PUN also really good to read even if you don't need it.
Hope this will help you.
-Menno
I found the answer in Demo Synchronization that provided in PUN plugin, it is really helpful.
I'm facing audio problems in my project. There are three paddle game objects and one cube. Cube has Rigidbody2d or BoxCollider2d. And there is also a button script attach to cube which has button function when we click on button cube Kinematic becomes false and drop on the paddle. When it's collide with any paddle cube destroy and instantiate again with new prefab. When Cube is falling sound is play and cube is destroy. New cube is instantiate when again click on button then error came.
MissingReferenceException: The object of type AudioSource has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object.
Scripts on all paddles:
public class Paddle : MonoBehaviour
{
[SerializeField]
private AudioSource source;
[SerializeField]
private AudioClip hit;
[SerializeField]
private BoxCollider2D collide;
[SerializeField]
private GameObject Clone;
void Awake()
{
collide.isTrigger = true;
}
void OnTriggerEnter2D(Collider2D target)
{
source.PlayOneShot(hit);
Destroy(target.gameObject);
Instantiate(Clone, new Vector3(0f, 4.51f, 0f), Quaternion.identity);
}
}
Cube Script:
public class Cube : MonoBehaviour
{
[SerializeField]
private AudioSource source;
[SerializeField]
private Rigidbody2D body;
[SerializeField]
private AudioClip play;
private Button bt;
private float pos;
public bool check;
void Start()
{
bt = GameObject.FindGameObjectWithTag("Button").GetComponent<Button>();
bt.onClick.AddListener(Fire);
body.isKinematic = true;
}
void Update()
{
if (check)
{
return;
}
Vector3 temp = transform.position;
pos = Camera.main.ScreenToWorldPoint(Input.mousePosition).x;
temp.x = Mathf.Clamp(pos, -6.25f, 6.25f);
body.position = temp;
}
public void Fire()
{
GameObject.FindGameObjectWithTag("Player").GetComponent<Cube>().check = true;
GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody2D>().isKinematic = false;
source.PlayOneShot(play);
}
}
Cube image:
Paddles image:
New Export package:
https://drive.google.com/file/d/0B1H5fdK2PJAnSXVPdmE5Z3J1SUU
Problem in Video:
https://drive.google.com/file/d/0B1H5fdK2PJAnYzRfVnlQT1FyTlE
Your linked package works fine, it does play the sound every time I launch and destroy a cube.
Nonetheless, you should remove the method on destroy. Unity does not throw any error nor warning when a non-persistent listener is null, kinda weird but this is how it is. You should remove it manually:
void OnDestroy(){
bt.onClick.RemoveListener (Fire);
}
But your package does not throw any error when I run it.
Though, I would rethink your approach, instead of the cube assigning its Fire method to the Button event, I would have a script on the button containing the Fire method as well as the AudoiSource and clip. Then on Start, the Cube would pass itself so that the button could access its Cube and Rigidbody2D component.
Best would be to pass a class that contains those are members:
public class CubeArgument{
public readonly Rigidbody2D rg = null;
public readonly Cube cube = null;
public CubeArgument(Rigidbody2D rg, Cube cube){
this.rg = rg;
this.cube = cube;
}
}
then here goes your Cube start method:
void Start () {
bt = GameObject.FindGameObjectWithTag ("Button");
bt.GetComponent<ButtonController> ().Init(new CubeArgument(body, this));
body.isKinematic = true;
}
The ButtonController reference could even be made static since there is only one for the whole level.
and then on the button you have a ButtonController:
public class ButtonController : MonoBehaviour{
Cube currentCube = null;
Rigodbody2D currentRig = null;
public void Init(CubeArgument ca){
currentRig = ca.rg;
currentCube = ca.cube;
}
public void Fire(){
if(currentCube != null){ currentCube.check = true; }
if(currentRig != null) { currentRig.isKinematic = false; }
}
}
Fire is passed as listener to the Button onClick and this is it.
your problem can be from many different places. my guesses:
1)problem with your build: rebuild your code or export all of your package to new project and retest.
2)your target framework: latest version of unity(i have 5.1) just support to .net 3,5 and unity 4.x supports 2,5 i think. so chek your target framework to not to use functions that is not functional in your version
3)settings of your platform that your editor is running on: it can be volume of your platform to many other settings, first option to know that is run your project on other machine (maybe its a unity bug that part of code is not good for your hardware or driver on platform)
Edit: OP was using version 5.1.1. This error does not repeat after Unity 5.2.
Tried your package and it is working fine!
No errors. Audio played when instantiated.
Are you still facing errors? Weird, dude. Sounds like something is not clean enough.
Maybe there is some issue with your build.
Few things you could try:
1- Try restarting your Unity. Maybe this will force Clean things.
2- Create a new project and import your own package to test it!
3- I'm using Unity 5.3.0f4 what Unity version are you using? Try an update.
If none of above works, there is something wicked going on with your AudioSource reference and I can't help you with my actual knowledge. But still we can try to do different approaches for that.
Instead of physically referenced it (Dragging and dropping), do it at the start of your script.
Desperate approach 1
On Cube.cs:
First remove [SerializeField] located above of private AudioSource source; and add the following line on the Start() method:
source = gameObject.GetComponent<AudioSource> ();
Practically the same, but now the script is referencing the own object Audio Source for you. Do the same approach with Paddle.cs. [remember to check the audio name when applying the approach for Paddle script. On paddle the audio name is PlayOneShot (hit) not PlayOneShot (play)].
If this still trigger some error. Let's try another approach.
Desperate approach 2
On Cube.cs:
Remove/Comment the following line on Fire() method:
source.PlayOneShot (play);
Add the following line to Fire() method:
gameObject.GetComponent<AudioSource> ().PlayOneShot (play);
This will get your actual AudioSource on the go. Do the same approach on Paddle.cs [remember to check the audio name when applying the approach for Paddle script. On paddle the audio name is PlayOneShot (hit) not PlayOneShot (play)].