- Tutorials
- Interface & Essentials
- Merry Fragmas 2.0 - Multiplayer FPS
Merry Fragmas 2.0 - Multiplayer FPS
Checked with version: 5.3
-
Difficulty: Intermediate
Merry Fragmas everyone! Mike Geig returns with a festive live training session packed with wanton yuletide destruction! In this session we will be building a Networked Multiplayer First Person Shooter game. The content will range from beginner to intermediate as we look at scripting and features specific to multiplayer networking. As this is an updated version of the Merry Fragmas live training from last December, some concepts will move more quickly. Another notable change is that this year we will be using the new networking system built into Unity instead of Photon. Pack a lunch because we are going to complete this in a single session even if it means we go over our usual 1 hour limit. Let the slay bells ring! Tutor: Mike Geig
ShootingScript
Code snippet
using UnityEngine;
using UnityEngine.Networking;
public class ShootingScript : NetworkBehaviour
{
public ParticleSystem _muzzleFlash;
public AudioSource _gunAudio;
public GameObject _impactPrefab;
public Transform cameraTransform;
ParticleSystem _impactEffect;
void Start()
{
_impactEffect = Instantiate(_impactPrefab).GetComponent<ParticleSystem>();
}
void Update()
{
if (Input.GetButtonDown("Fire2"))
CmdHitPlayer(gameObject);
if (Input.GetButtonDown("Fire1"))
{
_muzzleFlash.Stop();
_muzzleFlash.Play();
_gunAudio.Stop();
_gunAudio.Play();
RaycastHit hit;
Vector3 rayPos = cameraTransform.position + (1f * cameraTransform.forward);
if (Physics.Raycast(rayPos, cameraTransform.forward, out hit, 50f))
{
_impactEffect.transform.position = hit.point;
_impactEffect.Stop();
_impactEffect.Play();
if (hit.transform.tag == "Player")
{
CmdHitPlayer(hit.transform.gameObject);
}
}
}
}
[Command]
void CmdHitPlayer(GameObject hit)
{
hit.GetComponent<NetworkedPlayerScript>().RpcResolveHit();
}
}
NetworkedPlayerScript
Code snippet
using UnityEngine;
using UnityEngine.Networking;
public class NetworkedPlayerScript : NetworkBehaviour
{
public UnityStandardAssets.Characters.FirstPerson.FirstPersonController fpsController;
public Camera fpsCamera;
public AudioListener audioListener;
public ShootingScript shootingScript;
public CandyCaneMaterialSwitcher candyMaterialSwitcher;
Renderer[] renderers;
void Start()
{
renderers = GetComponentsInChildren<Renderer>();
}
public override void OnStartLocalPlayer()
{
fpsController.enabled = true;
fpsCamera.enabled = true;
audioListener.enabled = true;
shootingScript.enabled = true;
candyMaterialSwitcher.SwitchMaterial(true);
gameObject.name = "LOCAL Player";
base.OnStartLocalPlayer();
}
void ToggleRenderer(bool isAlive)
{
for (int i = 0; i < renderers.Length; i++)
renderers[i].enabled = isAlive;
}
void ToggleControls(bool isAlive)
{
fpsController.enabled = isAlive;
shootingScript.enabled = isAlive;
fpsCamera.cullingMask = ~fpsCamera.cullingMask;
}
[ClientRpc]
public void RpcResolveHit()
{
ToggleRenderer(false);
if (isLocalPlayer)
{
Transform spawn = NetworkManager.singleton.GetStartPosition();
transform.position = spawn.position;
transform.rotation = spawn.rotation;
ToggleControls(false);
}
Invoke("Respawn", 2f);
}
void Respawn()
{
ToggleRenderer(true);
if (isLocalPlayer)
ToggleControls(true);
}
}
CandyCaneMaterialSwitcher
Code snippet
using UnityEngine;
using System.Collections;
public class CandyCaneMaterialSwitcher : MonoBehaviour
{
public Material localCaneMaterial;
public Material notLocalCaneMaterial;
Renderer candyCaneRenderer;
void Awake()
{
candyCaneRenderer = GetComponent<Renderer>();
SwitchMaterial(false);
}
public void SwitchMaterial(bool isLocalPlayer)
{
if (isLocalPlayer)
candyCaneRenderer.material = localCaneMaterial;
else
candyCaneRenderer.material = notLocalCaneMaterial;
}
}
CustomNetworkManager
Code snippet
using UnityEngine;
using UnityEngine.Networking;
public class CustomNetworkManager : NetworkManager
{
[SerializeField] private GameObject sceneCamera;
[SerializeField] private GameObject reticuleCanvas;
public override void OnStartClient( NetworkClient client )
{
HideSceneCamera();
ShowReticuleCanvas();
}
public override void OnStartHost()
{
HideSceneCamera();
ShowReticuleCanvas();
}
public override void OnStopClient ()
{
ShowSceneCamera();
HideReticuleCanvas();
}
public override void OnStopHost ()
{
ShowSceneCamera();
HideReticuleCanvas();
}
private void HideSceneCamera()
{
if(sceneCamera)
sceneCamera.SetActive(false);
}
private void ShowSceneCamera()
{
if(sceneCamera)
sceneCamera.SetActive(true);
}
private void HideReticuleCanvas()
{
if (reticuleCanvas)
reticuleCanvas.SetActive(false);
}
private void ShowReticuleCanvas()
{
if (reticuleCanvas)
reticuleCanvas.SetActive(true);
}
}
Modified Standard Shader
Code snippet
//This is the standard shader (downloadable from the Unity3D website) with
// "Queue"="Overlay" and ZTest Always added to it
Shader "Custom/StandardOnTop"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo", 2D) = "white" {}
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
_Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
[Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
_MetallicGlossMap("Metallic", 2D) = "white" {}
_BumpScale("Scale", Float) = 1.0
_BumpMap("Normal Map", 2D) = "bump" {}
_Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
_ParallaxMap ("Height Map", 2D) = "black" {}
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {}
_EmissionColor("Color", Color) = (0,0,0)
_EmissionMap("Emission", 2D) = "white" {}
_DetailMask("Detail Mask", 2D) = "white" {}
_DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
_DetailNormalMapScale("Scale", Float) = 1.0
_DetailNormalMap("Normal Map", 2D) = "bump" {}
[Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
// Blending state
[HideInInspector] _Mode ("__mode", Float) = 0.0
[HideInInspector] _SrcBlend ("__src", Float) = 1.0
[HideInInspector] _DstBlend ("__dst", Float) = 0.0
[HideInInspector] _ZWrite ("__zw", Float) = 1.0
}
CGINCLUDE
#define UNITY_SETUP_BRDF_INPUT MetallicSetup
ENDCG
SubShader
{
Tags { "RenderType"="Opaque" "PerformanceChecks"="False" "Queue"="Overlay" }
LOD 300
// ------------------------------------------------------------------
// Base forward pass (directional light, emission, lightmaps, ...)
Pass
{
Name "FORWARD"
Tags { "LightMode" = "ForwardBase" "Queue" = "Overlay" }
Blend [_SrcBlend] [_DstBlend]
ZWrite[_ZWrite]
ZTest Always
CGPROGRAM
#pragma target 3.0
// TEMPORARY: GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT
#pragma exclude_renderers gles
// -------------------------------------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _PARALLAXMAP
#pragma multi_compile_fwdbase
#pragma multi_compile_fog
#pragma vertex vertBase
#pragma fragment fragBase
#include "UnityStandardCoreForward.cginc"
ENDCG
}
// ------------------------------------------------------------------
// Additive forward pass (one light per pass)
Pass
{
Name "FORWARD_DELTA"
Tags { "LightMode" = "ForwardAdd" "Queue" = "Overlay" }
Blend [_SrcBlend] One
Fog { Color (0,0,0,0) } // in additive pass fog should be black
ZWrite Off
ZTest LEqual
CGPROGRAM
#pragma target 3.0
// GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT
#pragma exclude_renderers gles
// -------------------------------------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _PARALLAXMAP
#pragma multi_compile_fwdadd_fullshadows
#pragma multi_compile_fog
#pragma vertex vertAdd
#pragma fragment fragAdd
#include "UnityStandardCoreForward.cginc"
ENDCG
}
// ------------------------------------------------------------------
// Shadow rendering pass
Pass {
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" "Queue" = "Overlay" }
ZWrite On ZTest LEqual
CGPROGRAM
#pragma target 3.0
// TEMPORARY: GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT
#pragma exclude_renderers gles
// -------------------------------------
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma multi_compile_shadowcaster
#pragma vertex vertShadowCaster
#pragma fragment fragShadowCaster
#include "UnityStandardShadow.cginc"
ENDCG
}
// ------------------------------------------------------------------
// Deferred pass
Pass
{
Name "DEFERRED"
Tags { "LightMode" = "Deferred" "Queue" = "Overlay" }
CGPROGRAM
#pragma target 3.0
// TEMPORARY: GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT
#pragma exclude_renderers nomrt gles
// -------------------------------------
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _PARALLAXMAP
#pragma multi_compile ___ UNITY_HDR_ON
#pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
#pragma multi_compile DIRLIGHTMAP_OFF DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE
#pragma multi_compile DYNAMICLIGHTMAP_OFF DYNAMICLIGHTMAP_ON
#pragma vertex vertDeferred
#pragma fragment fragDeferred
#include "UnityStandardCore.cginc"
ENDCG
}
// ------------------------------------------------------------------
// Extracts information for lightmapping, GI (emission, albedo, ...)
// This pass it not used during regular rendering.
Pass
{
Name "META"
Tags { "LightMode"="Meta" "Queue" = "Overlay" }
Cull Off
CGPROGRAM
#pragma vertex vert_meta
#pragma fragment frag_meta
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#include "UnityStandardMeta.cginc"
ENDCG
}
}
SubShader
{
Tags { "RenderType"="Opaque" "PerformanceChecks"="False" "Queue" = "Overlay" }
LOD 150
// ------------------------------------------------------------------
// Base forward pass (directional light, emission, lightmaps, ...)
Pass
{
Name "FORWARD"
Tags { "LightMode" = "ForwardBase" "Queue" = "Overlay" }
Blend [_SrcBlend] [_DstBlend]
ZWrite [_ZWrite]
ZTest Always
CGPROGRAM
#pragma target 2.0
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
// SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP
#pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE
#pragma multi_compile_fwdbase
#pragma multi_compile_fog
#pragma vertex vertBase
#pragma fragment fragBase
#include "UnityStandardCoreForward.cginc"
ENDCG
}
// ------------------------------------------------------------------
// Additive forward pass (one light per pass)
Pass
{
Name "FORWARD_DELTA"
Tags { "LightMode" = "ForwardAdd""Queue" = "Overlay" }
Blend [_SrcBlend] One
Fog { Color (0,0,0,0) } // in additive pass fog should be black
ZWrite Off
ZTest LEqual
CGPROGRAM
#pragma target 2.0
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
// SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP
#pragma skip_variants SHADOWS_SOFT
#pragma multi_compile_fwdadd_fullshadows
#pragma multi_compile_fog
#pragma vertex vertAdd
#pragma fragment fragAdd
#include "UnityStandardCoreForward.cginc"
ENDCG
}
// ------------------------------------------------------------------
// Shadow rendering pass
Pass {
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" "Queue" = "Overlay" }
ZWrite On ZTest LEqual
CGPROGRAM
#pragma target 2.0
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma skip_variants SHADOWS_SOFT
#pragma multi_compile_shadowcaster
#pragma vertex vertShadowCaster
#pragma fragment fragShadowCaster
#include "UnityStandardShadow.cginc"
ENDCG
}
// ------------------------------------------------------------------
// Extracts information for lightmapping, GI (emission, albedo, ...)
// This pass it not used during regular rendering.
Pass
{
Name "META"
Tags { "LightMode"="Meta" "Queue" = "Overlay" }
Cull Off
CGPROGRAM
#pragma vertex vert_meta
#pragma fragment frag_meta
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#include "UnityStandardMeta.cginc"
ENDCG
}
}
FallBack "VertexLit"
CustomEditor "StandardShaderGUI"
}


Merry Fragmas 2.0 - Multiplayer FPS
Intermediate Interface & Essentials
Related documentation
- Network Manager (Manual)
- Network Identity (Manual)
- Network Transform (Manual)
- Networking Overview (Manual)