/// <summary> /// This is the weapon fire code created for this example. It instantiates a cosmetic (not network synced) projectile. /// For a real project you should considered using pooled objects for projectiles. /// </summary> private void FireBullet(Frame frame, PlayerFireCustomMsg msg) { Vector3 originPos; Quaternion originRot; originPos = originGO.transform.position; originRot = originGO.transform.rotation; GameObject projGO = (projPoolId > -1) ? Pool.Spawn(projPoolId, originPos, originRot, 3).gameObject : CreatePlaceholderProjectile(originPos, originRot, false); //Rigidbody rb = projGO.GetComponent<Rigidbody>() ?? projGO.AddComponent<Rigidbody>(); Rigidbody rb = projGO.GetComponent <Rigidbody>(); INstProjectile nstProj = projGO.GetComponent <INstProjectile>(); if (nstProj != null) { nstProj.OwnerNst = nst; } // This needs the projectile to have a RB, so add one if it is missing if (rb == null) { rb = projGO.AddComponent <Rigidbody>(); rb.useGravity = false; } rb.velocity = projGO.transform.forward * projVelocity; // Changes color to color sent over the network as an example of how to use the Custom Message projGO.GetComponentInChildren <MeshRenderer>().material.color = msg.color; }
/// <summary> /// Player with local authority fires by calling this. This tells the NST to create a custom message and attach your data to it. /// </summary> /// <param name="wid"></param> public void PlayerFire() { PlayerFireCustomMsg customMsg = new PlayerFireCustomMsg { weaponId = (byte)weaponId, color = new Color(Random.value, Random.value, Random.value), }; nst.SendCustomEvent(customMsg); }
/// <summary> /// Player with local authority fires by calling this. This tells the NST to create a custom message and attach your data to it. /// </summary> /// <param name="wid"></param> public void PlayerFire() { PlayerFireCustomMsg customMsg = new PlayerFireCustomMsg { weaponId = (byte)weaponId, color = new Color(Random.value, Random.value, Random.value), }; NetworkSyncTransform.SendCustomEventSimple(customMsg); }
/// <summary> /// When a custom message is received, the OnCustomMsgRcvEvent is fired. Note that the rotations will only be correct if you have the NST set to update /// rotations on events. If it is set to 'changes only' these rotations values will be zero. /// </summary> private static void OnCustomMsgRcv(NetworkConnection ownerConn, byte[] bytearray, NetworkSyncTransform shooterNst, Vector3 pos, List <GenericX> positions, List <GenericX> rotations) { //For this example we fired locally already when the custom message was sent (with OnCustomMsgSend). Firing again here on the local player would cause repeat fire events. //Note however that code for the local player can be added here to sync the projectile with the server, by adding a projectileID to your custom events. Weapon wpn = shooterNst.GetComponent <Weapon>(); PlayerFireCustomMsg weaponFireMsg = bytearray.DeserializeToStruct <PlayerFireCustomMsg>(); if ((WeaponType)weaponFireMsg.weaponId == WeaponType.Bullet) { if (!shooterNst.isLocalPlayer) { wpn.FireBullet(pos, rotations[0], weaponFireMsg); } } // Hitscan arrive on server/other clients else if ((WeaponType)weaponFireMsg.weaponId == WeaponType.Hitscan) { //DebugText.Log(weaponFireMsg.hitmask.PrintBitMask() + " RCV " + (WeaponType)weaponFireMsg.weaponId); uint confirmedHitmask = 0; // Draw the graphic if this isn't the local player if (!shooterNst.isLocalPlayer) { wpn.DrawRay(pos, rotations[0]); } // Server needs to test if this was a hit. if (NetworkServer.active) { for (int i = 0; i < 32; i++) { if (weaponFireMsg.hitmask.GetBitInMask(i) == false) { continue; } NetworkSyncTransform hitNst = NetworkSyncTransform.GetNstFromId((uint)i); bool hit = hitNst.TestHitscanAgainstRewind(ownerConn, new Ray(pos, (Quaternion)rotations[0] * Vector3.forward)); if (hit) { ((int)hitNst.NstId).SetBitInMask(ref confirmedHitmask, true); } } DebugText.Log("Rewind Confirmation Mask : \n" + confirmedHitmask.PrintBitMask(), true); } } }
// it is advisable to create a reusable byte array of the size you need to avoid GC by creating 'new' every time. //private static byte[] reusablebyte = new byte[1]; /// <summary> /// This is the weapon fire code created for this example. It just creates a basic non-synced projectile. /// </summary> private void FireBullet(Vector3 pos, Quaternion rot, PlayerFireCustomMsg msg) { GameObject bullet = Instantiate(bulletPrefab); Rigidbody rb = bullet.GetComponent <Rigidbody>(); bullet.transform.position = pos; bullet.transform.rotation = rot; bullet.GetComponentInChildren <MeshRenderer>().material.color = msg.color; Destroy(bullet, 1f); rb.velocity = bullet.transform.forward * 10; }
/// <summary> /// This is the secondary weapon fire code created for this example. It just creates a basic non-synced projectile. /// </summary> private void FireMine(Vector3 pos, Quaternion rot, PlayerFireCustomMsg msg) { Debug.DrawRay(pos, rot * Vector3.forward * 10, Color.red, .5f, true); GameObject bullet = Instantiate(minePrefab); Rigidbody rb = bullet.GetComponent <Rigidbody>(); bullet.transform.position = pos; bullet.transform.rotation = turret.transform.parent.rotation * rot; bullet.GetComponentInChildren <MeshRenderer>().material.color = msg.color; Destroy(bullet, 2f); rb.velocity = bullet.transform.forward * 5; }
/// <summary> /// OnCustomMsgSndEvent fires on the originating client when a custom event is sent. The position and rotation information will contain the same /// lossy rounding errors/ranges that are being sent to the network. Useful for ensuring that your local events use the exact same pos/rot data /// the server and clients will be using (such as projectile vectors). /// </summary> public void OnSnd(Frame frame) { if (frame.updateType != UpdateType.Cust_Msg) { return; } PlayerFireCustomMsg weaponFireMsg = frame.customData.DeserializeToStruct <PlayerFireCustomMsg>(); if (weaponFireMsg.weaponId != weaponId) { return; } FireBullet(frame, weaponFireMsg); }
/// <summary> /// OnCustomMsgSndEvent fires on the originating client when a custom event is sent. The position and rotation information will contain the same /// lossy rounding errors/ranges that are being sent to the network. Useful for ensuring that your local events use the exact same pos/rot data /// the server and clients will be using (such as projectile vectors). /// </summary> /// <param name="rootPos">Lossy position after compression - exactly what is sent.</param> /// <param name="rotations">Lossy rotation after compression - exactly what is sent.</param> private static void OnCustomMsgSnd(NetworkConnection ownerConn, byte[] bytearray, NetworkSyncTransform nst, Vector3 rootPos, List <GenericX> positions, List <GenericX> rotations) { Weapon wpn = nst.GetComponent <Weapon>(); PlayerFireCustomMsg weaponFireMsg = bytearray.DeserializeToStruct <PlayerFireCustomMsg>(); if (weaponFireMsg.weaponId == (byte)WeaponType.Bullet) { wpn.FireBullet(rootPos, rotations[0], weaponFireMsg); } else if (weaponFireMsg.weaponId == (byte)WeaponType.Mine) { wpn.FireMine(wpn.turret.transform.position, rotations[1], weaponFireMsg); } }
/// <summary> /// When a custom message is taken from the buffer and applied for interpolation, the OnCustomMsgRcvEvent is fired. Note that the rotations /// will only be correct if you have the NST set to update rotations on events. If it is set to 'changes only' these rotations values will be zero. /// </summary> private static void OnCustomMsgApply(NetworkConnection ownerConn, byte[] bytearray, NetworkSyncTransform nst, Vector3 pos, List <GenericX> positions, List <GenericX> rotations) { Weapon wpn = nst.GetComponent <Weapon>(); PlayerFireCustomMsg weaponFireMsg = bytearray.DeserializeToStruct <PlayerFireCustomMsg>(); if (nst == NetworkSyncTransform.lclNST) { return; } if (weaponFireMsg.weaponId == (byte)WeaponType.Mine) { wpn.FireMine(wpn.turret.transform.position, rotations[1], weaponFireMsg); } }
/// <summary> /// Player with local authority fires by calling this. This tells the NST to create a custom message and attach your data to it. /// </summary> /// <param name="wid"></param> public void PlayerFire(WeaponType w) { int hitId = 0; uint hitmask = 0; if (w == WeaponType.Hitscan) { hitmask = CastRay(transform.position, transform.rotation, out hitId); } PlayerFireCustomMsg customMsg = new PlayerFireCustomMsg { weaponId = (byte)w, color = new Color(Random.value, Random.value, Random.value), hitmask = hitmask }; NetworkSyncTransform.SendCustomEventSimple(customMsg); }
/// <summary> /// This is the weapon fire code created for this example. It instantiates a cosmetic (not network synced) projectile. /// For a real project you should considered using pooled objects for projectiles. /// </summary> private void FireBullet(Frame frame, PlayerFireCustomMsg msg) { Vector3 originPos; Quaternion originRot; originPos = originGO.transform.position; originRot = originGO.transform.rotation; Pool poolProj = Pool.Spawn(projPrefab, originPos, originRot, lifespanSecs); INstProjectile nstProj = poolProj.gameObject.GetComponent <INstProjectile>(); if (nstProj != null) { nstProj.OwnerNst = nst; } poolProj.rb.velocity = poolProj.gameObject.transform.forward * projVelocity; // Changes color to color sent over the network as an example of how to use the Custom Message poolProj.GetComponentInChildren <MeshRenderer>().material.color = msg.color; }
/// <summary> /// When a custom message is taken from the buffer and applied for interpolation, the OnCustomMsgRcvEvent is fired. Note that the rotations /// will only be correct if you have the NST set to update rotations on events. If it is set to 'changes only' these rotations values will be zero. /// </summary> public void OnEndInterpolate(Frame frame) { if (frame.updateType != UpdateType.Cust_Msg) { return; } // TODO: find a way to deserialize this only once PlayerFireCustomMsg weaponFireMsg = frame.customData.DeserializeToStruct <PlayerFireCustomMsg>(); if (weaponFireMsg.weaponId != weaponId) { return; } // Don't call the fire graphics if this is the owner client - it already fired on send. if (frame.nst.na.IsLocalPlayer) { return; } FireBullet(frame, weaponFireMsg); }