Photon Unity3d: OnPhotonSerializeView() doesn't work - unity3d

I'm working in a car game with multiplayer.
All works fine, master and clients are connected to the room fine. (I have been using Marco Polo tutorial)
The problem is when I see other cars moving in the screen, the position updates by teleporting the cars. Appears and disappears all the time.
Part of my code:
PhotonNetwork.automaticallySyncScene = false;
public class CNPlayerManager : Photon.MonoBehaviour
{
...
void FixedUpdate()
{
if (photonView.isMine)
{
//it works fine
}
else
{
transform.position= Vector3.Lerp(transform.position, this.correctPosition, Time.deltaTime * 5);
transform.rotation= Quaternion.Lerp(transform.rotation, this.correctRotation, Time.deltaTime * 5);
}
}
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
}
else
{
this.correctPosition = (Vector3)stream.ReceiveNext(); //Line 100
this.correctRotation = (Quaternion)stream.ReceiveNext(); //Line 101
}
}
My PhotonView in my car prefab is like this:
(source: photonengine.com)
but in my Photon Version I have more options. In Owner I have "set at runtime" and "Fixed". And in "Observed components" I have 2 components, my car prefab and the script CNPlayerManager.
When I playing with 2 cars, In the first car I sometimes get this error: "IndexOutOfRangeException: Array index is out of range. PhotonStream.ReceiveNext ()..." In the line 100.
In the second car I get the same.
Could you help me please?

what do you mean when you say that you "observe the car prefab"? I think it is enough to just observe the CNPlayerManager script in this case since it handles the position and rotation synchronization.
However I would like to recommend you taking a look at the scripts the PUN package already contains, especially the PhotonTransformView script. This one does what you want to achieve and also provides some synchronization options you can check and see which one fits your needs best.
Please let us know if you still run into problems.

Related

How to generate a random number and make sure it is the same for everyone using Photon in Unity?

Hi Guys I am converting a single player Sudoko game into a multiplayer game (right now 2 players) using Photon in Unity.
The basic logic of the Sudoku game is that there are 300 puzzle data already loaded in it. A random number between 1 to 300 is picked up and the corresponding puzzle is loaded.
But the problem I am facing is even though I have made sure that the same number is getting picked up for both the client and the master server, different puzzles are getting loaded.
So basically I script called MultiManager attached to the MultiManager GameObject in the Sudoku screen.The script looks something like this.
void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsMasterClient)
{
puzzleIndex = Random.Range(0, 300);
PV.RPC("RPC_PuzzleIndex", RpcTarget.Others, puzzleIndex);
}
gameManager = FindObjectOfType(typeof(GameManager)) as GameManager;
gameManager.PlayNewGame("easy");
}
[PunRPC]
void RPC_PuzzleIndex(int puzzleIndexNUmber)
{
puzzleIndex = puzzleIndexNUmber;
}
So in the GameManager script you have these functions:
public void PlayNewGame(string groupId)
{
// Get the PuzzleGroupData for the given groupId
for (int i = 0; i < puzzleGroups.Count; i++)
{
PuzzleGroupData puzzleGroupData = puzzleGroups[i];
if (groupId == puzzleGroupData.groupId)
{
PlayNewGame(puzzleGroupData);
return;
}
}
}
private void PlayNewGame(PuzzleGroupData puzzleGroupData)
{
// Get a puzzle that has not yet been played by the user
PuzzleData puzzleData = puzzleGroupData.GetPuzzle();
// Play the game using the new puzzle data
PlayGame(puzzleData);
}
And in the PuzzleGroupData class you have this function :
public PuzzleData GetPuzzle()
{
return new PuzzleData(puzzleFiles[MultiManager.puzzleIndex], shiftAmount, groupId);
}
I don't quite get as to whats wrong which is happening. I tried to use other variations like keeping that random number outside of the condition inside of PhotonNetwork.isMasterClient and all, but doesn't work.
If anyone can help it would be great. By the way this Sudoku game was purchased and I am trying to convert it to a mutliPlayer game
Since the master is usually the first one in a room so also the first one getting Start called I think what happens is that your other clients are simply not connected yet when the RPC is called.
Further it might also happen (actually pretty likely) that Start is called before the RPC has the chance to be received.
I would rather actually wait until you have the value and do
void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsMasterClient)
{
PV.RPC(name of(RPC_PuzzleIndex), RpcTarget.AllBuffered, Random.Range(0, 300));
}
}
[PunRPC]
void RPC_PuzzleIndex(int puzzleIndexNUmber)
{
puzzleIndex = puzzleIndexNUmber;
gameManager = FindObjectOfType(typeof(GameManager)) as GameManager;
gameManager.PlayNewGame("easy");
}
This way
the RpcTarget.AllBuffered makes sure that also clients joining later will receive the call
there is no way you start a game without receiving the random value first

Unity: Weird scene transition bug after adding a "Persistent-Scene" with a GameManager

I made a pretty basic 2D game to learn. I have 2 Scenes, and switching between them worked great. I used empty gameObjects as Start/Exit point of the Scene, so that the game would know to put player on point X after exiting through point X (for example exit outside house if I walk out the door).
Then I added a "Scene0", to use for persistent general scripts like GameManager, Sounds, Music, etc. With just one object called "Controller" that I DontDestroyOnLoad().
After adding this Scene and then just switching Scenes right away to my MainScene, all of a sudden the game starts acting really strange;
the first time I move from my MainScene (Scene1), to my secondary Scene (Scene2), it works fine, but then when I leave Scene2 to go back to Scene1, the player spawns in the middle of nowhere.
And this ONLY happens if I launch the game from my Persistent Scene.
I have no idea what is wrong, I don't add anything that interferes with my scene transitions, all I've added so far is playerHealth, for testing.
Scripts attached to my (persistent) Controller:
DDOL:
public class DDOL : MonoBehaviour {
// Use this for initialization
void Awake () {
DontDestroyOnLoad (gameObject);
}
}
GameManager:
public class GameManager : MonoBehaviour {
public static GameManager manager;
public int playerMaxHealth;
public int playerCurrentHealth;
void Awake(){
if (manager == null) {
manager = this;
} else if (manager != this) {
Destroy (gameObject);
}
}
// Use this for initialization
void Start () {
SceneManager.LoadScene("test_scene");
}
// Update is called once per frame
void Update () {
}
}
Scripts attached to my StartPoint:
PlayerStartPoint:
public class PlayerStartPoint : MonoBehaviour {
private PlayerController thePlayer;
private CameraController theCamera;
public Vector2 startDir;
public string pointName;
// Use this for initialization
void Start () {
thePlayer = FindObjectOfType<PlayerController> ();
if (thePlayer.startPoint == pointName) {
thePlayer.transform.position = transform.position;
thePlayer.lastMove = startDir;
theCamera = FindObjectOfType<CameraController> ();
theCamera.transform.position = new Vector3(transform.position.x, transform.position.y, theCamera.transform.position.z);
}
}
}
And finally ExitPoint:
LoadNewArea:
public class LoadNewArea : MonoBehaviour {
public string levelToLoad;
public string exitPoint;
private PlayerController thePlayer;
// Use this for initialization
void Start () {
thePlayer = FindObjectOfType<PlayerController> ();
}
void OnTriggerEnter2D(Collider2D other){
if (other.gameObject.name == "Player")
{
SceneManager.LoadScene(levelToLoad);
thePlayer.startPoint = exitPoint;
}
}
}
EDIT:
After moving all my DDOL gameObject to the Pre-Scene, it worked. So, I can assume the fault is inside Player or Cameras Start() functions since when they start in Scene1 they get called every time I enter the Scene (only DDOL).
I tried adjusting their Start()functions like follows:
Original camera:
void Start () {
Debug.Log("Starting camera");
if (!cameraExists) {
cameraExists = true;
DontDestroyOnLoad (gameObject);}
else{
Destroy (gameObject);
}
}
Changed Camera:
void Start () {
DontDestroyOnLoad (gameObject);
}
The exact same changes was made in Player.
Obviously this doesnt work because it creates a new Camera/Player every time I enter Scene1 (btw why does it not try to create them when I enter Scene2?, is it because they start in Scene1?). HOWEVER, the new player/camera do start at the correct position, and if I zoom out I can see the old player/camera at that same wrong position as before. So something weird happens when their Start() is called a second time it seems.
You've now mentioned that you had code something like this,
void Start () {
Debug.Log("Starting camera");
if (!cameraExists) {
cameraExists = true;
DontDestroyOnLoad (gameObject);}
else{
Destroy (gameObject);
}
}
Note that this is unfortunately just "utterly incorrect", heh :)
The issues you mention in the question (preload scenes etc) are just totally unrelated to the problem here.
In Unity if you have a character C that persists between scenes a, b, c as you load those scenes, you must kick-off C in it's own (perhaps otherwise empty) scene, you can not use "a" as a matter of convenience to kick off C.
The pattern is, in each of a, b, c just have a line of code like p = FindObjectOfType<Player>(); which runs when the scene loads, and position C as you wish.
Now, regarding your specific puzzle about the unusual behavior you are seeing.
I understand that you want to know why you are observing what you do.
It is a combination of confusion over the following issues: 1 - difference between Awake and Start, 2 - confusion over script execution order {but see below1} 3 - confusion about Destroy versus DestroyImmediate 4 - Not using Debug.Log enough, and not using gameObject.name in there (it's a common in Unity to be wildly confused about which object is talking in Debug.Log) 5 - where you mention you see the other object "off to the side", it's common to drastically confuse which one is which in such situations 6 - confusion between the computer programming concept of "instantiation" (ie, of a class or object) and "instantiating" (confusingly, it's the same word - utterly unrelated) game objects in nity.
If you fiddle around with all those issues, you'll discover an explanation for the behavior you're seeing!
But it doesn't amount to much; in Unity in the "C .. a b c" example you have to create C separately beforehand.
1 {aside, never fiddle with the script execution ordering system in Unity in an effort to solve problems; it's only there for R&D purposes; however it could in fact help you investigate the behavior at hand in this problem, if you are particularly keen to fully understand why you're seeing what you're apparently seeing}
Use the debugger. Have breakpoints at the relevant spots, like PlayerStartPoint.Start() and LoadNewArea.OnTriggerEnter2D() and check that they are executed
At the right time
The right number of times
With the expected values
This should make you see where things get out of hand.
If you use Visual Studio, install https://marketplace.visualstudio.com/items?itemName=SebastienLebreton.VisualStudio2015ToolsforUnity to be able to debug Unity from within Visual Studio.
If you are not using Visual Studio, you probably should.
Is player persistent between scenes (does he have DontDestroyOnLoad)? If no then this might be the reason - you can either try loading the scenes by using the additive mode or by instantiating the player on scene load in correct position.

Building/constructing buildings at runtime

I'm playing around with an FPS where I want my player(s) to be able to build/construct their own buildings from scratch. I've searched around for exisiting solutions/theories, but have so far been unable to find anything suitable to my needs. Please point me in the right direction if I've missed anything.
Where I am right now is that I have three prefabs; floor, wall and wall with a door opening. First I want to instantiate floor tiles which I then can put walls on, and hopefully being able to have the walls snap to the edges/corners of the floor tiles.
Can anyone please point me in the right direction for how to do this? Also, does my desired "work flow" at all make sense? Any pitfalls in there?
Thanks in advance!
UPDATE: Here's what I have in regards to instantiation prefabs, and while this works (except it's like I'm shooting walls), I would like the wall to snap to the corners/edges of the nearest floor (which has already been instantiated in the same fashion.
[RequireComponent (typeof (CharacterController))]
public class PlayerController : MonoBehaviour {
// Declare prefabs here
GameObject wallPrefab;
// Initialise variables before the game starts
void Awake () {
wallPrefab = (GameObject)Resources.Load( "WoodWall" );
}
// This happens every frame
void Update () {
if ( Input.GetButtonDown("Fire1") ) {
// Instantiate new wall
Instantiate( wallPrefab, cc.transform.position + cc.transform.forward + Vector3.up * 1.0f, wallPrefab.transform.rotation );
}
}
}
hmm... well one solution I can think of, is to have the wall raycast downwards in order to find a floor, then move to a predetermined position in relation to that floor (if it found any). Stick this in a script in the wall prefabs:
void Start()
{
var down = transform.TransformDirection (Vector3.down); //down might not actually be the down direction of your object, check to make sure
RaycastHit hit;
if (Physics.Raycast(transform.position, down, out hit) && hit.collider.gameObject.name == "myFloorName") //Maybe use tags here instead of name
{
Vector3 floorPos = hit.collider.gameObject.transform.position;
Vector3 floorSize = hit.collider.gameObject.transform.localScale;
this.transform.position = new Vector3(floorPos.x - floorSize.x/2, floorPos.y - this.tranform.localScale.y/2, floorPos.z); //These might need fiddling with to get right
}
}
void Update()
{
}
Vector3.down may not correspond to the down direction for the wall, since this can depend on the 3d model too, so you might need to fiddle with that. The position might also need fiddling with (this assumes that y corresponds to height, which might not be the case), but hopefully this gives you a rough idea of how it can be done. Also, if you don't know what the name of the floor object is, you could probably check by tags, which is probably easier.
If anything else needs clarifying, leave a comment and I'll get back to you

UNITY Lose one life when player hits the water

I want to change the number of lives every time the player hits the water. I wrote this code so far:
public var dieSound:AudioClip;
static var lives = 3;
function Start () {
}
function Update () {
if(lives == 0)
{
Application.LoadLevel("menu");
}
}
public function OnGUI()
{
GUI.backgroundColor = Color.blue;
GUI.Button (Rect (10, 10, 100, 30), "Lives: " + lives);
}
function OnControllerColliderHit (hit : ControllerColliderHit)
{
if (hit.collider.tag == "Water")
{
// play dying sound
audio.PlayOneShot(dieSound);
// show mission fail UI
GameObject.Find("MissionTXT").guiText.enabled = true;
// wait until it's ended
yield WaitForSeconds(dieSound.length + 0.01);
transform.position = GameObject.FindWithTag("Respawn").transform.position;
if (transform.position == GameObject.FindWithTag("Respawn").transform.position)
{
GameObject.Find("MissionTXT").guiText.enabled = false;
lives = lives - 1;
}
}
}
The problem is that when the player hits the water, lives change from 3 to -120. I think that happens because the player is on the water for like 6-7 seconds. So character may hits the water 120 times until he goes back to the original position (Respawn position).
Can anyone help me with that please?
The first thing that comes to mind is as follows:
On your water GameObject, add a Collider component. I would think that a BoxCollider would fit in this scenario. Don't forget to mark the Is Trigger checkbox.
On your player GameObject, add both a RigidBody and a CharacterController (since it looks like you are using the CharacterController component). Make sure to check the RigidBody's Is Kinematic checkbox. Also, give your GameObject a meaningful tag like "Player".
Back to the water GameObject, add a new script that should look something like this:
public class Water : MonoBehaviour {
void OnTriggerEnter(Collider collider) {
if(collider.CompareTag("Player")) {
collider.SendMessage("Kill");
}
}
}
Back to the player GameObject, add a new script that should look something like this:
public class Player : MonoBehaviour {
public void Kill() {
//Perform all necessary steps to kill the player such as...
//Reduce the amount of lives by 1
//Play the death sound
//etc. etc. etc.
}
}
That's the "jist" of things, or this should at least get you started. Unity has some really good documentation and practically anything you need is there, you just have to know where to look. I'm not going to go in-depth of each thing I have mentioned above because as I've said, "Unity has some really good documentation." With that in mind, I highly recommend looking into each of the things I have mentioned. Hope this helps! =)

Unity3D OffNavMesh jump issue

I have set up Unity navigation meshes (four planes), navigation agent (sphere) and set up automatic and manual off mesh links. It should now jump between meshes. It does jump between meshes, but it does that in straight lines.
In other words, when agent comes to an edge, instead of actually jumping up (like off mesh link is drawn) it just moves straight in line but a bit faster. I tried moving one plane higher than others, but sphere still was jumping in straight line.
Is it supposed to be like this? Is it possible to set up navigation to jump by some curve? Or should I try to implement that myself?
I came by this question, and had to dig through the Unity sample. I just hope to make it easier for people by extracting the important bits.
To apply your own animation/transition across a navmesh link, you need to tell Unity that you will handle all offmesh link traversal, then add code that regularly checks to see if the agent is on an offmesh link. Finally, when the transition is complete, you need to tell Unity you've moved the agent, and resume normal navmesh behaviour.
The way you handle link logic is up to you. You can just go in a straight line, have a spinning wormhole, whatever. For jump, unity traverses the link using animation progress as the lerp argument, this works pretty nicely. (if you're doing looping or more complex animations, this doesn't work so well)
The important unity bits are:
_navAgent.autoTraverseOffMeshLink = false; //in Start()
_navAgent.currentOffMeshLinkData; //the link data - this contains start and end points, etc
_navAgent.CompleteOffMeshLink(); //Tell unity we have traversed the link (do this when you've moved the transform to the end point)
_navAgent.Resume(); //Resume normal navmesh behaviour
Now a simple jump sample...
using UnityEngine;
[RequireComponent(typeof(NavMeshAgent))]
public class NavMeshAnimator : MonoBehaviour
{
private NavMeshAgent _navAgent;
private bool _traversingLink;
private OffMeshLinkData _currLink;
void Start()
{
// Cache the nav agent and tell unity we will handle link traversal
_navAgent = GetComponent<NavMeshAgent>();
_navAgent.autoTraverseOffMeshLink = false;
}
void Update()
{
//don't do anything if the navagent is disabled
if (!_navAgent.enabled) return;
if (_navAgent.isOnOffMeshLink)
{
if (!_traversingLink)
{
//This is done only once. The animation's progress will determine link traversal.
animation.CrossFade("Jump", 0.1f, PlayMode.StopAll);
//cache current link
_currLink = _navAgent.currentOffMeshLinkData;
//start traversing
_traversingLink = true;
}
//lerp from link start to link end in time to animation
var tlerp = animation["Jump"].normalizedTime;
//straight line from startlink to endlink
var newPos = Vector3.Lerp(_currLink.startPos, _currLink.endPos, tlerp);
//add the 'hop'
newPos.y += 2f * Mathf.Sin(Mathf.PI * tlerp);
//Update transform position
transform.position = newPos;
// when the animation is stopped, we've reached the other side. Don't use looping animations with this control setup
if (!animation.isPlaying)
{
//make sure the player is right on the end link
transform.position = _currLink.endPos;
//internal logic reset
_traversingLink = false;
//Tell unity we have traversed the link
_navAgent.CompleteOffMeshLink();
//Resume normal navmesh behaviour
_navAgent.Resume();
}
}
else
{
//...update walk/idle animations appropriately ...etc
Its recommended to solve your problems via animation. Just create a Jump animation for your object, and play it at the correct time.
The position is relative, so if you increase the Y-position in your animation it will look like the object is jumping.
This is also how the Unity sample is working, with the soldiers running around.
Not sure what version of unity you are using but you could also try this, I know it works just fine in 4:
string linkType = GetComponent<NavMeshAgent>().currentOffMeshLinkData.linkType.ToString();
if(linkType == "LinkTypeJumpAcross"){
Debug.Log ("Yeah im in the jump already ;)");
}
also just some extra bumf for you, its best to use a proxy and follow the a navAgent game object:
Something like:
AIMan = this.transform.position;
AI_Proxy.transform.position = AIMan;
And also be sure to use:
AI_Proxy.animation["ProxyJump"].blendMode = AnimationBlendMode.Additive;
If you are using the in built unity animation!
K, that's my good deed for this week.
Fix position in update()
if (agent.isOnOffMeshLink)
{
transform.position = new Vector3(transform.position.x, 0f, transform.position.z);
}