Syncing a raycast over the network (Mirror/Unity) - c#

I have a very simple building script for a first person survival game for testing, the host and client can press 'T' and it will spawn a cube onto the terrain which moves around with the raycast hit (coming out from the players camera)
For the host, it works fine, the client is able to see it move around with the network transform attached to the cube.
However for the client, currently it will spawn but it seems to spawn in the middle of the map as if it is not detecting the clients raycast? (this is from the hosts perspective, it is fine what the client see's) or either it is fighting with the host's raycast (not quite sure) It also looks like it is fighting for the clients raycasted position potentially, the cube is flashing on and and off occasionally very very quickly.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using UnityEngine.SceneManagement;
public class SteamBuildScript : NetworkBehaviour
{
public GameObject[] list = new GameObject[3];
public GameObject hand;
public LayerMask layer;
public Material handMat;
public Material readyMat;
public Material placeMatWall;
public Material placeMatFoundation;
public Material placeMatFloor;
public MeshCollider box;
int index = 0;
public float foundationX;
public float foundationY;
public float foundationZ;
public float wallX;
public float wallY;
public float wallZ;
public float floorX;
public float floorY;
public float floorZ;
public float wallXRotation, wallYRotation, wallZRotation;
public bool activeGui;
public float moveSpeed = 0.1f;
public GameObject hammer;
public Inventory inv;
public GameObject cam;
public AudioSource source;
public bool hasInit = false;
RaycastHit hit = new RaycastHit();
[Command]
private void CmdInitialize()
{
RpcInit();
}
[Command]
private void CmdChangePos()
{
RpcChangePos();
}
[Command]
private void CmdTestButton()
{
RpcTestbutton();
}
[ClientRpc]
private void RpcInit()
{
hand = Instantiate(list[index]);
NetworkServer.Spawn(hand, connectionToClient);
hand.GetComponent<MeshRenderer>().material = readyMat;
hand.GetComponent<MeshRenderer>().enabled = false;
box = hand.GetComponent<MeshCollider>();
box.enabled = false;
}
[ClientRpc]
private void RpcChangePos()
{
hand.transform.position = hit.point + new Vector3(0, foundationY / 2, 0);
hand.name = "Test";
}
[ClientRpc]
public void RpcTestbutton()
{
index = 1;
Destroy(hand);
hand = Instantiate(list[index]);
NetworkServer.Spawn(hand, connectionToClient);
box = hand.GetComponent<MeshCollider>();
if (hand.tag == "Foundation")
{
hand.GetComponent<MeshRenderer>().material = readyMat;
}
else if (hand.tag == "Wall")
{
hand.GetComponent<MeshRenderer>().material = readyMat;
}
else if (hand.tag == "Floor")
{
hand.GetComponent<MeshRenderer>().material = readyMat;
}
box.enabled = false;
}
private void Update()
{
if (!isLocalPlayer) { return; }
if (hammer.activeInHierarchy)
{
if (SceneManager.GetActiveScene().name == "ProcGame" && !hasInit)
{
{
CmdInitialize();
hasInit = true;
}
}
if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, 20, layer))
{
if (hit.collider.transform != hand.transform)
{ // if we look ground
if (hit.collider.name == "Terrain Chunk")
{
if (hit.collider.transform != hand.transform && hand.tag == "Foundation")
{
CmdChangePos();
}
else
{
Debug.Log("You cant place it without foundation");
}
}
}
}
if (Input.GetKeyDown(KeyCode.T))
{
CmdTestButton();
}
}
}
}
I have gone back and forth between commands and rpc's for everything, I still get a bit confused over all of that
The cube does have a network identity/transform.
The script is on a player object which has a network Identity.
I have tried swapping client to server/server to client
I have tried using [command(requires authority = null)] instead of just [command]
I have switched on and off the tick box for client authority on the cube that spawns
The code will look a bit off probably, I have just been trying everything possible that I know and cannot seem to get this to sync correctly.
Thanks a bunch in advanced out there to anyone who can help :)

Related

Unity C# How to make aiming bool = true, when public void Aim() is active

Here is the code for the weapon
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeaponInfo : MonoBehaviour
{
[SerializeField] private Weapon weapon;
[SerializeField] private Transform muzzle;
[Header("Transform")]
public Transform playerCamera;
public Transform DefaultWeaponPos;
public Transform ADSWeaponPos;
AudioSource shootingSound;
[SerializeField] private AudioClip[] pistolClips = default;
private float timeSinceLastShot = 0f;
[HideInInspector] public bool aiming = false;
private void Start()
{
shootingSound = GetComponent<AudioSource>();
Player.shootInput += Shoot;
Player.reloadInput += StartReload;
Player.aimInput += Aim;
aiming = Player.shootInput != null;
}
public void StartReload()
{
if (!weapon.reloading)
StartCoroutine(Reload());
}
private IEnumerator Reload()
{
weapon.reloading = true;
shootingSound.PlayOneShot(pistolClips[2]);
yield return new WaitForSeconds(weapon.reloadTime);
weapon.currentAmmo = weapon.magSize;
weapon.reloading = false;
}
public void Aim()
{
aiming = true;
}
private bool CanShoot() => !weapon.reloading && timeSinceLastShot > 1f / (weapon.fireRate / 60f);
public void Shoot()
{
if(weapon.currentAmmo > 0)
{
if (CanShoot())
{
if (Physics.Raycast(playerCamera.position, playerCamera.forward, out RaycastHit hitInfo, weapon.maxDistance))
{
Debug.DrawLine(playerCamera.transform.position, hitInfo.point, Color.red, 10f);
print(hitInfo.transform.name);
}
shootingSound.PlayOneShot(pistolClips[0]);
weapon.currentAmmo--;
timeSinceLastShot = 0;
OnGunShot();
}
} else if (!weapon.reloading) shootingSound.PlayOneShot(pistolClips[1]);
}
private void Update()
{
timeSinceLastShot += Time.deltaTime;
Debug.DrawRay(playerCamera.position, playerCamera.forward);
transform.position = aiming ? ADSWeaponPos.position : DefaultWeaponPos.position;
}
private void OnGunShot()
{
}
}
Basically what I want is to make it so that when the player is Aiming it changes the weapon position to ADS, but when the player is not aiming it changes it back. I tried to make it so that when aiming = Aim != null but it didnt work, I also tried other methods but I dont know what to do since I tried looking for a solution but didnt find any
And here's the code for invoking the method in the Player Script
if (Input.GetKey(aimKey))
{
aimInput?.Invoke();
}
The aimInput is a public static Action
I figured it out I just need to create another method called sideAimInput in the player, and it makes it set to aiming = false in the weaponInfo script

I want to make my player move from point A to point B, execute animation and come back

I want to make my player move from point A to point B, execute animation and come back I am making a turned based battle system and I want my player character to move to the enemy character execute the attack and come back to its original transform. I want this done in a click of a button and I just can't seem to figure it out. I tried vector2.lerp and MoveTowards but both had to be in update to work or maybe I just missed something because I am relatively new to Unity
Source code below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using CodeMonkey.HealthSystemCM;
public class BattleHandler : MonoBehaviour
{
public GameObject playerCharacter;
public GameObject enemyCharacter;
private GameObject playerPrefab;
private GameObject enemyPrefab;
public CharacterBase playerCharacterBase;
public CharacterBase enemyCharacterBase;
private HealthSystemComponent enemyHealth;
private HealthSystemComponent playerHealth;
private ParticleSystem addStrenghtParticle;
private Transform playerPosition;
private Transform enemyPosition;
private void Start()
{
SpawnCharacter(true);
SpawnCharacter(false);
playerCharacterBase = playerPrefab.GetComponent<CharacterBase>();
enemyCharacterBase = enemyPrefab.GetComponent<CharacterBase>();
enemyHealth = enemyPrefab.GetComponent<HealthSystemComponent>();
playerHealth = playerPrefab.GetComponent<HealthSystemComponent>();
addStrenghtParticle = playerPrefab.GetComponentInChildren<ParticleSystem>();
playerPosition = playerPrefab.GetComponent<Transform>();
enemyPosition = enemyPrefab.GetComponent<Transform>();
}
public void AttackMovement()
{
playerPosition.position = Vector2.Lerp(playerPosition.position, enemyPosition.position, Time.deltaTime);
}
public void AddStrenght()
{
playerCharacterBase.AddStrenght();
addStrenghtParticle.Play();
}
public void AttackSequence()
{
Invoke("PlayerAttack", 0f);
Invoke("EnemyAttack", 1f);
}
private void PlayerAttack()
{
float damage1 = playerCharacterBase.CalcDamage();
playerCharacterBase.AttackAnim();
enemyCharacterBase.HurtAnim();
enemyHealth.Damage(damage1);
enemyHealth.Die();
}
private void EnemyAttack()
{
float damage2 = enemyCharacterBase.CalcDamage();
enemyCharacterBase.AttackAnim();
playerCharacterBase.HurtAnim();
playerHealth.Damage(damage2);
playerHealth.Die();
}
public void SpawnCharacter(bool isPlayerTeam)
{
Vector3 position;
if (isPlayerTeam)
{
position = new Vector3(-6, 15);
playerPrefab = Instantiate(playerCharacter, position, Quaternion.identity);
}
else
{
position = new Vector3(4, 15);
enemyPrefab = Instantiate(enemyCharacter, position, Quaternion.identity);
}
}
}

Why the when playing animation state it's executing the play twice in a row instead once?

The script is attached to empty gameobject
At this line i'm using the mouse left button to fire a bullet one time.
If i'm using a break point it will shot one bullet once. but if i'm not using a break point it will shot two bullets in a row one after the other.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
public class Shooting : MonoBehaviour
{
public CinemachineVirtualCamera cmf;
[Header("Main")]
public Rigidbody bulletPrefab;
public float launchForce = 700f;
public bool automaticFire = false;
public float bulletDestructionTime;
public bool go = false;
[Space(5)]
[Header("Slow Down")]
public float maxDrag;
public float bulletSpeed;
public bool bulletsSlowDown = false;
public bool overAllSlowdown = false;
[Range(0, 1f)]
public float slowdownAll = 1f;
public List<Transform> firePoints = new List<Transform>();
public Animator anim;
private void Start()
{
if (anim != null)
{
anim.SetBool("Shooting", true);
}
}
public void Update()
{
if (overAllSlowdown == true)
{
Time.timeScale = slowdownAll;
}
if (firePoints.Count > 0))
{
for (int i = 0; i < firePoints.Count; i++)
{
if (Input.GetMouseButton(0))
{
anim.SetTrigger("Shoot");
}
if (Input.GetMouseButton(1))
{
cmf.enabled = false;
}
if (go)
{
LaunchProjectile(firePoints[i]);
go = false;
}
}
}
}
private void LaunchProjectile(Transform firePoint)
{
Rigidbody projectileInstance = Instantiate(
bulletPrefab,
firePoint.position,
firePoint.rotation);
projectileInstance.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
cmf.enabled = true;
cmf.Follow = projectileInstance.transform;
cmf.LookAt = projectileInstance.transform;
projectileInstance.AddForce(new Vector3(0, 0, 1) * launchForce);
if (bulletsSlowDown == true)
{
if (projectileInstance != null)
{
StartCoroutine(AddDrag(maxDrag, bulletSpeed, projectileInstance));
}
}
}
IEnumerator AddDrag(float maxDrag, float bulletSpeed, Rigidbody rb)
{
if (rb != null)
{
float current_drag = 0;
while (current_drag < maxDrag)
{
current_drag += Time.deltaTime * bulletSpeed;
rb.drag = current_drag;
yield return null;
}
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
rb.drag = 0;
}
}
}
This script is attached to my player with animator and i'm using this method to reference event i added to animation in the animator controller. when the event happens the variable bool flag go is set to true.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThrowObject : MonoBehaviour
{
public Shooting shooting;
public void ThrowEvent()
{
shooting.go = true;
}
}
This is a screenshot of the animator controller.
I added a new state name Throwing with two transitions from and to the Grounded state.
The Grounded state is playing idle animation.
In the transition from the Grounded to the Throwing i added a condition name Shoot type trigger.
In the transition from the Throwing state to the Grounded there is no any conditions.
Afaik animator triggers are stackable!
So since you call this in a for loop it might happen that it adds multiple triggers at once but each transition only consumes one at a time!
What I ended up using in combination with triggers in an animator is this script
public class AnimatorTriggerResetter : StateMachineBehaviour
{
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
foreach(var p in animator.parameters)
{
if (p.type == AnimatorControllerParameterType.Trigger)
{
animator.ResetTrigger(p.name);
}
}
}
}
attach this to no specific state at all but directly to the Basic layer of your AnimatorController itself => It is called for each and every state that is entered => resets all existing triggers.
In your specific case though, why not rather pull the general calls out of the loop and rather make it
if (firePoints.Count > 0 && go)
{
if (Input.GetMouseButton(0))
{
anim.SetTrigger("Shoot");
}
if (Input.GetMouseButton(1))
{
cmf.enabled = false;
}
for (int i = 0; i < firePoints.Count; i++)
{
LaunchProjectile(firePoints[i]);
}
go = false;
}

How can I make my jump and land animations play reliably?

I can't make my jump and land animations play reliably Currently the jump animation doesn't play and the land animation seems to play randomly.
I've been working with unity 3d for several months but I am new to using animations. I may just be missing some basic information.
At one point the jump animation would play, but only after the player was in the air. How do I make animations play at the right time reliably?
This is my player movement script where I'm currently calling the jump and land animations from.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMvmt : MonoBehaviour
{
public Rigidbody player;
public float sideForce = 5;
public float jumpForce = 5;
public float fallingForce = 10;
public SphereCollider col;
public LayerMask groundLayers;
public Animator jump;
public Animator land;
private void Start()
{
jump = GetComponent<Animator>();
land = GetComponent<Animator>();
}
private void Update()
{
if ( Input.GetKey("a"))
{
player.AddForce(-sideForce, 0, 0);
}
if ( Input.GetKey("d"))
{
player.AddForce(sideForce, 0, 0);
}
if (IsGrounded() && Input.GetKey("w"))
{
jump.SetBool("playJump",true);
Invoke("StopJumpAnimation", .4f);
player.AddForce(0, jumpForce, 0);
}
OncollisionEnter();
if (IsGrounded() == false)
{
player.AddForce(0, -fallingForce, 0);
}
}
private bool IsGrounded()
{
return Physics.CheckCapsule(col.bounds.center,
new Vector3(col.bounds.center.x,
col.bounds.min.y, col.bounds.center.z), col.radius * .9f, groundLayers);
}
void StopJumpAnimation()
{
jump.SetBool("playJump", false);
}
void StopLandAnimation()
{
land.SetBool("playLand", false);
}
void OncollisionEnter()
{
land.SetBool("playLand", true);
Invoke("StopLandAnimation", .3f);
}
}
I can show you pictures of the animator if that helps. I might just need a basic explanation of how to use the animator since I'm new to animation.

How would I implement navmesh pathfinding into this AI following code. C# Unity

I have this code that makes the enemy follow my player(And attack etc) but im not sure how to add navmesh into this so it can navigate obstacles. Currently, it goes forward and gets stuck on walls and obstacles.
I have never used navmesh before.
How would I implement navmesh pathfinding into this code.
Thank you.
using UnityEngine;
using System.Collections;
public class wheatleyfollow : MonoBehaviour {
public GameObject ThePlayer;
public float TargetDistance;
public float AllowedRange = 500;
public GameObject TheEnemy;
public float EnemySpeed;
public int AttackTrigger;
public RaycastHit Shot;
void Update() {
transform.LookAt (ThePlayer.transform);
if (Physics.Raycast (transform.position, transform.TransformDirection (Vector3.forward), out Shot)) {
TargetDistance = Shot.distance;
if (TargetDistance < AllowedRange) {
EnemySpeed = 0.05f;
if (AttackTrigger == 0) {
transform.position = Vector3.MoveTowards (transform.position, ThePlayer.transform.position, EnemySpeed);
}
} else {
EnemySpeed = 0;
}
}
if (AttackTrigger == 1) {
EnemySpeed = 0;
TheEnemy.GetComponent<Animation> ().Play ("wheatleyattack");
}
}
void OnTriggerEnter() {
AttackTrigger = 1;
}
void OnTriggerExit() {
AttackTrigger = 0;
}
}
To start off, we will require a NavMeshAgent on the object that will hold this script and we will then save a reference to the agent. We will also need a NavMeshPath to store our path (This isn't an attachable component, we will create it within the code).
All we need to do is update the path using CalculatePath and SetPath. You may need to fine tune the code some, but this is the very basics. You can use CalculatePath to generate a path then decide if you want to execute that path by using SetPath.
Note: We could use SetDestination, but if you have many AI units it can become slow if you need instant paths, which is why I normally use CalculatePath and SetPath.
Now all that is left is to make your navmesh Window -> Navigation. In there you can finetune your agents and areas. One required step is to bake your mesh in the Bake tab.
Unity supports navmeshes on components for prefabs and other things, however, these components are not yet built into Unity, as you will need to download them into your project.
As you can see all of your speed and movement has been removed since it is now controlled by your NavMeshAgent.
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class wheatleyfollow : MonoBehaviour {
public GameObject ThePlayer;
public float TargetDistance;
public float AllowedRange = 500;
public GameObject TheEnemy;
public int AttackTrigger;
public RaycastHit Shot;
private NavMeshAgent agent;
private NavMeshPath path;
void Start() {
path = new NavMeshPath();
agent = GetComponent<NavMeshAgent>();
}
void Update() {
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out Shot)) {
TargetDistance = Shot.distance;
if (TargetDistance < AllowedRange && AttackTrigger == 0) {
agent.CalculatePath(ThePlayer.transform.position, path);
agent.SetPath(path);
}
}
if (AttackTrigger == 1) {
TheEnemy.GetComponent<Animation>().Play("wheatleyattack");
}
}
void OnTriggerEnter() {
AttackTrigger = 1;
}
void OnTriggerExit() {
AttackTrigger = 0;
}
}
Side Note: You should remove any using's that you are not using, as this can bloat your final build.

Categories