Multiplayer Updated
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Mirror.Examples.MultipleAdditiveScenes
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
public class MultiSceneNetManager : NetworkManager
|
||||
{
|
||||
[Header("Spawner Setup")]
|
||||
[Tooltip("Reward Prefab for the Spawner")]
|
||||
public GameObject rewardPrefab;
|
||||
|
||||
[Header("MultiScene Setup")]
|
||||
public int instances = 3;
|
||||
|
||||
[Scene]
|
||||
public string gameScene;
|
||||
|
||||
// This is set true after server loads all subscene instances
|
||||
bool subscenesLoaded;
|
||||
|
||||
// subscenes are added to this list as they're loaded
|
||||
readonly List<Scene> subScenes = new List<Scene>();
|
||||
|
||||
// Sequential index used in round-robin deployment of players into instances and score positioning
|
||||
int clientIndex;
|
||||
|
||||
public static new MultiSceneNetManager singleton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Runs on both Server and Client
|
||||
/// Networking is NOT initialized when this fires
|
||||
/// </summary>
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
#region Server System Callbacks
|
||||
|
||||
/// <summary>
|
||||
/// Called on the server when a client adds a new player with NetworkClient.AddPlayer.
|
||||
/// <para>The default implementation for this function creates a new player object from the playerPrefab.</para>
|
||||
/// </summary>
|
||||
/// <param name="conn">Connection from client.</param>
|
||||
public override void OnServerAddPlayer(NetworkConnectionToClient conn)
|
||||
{
|
||||
StartCoroutine(OnServerAddPlayerDelayed(conn));
|
||||
}
|
||||
|
||||
// This delay is mostly for the host player that loads too fast for the
|
||||
// server to have subscenes async loaded from OnStartServer ahead of it.
|
||||
IEnumerator OnServerAddPlayerDelayed(NetworkConnectionToClient conn)
|
||||
{
|
||||
// wait for server to async load all subscenes for game instances
|
||||
while (!subscenesLoaded)
|
||||
yield return null;
|
||||
|
||||
// Send Scene message to client to additively load the game scene
|
||||
conn.Send(new SceneMessage { sceneName = gameScene, sceneOperation = SceneOperation.LoadAdditive });
|
||||
|
||||
// Wait for end of frame before adding the player to ensure Scene Message goes first
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
base.OnServerAddPlayer(conn);
|
||||
|
||||
PlayerScore playerScore = conn.identity.GetComponent<PlayerScore>();
|
||||
playerScore.playerNumber = clientIndex;
|
||||
playerScore.scoreIndex = clientIndex / subScenes.Count;
|
||||
playerScore.matchIndex = clientIndex % subScenes.Count;
|
||||
|
||||
// Do this only on server, not on clients
|
||||
// This is what allows Scene Interest Management
|
||||
// to isolate matches per scene instance on server.
|
||||
if (subScenes.Count > 0)
|
||||
SceneManager.MoveGameObjectToScene(conn.identity.gameObject, subScenes[clientIndex % subScenes.Count]);
|
||||
|
||||
clientIndex++;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Start & Stop Callbacks
|
||||
|
||||
/// <summary>
|
||||
/// This is invoked when a server is started - including when a host is started.
|
||||
/// <para>StartServer has multiple signatures, but they all cause this hook to be called.</para>
|
||||
/// </summary>
|
||||
public override void OnStartServer()
|
||||
{
|
||||
StartCoroutine(ServerLoadSubScenes());
|
||||
}
|
||||
|
||||
// We're additively loading scenes, so GetSceneAt(0) will return the main "container" scene,
|
||||
// therefore we start the index at one and loop through instances value inclusively.
|
||||
// If instances is zero, the loop is bypassed entirely.
|
||||
IEnumerator ServerLoadSubScenes()
|
||||
{
|
||||
for (int index = 1; index <= instances; index++)
|
||||
{
|
||||
yield return SceneManager.LoadSceneAsync(gameScene, new LoadSceneParameters { loadSceneMode = LoadSceneMode.Additive, localPhysicsMode = LocalPhysicsMode.Physics3D });
|
||||
|
||||
Scene newScene = SceneManager.GetSceneAt(index);
|
||||
subScenes.Add(newScene);
|
||||
Spawner.InitialSpawn(newScene);
|
||||
}
|
||||
|
||||
subscenesLoaded = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is called when a server is stopped - including when a host is stopped.
|
||||
/// </summary>
|
||||
public override void OnStopServer()
|
||||
{
|
||||
NetworkServer.SendToAll(new SceneMessage { sceneName = gameScene, sceneOperation = SceneOperation.UnloadAdditive });
|
||||
StartCoroutine(ServerUnloadSubScenes());
|
||||
clientIndex = 0;
|
||||
}
|
||||
|
||||
// Unload the subScenes and unused assets and clear the subScenes list.
|
||||
IEnumerator ServerUnloadSubScenes()
|
||||
{
|
||||
for (int index = 0; index < subScenes.Count; index++)
|
||||
if (subScenes[index].IsValid())
|
||||
yield return SceneManager.UnloadSceneAsync(subScenes[index]);
|
||||
|
||||
subScenes.Clear();
|
||||
subscenesLoaded = false;
|
||||
|
||||
yield return Resources.UnloadUnusedAssets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is called when a client is stopped.
|
||||
/// </summary>
|
||||
public override void OnStopClient()
|
||||
{
|
||||
// Make sure we're not in ServerOnly mode now after stopping host client
|
||||
if (mode == NetworkManagerMode.Offline)
|
||||
StartCoroutine(ClientUnloadSubScenes());
|
||||
}
|
||||
|
||||
// Unload all but the active scene, which is the "container" scene
|
||||
IEnumerator ClientUnloadSubScenes()
|
||||
{
|
||||
for (int index = 0; index < SceneManager.sceneCount; index++)
|
||||
if (SceneManager.GetSceneAt(index) != SceneManager.GetActiveScene())
|
||||
yield return SceneManager.UnloadSceneAsync(SceneManager.GetSceneAt(index));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b982a1fd37427e64e8310a863d03d2c9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.MultipleAdditiveScenes
|
||||
{
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class PhysicsCollision : NetworkBehaviour
|
||||
{
|
||||
[Tooltip("how forcefully to push this object")]
|
||||
public float force = 12;
|
||||
|
||||
public Rigidbody rigidbody3D;
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
|
||||
if (rigidbody3D == null)
|
||||
rigidbody3D = GetComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
rigidbody3D.isKinematic = !isServer;
|
||||
}
|
||||
|
||||
[ServerCallback]
|
||||
void OnCollisionStay(Collision other)
|
||||
{
|
||||
if (other.gameObject.CompareTag("Player"))
|
||||
{
|
||||
// get direction from which player is contacting object
|
||||
Vector3 direction = other.contacts[0].normal;
|
||||
|
||||
// zero the y and normalize so we don't shove this through the floor or launch this over the wall
|
||||
direction.y = 0;
|
||||
direction = direction.normalized;
|
||||
|
||||
// push this away from player...a bit less force for host player
|
||||
if (other.gameObject.GetComponent<NetworkIdentity>().connectionToClient.connectionId == NetworkConnection.LocalConnectionId)
|
||||
rigidbody3D.AddForce(direction * force * .5f);
|
||||
else
|
||||
rigidbody3D.AddForce(direction * force);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c709489168fec9348b7f8290ee2e8466
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
// This sets up the scene camera for the local player
|
||||
|
||||
namespace Mirror.Examples.MultipleAdditiveScenes
|
||||
{
|
||||
public class PlayerCamera : NetworkBehaviour
|
||||
{
|
||||
Camera mainCam;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
mainCam = Camera.main;
|
||||
}
|
||||
|
||||
public override void OnStartLocalPlayer()
|
||||
{
|
||||
if (mainCam != null)
|
||||
{
|
||||
// configure and make camera a child of player with 3rd person offset
|
||||
mainCam.orthographic = false;
|
||||
mainCam.transform.SetParent(transform);
|
||||
mainCam.transform.localPosition = new Vector3(0f, 3f, -8f);
|
||||
mainCam.transform.localEulerAngles = new Vector3(10f, 0f, 0f);
|
||||
}
|
||||
else
|
||||
Debug.LogWarning("PlayerCamera: Could not find a camera in scene with 'MainCamera' tag.");
|
||||
}
|
||||
|
||||
public override void OnStopLocalPlayer()
|
||||
{
|
||||
if (mainCam != null)
|
||||
{
|
||||
mainCam.transform.SetParent(null);
|
||||
SceneManager.MoveGameObjectToScene(mainCam.gameObject, SceneManager.GetActiveScene());
|
||||
mainCam.orthographic = true;
|
||||
mainCam.orthographicSize = 15f;
|
||||
mainCam.transform.localPosition = new Vector3(0f, 70f, 0f);
|
||||
mainCam.transform.localEulerAngles = new Vector3(90f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cac48dc20f866148b44d0f9b5850761
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,186 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.MultipleAdditiveScenes
|
||||
{
|
||||
[RequireComponent(typeof(CapsuleCollider))]
|
||||
[RequireComponent(typeof(CharacterController))]
|
||||
[RequireComponent(typeof(NetworkTransformReliable))]
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class PlayerController : NetworkBehaviour
|
||||
{
|
||||
public enum GroundState : byte { Jumping, Falling, Grounded }
|
||||
|
||||
[Header("Avatar Components")]
|
||||
public CharacterController characterController;
|
||||
|
||||
[Header("Movement")]
|
||||
[Range(1, 20)]
|
||||
public float moveSpeedMultiplier = 8f;
|
||||
|
||||
[Header("Turning")]
|
||||
[Range(1f, 200f)]
|
||||
public float maxTurnSpeed = 100f;
|
||||
[Range(.5f, 5f)]
|
||||
public float turnDelta = 3f;
|
||||
|
||||
[Header("Jumping")]
|
||||
[Range(0.1f, 1f)]
|
||||
public float initialJumpSpeed = 0.2f;
|
||||
[Range(1f, 10f)]
|
||||
public float maxJumpSpeed = 5f;
|
||||
[Range(0.1f, 1f)]
|
||||
public float jumpDelta = 0.2f;
|
||||
|
||||
[Header("Diagnostics - Do Not Modify")]
|
||||
public GroundState groundState = GroundState.Grounded;
|
||||
|
||||
[Range(-1f, 1f)]
|
||||
public float horizontal;
|
||||
[Range(-1f, 1f)]
|
||||
public float vertical;
|
||||
|
||||
[Range(-200f, 200f)]
|
||||
public float turnSpeed;
|
||||
|
||||
[Range(-10f, 10f)]
|
||||
public float jumpSpeed;
|
||||
|
||||
[Range(-1.5f, 1.5f)]
|
||||
public float animVelocity;
|
||||
|
||||
[Range(-1.5f, 1.5f)]
|
||||
public float animRotation;
|
||||
|
||||
public Vector3Int velocity;
|
||||
public Vector3 direction;
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
|
||||
if (characterController == null)
|
||||
characterController = GetComponent<CharacterController>();
|
||||
|
||||
// Override CharacterController default values
|
||||
characterController.enabled = false;
|
||||
characterController.skinWidth = 0.02f;
|
||||
characterController.minMoveDistance = 0f;
|
||||
|
||||
GetComponent<Rigidbody>().isKinematic = true;
|
||||
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
public override void OnStartAuthority()
|
||||
{
|
||||
characterController.enabled = true;
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
public override void OnStopAuthority()
|
||||
{
|
||||
this.enabled = false;
|
||||
characterController.enabled = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (!characterController.enabled)
|
||||
return;
|
||||
|
||||
HandleTurning();
|
||||
HandleJumping();
|
||||
HandleMove();
|
||||
|
||||
// Reset ground state
|
||||
if (characterController.isGrounded)
|
||||
groundState = GroundState.Grounded;
|
||||
else if (groundState != GroundState.Jumping)
|
||||
groundState = GroundState.Falling;
|
||||
|
||||
// Diagnostic velocity...FloorToInt for display purposes
|
||||
velocity = Vector3Int.FloorToInt(characterController.velocity);
|
||||
}
|
||||
|
||||
// TODO: Turning works while airborne...feature?
|
||||
void HandleTurning()
|
||||
{
|
||||
// Q and E cancel each other out, reducing the turn to zero.
|
||||
if (Input.GetKey(KeyCode.Q))
|
||||
turnSpeed = Mathf.MoveTowards(turnSpeed, -maxTurnSpeed, turnDelta);
|
||||
if (Input.GetKey(KeyCode.E))
|
||||
turnSpeed = Mathf.MoveTowards(turnSpeed, maxTurnSpeed, turnDelta);
|
||||
|
||||
// If both pressed, reduce turning speed toward zero.
|
||||
if (Input.GetKey(KeyCode.Q) && Input.GetKey(KeyCode.E))
|
||||
turnSpeed = Mathf.MoveTowards(turnSpeed, 0, turnDelta);
|
||||
|
||||
// If neither pressed, reduce turning speed toward zero.
|
||||
if (!Input.GetKey(KeyCode.Q) && !Input.GetKey(KeyCode.E))
|
||||
turnSpeed = Mathf.MoveTowards(turnSpeed, 0, turnDelta);
|
||||
|
||||
transform.Rotate(0f, turnSpeed * Time.deltaTime, 0f);
|
||||
}
|
||||
|
||||
void HandleJumping()
|
||||
{
|
||||
// Handle variable force jumping.
|
||||
// Jump starts with initial power on takeoff, and jumps higher / longer
|
||||
// as player holds spacebar. Jump power is increased by a diminishing amout
|
||||
// every frame until it reaches maxJumpSpeed, or player releases the spacebar,
|
||||
// and then changes to the falling state until it gets grounded.
|
||||
if (groundState != GroundState.Falling && Input.GetKey(KeyCode.Space))
|
||||
{
|
||||
if (groundState != GroundState.Jumping)
|
||||
{
|
||||
// Start jump at initial power.
|
||||
groundState = GroundState.Jumping;
|
||||
jumpSpeed = initialJumpSpeed;
|
||||
}
|
||||
else
|
||||
// Jumping has already started...increase power toward maxJumpSpeed over time.
|
||||
jumpSpeed = Mathf.MoveTowards(jumpSpeed, maxJumpSpeed, jumpDelta);
|
||||
|
||||
// If power has reached maxJumpSpeed, change to falling until grounded.
|
||||
// This prevents over-applying jump power while already in the air.
|
||||
if (jumpSpeed == maxJumpSpeed)
|
||||
groundState = GroundState.Falling;
|
||||
}
|
||||
else if (groundState != GroundState.Grounded)
|
||||
{
|
||||
// handles running off a cliff and/or player released Spacebar.
|
||||
groundState = GroundState.Falling;
|
||||
jumpSpeed = Mathf.Min(jumpSpeed, maxJumpSpeed);
|
||||
jumpSpeed += Physics.gravity.y * Time.deltaTime;
|
||||
}
|
||||
else
|
||||
jumpSpeed = Physics.gravity.y * Time.deltaTime;
|
||||
}
|
||||
|
||||
// TODO: Directional input works while airborne...feature?
|
||||
void HandleMove()
|
||||
{
|
||||
// Capture inputs
|
||||
horizontal = Input.GetAxis("Horizontal");
|
||||
vertical = Input.GetAxis("Vertical");
|
||||
|
||||
// Create initial direction vector without jumpSpeed (y-axis).
|
||||
direction = new Vector3(horizontal, 0f, vertical);
|
||||
|
||||
// Clamp so diagonal strafing isn't a speed advantage.
|
||||
direction = Vector3.ClampMagnitude(direction, 1f);
|
||||
|
||||
// Transforms direction from local space to world space.
|
||||
direction = transform.TransformDirection(direction);
|
||||
|
||||
// Multiply for desired ground speed.
|
||||
direction *= moveSpeedMultiplier;
|
||||
|
||||
// Add jumpSpeed to direction as last step.
|
||||
direction.y = jumpSpeed;
|
||||
|
||||
// Finally move the character.
|
||||
characterController.Move(direction * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 479a5196564ede84791870b414a13645
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.MultipleAdditiveScenes
|
||||
{
|
||||
public class PlayerScore : NetworkBehaviour
|
||||
{
|
||||
[SyncVar]
|
||||
public int playerNumber;
|
||||
|
||||
[SyncVar]
|
||||
public int scoreIndex;
|
||||
|
||||
[SyncVar]
|
||||
public int matchIndex;
|
||||
|
||||
[SyncVar]
|
||||
public uint score;
|
||||
|
||||
public int clientMatchIndex = -1;
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (!isServerOnly && !isLocalPlayer && clientMatchIndex < 0)
|
||||
clientMatchIndex = NetworkClient.connection.identity.GetComponent<PlayerScore>().matchIndex;
|
||||
|
||||
if (isLocalPlayer || matchIndex == clientMatchIndex)
|
||||
GUI.Box(new Rect(10f + (scoreIndex * 110), 10f, 100f, 25f), $"P{playerNumber}: {score}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8be750efa9df50f47b65ae156053d149
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror.Examples.MultipleAdditiveScenes
|
||||
{
|
||||
[RequireComponent(typeof(Common.RandomColor))]
|
||||
public class Reward : NetworkBehaviour
|
||||
{
|
||||
public bool available = true;
|
||||
public Common.RandomColor randomColor;
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
|
||||
if (randomColor == null)
|
||||
randomColor = GetComponent<Common.RandomColor>();
|
||||
}
|
||||
|
||||
[ServerCallback]
|
||||
void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (other.gameObject.CompareTag("Player"))
|
||||
ClaimPrize(other.gameObject);
|
||||
}
|
||||
|
||||
[ServerCallback]
|
||||
void ClaimPrize(GameObject player)
|
||||
{
|
||||
if (available)
|
||||
{
|
||||
// This is a fast switch to prevent two players claiming the prize in a bang-bang close contest for it.
|
||||
// First hit turns it off, pending the object being destroyed a few frames later.
|
||||
available = false;
|
||||
|
||||
Color32 color = randomColor.color;
|
||||
|
||||
// calculate the points from the color ... lighter scores higher as the average approaches 255
|
||||
// UnityEngine.Color RGB values are float fractions of 255
|
||||
uint points = (uint)(((color.r) + (color.g) + (color.b)) / 3);
|
||||
|
||||
// award the points via SyncVar on the PlayerController
|
||||
player.GetComponent<PlayerScore>().score += points;
|
||||
|
||||
// spawn a replacement
|
||||
Spawner.SpawnReward(gameObject.scene);
|
||||
|
||||
// destroy this one
|
||||
NetworkServer.Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10da7fdf8caa1eb4697658bf129457fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Mirror.Examples.MultipleAdditiveScenes
|
||||
{
|
||||
internal class Spawner
|
||||
{
|
||||
[ServerCallback]
|
||||
internal static void InitialSpawn(Scene scene)
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
SpawnReward(scene);
|
||||
}
|
||||
|
||||
[ServerCallback]
|
||||
internal static void SpawnReward(Scene scene)
|
||||
{
|
||||
Vector3 spawnPosition = new Vector3(Random.Range(-19, 20), 1, Random.Range(-19, 20));
|
||||
GameObject reward = Object.Instantiate(((MultiSceneNetManager)NetworkManager.singleton).rewardPrefab, spawnPosition, Quaternion.identity);
|
||||
SceneManager.MoveGameObjectToScene(reward, scene);
|
||||
NetworkServer.Spawn(reward);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f522bf510b49da44caa9f3ca0ac17f3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user