Logic issue syncronizing flip of gameobjects child through Photon - unity3d

In my project I am creating a "Madness combat"-like game but multiplayer through Photon network. I am currently having a issue were the wrong clients "hand + gun" is flipped.
When client 1 flips his character his hand is flipped locally and client 2's hand is flipped locally.
Pictures: https://imgur.com/a/NkP3KEF
I beleve the issue is the following:
In my flip logic of the character I call for a PunRPC that flips the wrong clients hand.
Main character flip function:
void FixedUpdate()
{
if (!disableMove)
{
//if (!devTesting)
//{
if (photonView.isMine)
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, checkRadius, whatIsGround);
moveInput = Input.GetAxis("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if (facingRight == false && moveInput > 0)
{
photonView.RPC("Flip", PhotonTargets.AllBuffered);
photonView.RPC("Fliphand", PhotonTargets.AllBuffered); //FLIPS HAND+GUN
//Flip();
}
else if (facingRight == true && moveInput < 0)
{
photonView.RPC("Flip", PhotonTargets.AllBuffered);
photonView.RPC("Fliphand", PhotonTargets.AllBuffered); //FLIPS HAND+GUN
//Flip();
}
}
}
The above function calls for this two PunRPC functions.
Character flip:
[PunRPC]
void Flip()
{
facingRight = !facingRight; //toggle flipping
GetComponent<SpriteRenderer>().flipX = !facingRight;
}
Hand flip (child of above gameobject):
[PunRPC]
void Fliphand()
{
child = GameObject.Find("Hand");
sight = GameObject.Find("Granny_warr_lasersight");
deagle = GameObject.Find("deagle_animation");
facingRight2 = !facingRight2;
child.GetComponent<SpriteRenderer>().flipY = !facingRight2;
sight.GetComponent<SpriteRenderer>().flipY = !facingRight2;
deagle.GetComponent<SpriteRenderer>().flipY = !facingRight2;
}
I think the issue is since two clients join the room and both clients have child objects called "Hand" etc. And by calling this PunRPC from client 1 it flips the "Hand" on client 2's screen.
Can anyone help me sort this issue out? I will try make it more clear if needed and provide more pictures if requested.
NOTE: Character flip works fine on both clients.

I solved it thanks to the link provided by Tomer Shahar!
[PunRPC]
void Fliphand()
{
//if (photonView.isMine)
//{
//child = GameObject.Find("Hand");
//sight = GameObject.Find("Granny_warr_lasersight");
//deagle = GameObject.Find("deagle_animation");
child = player.transform.Find("Hand").gameObject;
sight = child.transform.Find("Granny_warr_lasersight").gameObject;
deagle = child.transform.Find("deagle_animation").gameObject;
facingRight2 = !facingRight2;
child.GetComponent<SpriteRenderer>().flipY = !facingRight2;
sight.GetComponent<SpriteRenderer>().flipY = !facingRight2;
deagle.GetComponent<SpriteRenderer>().flipY = !facingRight2;
//}
}
The //disabled code is the old code that didnt work for my aplication. The new one works like a charm!

Related

How do you spawn a gameobject in netcode?

So I'm trying to implement shooting in a multiplayer game. I want to spawn a bullet out of a weapon. On the server side it works, but as I shoot as a client it says [Netcode] Behaviour index was out of bounds. Did you mess up the order of your NetworkBehaviours?.
The bullet is a prefab which I included in NetworPrefabs. The prefab also has a Network Object component.
This is where I call shoot()
if(shot == false && transform.GetChild(0).GetComponent<Weapon>().bulletInMag > 0 && reloading == false)
{
shot = true;
StartCoroutine(shoot());
}
This is what shoot() does
IEnumerator shoot()
{
firePoint = transform.GetChild(0).GetChild(0).transform;
weapon = transform.GetChild(0).gameObject.GetComponent<Weapon>();
if (weapon.threeShot)
{
for(int i=0; i<3; i++)
{
if(NetworkManager.Singleton.IsServer)
{
spawnBullet();
} else
{
spawnBulletServerRpc();
}
weapon.bulletInMag -= 1;
SharedFunctions.Instance.updateAmmoCount();
yield return new WaitForSeconds(0.2f);
}
yield return new WaitForSeconds(weapon.shootingIntervall);
shot = false;
}
else
{
if (NetworkManager.Singleton.IsServer)
{
spawnBullet();
}
else
{
spawnBulletServerRpc();
}
weapon.bulletInMag -= 1;
SharedFunctions.Instance.updateAmmoCount();
yield return new WaitForSeconds(weapon.shootingIntervall);
shot = false;
}
}
void spawnBullet()
{
GameObject bullet = Instantiate(prefabBullet, firePoint.position, transform.rotation);
bullet.GetComponent<Rigidbody2D>().AddForce(firePoint.up * weapon.bulletSpeed, ForceMode2D.Impulse);
bullet.GetComponent<NetworkObject>().Spawn();
}
[ServerRpc]
void spawnBulletServerRpc()
{
spawnBullet();
}
I'm thankful for any help.
I finally fixed the issue. The error [Netcode] Behaviour index was out of bounds. Did you mess up the order of your NetworkBehaviours? basically means that there is a mismatch between server and client representation of a network object.
At the beginning of my Player script I wrote:
public override void OnNetworkSpawn()
{
if (!IsOwner) Destroy(this);
}
which deletes the component if your not the owner.
So when I joined I had the script on client side but it wasn't there on server side. So when I tried to make an ServerRpc it didn't know where to send it. The same goes for ClientRpc.
A very helpful source was the Unity Mulitplayer Networking Discord: https://discord.gg/buMxnnPvTb

Unity Mobile - How to make fluid swipe to move movement?

So I am working on a personal project, and I had this idea for a movement that I don't know to realize. So the project(game) is for phone, and what I want to, is to when I swap in a direction, that vector becomes its closest axis (ex: 70 degrees will be (0,1) while 35 will be (1,0)). So when I get a swipe, I want my character to move in that direction. MAIN ISSUE is I don't know how to make him dash to that location. I know how to make him TELEPORT, but not how to make him DASH. I tried going for Vector2.Lerp, but it is a bit buggy and feels like character does not react on time. Here is the script:
private void Update()
{
if (gameObject.GetComponent<OnDeath>().isDead == false)
{
if (trigger)
{
fractionOfWayThere += 0.1f;
transform.position=Vector2.Lerp(transform.position, finish, fractionOfWayThere);
isMoving = true;
}
if ((Vector2)transform.position == finish)
{
isMoving = false;
animator.SetBool("isMoving", false);
}
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
startPos = cam.ScreenToWorldPoint(touch.position);
}
if (touch.phase == TouchPhase.Ended)
{
endPos = cam.ScreenToWorldPoint(touch.position);
destination = endPos - startPos;
if (Mathf.Abs(destination.x) > Mathf.Abs(destination.y))
{
destination = new Vector2(Funkcija(destination.x), 0);
}
if (Mathf.Abs(destination.x) < Mathf.Abs(destination.y))
{
destination = new Vector2(0, Funkcija(destination.y));
}
if (Mathf.Abs(destination.x) == Mathf.Abs(destination.y))
{
destination = new Vector2(Funkcija(destination.x), 0);
}
finish = (Vector2)transform.position + destination.normalized;
if (Physics2D.OverlapCircle(finish, 0.1f, spike) != null)
{
if (finish.x < transform.position.x)
{
sr.flipX = true;
}
else if (finish.x > transform.position.x)
sr.flipX = false;
trigger = true;
fractionOfWayThere = 0;
isMoving = true;
animator.SetBool("isMoving", true);
}
else
{
finish = transform.position;
}
}
}
}
}
Most of the code is getting the swipe into its axis and flipping sprite, only thing regarding the movement is this:
if (trigger)
{
fractionOfWayThere += 0.1f;
transform.position=Vector2.Lerp(transform.position, finish, fractionOfWayThere);
isMoving = true;
}
Playtesting, this feels realy weird. I tried Vector2.MovePosition(), rb.AddForce() and rb.MovePosition. Any other way to do this, or even using those functionts I have not thought of? All feedback would be highly appreciated!
Lerp is a good Start, but it's linear. A Dash would typically start instantly and slow down over time.
to achieve framerate independent movement, you should use Time.deltaTime in Update. It's neglectable in FixedUpdate as these occur at (almost) fixed timesteps.
You can achieve that by adding an AnimationCurve to your code:
[...]
public AnimationCurve dashCurve = new AnimationCurve.Linear(0,0,1,1); // modify this in inspector!
[...]
if (trigger)
{
fractionOfWayThere += 0.5f * Time.deltaTime; // fractionOfWayThere takes 0.5 second to grow from 0 to 1.
transform.position = Vector2.Lerp(transform.position, finish, dashCurve.Evaluate(fractionOfWayThere));
isMoving = true;
}
Set up the AnimationCurve like this:

Resetting Character Position in Unity is Not Working

I'm working on a sports game where if a player skates into the goalie crease, I want all the player positions to reset to the center of the ice and do a three second countdown before play resumes.
I have tried to hardcode the starting position for the main player in a variable called PlayerStart and I call Player.transform.position = PlayerStart. When I did this, the player didn't move so I tried to switch the object I was setting as the player. This did what I wanted, but the mouse functionality changes for some reason and when the countdown ends, the player just goes right back to the position they were in before the crease violation was called.
Other things I've tried:
transform.SetPositionAndRotation
PlayerStart = Player.transform.position (instead of hard coding the numbers in)
Here is my code:
public class Crease : MonoBehaviour
{
private float Delay = 0;
private bool CreaseViolated = false;
private GameObject Player;
public Vector3 PlayerStart;
// Start is called before the first frame update
void Start()
{
Ring = GameObject.Find("Ring");
Player = GameObject.Find("FPSController");
PlayerStart = new Vector3(29.75f, 6.03999996f, 4.42000008f);
}
// Update is called once per frame
void Update()
{
Delay -= Time.deltaTime;
if (CreaseViolated)
{
CreaseViolation();
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name != "Ring"
&& other.gameObject.name != "TeamGoalie"
&& other.gameObject.name != "OpponentGoalie")
{
CreaseViolated = true;
Delay = 3;
}
}
void CreaseViolation()
{
if (Delay > 0)
{
PlayerTip.GetComponent<PickupRing>().HasRing = false;
Opponent.GetComponent<AI>().HasRing = false;
Ring.transform.parent = null;
}
else
{
text.text = " ";
if (CreaseViolated)
{
Debug.Log("Player position before: " + Player.transform.position);
Player.transform.position = PlayerStart;
Debug.Log("Player position after: " + Player.transform.position);
//Player.transform.SetPositionAndRotation(PlayerStart + new Vector3(0f, 0.800000012f, 0f), new Quaternion(0f, -0.707106829f, 0f, 0.707106829f) + new Quaternion(0f, 0f, 0f, 1f));
GameObject.Find("Countdown").GetComponent<CountdownText>().timeRemaining = 4;
CreaseViolated = false;
}
}
}
}
Here is a short YouTube video showing my code and the demo: https://www.youtube.com/watch?v=mZt_4AppBh8
this problem is all solved now thanks to an awesome person at my university helping me out! The solution was disabling the CharacterController before repositioning the player and then enabling it again after.
So this:
Player.transform.position = PlayerStart;
in the CreaseViolation function becomes
cc.enabled = false;
PlayerController.transform.position = PlayerControllerStart;
cc.enabled = true;
with cc being declared earlier as
private CharacterController cc;
and in the start function I assigned it with the value
cc = Player.GetComponent<CharacterController>();
with PlayerController being set to the FPSController.
I renamed Player to PlayerController for more clarity.
Hopefully this helps anyone having the same problem I was having!
The Above answer works perfectly but might bring some issues when doing networked changes in player transform positions.
The reason the transform change doesn't work without disabling the Character Controller is because the character controller overrides the transforms.
To Disable this and enable an Auto Synchronization between transforms GoTo:
Edit > Project Settings > Physics > (Enable Auto Sync Transforms)

Moving object with raycast issues

I have written a script where a gameobject is intended to move to a raycast.point thrown from the player camera. For the most part this works fine, however there are times (approximately when camera is 45 degrees up from the object) when the object rapidly moves towards the camera (i.e. raycast source).
I have tried a number of approaches attempting to resolve this, however I can’t seem to dig out the root of this issue. Managed to prevent this from occurring by deactivating the collider attached to the object being moved. However I need the collider for various reasons so this approach is not appropriate.
If anyone can provide any pointers as to where I am going wrong I would be incredibly grateful.
NB: coding in uJS
Many thanks in advance, Ryan
function FixedUpdate() {
if (modObj != null && !guiMode) {
//Panel Control
if (!selectObjPanel.activeSelf && !modifySelectObjPanel.activeSelf) //if the selectpanel not open and modSelect not already activated
{
activateModSelectObjPanel(true); //activate it
} else if (selectObjPanel.activeSelf) {
activateModSelectObjPanel(false);
}
//Move
if (Input.GetKey(KeyCode.E)) {
if (Input.GetKeyDown(KeyCode.E)) {
// modObj.GetComponent(BoxCollider).enabled = false;
modObj.GetComponent(Rigidbody).isKinematic = true;
modObj.GetComponent(Rigidbody).useGravity = false;
//
initPos = modObj.transform.position;
var initRotation = modObj.transform.rotation;
}
moveObject(modObj, initPos, initRotation);
} else {
// modObj.GetComponent(BoxCollider).enabled = true;
modObj.GetComponent(Rigidbody).isKinematic = false;
modObj.GetComponent(Rigidbody).useGravity = true;
}
}
}
function moveObject(modObj: GameObject, initPos: Vector3, initRotation: Quaternion) {
//Debug.Log("Moving Object");
var hit: RaycastHit;
var foundHit: boolean = false;
foundHit = Physics.Raycast(transform.position, transform.forward, hit);
//Debug.DrawRay(transform.position, transform.forward, Color.blue);
if (foundHit && hit.transform.tag != "Player") {
//Debug.Log("Move to Hit Point: " + hit.point);
modifyObjGUIscript.activateMoveDisplay(initPos, hit.point);
var meshHalfHeight = modObj.GetComponent. < MeshRenderer > ().bounds.size.y / 2; //helps account for large and small objects
// Debug.Log("CurObj Mesh Min: " + meshHalfHeight);
// modObj.transform.position = hit.point; //***method 01***
// modObj.transform.position = Vector3.Lerp(initPos, hit.point, speed); //***method 02***
// modObj.transform.position = Vector3.SmoothDamp(initPos, hit.point, velocity, smoothTime); //***method 02***
var rb = modObj.GetComponent. < Rigidbody > ();
rb.MovePosition(hit.point); //***method 03***
modObj.transform.position.y = modObj.transform.position.y + meshHalfHeight + hoverHeight;
modObj.transform.rotation = initRotation;
}
}
Turns out the issue was being caused by the raycast hitting the object being moved. Resolved this by only allowing hits from the terrain to be used as points to move to.
if(foundHit && hit.transform.tag == "Terrain")

Unity3D scene not working properly and lagging after reloading

I have looked all over internet, but I cannot find a solution to my problem. I am trying to create a game and it has 3 scenes: the Game Start, Main and Game over.
The problem is that when trying to load the main level from other scenes it does not do what it has to do (e.g. jump) and the scene itself is lagging. When I try to load only the Main Scene it works ok, but after the character dies and the scene is reloaded, it starts to lag again and I cannot jump or do anything it is supposed to do.
Any ideas on what the problem might be?
using UnityEngine;
using System;
public class Player : MonoBehaviour
{
// The force which is added when the player jumps
// This can be changed in the Inspector window
public Vector2 jumpForce = new Vector2(0, 300);
private bool shouldJump = true;
// Update is called once per frame
private float jumpThreshold = 0.5f;
private float previousJumpTime;
void FixedUpdate ()
{
// Jump
float mc = MicControl.loudness;
//Debug.Log (mc);
if (mc>1.3f && shouldJump)
{
shouldJump = false;
previousJumpTime = Time.time;
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
GetComponent<Rigidbody2D>().AddForce(jumpForce);
}
if (!shouldJump)
{
if(Time.time-previousJumpTime>jumpThreshold)
{
shouldJump = true;
}
}
// Die by being off screen
Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (screenPosition.y > Screen.height || screenPosition.y < 0)
{
Die();
}
}
// Die by collision
void OnCollisionEnter2D(Collision2D other)
{
Die();
}
void Die()
{
Application.LoadLevel ("main");
}
}
You said your level is called Main, but in the code you are loading "main", i'm not sure if that's the problem, but you seem to be loading the level correctly, so check if is main or Main the exact name of the level.
Also, when compiling, make sure you have all levels checked
With provided data its impossible to say what causes low performance, but I recommend you to use Profiler tool (can be found in personal version of Unity 5) and figure out what scripts and functions are problematic.
Also, try to avoid calling GetComponent<Rigidbody2D>() in Update/FixedUpdate/LateUpdate and cache this component instead.
I second what Utamaru said.
Rigidbody2D myRigidbody2d;
void Start ()
{
myRigidbody2d = GetComponent<Rigidbody2D>(); //Do this once
}
Inside your fixed update, you can do this:
void FixedUpdate ()
{
// Jump
float mc = MicControl.loudness;
//Debug.Log (mc);
if (mc>1.3f && shouldJump)
{
shouldJump = false;
previousJumpTime = Time.time;
myRigidbody2d.velocity = Vector2.zero;
myRigidbody2d.AddForce(jumpForce);
}
if (!shouldJump)
{
if(Time.time-previousJumpTime>jumpThreshold)
{
shouldJump = true;
}
}
// Die by being off screen
Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (screenPosition.y > Screen.height || screenPosition.y < 0)
{
Die();
}
}
I can't see the rest of your code so I am not really sure that is the problem but give this a try.