How do I correctly instantiate a projectile using PUN??
Large code ahead, avoid this post if you hate reading and programming!!!
I am creating an multiplayer isometric car shooting game much like Crash of Cars for android using Unity PUN. I want to control my game with two joysticks, one for movement and another one for shooting. My car prefab looks like image below
I have created a gun with cubes put those cube pieces inside an empty Game Object. I have two different scripts, one on car and another one on Mounted Gun Muzzle part. I want muzzle part of cube to rotate around the pivot part of the gun and muzzle must face the direction of joystick. Currently attached scripts are : CarController.cs
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using Photon.Pun;
using Photon.Realtime;
public class CarController : MonoBehaviourPunCallbacks
{
public Joystick joystick;
public Button brake;
private float hor;
private float ver;
[SerializeField]
private bool isBraking;
private float currentBrakingForce;
private float steerAngle;
public float speed;
[SerializeField] private float motorSpeed;
[SerializeField] private float brakeForce;
[SerializeField] private float maxSteeringAngle;
[SerializeField] private WheelCollider frontLeftCollider;
[SerializeField] private WheelCollider frontRightCollider;
[SerializeField] private WheelCollider rearLeftCollider;
[SerializeField] private WheelCollider rearRightCollider;
[SerializeField] private Transform frontLeftWheel;
[SerializeField] private Transform frontRightWheel;
[SerializeField] private Transform rearLeftWheel;
[SerializeField] private Transform rearRightWheel;
private EventTrigger trig;
private void Start()
{
if (!photonView.IsMine) return;
joystick = GameObject.FindGameObjectWithTag("Joystick Movement").GetComponent<Joystick>();
brake = GameObject.FindGameObjectWithTag("Brake").GetComponent<Button>();
brake.gameObject.AddComponent<EventTrigger>();
trig = brake.gameObject.GetComponent<EventTrigger>();
CameraFollow _cameraWork = this.gameObject.GetComponent<CameraFollow>();
if (_cameraWork != null)
{
if (photonView.IsMine)
{
_cameraWork.OnStartFollowing();
}
}
else
{
Debug.LogError("<Color=Red><a>Missing</a></Color> CameraWork Component on playerPrefab.", this);
}
brake.onClick.AddListener(OnButtonClick);
}
private void FixedUpdate()
{
if (photonView.IsMine && PhotonNetwork.IsConnected)
GetInput();
HandleMotor();
HandleSteering();
UpdateWheels();
speed = GetComponent<Rigidbody>().velocity.magnitude / 1000;
}
private void UpdateWheels()
{
UpdateSingleWheel(frontLeftCollider, frontLeftWheel);
}
private void UpdateSingleWheel(WheelCollider collider, Transform body)
{
Vector3 pos;
Quaternion rot;
collider.GetWorldPose(out pos, out rot);
body.position = pos;
body.rotation = rot;
}
private void HandleSteering()
{
steerAngle = maxSteeringAngle * hor;
frontLeftCollider.steerAngle = steerAngle;
frontRightCollider.steerAngle = steerAngle;
}
private void HandleMotor()
{
frontLeftCollider.motorTorque = ver * motorSpeed;
frontRightCollider.motorTorque = ver * motorSpeed;
rearLeftCollider.motorTorque = ver * motorSpeed;
rearRightCollider.motorTorque = ver * motorSpeed;
currentBrakingForce = isBraking ? brakeForce : 0f;
if (isBraking)
{
ApplyBrakes();
isBraking = false;
}
}
public void ApplyBrakes()
{
frontLeftCollider.brakeTorque = currentBrakingForce;
frontRightCollider.brakeTorque = currentBrakingForce;
rearLeftCollider.brakeTorque = currentBrakingForce;
rearRightCollider.brakeTorque = currentBrakingForce;
}
private void GetInput()
{
hor = joystick.Horizontal;
ver = joystick.Vertical;
}
public void OnButtonClick()
{
isBraking = true;
}
}
ShootProjectile.cs
using UnityEngine;
using UnityEngine.UI;
using Photon.Pun;
using Photon.Realtime;
public class ShootProjectiles : MonoBehaviourPunCallbacks
{
public GameObject projectile;
public float speed;
public GameObject explosive;
public Transform muzzle;
public GameObject pivot;
public float rotationSpeed;
public float xRotationLimit;
public float xRotation = 0f;
public float mouseX, mouseY;
public Joystick shootButton;
public float waitTime;
Rigidbody rb;
private void Start()
{
shootButton = GameObject.FindGameObjectWithTag("Shoot Button").GetComponent<Joystick>();
private void Update()
{
if(photonView.IsMine && shootButton.Horizontal >=.9 || shootButton.Vertical <= .9)
{
photonView.RPC("RPC_Shoot", RpcTarget.All);
}
}
[PunRPC]
public void RPC_Shoot()
{
var obj = PhotonNetwork.Instantiate("Bullet", muzzle.position, muzzle.rotation) as GameObject;
rb = obj.GetComponent<Rigidbody>();
rb.AddForce(obj.transform.forward * speed);
}
IEnumerator ShootCooldown()
{
yield return new WaitForSeconds(waitTime);
}
}
Using above shooting script, my projectile either doesn't appears or appears a lot of times. I have PhotonView on Car,Gun Muzzle and Bullet which Ik is a bad practice. How should I deal with this problem. Also, when I press brake button, there is no mechanism to set isBraking to false as a button has no onRelease event. My movement UI is preset which means it is inside scene before player loads and I am assigning variables inside my movement and shooting script at runtime, whereas my usename and life UI which moves with player is instantiated when player appears. Is this a good practice ? I am new to multiplayer and PUN so please help me.
Things I want to be done are : rotate muzzle around pivot and make it face direction of joystick. shoot projectile when joystick is near its extreme positions on any x or y axis.
Answer by GetLitGames · Dec 08, 2020 at 03:57 AM
Yes that is fine, you will only ever have one movement UI and it can manipulate variables inside other scripts (like logic scripts on the player). Generally you may want to call methods instead of direct variable/field manipulation since directly manipulating public variables in other classes is not the best practice (unless you need the performance), since quite often that class might need to do other things internally when the variable changes (or needs to know about it changing).
Anything related to the player and would be needed in the world for any replicated player (like your name plate and life UI that you want moved with the player and all players to have) can be instantiated along with the player, since other players spawning in would need those components too.
Input related scripts and other singleton type of scripts shouldn't be in the player prefab/on the player, since when other players get replicated/spawned you don't want duplicate input scripts reading input touches etc. It's best to keep those separate, or in the UI - in any case it is good that it's not a part of the player.
Under your Start() it is missing a closing brace and wouldn't compile. But you probably knew that.
At the IF statement in your Update() remember that this happens multiple times per second (the frame rate), and you need a cooldown on the shooting. You can use Time.time and your own cooldown variable, I like to set a variable into the future like (Time.time + fractionOfseconds) and your IF() checks if Time.time is >= that variable before it works, and then sets the variable to the future again.
ShootCooldown coroutine is not a good way to do it, I would remove that although it looks like it wasn't used yet. What I described above is best and remember that Update() is generally the correct place to read input whether touches, keypress, mouse, etc. If you find later that you need to affect physics like adding force etc inside FixedUpdate instead of Update, you can set class variables within Update() that are then used in FixedUpdate to apply your physics changes.
For a mobile game with fast moving and repeating projectiles you probably won't have much success with them being networked. Also if you were going to check for collisions within the projectiles you will have a problem with all X copies of players' projectiles triggering collisions unless you account for that (making sure only one machine does the collision check). A better way to handle it is just raycast when they fire, determine whether it hit or not and then just delay taking the damage from the enemy by half a second or whatever. For better timing, you know the speed of your projectile and you can estimate the amount of time to wait before playing a hit sound and taking damage. Your code would be the same, but your projectiles would just destroy themselves on collision with anything and just be for visual effect. You could also play a destroy sound when they collide. The problem in general with collisions is that fast moving objects and small colliders may or may not collide at all - they can go through sometimes. It's better to rely on raycasts and fake whatever you need to.
Your answer
Follow this Question
Related Questions
Unity .NET 4.6 Socket Exception 2 Answers
Is it possible in Pun2 to play with player without spawning? 0 Answers
MULTIPLAYER NETWORKING 0 Answers
Multiplayer First Person Game 0 Answers
So I am trying to get a multiplayer system working. 0 Answers