Shooting like in demo project Angry Bots.

Hello all, I would like to understand or just sample code with explains "Projectile Shooting". There is a Demo asset calls Unity Angry Bots https://www.assetstore.unity3d.com/en/#!/content/1917
I like the shooting in this project. I can see how bullets throws and flies over network. I have similar project that players must see their bullets. I made projectile shooting Instantiting game object with rigidbody commponent attached. I have tried many ways PhotonNetwork.Instantiate, OnSerialize...., not fully RPC (because I didn't understand fully RPC method). tried to observe rigidbody, transform. But results are not makes me happy. I found Angry bots project which has already integrated photon networking. But all codes are in JavaScript. Even JS I have tried to understand but all codes have connected with each other. there are too many codes that maakes happen shooting.
There is a link for my first question: http://forum.photonengine.com/discussion/7608/sync-projectile-shooting-with-photon-networking#latest

I hope that someone can help me. Thanks in advance.

Answers

  • Hi, here is my explaination of RPC:

    Basic need to know about RPCs

    • RPC means Remote Procedure Call. A remote Procedure is just a normal Method of a Class, marked with Attribute [PunRPC].
    • Every PunBehaviour can have an unlimited amount of these remotely callable methods.
    • Every GameObject with a PhotonView (the 'heart' of PUN) can have PunBehaviours containing Remote Procedures.
    • Remote Procedure Calls (RPC) are used to call a Method in an other Instance (on an other Computer) of the calling PunBehviour.
    Example: You got an GameObject Soldier which got an PhotonView and a PunBehaviour called SoldierController. The PunBehaviour is linked to the PhotonView. The PhotonView's ID is unique in your game room and is same on each Client running your game.
    There is a [PunRPC]-marked method PlayJumpMotion in the PunBehaviour.


    Imagine you got 2 of the Soldier GameObject (Soldier A and B ). Soldier A got PhotonView ID 2001 and SoldierB got PhotonView ID 2002.

    If you call the RPC, something like this happens internally (very simplified):

    1) Soldier A (ID 2001) sends Message cointaining "I want to raise RPC SoldierController.PlayJumpMotion on every Instance of SoldierController linked to a PhotonView."
    2) Server send the Message "Call Remote Procedure SoldierController.PlayJumpMotion" to every PhotonView.
    3) Every PhotonView gets this Message and checks if they got an script SoldierController with PlayJumpMotion attached.
    -> If true, the PhotonView calls this Method.

    You see, no Magic. Just an Message sent over Network which tells the Client to call an Method of a PunBehaviour observed by an PhotonView.
  • Hi, here is my explaination of RPC:

    You see, no Magic. Just an Message sent over Network which tells the Client to call an Method of a PunBehaviour observed by an PhotonView.

    Thank you dingodition, yes this is a basic info about what is PunRPC. Please look to my codes. First code calls "ScriptEnabler", this is ok, everything works fine with [public void OnPhotonSerializeView]. moving, rotating, animating the cannons.
    Second script calls "ThrowBullets". ThrowBullets is my Shooting, Firing script. I have problem with it. I can see very good shooting if i shoot. But if there is another player shoots I can see his shooting like bad, not smooth, not exact shooting. Because I still don't know what to write and what observe with my photonView which has attached to "Bullet Prefab".

    So there are codes:

    //First Code which is ok. Player mover, rotate, animater
    using UnityEngine;
    using System.Collections;
    using Photon;
    
    public class ScriptEnabler : Photon.MonoBehaviour {
    
    	Vector3 position;
    	Quaternion rotation;
    	float smoothing = 10f;
    	public GameObject canvas;
    	public GameObject BulletHolder;
    
    
    
    	void Start () 
    	{
    		if (photonView.isMine) 
    		{
    
    			Instantiate(canvas);
    
    			if (PhotonNetwork.isMasterClient) 
    			{
    
    				BulletHolder.tag = "DuoLeft";
    			}
    
    			if (!PhotonNetwork.isMasterClient) 
    			{
    
    				BulletHolder.tag = "DuoRight";
    			}
    
    		}
    
    		else 
    		{
    			StartCoroutine("UpdateData");
    		}
    
    		GetComponent<BoxCollider> ().enabled = true;
    
    		BulletHolder.GetComponent<ThrowBullet>().enabled = true;
    
    		gameObject.name = gameObject.name + photonView.viewID;
    	}
    
    
    	IEnumerator UpdateData()
    	{
    		while(true)
    		{
    			transform.position = Vector3.Lerp(transform.position, position, Time.deltaTime * smoothing);
    			transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.deltaTime * smoothing);
    			yield return null;
    		}
    
    
    	}
    
    	void Update ()
    	{
    
    	}
    
    
    
    	public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) 
    	{
    		if (stream.isWriting) 
    		{
    			stream.SendNext (transform.position);
    			stream.SendNext (transform.rotation);
    		}
    		else 
    		{
    			position = (Vector3)stream.ReceiveNext ();
    			rotation = (Quaternion)stream.ReceiveNext ();
    		}
    	}
    	
    }
    
    //Second Code which shoots by projectile method
    using UnityEngine;
    using System.Collections;
    
    public class ThrowBullet : Photon.MonoBehaviour {
    
    	public Transform bullet;
    	public bool allowShoot;
    	public GameObject BulletSpawnName;
    	public int BulletForce = 2000; 
    	public float ShotDelay;
    	
    
    	void Start () 
    	{
    		allowShoot = true;
    	}
    	
    
    	void Update () 
    	{
    
    	}
    
    	public void Fire ()
    	{
    
    		if (allowShoot == true) 
    		{
    
    			Vector3 bulletpos = BulletSpawnName.transform.position;
    			GameObject Bullet = PhotonNetwork.Instantiate ("Bullet", bulletpos, Quaternion.identity, 0);
    			Bullet.GetComponent<Rigidbody> ().AddForce (transform.forward * BulletForce);
    			photonView.RPC("BulletTransit", PhotonTargets.All, bulletpos, BulletForce);
    
    		}
    	}
    
    	[PunRPC]
    	public void BulletTransit (Vector3 bulletpos, int BulletForce)
    	{
    		bullet.transform.position = bulletpos * Time.deltaTime;
    	}
    
    	public IEnumerator DoTheDance() 
    	{
    		allowShoot = false;
    		yield return new WaitForSeconds(ShotDelay); // waits 1 seconds
    		allowShoot = true; 
    	}
    }
  • dingoedition
    edited May 2016
    First, don't place a PhotonView on each Bullet Prefab. You are limited to 1000 PhotonViews (excluding scene controlled PhotonViews) and network instantiation is slow. A typical automatic gun in a game fires tons of bullets each second, you simply cant spawn that amount via PhotonNetwork.Instantiate.
    Just call a RPC on other clients, which handles bullet spawning, moving, animation, effects (localy, GameObject.Instantiatie, not PhotonNetwork.Instantiate).

    Second here are some pseudocode inspirations on how i handle shooting via network:

    1) If (player pressed fire key)
    2) Call local Fire(void) function
    -> Spawn the Bullet locally on Barrel position and apply velocity to rigidbody
    -> Handle animations
    -> Handle muzzleflash, sounds, and other shooting related effects
    -> call RPC RemoteFire(void) on every machine, but not on this one
    3) RPC RemoteFire() does exactly the same, but without calling the RPC ( because even my instantiated network players got same animation, barrel position, and such ... full body awareness rules)
    4) it's your choice if MasterClient or the shooting player checks who or what got hit by the bullets
    5) If bullet hits something, the master calls RPCs for damage, bullethole, hit effect whatever.

    Essence of this pseudocode:
    -> Using RPCs instead of Instantiation is important, for bandwidth
    -> Let the other clients handle only the visual stuff (bullet movement, bullet flying effect, trail...)
    -> But only one client (masterclient or the shooting player's client) checks who got hit and applies damage and such

    Improvements:
    -> For guns with a high rate of fire, even this system consumes to much bandwidth. In this case you just send a RPC when player start firing and one when it ends and let the clients handle everything between this events themselves. , instead of calling a RPC for each bullet fired. But damage calculation and such stays on masterclient/shooting player's client

    Addition according your ThrowBullet.cs :
    -> You got a var allowShoot, if you want next to zero bandwidth consuming, just sync this in OnPhotonSerializeView and let the magic happen

    Addion #2:
    -> Take a look on Exitgame's Angry Bots Multiplayer sample from assetstore (free, up to date)

    Additon #3:
    -> (from german to english) German software developer saying: "You see further standing on giant's shoulders."
    Don't try to do everything yourself. When you are new to programming and software development at all, this is a common mistake people use to do. Learn programming by actually developing your game, Use frameworks, understand how they use to do things and make them fit your needs. There are tons of good (and well priced, some even free) solutions for making FPS/TPS games in unity. A few support Photon natively. Dont waster your time in reinventing things, when not nessecary.
  • NomadicWarrior
    edited May 2016
    Hello, thank you for reply. I have tried Angry Bots Multiplayer, but all codes in JS and there are too many codes that connects with each other. To get understand and follow that project I have to be Expert programmer:). I'll try to open and try to understand more and more times of course.

    I have implemented simple PunRPC. Its shows Players HUD (UI) both your and enemies. Code looks like this:
    using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;
    
    public class PlayerHUD : Photon.MonoBehaviour {
    
    	public GameObject lPanel;
    	public GameObject rPanel;
    
    
    	void Update ()
    	{
    		if (photonView.isMine) {
    			if (PhotonNetwork.isMasterClient) 
    			{
    				photonView.RPC("Master", PhotonTargets.All, null);
    			}
    
    			if (!PhotonNetwork.isMasterClient) 
    			{
    				photonView.RPC("NoMaster", PhotonTargets.All, null);
    			}
    		}
    	}
    
    	[PunRPC]
    	public void Master ()
    	{
    		lPanel.SetActive (true);
    	}
    
    	[PunRPC]
    	public void NoMaster ()
    	{
    		rPanel.SetActive (true); 
    	}
    }
    
    I also created Cannon shooting script but It's a little bit hard for me to call visible bullet shooting with PunRPC. Please could you help me with this?
    using UnityEngine;
    using System.Collections;
    
    public class Shooting : MonoBehaviour {
    	
    	public bool allowShoot;
    	public GameObject projectilePrefab;
    	public GameObject BulletSpawnName;
    	public int BulletForce = 3000; 
    	public float ShotDelay;
    
    	void Start () 
    	{
    		allowShoot = true;
    	}
    	
    	public void FireRocket ()
    	{
    
    		if (allowShoot == true) 
    		{
    			projectilePrefab = (GameObject)Instantiate (projectilePrefab, BulletSpawnName.transform.position, BulletSpawnName.transform.rotation);
    			projectilePrefab.GetComponent<Rigidbody> ().AddForce (transform.forward * BulletForce);
    			StartCoroutine ("DoTheDance");
    		}
    	}
    
    	public IEnumerator DoTheDance() 
    	{
    		allowShoot = false;
    		yield return new WaitForSeconds(ShotDelay); // waits 1 seconds
    		allowShoot = true; 
    
    	}
    }
  • I add PunRPC to my Shooting script. But I'm having strange error
    PhotonView with ID 1001 has no method "FireRocket" marked with the [PunRPC](C#) or @PunRPC(JS) property! Args: Vector3, Quaternion, Int32
    UnityEngine.Debug:LogError(Object)
    using UnityEngine;
    using System.Collections;
    
    public class Shooting : Photon.MonoBehaviour {
    	
    	public bool allowShoot;
    	public GameObject projectilePrefab;
    	public GameObject BulletSpawnName;
    	public int BulletForce = 3000; 
    	public float ShotDelay;
    	public GameObject CannonPlayer;
    
    	void Start () 
    	{
    		allowShoot = true;
    		PhotonView pview = CannonPlayer.GetComponent<PhotonView> ();
    
    	}
    
    	public void Shoot ()
    	{
    
    		CannonPlayer.GetComponent<PhotonView>().RPC ("FireRocket", PhotonTargets.All, BulletSpawnName.transform.position, BulletSpawnName.transform.rotation, BulletForce);
    
    	}
    
    	[PunRPC]
    	public void FireRocket ()
    	{
    
    		if (allowShoot == true) 
    		{
    			projectilePrefab = (GameObject)Instantiate (projectilePrefab, BulletSpawnName.transform.position, BulletSpawnName.transform.rotation);
    			projectilePrefab.GetComponent<Rigidbody> ().AddForce (transform.forward * BulletForce);
    			StartCoroutine ("DoTheDance");
    		}
    	}
    
    	public IEnumerator DoTheDance() 
    	{
    		allowShoot = false;
    		yield return new WaitForSeconds(ShotDelay); // waits 1 seconds
    		allowShoot = true; 
    
    	}
    }

    CannonPlayer is my Player object so I use my players photonView component here, that instead of adding new photon view component to child object. Please any help will be appreciated. thanks
  • I got worked my RPC projectile shooting. need work with explosions and muzzle flash, sound effects. Can't wait to work with them :)