I am currently building my first multiplayer game in unity and having a little bit of an issue changing/transmitting the players direction over the network.
My current player controls class is this:
using UnityEngine;
using System.Collections;
public class playerControls : MonoBehaviour {
#region
//Vars
//Movements Vars
public float runSpeed;
Rigidbody mybody;
Animator myAnimator;
bool playerDirectionE;
#endregion
// Use this for initialization
void Start () {
mybody = GetComponent<Rigidbody>();
myAnimator = GetComponent<Animator>();
playerDirectionE = true;
mybody.transform.eulerAngles = new Vector3(0, 90, 0);
}
// Update is called once per frame
void Update () {
}
void FixedUpdate()
{
float move = Input.GetAxis("Horizontal");
myAnimator.SetFloat("speed", Mathf.Abs(move)); //To intiate the charecter transition(move)
mybody.velocity = new Vector3(move * runSpeed, mybody.velocity.y, 0); //move charecter along the x axis, and keep y on gravity, not touching the z axis
if(move>0 && !playerDirectionE)
{
Flip();
}
else if(move<0 && playerDirectionE)
{
Flip();
}
}
void Flip()
{
playerDirectionE = !playerDirectionE;
Vector3 theScale = transform.localScale;
theScale.z *= -1;
transform.localScale = theScale;
}
}
I am using the following client information to send commands over the network:
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using System.Collections.Generic;
public class PlayerSyncRotation : NetworkBehaviour {
[SyncVar(hook ="OnPlayerRotSynced")]
private float syncPlayerRotation;
[SerializeField]
private Transform playerTransform;
private float lerpRate = 15;
private float lastPlayerRot;
private float threshold = 1;
private List<float> syncPlayerRotList = new List<float>();
private float closeEneough = 0.3f;
[SerializeField]
private bool userHistoricalInterpolation;
[Client]
void OnPlayerRotSynced(float latestPlayerRotation)
{
syncPlayerRotation = latestPlayerRotation;
syncPlayerRotList.Add(syncPlayerRotation);
}
[Command]
void CmdProvideRotationsToServer(float playerRot)
{
syncPlayerRotation = playerRot;
}
[Client]
void transmitRotations()
{
if (isLocalPlayer)
{
if(CheckIfBeyondThreshold(playerTransform.localScale.z, lastPlayerRot)){
lastPlayerRot = playerTransform.localScale.z;
CmdProvideRotationsToServer(lastPlayerRot);
}
}
}
bool CheckIfBeyondThreshold(float rot1, float rot2)
{
if (Mathf.Abs(rot1 - rot2) > threshold)
{
return true;
}
else
{
return false;
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
lerpRotation();
}
void FixedUpdate()
{
transmitRotations();
}
void lerpRotation()
{
if (!isLocalPlayer)
{
HistoricalInterpolation();
}
}
void HistoricalInterpolation(){
if (syncPlayerRotList.Count > 0)
{
LerpPlayerRotation(syncPlayerRotList[0]);
if(Mathf.Abs(playerTransform.localEulerAngles.z - syncPlayerRotList[0]) < closeEneough)
{
syncPlayerRotList.RemoveAt(0);
}
Debug.Log(syncPlayerRotList.Count.ToString() + "syncPlayerRotList Count");
}
}
void LerpPlayerRotation(float rotAngle)
{
Vector3 playerNewRot = new Vector3(0, 0, rotAngle);
playerTransform.rotation = Quaternion.Lerp(playerTransform.rotation, Quaternion.Euler(playerNewRot),lerpRate*Time.deltaTime);
}
}
My rotation looks fine on the client, but over the network on the second client, the rotation is broken and looks very wrong.
I have attached a link to a Webm where you can see the short video snippet of my problem HERE.
Would anyone here have any input as to what I could be doing wrong or how I could fix this issue? any suggestions would be appreciated.
Instead Using your custom code, i will like to say you that use Unet NetworkTransform. It is Unet high level API with different option of customization.
A component to synchronize the position and rotation of networked
objects(more).
Unet HLAPI are open source so you can also get the coding of the networkTransform at bitbucket.
Related
I was making a platform that bounces the player after a certain period of time when the player touches it. I attached this code component to the GameObject, which is a tilemap, and it doesn't work properly. What's wrong with my code? The code and scriptable object are as follows.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExplosionPlatform : PlatformBase
{
public float explosionPower = 20f;
public float waitTime = 1.5f;
public float colTime = 0f;
public bool isDamageOnce = false;
public Vector2 area = new Vector2(2, 2);
public Vector2 positionModify = new Vector2(1, 0);
private void OnCollisionEnter2D(Collision2D collision)
{
StartCoroutine(Explosion());
}
private IEnumerator Explosion()
{
Debug.Log("ASDF");
yield return new WaitForSeconds(waitTime);
Collider2D[] player = Physics2D.OverlapBoxAll(transform.position + (Vector3)positionModify, area, 0);
foreach (Collider2D col in player)
{
if (col.CompareTag("Player"))
{
col.gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * explosionPower, ForceMode2D.Impulse);
Damage();
isDamageOnce = true;
yield return new WaitForSeconds(3f);
isDamageOnce = false;
}
}
}
private void OnDrawGizmos()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(transform.position + (Vector3)positionModify, area);
}
}
I have confirmed that the code below works properly on GameObject, which is Unity 2d Sprite. Is there any way to use that code while using the tile map?
so I have this code :
public class ball_physics : MonoBehaviour
{
public Rigidbody ball;
public open2close claw;
public Vector3 offset;
Rigidbody rb;
private float forceMultiplier = 20;
private bool isShoot;
Vector3 start_position;
public path path;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.Sleep();
start_position = transform.position;
}
void Update()
{
// *************
}
void Shoot(Vector3 Force)
{
if (isShoot)
{
print("isshot false");
return;
}
rb.AddForce(new Vector3(Force.x, Force.y,Force.z)* forceMultiplier);
isShoot = true;
path.Instance.HideLine();
}
private void OnTriggerStay(Collider ball)
{
if (isShoot)
{
return;
}
print("ontriggerstay");
if (claw.isClosed && claw.transform.gameObject.tag == "claw" )
{
print("claw");
//rb.Sleep();
transform.position = claw.rightClaw.transform.position + offset;
Vector3 forceInit = (start_position - transform.position);
path.Instance.UpdateTrajectory(forceInit * forceMultiplier, rb, transform.position);
}
}
private void OnTriggerExit(Collider ball)
{
if (claw.isClosed && claw.transform.gameObject.tag == "claw")
{
rb.WakeUp();
}
if (claw.isClosed == false)
{
Shoot(start_position - transform.position);
}
}
}
So on my other codes I have OnStayTrigger, which basically a clawhand grabs a ball and pulls. But this code is suppose to show a trajectory line and shoot. When I shoot the first time it works. But when I try again, The clawhand can grab the ball but it doesnt show the trajectory line nor shoots it. So im guessing this is the code that needs to be fixed. How can I make OnTriggerStay work all the time. I wouldnt want to change any code because it works perfectly. How can I just keep updating the OnTriggerstay so it can work?
At first glance it appears that You never set isShoot back to false, so it would never fire again because in OnTriggerStay you have it return if isShoot = true.
I am trying to create a multiplayer TPS where I instantiate a bullet locally (in my editor) and apply force to it. Once it hits other player I would want to call that players TakeDamage RPC.
Sadly I am encountering this weird error message that I can't fix (already taking me days).
my projectile script:
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Arrow : MonoBehaviour
{
[SerializeField] float timeToDestroy;
[SerializeField] ParticleSystem hitEffect;
[SerializeField] TrailRenderer trailRenderer;
float timer;
private Rigidbody rb;
private Collider col;
private void Awake()
{
rb = GetComponent<Rigidbody>();
col = GetComponent<Collider>();
}
private void Update()
{
timer += Time.deltaTime;
if (timer > timeToDestroy) Destroy(gameObject);
}
private void OnCollisionEnter(Collision collision)
{
rb.isKinematic = true;
rb.velocity = Vector3.zero;
trailRenderer.enabled = false;
ShowHitEffect(collision.GetContact(0));
col.enabled = false;
if (collision.gameObject.CompareTag("Player"))
{
rb.isKinematic = true;
rb.velocity = Vector3.zero;
trailRenderer.enabled = false;
PhotonView pv = collision.gameObject.GetPhotonView();
Debug.Log("hit " + pv.Controller.NickName);
pv.RPC("DealDamage", RpcTarget.All, PhotonNetwork.LocalPlayer.NickName, .5f, PhotonNetwork.LocalPlayer.ActorNumber);
}
Destroy(gameObject, 2f);
}
private void ShowHitEffect(ContactPoint cp)
{
hitEffect.transform.position = cp.point;
hitEffect.transform.forward = cp.normal;
hitEffect.Emit(1);
}
}
weapon controller script
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeaponController : MonoBehaviourPunCallbacks
{
[Header("Fire Rate")]
[SerializeField] float fireRate;
float fireRateTimer;
[SerializeField] bool semiAuto;
[Header("Bullet Property")]
[SerializeField] GameObject bullet;
[SerializeField] Transform barrelPos;
[SerializeField] float bulletVelocity;
[SerializeField] byte bulletPerShot;
public AimController aim;
[SerializeField] AudioClip gunshot;
public AudioSource audioSource;
public WeaponAmmo ammo;
public ParticleSystem muzzleFlash;
public ParticleSystem hitEffect;
// Start is called before the first frame update
void Start()
{
fireRateTimer = fireRate;
}
private void Update()
{
if (!photonView.IsMine) return;
if (ShouldFire()) Fire();
}
bool ShouldFire()
{
fireRateTimer += Time.deltaTime;
if (fireRateTimer < fireRate) return false;
if (ammo.currentAmmo == 0) return false;
if (semiAuto && Input.GetKeyDown(KeyCode.Mouse0)) return true;
if (!semiAuto && Input.GetKey(KeyCode.Mouse0)) return true;
return false;
}
[PunRPC]
private void EmitMuzzleFlash()
{
muzzleFlash.Emit(1);
}
void Fire()
{
fireRateTimer = 0;
barrelPos.LookAt(aim.actualAimPos);
audioSource.PlayOneShot(gunshot);
EmitMuzzleFlash();
//photonView.RPC("EmitMuzzleFlash", RpcTarget.All);
ammo.currentAmmo--;
for (int i = 0; i < bulletPerShot; i++)
{
GameObject currentBullet = Instantiate(bullet, barrelPos.position, barrelPos.rotation);
Rigidbody rigidbody = currentBullet.GetComponent<Rigidbody>();
rigidbody.AddForce(barrelPos.forward * bulletVelocity, ForceMode.Impulse);
// Projectile Instantiate
/*{
GameObject currentProjectile = (GameObject)PhotonNetwork.Instantiate(bullet.name, barrelPos.position, barrelPos.rotation);
Rigidbody rb = currentProjectile.GetComponent<Rigidbody>();
rb.AddForce(barrelPos.forward * bulletVelocity, ForceMode.Impulse);
}*/
}
}
}
player controller script (where rpc is located)
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviourPunCallbacks
{
private PlayerStats stats = new PlayerStats();
void Start()
{
if (photonView.IsMine)
{
UIController.instance.healthSlider.maxValue = stats.maxHealth;
UIController.instance.healthSlider.value = stats.GetCurrentHealth();
}
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update()
{
FocusWindows();
}
void FocusWindows()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
Cursor.lockState = CursorLockMode.None;
}
else if (Cursor.lockState == CursorLockMode.None)
{
if (Input.GetMouseButton(0))
{
Cursor.lockState = CursorLockMode.Locked;
}
}
}
#region
[PunRPC]
public void DealDamage(string damager, float damageAmount, int actor)
{
Debug.Log("called rpc deal damage");
Debug.Log("damager " + damager + " damageAmount " + damageAmount + " actor " + actor);
TakeDamage(damager, damageAmount, actor);
}
public void TakeDamage(string damager, float damageAmount, int actor)
{
if (photonView.IsMine)
{
stats.ReduceHealth(damageAmount);
if (stats.GetCurrentHealth() == 0)
{
PlayerSpawner.Instance.Die(damager);
MatchManager.instance.UpdateStatSend(actor, 0, 1);
}
UIController.instance.healthSlider.value = stats.GetCurrentHealth();
}
}
#endregion
}
Projectile Prefab(resource)
enter image description here
Player prefab
enter image description here
Weird thing is if I go with "Instantiate Arrow in the network" the rpc is getting called. But this is not ideal since it will because it will send the transform of the projectile every time it is flying. So I just want to check in the local (arrow with no photonView) if it hits an enemy then call the rpc of that enemy to make him take damage and possibly display a fake project coming from the damager. If my approach is naive / wrong please tell me a better approach please.
I transfered the rpc call on the same script where the photonView is located as mentioned by #derHugo. I think the main problem was my understanding of RPC. I thought rpc is like a public method where you can call it outside. I think I need to reread the documentations about rpc and raise events.
Trying to run a raycast from my camera to Z = 0 that will hit objects on the TransparentFX layer and temporarily make them transparent as they are likely blocking the view of the player. But the raycast never hits anything.
Camera
https://imgur.com/lyTo8OZ
Tree
https://imgur.com/bgNiMWR
ClearSight.cs
[RequireComponent(typeof(Camera))]
public class ClearSight : MonoBehaviour
{
[SerializeField]
private LayerMask raycastLayers;
// Update is called once per frame
void Update()
{
Vector3 forward = transform.TransformDirection(Vector3.forward) * 10;
Debug.DrawRay(transform.position, forward, Color.green);
RaycastHit2D[] hits = Physics2D.RaycastAll(transform.position, transform.TransformDirection(Vector3.forward), 10f, raycastLayers);
if(hits != null && hits.Length > 0)
{
Debug.Log("Found objects blocking player!");
foreach(RaycastHit2D hit in hits)
{
Debug.Log("Making " + hit.transform.gameObject.name + " transparent!");
AutoTransparent at = hit.transform.GetComponent<AutoTransparent>();
if(at == null)
{
at = hit.transform.gameObject.AddComponent<AutoTransparent>();
}
at.MakeTransparent();
}
}
}
}
AutoTransparent.cs
[RequireComponent(typeof(SpriteRenderer))]
public class AutoTransparent : MonoBehaviour
{
[SerializeField]
private SpriteRenderer[] renderTargets;
[SerializeField]
private float transparentRecoveryTime = 0.1f;
private bool isTransparent = false;
private float transparencyTimer = 0;
private void Update()
{
if (isTransparent)
{
UpdateTransparencyTimer();
}
}
private void UpdateTransparencyTimer()
{
transparencyTimer += Time.deltaTime / transparentRecoveryTime;
if(transparencyTimer >= 1)
{
MakeOpaque();
}
}
public void MakeTransparent()
{
transparencyTimer = 0;
if (!isTransparent)
{
isTransparent = true;
foreach (SpriteRenderer renderer in renderTargets)
{
Color c = renderer.color;
renderer.color = new Color(c.r, c.g, c.b, 0.3f);
}
}
}
public void MakeOpaque()
{
isTransparent = false;
foreach(SpriteRenderer renderer in renderTargets)
{
Color c = renderer.color;
renderer.color = new Color(c.r, c.g, c.b, 1);
}
}
}
Figured it out. I was using RaycastHit2D and Physics2D.RaycastAll, which uses Vector2 parameters so the Z forward variable was being taken out of the equation. Switched up to Box Collider and Physics.RaycastAll and it works great.
I was trying to make an endless game from youtube video https://www.youtube.com/watch?v=xTTBbXYH9BA&t=585s. In the video, the code is working properly. But in my Visual Studio, it saying "Vector3 does not contain a constructor that takes four arguments".
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
private bool ismovingright = false;
[SerializeField]
float speed = 4f;
void Start ()
{
rb = this.GetComponent<Rigidbody>();
}
private Rigidbody rb;
void Update ()
{
if (Input.GetMouseButtonDown(0))
{
ChangeBoolean();
ChangeDirection();
}
}
private void ChangeBoolean()
{
ismovingright = !ismovingright;
}
private void ChangeDirection()
{
if (ismovingright)
{
rb.velocity = new Vector3(speed, 0f, 0f,);
}
else
{
rb.velocity = new Vector3(0f, 0f, speed);
}
}
}
You have a typo, there is an additional comma after 0f
if (ismovingright)
{
rb.velocity = new Vector3(speed, 0f, 0f,);
}else