2017-06-27 8 views
1

これは以前私がこの種のことをやったことがないので、これは私にとってイライラしています。ユニティ - ネットワーキング。ホストはクライアントの変更を見ることができません

私のゲームは2Dであり、限られた時間内にできるだけ多くのリソースを地図上で収集することになります。世界は、ランダムブロックで地図のすべてのタイルにスプライトをインスタンス化することによって生成され、プレイヤーは自分の道を掘り起こし、彼らが採掘する資源に応じてポイントを取得します。ブロックは、パブリック2D配列にも保存されます。

私はLevelGeneratorというスクリプトがあります。このスクリプトは、プレイヤーがゲームを開始したときに実行されます。スクリプトでは、ブロックがランダムに選択され、次にインスタンス化され、NetworkServer.Spawnによって生成されます。世代全体が[コマンド]

と呼ばれます。levelgeneratorスクリプトを保持するgameオブジェクトには、サーバーのみまたはローカルプレーヤー権限のないネットワークIDがあります。

プレイヤースクリプト内に、プレイヤーからマウスの位置にレイキャストをキャストするコードがありますが、ゲームオブジェクトの破損が壊れています。プレーヤーが左マウスを押してレイキャストがブロックに当たったとき、ヒットしたオブジェクトを破棄し、ブロックの2D配列のブロックのポイント値と同じ量のポイントを追加してから、そのブロックをnullに設定します。ホストがブロックを切断すると、クライアントはそのことを知ることができますが、クライアントがブロックを切断すると、クライアントは明らかにブロックを破損しているように見えますが、ホストはそのクライアントからの変更を見ることができません。これは関連しないかもしれませんが、findobjectoftypeでレベル生成器を直接探していても、ブロックの配列がnullであるというエラーがスローされます。しかし、これはホスト上では起こりません。マップは同じですので、networkserver.spawnは機能していますが、ブロックの同期や壊れているかどうかに問題があります。

ブロックはプレファブであり、ボックスはチェックされておらず、送信されたときから移動していないため、ネットワークの送信レートが0のネットワーク変換もありません。

だから私はこれを理解できません。どんな助けもありがとうございます。ありがとう。

LevelGenerator:

using UnityEngine; 
using System.Collections.Generic; 
using System.Linq; 
using UnityEngine.Networking; 

public class LevelGenerator : NetworkBehaviour 
{ 
    [SerializeField] private List<Block> blocks; // Blocks are pre-defined in the Unity Editor. 
    [SerializeField] private int mapWidth; // How wide the map is. 
    [SerializeField] private int mapHeight; // How deep the map is. 
    public const int blockSize = 1; 
    public Block[,] blockMatrix; // 2D array of all blocks. 
    private List<Block> resourceBlocks = new List<Block>(); 

    private List<GameObject> blockObjects = new List<GameObject>(); 

    void Start() 
    { 
     CmdSpawnMap(); 
    } 

    [Command] 
    public void CmdSpawnMap() 
    { 
     blockMatrix = new Block[mapWidth, mapHeight]; 

     resourceBlocks = blocks.Where(z => z.blockType == "Resource").ToList(); // List of all the blocks that are tagged as resources. 
     resourceBlocks = resourceBlocks.OrderBy(z => z.rarity).ToList(); 

     for (int r = 0; r < resourceBlocks.Count; r++) 
     { 
      resourceBlocks[r].spawnPercentagesAtLevels = new float[mapHeight]; 
      for (int s = 1; s < resourceBlocks[r].spawnPercentagesAtLevels.Length; s++) 
      { 
       resourceBlocks[r].spawnPercentagesAtLevels[s] = resourceBlocks[r].basePercentage * Mathf.Pow(resourceBlocks[r].percentageMultiplier, resourceBlocks[r].spawnPercentagesAtLevels.Length - s); 
      } 
     } 

     // For every block in the map. 
     System.Random random = new System.Random(); 
     for (int y = 0; y < mapHeight; y++) 
     { 
      for (int x = 0; x < mapWidth; x++) 
      { 
       if (y == mapHeight - 1) 
       { 
        Block grass = blocks.Where(z => z.blockName == "Grass").FirstOrDefault(); 
        blockMatrix[x, y] = grass; 
        GameObject blockInstance = Instantiate(grass.blockPrefabs[random.Next(0, grass.blockPrefabs.Count - 1)]); 
        blockInstance.transform.position = new Vector3(x * blockSize, y * blockSize, 0); 
        blockInstance.transform.SetParent(transform, false); 
        blockInstance.name = blockMatrix[x, y].blockName + " => " + x + ", " + y; 
        blockObjects.Add(blockInstance); 
        NetworkServer.Spawn(blockInstance); 
        continue; 
       } 

       bool resourceSpawned = false; 
       for (int i = resourceBlocks.Count - 1; i >= 0; i--) 
       { 
        float roll = Random.Range(0.0f, 100.0f); 
        if (roll <= resourceBlocks[i].spawnPercentagesAtLevels[y]) 
        { 
         blockMatrix[x, y] = resourceBlocks[i]; 
         GameObject blockInstance = Instantiate(resourceBlocks[i].blockPrefabs[random.Next(0, resourceBlocks[i].blockPrefabs.Count - 1)]); 
         blockInstance.transform.position = new Vector3(x * blockSize, y * blockSize, 0); 
         blockInstance.transform.SetParent(transform, false); 
         blockInstance.name = blockMatrix[x, y].blockName + " => " + x + ", " + y; 
         blockObjects.Add(blockInstance); 
         resourceSpawned = true; 
         NetworkServer.Spawn(blockInstance); 
         break; 
        } 
       } 

       if (!resourceSpawned) 
       { 
        Block ground = blocks.Where(z => z.blockName == "Ground").FirstOrDefault(); 
        blockMatrix[x, y] = ground; 
        GameObject blockInstance = Instantiate(ground.blockPrefabs[random.Next(0, ground.blockPrefabs.Count - 1)]); 
        blockInstance.transform.position = new Vector3(x * blockSize, y * blockSize, 0); 
        blockInstance.transform.SetParent(transform, false); 
        blockInstance.name = blockMatrix[x, y].blockName + " => " + x + ", " + y; 
        blockObjects.Add(blockInstance); 
        NetworkServer.Spawn(blockInstance); 
       } 

       resourceSpawned = false; 
      } 
     } 
    } 
} 

LevelGenerator Script

プレーヤー:

using UnityEngine; 
using UnityEngine.Networking; 

[RequireComponent(typeof(Rigidbody2D))] 
public class Player : NetworkBehaviour 
{ 
    private Rigidbody2D rb; 

    [Header("Player Movement Settings")] 
    [SerializeField] private float moveSpeed; 

    private bool facingRight; 
    [HideInInspector] public int points; 

    void Start() 
    { 
     rb = GetComponent<Rigidbody2D>(); 
     facingRight = true; // Start facing right 
    } 

    void FixedUpdate() 
    { 
     float horizontalSpeed = Input.GetAxis("Horizontal") * moveSpeed; 
     float jumpSpeed = 0; 

     if (horizontalSpeed > 0 && !facingRight || horizontalSpeed < 0 && facingRight) // If you were moving left and now have a right velocity 
     { // or were moving right and now have a left velocity, 
      facingRight = !facingRight; // change your direction 
      Vector3 s = transform.localScale; 
      transform.localScale = new Vector3(s.x * -1, s.y, s.z); // Flip the player when the direction has been switched 
     } 

     rb.velocity = new Vector2(horizontalSpeed, jumpSpeed); 
    } 

    void Update() 
    { 
     GameManager gm = FindObjectOfType<GameManager>(); 
     LevelGenerator levelGen = FindObjectOfType<LevelGenerator>(); 
     if (gm.playing) 
     { 
      Camera playerCam = GetComponentInChildren<Camera>(); 
      Vector3 direction = playerCam.ScreenToWorldPoint(Input.mousePosition) - transform.position; 
      direction.z = 0; 
      RaycastHit2D hit = Physics2D.Raycast(transform.position, direction, 1); 
      Debug.DrawRay(transform.position, direction, Color.red); 
      if (hit) 
      { 
       if (Input.GetButtonDown("BreakBlock")) 
       { 
        Destroy(hit.transform.gameObject); 
        points += levelGen.blockMatrix[Mathf.FloorToInt(hit.transform.position.x), Mathf.FloorToInt(hit.transform.position.y)].blockPointsValue; 
        levelGen.blockMatrix[Mathf.FloorToInt(hit.transform.position.x), Mathf.FloorToInt(hit.transform.position.y)] = null; 
       } 
      } 
     } 
    } 
} 
+0

質問にコードを追加することを検討する必要があります。これはあいまいで消化困難です。 – Jerdak

+0

私はコードが追加されている場合は、私に何か他のものがあることをお知らせしました –

答えて

1

はかなり単に、あなたがブロックを破壊するためにサーバーを伝えるために、クライアントからネットワークメッセージを送信することはありません。サーバー上のブロックを破壊すると、自動的にそのブロックをすべて削除するように指示します(ただし、NetworkServer.Destroy()を使用する必要がありますが、Destroy()も同様です)。一方、クライアントにはブロックが壊れていることを他のクライアントに伝える権限がありません。

クライアントからサーバーにメッセージを送信してブロックを破棄すると、サーバーはブロックを破棄する必要があることをすべてのクライアントに伝えることができます。

これは、クライアントからサーバーにCommand callを通して実行できます。ブロックを破壊する代わりに、破壊したいブロックを使ってクライアントからサーバーにメッセージを送信します(ブロックGameObjectはネットワークIDを持ち、引数としてGameObjectを渡すことができます)。

if (hit) 
{ 
    if (Input.GetButtonDown("BreakBlock")) 
    { 
     CmdBreakBlock(hit.transform.gameObject); 
     points += levelGen.blockMatrix[Mathf.FloorToInt(hit.transform.position.x), Mathf.FloorToInt(hit.transform.position.y)].blockPointsValue; 
     levelGen.blockMatrix[Mathf.FloorToInt(hit.transform.position.x), Mathf.FloorToInt(hit.transform.position.y)] = null; 
    } 
} 

次に、(サーバー上で実行される)Cmd関数を追加します。

[Command] 
void CmdBreakBlock(GameObject block) 
{ 
    NetworkServer.Destroy(block); 
} 

すぐに問題が発生するはずです。クライアントがブロックを破りたいときと実際に起こるときとの間に少しの待ち時間があります。サーバーがブロックを破棄してクライアントにメッセージを返す前に、クライアントがブロックに再び当たった場合は、多くの問題が発生します(クライアントは現在nullブロックを破棄する別のメッセージを送信します)。

なぜ、クライアント上のブロックを破棄してサーバーにそのバージョンを破棄するように指示するのはなぜですか?サーバーがそれを破壊してそのクライアントにメッセージを送信すると、クライアントは既に破棄されているものが何であるかを知りません。

高レベルのAPIを使用すると、私が知っているこれまでの清潔な解決策はあまりありません。あなたの最善の策は、実際にサーバー上で破壊されている間に破棄しようとしているクライアント上のオブジェクトを非アクティブにすることですが、これはちょっとしたジャンク解です。

残念ながら、それでもこの問題を解決するには十分ではありません。あなたが言及したように、ブロック行列がクライアントにとってnullであるという問題があります。クライアントのバージョンLevelGeneratorが実行されることはないので意味があります。その理由を説明しましょう。

ネットワークIDがあるが権限がないオブジェクトがある場合、どのクライアントでもCmdコールが機能しなくなることがあります。権限を持たないコマンドはサーバーからのみ送信できるため、サーバー上で実行されている唯一のLevelGeneratorスクリプトです。

これはおそらく、各クライアントとサーバーのすべてのレベルジェネレータが実行されたように、サーバーに地図生成スクリプトを実行するように指示するだけです。あなたのクライアントにはまだ何も起こりません(ただし、GameObjectsがより多くブロックされることはありません)。

  • クライアントにブロック行列を同期するようにしてください:この時点で

    は、私はあなたが3つのオプションを持っていると言うでしょう。このマトリックスをクライアントに同期させる理想的な方法は、自動的に更新する[SyncVar]属性を使用することですが、最後にチェックしたところ、カスタムタイプの多次元配列に問題がありました。現実的には、カスタムOnSerializeOnDeserializeを書く必要があり、それは楽しいものではないと思いますので、私はそれをお勧めしません。

  • レベルを生成するために使用されたシードを同期し、オブジェクトを生成せずにクライアントでレベル生成コードを実行します。これは、ブロックが破棄される前にクライアントが接続してもクライアントとサーバー上のブロックマトリックスを維持する必要があるため、私はそれをお勧めします
  • マトリックスを同期しようとするのではなく、サーバー上のマトリックス現在のところ、クライアントが行列を気にする唯一の時間はポイント値を得るためですが、ブロックが破棄されるたびにClientRPC callを追加することで簡単に変更できます。

私はお勧めします、とあなたは多分このようにそれを試みることができる最後の1:

if (Input.GetButtonDown("BreakBlock")) // Ran on client 
    { 
     CmdBreakBlock(hit.transform.gameObject); 
     hit.transform.gameObject.SetActive(false); // Mimic that it was destroyed on the client so they don't try to mine it again. 
    } 

//... 

[Command] 
void CmdBreakBlock(GameObject hit) // Ran on server, arguments from client 
{ 
    NetworkServer.Destroy(hit); 
    RpcAddPoints(levelGen.blockMatrix[Mathf.FloorToInt(hit.transform.position.x), Mathf.FloorToInt(hit.transform.position.y)].blockPointsValue); 
    levelGen.blockMatrix[Mathf.FloorToInt(hit.transform.position.x), Mathf.FloorToInt(hit.transform.position.y)] = null; 
} 

[ClientRpc] 
void RpcAddPoints(int p) // Ran on client, arguments from server 
{ 
    points += p; 
} 

申し訳ありませんが、私は多くの助けであることができなかった場合、私は低レベルのAPIにより慣れているが、ご質問がありましたら、私はそれらにお答えしようと思います!

+0

うわー、どんな華麗な応答。私は本当にこれに感謝し、それは実際に問題を解決しました!あなたのお手伝いをしてくれてありがとうございました。あなたが言っていたことは理にかなっていて、問題は何かを知っていましたが、それを解決するのに十分な知識がなかったので、ありがとう! –

+0

問題はありません、いつも仲間のトマスを助けてくれるでしょう。 –

+0

これに戻って申し訳ありませんが、私はサーバー上のレベル生成器からどのようにブロックを取り出すことができるのだろうかと思っていましたか? 「outlineTexture」と呼ばれるブロックに属性を追加して、ブロックを見ると強調表示され、そのためにblockMatrixにアクセスする必要があります。どうやってやるの?ありがとう。 –

関連する問題